11218 sujets

JavaScript, DOM et API Web HTML5

Pages :
Salut

J'aimerais que mon sous-menu qui se déroule par un :hover se referme lors du clic sur un des items ; et ce, surtout sur mobile... (Sur desktop il suffit de sortir le pointeur du menu mais sur mobile il y a besoin de cliquer en dehors.)

Structure du sous-menu déroulant :
<div class="rubrique visible">
	<a href="#nogo">
	Informations
	</a>
	<div class="sousmenu sousmenuvisible">
		<div class="pagesousmenu">
			<a href="#infos1" 
			onclick="montrer('infos1');fermermenu();">
			infos1
			</a>
		</div>
		<div class="pagesousmenu">
			<a href="#infos2" 
			onclick="montrer('infos2');fermermenu();">
			infos2
			</a>
		</div>
		<div class="pagesousmenu">
			<a href="#infos3" 
			onclick="montrer('infos3');fermermenu();">
			infos3
			</a>
		</div>
		<div class="pagesousmenu">
			<a href="#infos4" 
			onclick="montrer('infos4');fermermenu();">
			infos4
			</a>
		</div>
	</div>
</div>

CSS (je n'ai pas tout mis) :
	.rubrique:hover .sousmenuvisible { 
		opacity: 1; 
		visibility: visible; 
		top: 46px; 
		transition: all 0.25s ease;
		}
	.rubrique_unhover { pointer-events: none; }

Pour le javascript "fermermenu()" j'ai essayé :
	const y = document.querySelectorAll("div.sousmenu");
	y[0].classList.remove("sousmenuvisible");
	setTimeout(() => { y[0].classList.add("sousmenuvisible"); }, 300);

ou :
	const z = document.querySelectorAll("div.rubrique");
	z[0].classList.add("rubrique_unhover");
	setTimeout(() => { z[0].classList.remove("rubrique_unhover"); }, 300);

ainsi que les deux mélangés.

Les deux méthodes marchent sur desktop, mais sur mobile ça ne veut pas ! On voit le sous-menu disparaitre, mais il réapparait dès que "pointer-events: none;" est annulé ou ".sousmenuvisible" revient (l'élément sélectionné ne semble pas s'annuler entre deux).
La réversion de ces états étant nécessaire pour le menu soit à nouveau opérationnel.

Comment puis-je faire pour qu'un javascript fasse que le dernier item touché/sélectionné soit oublié ?

Merci d'avance
Modifié par kerlutinoec (04 Jan 2022 - 11:26)
Bonjour,

j'ai été confronté au même problème, celui du menu qui ne se referme pas quand on clique sur un des items sous-menus, sur les tactiles. J'avais posé la question ici et focus-within était la solution proposée. Malgré plusieurs essais, je n'ai pas réussi. J'ai galéré sur ce problème et finalement opté pour un menu commandé par la pseudo-class:target. Je n'ai plus aucun problème depuis sur les mobiles. Et ça fonctionne sans JavaScript. À défaut de pouvoir régler le problème sur ton code, je te propose de voir cette solution.
Si tu fait une recherche "menu avec la pseudo-class:target", tu vas trouver des exemples.
Modérateur
Salut,

Avoir la totalité de ton code (dans un codenpen par exemple) aurait pu nous permettre de tester ça plus précisément mais je lance une piste ou deux au cas ou :
Pourquoi ne pas faire sauter le focus (j'imagine que sur mobile c'est ce qui bloque) en passant par activeElement ? ou sinon donner le focus à un autre élément (neutre) ?

Bonne soirée
_laurent a écrit :
Salut,

Avoir la totalité de ton code (dans un codenpen par exemple) aurait pu nous permettre de tester ça plus précisément mais je lance une piste ou deux au cas ou :
Pourquoi ne pas faire sauter le focus (j'imagine que sur mobile c'est ce qui bloque) en passant par activeElement ? ou sinon donner le focus à un autre élément (neutre) ?

Bonne soirée


J'aime bien cette idée. C'est à peu près ce que je cherchais mais ne connaissais pas, ne pratiquant que très peu le javascript (c'est pour un site amateur).
J'avais essayé .blur() mais n'avait pas réussi. Je vais tenté de mettre le .focus() sur l'arrière de la page.
kerlutinoec a écrit :


J'aime bien cette idée. C'est à peu près ce que je cherchais mais ne connaissais pas, ne pratiquant que très peu le javascript (c'est pour un site amateur).
J'avais essayé .blur() mais n'avait pas réussi. Je vais tenté de mettre le .focus() sur l'arrière de la page.


En lisant ça :
https://www.w3schools.com/jsref/met_html_focus.asp
, je me demande si on mettre le focus sur autre chose qu'un <a> ou un champs de saisie texte ? (par exemple body)
Modérateur
Ouais non ca a pas l'air concluant... en full CSS aucune idée en tout cas... apres, quitte a avoir du Js, autant tout faire en Js non ? Ca sera plus simple a gérer
_laurent a écrit :
Ouais non ca a pas l'air concluant... en full CSS aucune idée en tout cas... apres, quitte a avoir du Js, autant tout faire en Js non ? Ca sera plus simple a gérer


Par défaut je veux que ça marche sans JS et je souhaite garder le :hover sur desktop. Si je ne résoud pas ce pb de fermeture automatique sur mobile c'est pas grave. Mais ça serait plus élégant.
"Ouais non" ça veut dire que .focus() peut marcher sur body ou pas ?
Bon bein ça veut pas !
J'ai essayé :
	const y = document.querySelectorAll("div.sousmenu");
	y[0].classList.remove("sousmenuvisible");
	document.body.focus();
	setTimeout(() => { y[0].classList.add("sousmenuvisible"); }, 100);

et ça ne marche pas...
Sur mobile, le menu déroulant disparait pendant 100ms puis réapparait (alors que ça marche sur desktop).
Modérateur
Bonjour,

comme @_laurent te la suggérer , ce serait bien d'avoir ton code du menu dans un exemple fonctionnel dans son intégrabilité ... dans un codepen , c'est pratique et ça permet d'être raccord avec les défauts que tu as. (ton extrait de js montre une class enlevée puis remise 0.1 s plus tard , du moins c'est ce que je vois hors contexte)

Pour donner le focus à un élément, il faut qu'il soit focusable, les liens et éléments de formulaire interactif le sont, body ne l'est pas par défaut. Pour ajouter un élément à la liste des éléments focusable de ta page, tu peut leur attribuer l'attribut tabindex avec une valeur (sans valeur = aucun effet) à 0 par exemple.

Les menus CSS pour mobile, (classiques 'hamburger') s’appuient souvent sur un label et une checkbox pour passer d'un état à l'autre et appliquer une règle différente selon, sans avoir recours à JavaScript. As tu volontairement exclus cette option?

Cdt
gcyrillus a écrit :
Bonjour,

comme @_laurent te la suggérer , ce serait bien d'avoir ton code du menu dans un exemple fonctionnel dans son intégrabilité ... dans un codepen , c'est pratique et ça permet d'être raccord avec les défauts que tu as. (ton extrait de js montre une class enlevée puis remise 0.1 s plus tard , du moins c'est ce que je vois hors contexte)

Pour donner le focus à un élément, il faut qu'il soit focusable, les liens et éléments de formulaire interactif le sont, body ne l'est pas par défaut. Pour ajouter un élément à la liste des éléments focusable de ta page, tu peut leur attribuer l'attribut tabindex avec une valeur (sans valeur = aucun effet) à 0 par exemple.

Les menus CSS pour mobile, (classiques 'hamburger') s’appuient souvent sur un label et une checkbox pour passer d'un état à l'autre et appliquer une règle différente selon, sans avoir recours à JavaScript. As tu volontairement exclus cette option?

Cdt


Je vous fais un codepen asap.
La class enlevée est celle qui s'applique seulement au :hover. Je l'enlève temporairement pour passer le focus en dessous sur desktop.
Je vais tenter de mettre un tabindex sur body (si on a le droit).
Ce n'est pas mon menu hamburger dont je parle ici mais une version bandeau qui est utiliser aussi sur tablette (ou smarphone en mode paysage) ; et donc c'est le même que sur desktop.
Bonjour,
@_Laurent
sans doutes, j'aurais pu donner mon code, mais comme à mon habitude, j'ai réalisé ce menu en collant du code venant d'ailleurs et en l'adaptant. Ça fonctionne parfaitement, mais il n'est pas certain que l'agencement fait par mes soins soit au top; sans doute il y a à épurer le code. C'est la raison pour laquelle je ne l'ai pas donné. Et j'ai perdu le mot de pass de mon login sur codepen et ils ne me le renvoient pas. Voici le code :
<div class="monmenu">
                    <div class="smart-popin" id="popin2">
    <div class="sp-table">
        <div class="sp-cell">
            <div class="sp-body1">
            <ul>  					
			<li> <a href="#popin1">Biographie</a>
			<li> <a href="#goni">N'goni</a>
			<li> <a href="#seve">Sève montante</a>
			<li> <a href="#3/4">Trois temps quatre</a>
      			<li> <a href="#bala">Bala</a>
      			<li> <a href="#valse">Valse d'automne</a>			
      			<li> <a href="#foot">Contacts, copyright, mentions</a>
      			<li> <a href="https://musiquenvrac.fr/Blog">Blog musical</a>
			<li> <a href="#!">X</a>
</li>
	</ul>
            </div>
            <a href="#" class="sp-back" title="Revenir"></a>
 </div>
    </div>
       </div>
           </div> 

.smart-popin1 {
    position:fixed;
    left:0; right:0;
    top:0; bottom:0;
    overflow:auto; 
    opacity:0;
    z-index:100;
    visibility:hidden;
    transition: all 0.8s ease;}
.smart-popin:target {
    opacity:1;
    visibility:visible;
    transition:all 1s;}
.smart-popin .sp-table {
    display:table;
    height:100%;
    width:100%;}
.smart-popin .sp-cell {
    display:table-cell;
    vertical-align:middle;}
.smart-popin .sp-body   {
    position:relative; 
    width:86%;  
    margin:0 auto;
    padding:1em;
    text-align:center;
    font-size:1em;
    background-image:linear-gradient(lightblue, lightgray, lightblue, lightgray, rgba(255, 255, 140, 1));
    box-shadow:18px 18px 18px 16px rgba(0,0,0,0.55);
    z-index:9800;}
    	
#menubas {
	display:flex;
	justify-content:space-evenly;
	align-items: center;
	position:fixed;
	width:98%;
	height:3.4em;	
	bottom:0;
	padding-top:8px;
	background:white;
	transition: bottom 0.4s;
  	transition-delay: 0.2s;
	z-index:9000;}
