Hallo,

Ik wil graag een + toevoegen aan elk menu-item waaronder een submenu hangt. Om submenu open of dicht te kunnen klappen.

Dit is globaal de opbouw vh menu:

<ul id="mobile-nav">
	<li>Item 1</li>
	<li class="menu-item-has-children">Item 2</li>
		<ul>
			<li>Subitem 1</li>
			<li>Subitem 2</li>
		</ul>
	<li>Item 3</li>
</ul>


Ik heb deze JS die in de head geladen wordt, maar er gebeurt niets:

var x = document.createElement("button");
x.setAttribute("name", " + ");
document.getElementById("mobile-nav").getElementsByClassName("menu-item-has-children").appendChild(x);



Wat doe ik verkeerd?

Guido
Waarschijnlijk omdat de "head" er eerder is dan het menu zelf. Op het moment dat bovenstaande code loopt is er dus nog geen menu (de HTML parser is pas in de "head"), en gebeurt er ook niks.

Het makkelijkst is om je JS-code gewoon helemaal onderaan te hangen (net voor de </body> tag). als dat niet gaat (je de JS dus alleen in de head toe kunt voegen), dan de volgende event listener er "omheen":

document.addEventListener('DOMContentLoaded', function() {
  //hocus pocus hier
}, false);
De "hocus pocus" wordt dan pas uitgevoerd als het document (en dus ook het menu) helemaal is geladen.
Hoi,

Dank, goed punt.
Het werkt overigens nog niet, als ik "mijn" JS binnen deze functie zet zou het uitgevoerd moeten worden, lijkt me? Of is mijn JS niet juist?

Guido
Wat je nog moet doen is door de lijst van nodes van getElementsByTagName() gaan met een lus. En je moet de <ul> van het submenu insluiten in de <li> tag:
<!DOCTYPE html>
<html>
  <head>
    <script>
      window.addEventListener('DOMContentLoaded', function() {
        let button = document.createElement('button');
        button.innerHTML = '+';
        button.addEventListener('click', function() {
          let ul = this.parentNode.getElementsByTagName('ul')[0];
          ul.style.display = (ul.style.display == '') ? 'none' : '';
        });
        let list = document.getElementById('mobile-nav')
          .getElementsByClassName('menu-item-has-children');
        for (let index = 0; index < list.length; index++) {
          let li = list[index];
          let ul = li.parentNode.getElementsByTagName('ul')[0];
          li.insertBefore(button, ul);
        }
      });
    </script>
  </head>
  <body>
    <ul id="mobile-nav">
      <li>Item 1</li>
      <li class="menu-item-has-children">Item 2
        <ul>
          <li>Subitem 1</li>
          <li>Subitem 2</li>
        </ul>
      </li>
      <li>Item 3</li>
    </ul> 
  </body>
</html>
Insluiten van submenu in de li is een domme fout van me. Had ik moeten weten.
Bedankt voor dit werkende voorbeeld, ik had niet verwacht dat er nog zo veel extra code benodigd is om dit voor elkaar te krijgen.

Guido
Als je liever geen gebruik maakt van JavaScript, dan kan je ook menu's maken met alleen HTML en CSS.
Iemand die daar al jarenlang mee bezig is, heeft allerlei voorbeelden op z'n site die je zo kan overnemen.
Zie http://www.cssplay.co.uk/menus
Hoi,

Ik merk nu wel dat de "toggle" alleen wordt toegevoegd aan 1 item met subpagina's.

Guido

[size=xsmall]Toevoeging op 17/04/2021 11:08:01:[/size]

Ad Fundum op 17/04/2021 10:59:58

Als je liever geen gebruik maakt van JavaScript, dan kan je ook menu's maken met alleen HTML en CSS.


Dank, ga ik ook even naar kijken.. CSS is meer mijn ding ;-)
Hoi opnieuw,

Ik heb na veel werk en met het voorbeeld hierboven een werkend script gemaakt:


<script>
// Verberg submenu
window.addEventListener('DOMContentLoaded', function() {
	var div = document.getElementById("mobile-nav");
	var sub = div.getElementsByClassName("sub-menu");
	for (let i = 0; i < sub.length; ++i) {
		sub[i].style.display = "none";
	}
});

// Voeg klik button toe en toon submenu
window.addEventListener('DOMContentLoaded', function() {
	var x = document.getElementById("mobile-nav").querySelectorAll('.menu-item-has-children > a');
	for( let index = 0; index < x.length; index++ ) {
		var button = document.createElement('button'); button.className = 'subnav-toggle'; button.innerHTML = '+';
		x[index].parentNode.insertBefore(button, x[index].nextSibling);   
		button.addEventListener('click', function() {
			var ul = this.parentNode.getElementsByTagName('ul')[0];
			ul.style.display = (ul.style.display == 'block') ? 'none' : 'block';
		});     
	};
});	
</script>

<div id="mobile-nav">
	<ul>
		<li>Item 1</li>
		<li class="menu-item-has-children"><a>Item 2</a>
			<ul class="sub-menu">
				<li><a>Subitem 1</a></li>
				<li><a>Subitem 2</a></li>
			</ul>
		</li>
		<li><a>Item 3</a></li>
		<li class="menu-item-has-children"><a>Item 4</a>
			<ul class="sub-menu">
				<li><a>Subitem 3</a></li>
				<li><a>Subitem 4</a></li>
			</ul>
		</li>
	</ul>
</div>


Zou je die eens kunnen nakijken op fouten en verbeteringen, want ben nog een echte leek ;-)

Guido
Als het werkt is het goed toch?

Er zijn een paar dingetjes:
- JavaScript kan met CSS werken, en die eerste functie kan in 1 regel CSS
- Verder gebruik ik bij voorkeur let boven var, vanwege de variabele scope
- Als laatste zou ik de namen van variabelen zinvolle namen geven, dus liever "list" in plaats van "x"

<style>
  .menu-item-has-children > .sub-menu {display: none;}  /* Verberg submenu */
</style>
Hoi,

Bedankt voor je reactie.

1) Ik weet dat ik het kan verbergen met CSS (ga ik ook doen, als fall-back), maar ik wou alles ook via JS regelen. Of is dat geen best-practice?

2) Ik begreep dat let alleen binnen de betreffende loop werkt. In mijn 2 functies wordt de var/let niet opnieuw aangeroepen buiten de loop, dus betekent dit dat ik alle var door let kan vervangen?

Guido

Reageren