I have to build a little dropdown system that is triggered with clicks. I've managed how to handle the "click out of the div" question (thanks to this previously posted question).
I can hide the current dropdown clicking on another dropdown's trigger or clicking outside the dropdown itself. So, the problems are these:
- Click the current dropdown's trigger it's useless; the dropdown doesn't go away while the other things are working properly
- Clicking inside the current dropdown (e.g.: the .divider item), causes the dropdown going away
So, here is the html code (say that I have multiple dropdowns in the header)
...
<div id="header" class="navbar">
<div class="line">
<a href="#" class="logo">Site Name</a>
<ul class="nav right">
<li>
<a href="#" class="has-drop">Item 1</span>
<ul class="dropdown-menu">
<li><a href="#">Action A</a></li>
<li><a href="#">Action B</a></li>
<li><a href="#">Action C</a></li>
<li class="divider"></li>
<li><span>Inline note</span></li>
</ul>
</li>
<li>
<a class="has-drop" href="#">Item 2</a>
<ul class="dropdown-menu">
<li><a href="#">Action D</a></li>
<li><a href="#">Action E</a></li>
<li><a href="#">Action F</a></li>
<li class="divider"></li>
<li><a href="#">Action G</a></li>
</ul>
</li>
</ul>
</div>
</div> <!-- end #header -->
...
And this is actually the JS code I'm using, in a dedicated file
$(document).ready(function(){
$("html").on({
click: function(e) {
$(".dropdown-menu.opened").toggleClass("opened");
}
});
$(".nav > li > a").on({
click: function(e){
e.stopPropagation();
$(".dropdown-menu.opened").toggleClass("opened"); // should close each .dropdowns before opening the current one
$(e.target).siblings(".dropdown-menu").toggleClass("opened");
}
});
});
Why do you think the dropdown does not hide when I re-click its trigger? Because inside $(".nav > li > a").on()
doing practically the same thing?
Also, there is a way to prevent the dropdown hide itself when some of it children are clicked? But maybe this is more related to another question, I don't know.
Last but not least: is this the proper way to do the whole thing, in your opinion?
Thank you all in advance
EDIT: Thanks to @Beetroot-Beetroot I managed how to solve the problem, so here is the current working code $(document).ready(function(){
$("html").on("click", function(e) {
$(".dropdown-menu.opened").removeClass("opened");
});
$(".nav").find("li :first-child").on("click", function(e) {
e.preventDefault();
e.stopPropagation();
var $thisMenu = $(this).siblings(".dropdown-menu").toggleClass("opened");
$(".dropdown-menu").not($thisMenu).removeClass("opened");
});
});
The "secret" was the clever use of .not(), which I now understood more.
I just changed the target (that can <a>
or <span>
) into a more generic one. Not sure about the performances in the long term but is certainly a good starting point.
From
$(".nav").find("a.has-drop").on(...
To
$(".nav").find("li :first-child").on(...
Also, here
$(this).closest(".nav").find(".dropdown-menu").not(... I made it more generic like this $(".dropdown-menu").not(... That's because I want to hide all the opened dropdowns. The previous behaviour was considering only one kind of target and only one origin area (e.g.: maybe I need these dropdowns as well in the header and inside some kind of toolbar into the content).
EDIT 2: @Beetroot-Beetroot helped me again. As him suggested, while I was porting the current code in jsFiddle, suddenly the solution came up quite itself. So now the $("html").on("click", ...
part looks like this
$("html").on("click", function(e) {
if ( $(e.target).hasClass("dropdown-menu") || $(e.target).parents(".dropdown-menu").length ) {
// do nothing, basically
} else {
$(".dropdown-menu.opened").removeClass("opened");
}
});
Basically I just watch if the click from $("html")
has something to do width the dropdown. If yes, do nothing. If no, then this does mean that I have clicked anywere but not where the dropdown was.