#menubas.hidden {
  bottom: -80px;}
.monmenu {
	display:inline-block;
	position:fixed;
	top:5em;
	width:1em;
	height:1.1em;	  
    	font-size:1em;
	padding:0;}
li { 
    	display:flex;
    	justify-content: center;
    	position:relative;
    	width:14em;
    	height:2.4em;
    	top:0;
    	padding:6px;    
    	margin:auto;
    	border:solid 2px black;
    	border-radius:5px;
    	background:gray;}


Il y a aussi un bout de JavaScript, mais il n'est pas là pour le fonctionnement du menu à proprement parler. Il est là pour faire aparaître/disparaître le menu au scroll de la page, menu que j'ai placé en bas d'écran. Ce code JavaScript va avec la partie #menubas.
<script>
    let header = document.querySelector("#menubas");
let lastScrollValue = 0;

document.addEventListener('scroll',() => {
		let top  = document.documentElement.scrollTop;
    if(lastScrollValue < top) {
    	header.classList.add("hidden");
    } else {
    	header.classList.remove("hidden");
    }
    lastScrollValue = top;
}); 
</script> 

Modifié par Bongota (05 Jan 2022 - 12:45)
Bon je viens d'essayer en ajoutant
<body tabindex="0">
et ça ne donne rien de plus !
Si tu veux te faire une idée du fonctionnement de mon menu, vas voir directement, sur un mobile. En desktop, je le cache pour un autre menu, différent.
https://musiquenvrac.fr
C'est sûr qu'un menu en bas d'écran, ça surprend, mais c'est une tendance. Rien ne t'empêche de le mettre en haut de page. Il suffit d'enlever bottom:0; et de mettre top:0 à la place, dans #menubas. Tu peux même te payer le luxe de le faire aussi disparaître au scroll en changeant
#menubas.hidden {
  top: -80px;}
