I have built a "mega menu" that is working, but I would appreciate some advice on how my javascript can be improved.
Step 1 is looping through the top level menu items and applying the correct megamenu class names where necessary:
const megamenu = document.getElementById('megamenu');
let megamenuChildren = megamenu.children;
let megamenuChildrenArray = Array.from(megamenuChildren);
for (let i = 0; i < megamenuChildrenArray.length; i++) {
let sub = megamenuChildrenArray[i].children;
// if the nav li contains x1 children it is a regular link
// if the nav li contains x2 children it is a megamenu item and we add the classes
if (sub.length === 2) {
let subArray = Array.from(sub);
subArray[0].classList.add('megamenu-trigger-' + i);
subArray[1].classList.add('megamenu-sub-' + i);
}
}
Step 2 Earlier in my development I learned that we shouldn't add eventListener inside a loop! So instead I am adding an eventListener to the document to listen for all clicks.
There are only ever a maximum of x5 li's in the top level menu, so I am checking each one for a click and swapping the class 'dn'
(display: none;) for 'db'
(display: block;). I am also adding the class 'megamenu-sub-triggered'
:
document.addEventListener('click', function(event) {
if (event.target.matches('.megamenu-trigger-0')) {
console.log('trigger 0 clicked!');
event.preventDefault();
let subZero = document.getElementsByClassName('megamenu-sub-0')[0];
subZero.classList.remove('dn');
subZero.classList.add('db', 'megamenu-sub-triggered');
}
if (event.target.matches('.megamenu-trigger-1')) {
console.log('trigger 1 clicked!');
event.preventDefault();
let subOne = document.getElementsByClassName('megamenu-sub-1')[0];
subOne.classList.remove('dn');
subOne.classList.add('db', 'megamenu-sub-triggered');
}
if (event.target.matches('.megamenu-trigger-2')) {
console.log('trigger 2 clicked!');
event.preventDefault();
let subTwo = document.getElementsByClassName('megamenu-sub-2')[0];
subTwo.classList.remove('dn');
subTwo.classList.add('db', 'megamenu-sub-triggered');
}
if (event.target.matches('.megamenu-trigger-3')) {
console.log('trigger 3 clicked!');
event.preventDefault();
let subThree = document.getElementsByClassName('megamenu-sub-3')[0];
subThree.classList.remove('dn');
subThree.classList.add('db', 'megamenu-sub-triggered');
}
if (event.target.matches('.megamenu-trigger-4')) {
console.log('trigger 4 clicked!');
event.preventDefault();
let subFour = document.getElementsByClassName('megamenu-sub-4')[0];
subFour.classList.remove('dn');
subFour.classList.add('db', 'megamenu-sub-triggered');
}
}
Step 3 Finally, inside the same document.addEventListener above, I am listening for clicks outside of the '.megamenu-sub-triggered'
element, and checking each possible megamenu to see (a) if it exists, and (b) if it contains the triggered class.
If conditions (a) and (b) are met, I am putting the classes back to their original state:
if (document.getElementsByClassName('megamenu-sub-triggered')) {
if (!event.target.closest('.megamenu-sub-triggered')) {
var subZero = document.getElementsByClassName('megamenu-sub-0')[0];
if (subZero && subZero.matches('.megamenu-sub-0.megamenu-sub-triggered')) {
subZero.classList.remove('db', 'megamenu-sub-triggered');
subZero.classList.add('dn');
}
var subOne = document.getElementsByClassName('megamenu-sub-1')[0];
if (subOne && subOne.matches('.megamenu-sub-1.megamenu-sub-triggered')) {
subOne.classList.remove('db', 'megamenu-sub-triggered');
subOne.classList.add('dn');
}
var subTwo = document.getElementsByClassName('megamenu-sub-2')[0];
if (subTwo && subTwo.matches('.megamenu-sub-2.megamenu-sub-triggered')) {
subTwo.classList.remove('db', 'megamenu-sub-triggered');
subTwo.classList.add('dn');
}
var subThree = document.getElementsByClassName('megamenu-sub-3')[0];
if (subThree && subThree.matches('.megamenu-sub-3.megamenu-sub-triggered')) {
subThree.classList.remove('db', 'megamenu-sub-triggered');
subThree.classList.add('dn');
}
var subFour = document.getElementsByClassName('megamenu-sub-4')[0];
if (subFour && subFour.matches('.megamenu-sub-4.megamenu-sub-triggered')) {
subFour.classList.remove('db', 'megamenu-sub-triggered');
subFour.classList.add('dn');
}
}
}
Any help improving this code, and my own javascript knowledge, is very much appreciated!
Many thanks