I'm trying to understand the logic happening in my basic multilevel menu click event. I understood what happening on clicking on "About" menu in the navigation. And it works as per my expecation of code. But when i click on "Profile" menu (Submenu of "About" menu), JS makes it's sublevel menu "display:none". I tried to think in the aspect of even bubbling. But eventhough the bubbling happens here, it should not be working like this. Actually for me, its really complicated to understand how JS works here. It would be a Great Help if anyone can explain with a simple and understandable way. Thank You Very Much in Advance!!!
let menus = document.querySelectorAll(".main-navigation ul li a");
menus.forEach((item) => {
if (item.parentElement.querySelector("ul")) {
item.parentElement.classList.add("has-submenu");
}
});
let submenu = document.querySelectorAll(".has-submenu");
submenu.forEach((item) => {
item.addEventListener("click", (e) => {
e.preventDefault();
let ul = e.target.parentElement.querySelector("ul");
let cs = window.getComputedStyle(ul).display;
if (cs === "none") {
ul.style.cssText = "display:block";
}
else {
ul.style.cssText = "display:none";
}
});
});
.main-navigation ul {list-style:none;margin:0;padding:0;font-family:arial;}
.main-navigation ul li {padding:.35rem;background:#f9f9f9;}
.main-navigation ul li ul {padding-left:1rem;display:none;}
.main-navigation ul li a {display:block;text-decoration:none;}
<div class="main-navigation">
<ul>
<li><a href="">Home</a></li>
<li><a href="">About </a>
<ul>
<li><a href="">Profile </a>
<ul>
<li><a href="">History</a></li>
<li><a href="">Management</a></li>
</ul>
</li>
<li><a href="">Vision</a></li>
<li><a href="">Mission</a></li>
</ul>
</li>
<li><a href="">Services </a>
<ul>
<li><a href="">Web Design</a></li>
<li><a href="">Web Development</a></li>
</ul>
</li>
<li><a href="">Contact</a></li>
</ul>
</div>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
Solution
If you add a console.log
inside your click
handler you will notice that the event for the nested item is called twice.
You probably knew that it could happen and you used preventDefault
.
However, preventDefault
is for the browser's default effects (for example, it prevents your page to refresh as you put an href
attribute) but in your case the double behaviour is from your own custom listener.
This means, you need to add stopPropagation
that prevents further propagation of the current event in the capturing and bubbling phases.
Working Demo
let menus = document.querySelectorAll(".main-navigation ul li a");
menus.forEach((item) => {
if (item.parentElement.querySelector("ul")) {
item.parentElement.classList.add("has-submenu");
}
});
let submenu = document.querySelectorAll(".has-submenu");
submenu.forEach((item) => {
item.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
let ul = e.target.parentElement.querySelector("ul");
let cs = window.getComputedStyle(ul).display;
if (cs === "none") {
ul.style.cssText = "display:block";
} else {
ul.style.cssText = "display:none";
}
});
});
.main-navigation ul {
list-style: none;
margin: 0;
padding: 0;
font-family: arial;
}
.main-navigation ul li {
padding: .35rem;
background: #f9f9f9;
}
.main-navigation ul li ul {
padding-left: 1rem;
display: none;
}
.main-navigation ul li a {
display: block;
text-decoration: none;
}
<div class="main-navigation">
<ul>
<li><a href="">Home</a></li>
<li><a href="">About </a>
<ul>
<li><a href="">Profile </a>
<ul>
<li><a href="">History</a></li>
<li><a href="">Management</a></li>
</ul>
</li>
<li><a href="">Vision</a></li>
<li><a href="">Mission</a></li>
</ul>
</li>
<li><a href="">Services </a>
<ul>
<li><a href="">Web Design</a></li>
<li><a href="">Web Development</a></li>
</ul>
</li>
<li><a href="">Contact</a></li>
</ul>
</div>
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>