Modérateur
Salut Bongota,

Bongota a écrit :
Bonjour,
@_Laurent
sans doutes, j'aurais pu donner mon code, mais comme à mon habitude, j'ai réalisé ce menu en collant du code venant d'ailleurs et en l'adaptant. Ça fonctionne parfaitement, mais il n'est pas certain que l'agencement fait par mes soins soit au top; sans doute il y a à épurer le code. C'est la raison pour laquelle je ne l'ai pas donné. Et j'ai perdu le mot de pass de mon login sur codepen et ils ne me le renvoient pas. Voici le code :


Bien que j'ai répondu juste après toi, c'est à kerlutinoec que je m'adressais... c'est à son soucis et donc a son code que je m'intéressais Smiley lol
J'ai essayé autrement :
.rubrique_unhover { display: none; }

const z = document.querySelectorAll("div.rubrique");
z[0].classList.add("rubrique_unhover");
setTimeout(() => { z[0].classList.remove("rubrique_unhover"); }, 20);


Cette méthode marche pour mon menu hamburger déclenché par un onclick : le sous menu "rubrique" a bien disparu au clic suivant sur le menu... mais là ça ne marche pas !!!
Le :hover émulé sur tactile se comporte vraiment différemment d'un simple click : on ne peut pas enlever ce :hover ou focus !
Ca m'agace !

PS : est-ce que le
document.body.addEventListener('touchstart', function(){}, false, {passive: true});

mis au début de mon javascript, pour gérer le :hover sur tactile, pourrait jouer là dessus ?
Modérateur
Bonsoir,

Merci pour le codepen, cela parait plus clair .

pour reprendre l'idée d'une checkbox parfois utilisée en se servant de classList.toggle() dans ton code, on peut faire quelque chose comme :
window.onload = fermermenu();
window.onresize = fermermenu();

function toggleMenu() {
  const y = document.querySelectorAll("div.sousmenu");
  y[0].classList.toggle("sousmenuvisible");
}

function fermermenu() {
  if (window.matchMedia("(max-width: 700px)").matches) {
    toggleMenu();
  } else {
    /* besoin de rien en principe desktop   ... */
    console.log("click");
  }
}


et en ajoutant la fonction fermermenu() aussi sur le lien informations. <a href="#nogo" onclick="fermermenu();">
Informations
</a>


https://codepen.io/gc-nomade/pen/xxXzPaV

Les pointer-events n'ont pas l'air d'avoir d'utilité ici.

C'est une autre piste Smiley cligne

Cdt
Meilleure solution
gcyrillus a écrit :
Bonsoir,

Merci pour le codepen, cela parait plus clair .

pour reprendre l'idée d'une checkbox parfois utilisée en se servant de classList.toggle() dans ton code, on peut faire quelque chose comme :
window.onload = fermermenu();
window.onresize = fermermenu();

function toggleMenu() {
  const y = document.querySelectorAll("div.sousmenu");
  y[0].classList.toggle("sousmenuvisible");
}

function fermermenu() {
  if (window.matchMedia("(max-width: 700px)").matches) {
    toggleMenu();
  } else {
    /* besoin de rien en principe desktop   ... */
    console.log("click");
  }
}


et en ajoutant la fonction fermermenu() aussi sur le lien informations. &lt;a href="#nogo" onclick="fermermenu();"&gt;
Informations
&lt;/a&gt;


https://codepen.io/gc-nomade/pen/xxXzPaV

Les pointer-events n'ont pas l'air d'avoir d'utilité ici.

C'est une autre piste Smiley cligne

Cdt


Merci pour ta suggestion.
Je vais étudier de plus près ce "window.matchMedia" que je ne connaissais pas et qui devrait me permettre de séparer le fonctionnement tablette de desktop (768<w<1366 selon mon choix)...
kerlutinoec a écrit :


Merci pour ta suggestion.
Je vais étudier de plus près ce "window.matchMedia" que je ne connaissais pas et qui devrait me permettre de séparer le fonctionnement tablette de desktop (768&lt;w&lt;1366 selon mon choix)...


Petite question :
Si le toggleMenu se déclenche sur tactile, la fonction :hover n'aura pas lieu ???
(Ou faut-il que je la désactive entre 768 et 1366px ?)
Modérateur
kerlutinoec a écrit :


Petite question :
Si le toggleMenu se déclenche sur tactile, la fonction :hover n'aura pas lieu ???
(Ou faut-il que je la désactive entre 768 et 1366px ?)


C'est selon ton choix et les test que tu auras fait pour déterminer si il est judicieux de garder les deux ou pas, ... ma fille à un vieux sony de 21pouces tout en un et tactile (pas tester ton code avec cependant) , qu'elle utilise la plupart du temps au clavier et souris mais en se servant parfois du tactile . je dirais autant garder les deux et écouter les retours utilisateurs si tu peut en avoir pour determiner si le onclick est suffisant.

Pour le toggle(), cela me rappelle ce post Smiley cligne https://forum.alsacreations.com/topic-4-88922-1-Resolu-Menu-responsive-mobile-ne-reaparait-pas-en-mode-paysage.html
Modifié par gcyrillus (06 Jan 2022 - 15:00)