Close dropdown menu when clicking outside of it - javascript

I've got a problem with the navigation dropdown on my site that I've almost solved but can't quite fix. I'm worried I may have just made a mess out of my code.
When I introduced a "scroll to anchor tags" function with a preventDefault event, it broke my nav menu. The menu wouldn't close unless you clicked on the menu button again. I've finally got it to close after you click one of the links, but that's now the only way to close it. How do I have it close when clicking on the menu button or anywhere else on the site? I'm sure that bit of jQuery is the culprit, but don't know how to fix it or work around it.
HTML for the menu:
<div class="main-nav navbtn">
<div class="dropdown"><i onclick="myFunction()" class="dropbtn material-icons md-48"></i>
<div id="myDropdown" class="dropdown-content">
Home
About
Services
Work
Testimonials
Contact
Blog
</div>
</div>
</div>
And the related jQuery:
// When the user clicks on the button, toggle between hiding and showing the dropdown content
function myFunction() {
document.getElementById("myDropdown").classList.toggle("show");
}
//// Close the dropdown menu if the user clicks outside of it
window.onclick = function (event) {
if (!event.target.matches('.dropbtn')) {
dropdowns.forEach(function (openDropdown) {
dropdown.classList.contains('show') && dropdown.classList.remove('show');
});
}
};
////This is the section I made for it to close after clicking a link
jQuery(document).ready(function ($) {
$('.dropbtn').on('click', function () {
$(".dropdown-content").show();
});
$('.navlink').on('click', function () {
$(".dropdown-content").hide();
});
});
This is the potential problem that's screwing the other functions up.
//Scroll to anchor tags
$(document).on('click', 'a:not(.external)', function (event) {
event.preventDefault();
$('html, body').animate({
scrollTop: $($.attr(this, 'href')).offset().top
}, 500);
});
var $root = $('html, body');
$('a').click(function () {
$root.animate({
scrollTop: $($.attr(this, 'href')).offset().top
}, 500);
return false;
});
What in the world should I do to fix my menu?
You can take a look at the work-in-progress site at http://idpdx.kreigd.com
Update: I think I've got a lead on where things are getting confused. The function I'm using to add the dropdown functionality requires that the class "show" be added once the .dropbtn element is clicked, and removed when it is clicked again.
So what I really need to do is rework the code so that clicking .dropbtn opens the dropdown, and clicking on anything else, including the nav buttons and the .dropbtn element, will close it.
Update 2: Trying a different method. Ignore that first update.

Try this & let me know
$(document).click(function() {
$(".dropdown-content").hide();
// $(".dropdown-content").removeClass("show");
});
$(".dropdown-content").click(function(e) {
e.stopPropagation(); // if you click on the div itself it will cancel the click event.
});

you can try with something like this
$('body').not("#myDropdown").off('click').on('click',function(){$("#myDropdown").removeClass("show")});
I tried the code in your website but you have written some code on window.click which is causing issue.

#Nikhil's answer got me further but has it's downsides. I came up with the following solution:
$(document).click(function (e) {
var target = $(e.target);
// check the actual element being clicked is not the dropdown trigger itself
if (!target.hasClass('dropdown-trigger') && !target.closest('.dropdown-trigger').length) {
// use the framework to close the dropdown instead of just hiding it: hiding it will require you to click the trigger 2 times to reopen!
$('dropdown-trigger').dropdown('close');
}
});

Related

How to make function work only for one button? Bootstrap-5 scroll to modals bottom

What I want to achieve is to create two buttons:
1: that opens modal window
2: opens same modal and scrolls to bottom
code:
$('#menu-item-6706').on('click', function (){
$('#exampleModal').on('shown.bs.modal', function (event) {
$("#exampleModal .modal-body").animate({ scrollTop: $('#exampleModal .modal-body').prop("scrollHeight")}, 'slow');
});
});
Here is full jsfiddle snippet: https://jsfiddle.net/mkx2auhj/1/
Script is almost working:
If you trigger for the the first time “launch demo modal” it opens modal.
If you trigger “Contact” it opens modal and scrolls to bottom. So it’s correct.
The problem is if you hit “launch demo modal” again it scrolls to bottom again, which is not desirable. I want it to open standard modal like the first time.
Have a bunch of buttons that all trigger the same modal with slightly
different contents? Use event.relatedTarget and HTML data-bs-*
attributes to vary the contents of the modal depending on which button
was clicked. https://getbootstrap.com/docs/5.1/components/modal/#varying-modal-content
Move the 'shown.bs.modal' event listener outside the click event and use event.relatedTarget to detect when the "Contact" button toggles the modal and then animate the modal accordingly. Try this
$('#exampleModal').on('shown.bs.modal', function (event) {
if( $(event.relatedTarget).is('#menu-item-6706') ){
$("#exampleModal .modal-body").animate({
scrollTop: $('#exampleModal .modal-body').prop("scrollHeight")
}, 'slow');
}
});

CSS transitions playing too many times; Added via jQuery

So I have 2 divs, one of them being hidden on load using jQuery .hide()
One div is id is menus and the other is prix-fix-menu
I want to fade and slide between the 2 with button clicks. Its a restaurant menu, so I want to smoothly scroll to the different sections of the menu (such as apps, entrees, dessert etc.) but then if the user clicks the Prix Fix button, the page will fade and slide out the regular menu and fade and slide in the Prix Fix menu. If they have the Prix Fix menu open, I want any of the links OTHER THAN the Prix Fix link, to take the user back to the main menu page, sliding out the Prix Fix and sliding in the regular menu.
To accomplish this, Im just dynamically adding and removing classes from Animate.css. The problem is that the animations keep playing more than once (even though Im using .one() method) . I believe it may be because Im listening to the animationend event to then play another animation causing a loop. But whats weird is that it plays it exactly 4 times, not infinitely.
Here's the javascript:
$('#prix-fix-menu, #catering-menu').hide();
$("a[href='#prix-fix']").on('click', function () {
$('#menus').removeClass().addClass('animated fadeOutLeft');
$('#menus').one('animationend webkitAnimationEnd', function () {
$("#menus").hide();
$('#prix-fix-menu').show().removeClass().addClass('animated fadeInRight');
});
});
$("a:not(a[href='#prix-fix'])").on('click', function () {
$('#prix-fix-menu').removeClass().addClass('animated fadeOutRight');
$('#prix-fix-menu').one('animationend webkitAnimationEnd', function () {
$("#prix-fix-menu").hide();
$('#menus').show().removeClass().addClass('animated fadeInLeft');
});
});
I also have a smooth scroll script (positioned after the previous script in at the bottom of the tag) working with the anchor links as well. Putting this here just in-case this is whats causing the problem even though I dont think so:
$('a[href^="#"]').on('click',function (e) {
e.preventDefault();
var target = this.hash;
var $target = $(target);
$('html, body').stop().animate({
'scrollTop': $target.offset().top
}, 900, 'swing', function () {
window.location.hash = target;
});
});
If more information like CSS or HTML are needed then let me know and Ill added but there's quite a lot of markup for the menu because of all the items.
Link to the live project: Project
Thanks in advance!
Following this CODE1
CODE1
$('#prix-fix-menu, #catering-menu').hide();
$("a[href='#prix-fix']").on('click', function () {
//add this line
$('#prix-fix-menu').unbind('animationend webkitAnimationEnd');
$('#menus').removeClass().addClass('animated fadeOutLeft');
$('#menus').one('animationend webkitAnimationEnd', function () {
$("#menus").hide();
$('#prix-fix-menu').show().removeClass().addClass('animated fadeInRight');
});
});
$("a:not(a[href='#prix-fix'])").on('click', function () {
//add this line
$('#menus').unbind('animationend webkitAnimationEnd');
$('#prix-fix-menu').removeClass().addClass('animated fadeOutRight');
$('#prix-fix-menu').one('animationend webkitAnimationEnd', function () {
$("#prix-fix-menu").hide();
$('#menus').show().removeClass().addClass('animated fadeInLeft');
});
});
your code may be caused multiple binding animationend webkitAnimationEnd, so animationend webkitAnimationEnd unbind before start animation.
WHY
Problem is animationend webkitAnimationEnd. animationend and webkitAnimationEnd is different event.
In your case, you tried to add two event at the same time. so another event leaves after trigger animation end. You can easily understand about this situation. Try following CODE2.
CODE2
$('#prix-fix-menu, #catering-menu').hide();
$("a[href='#prix-fix']").on('click', function () {
$('#menus').removeClass().addClass('animated fadeOutLeft');
$('#menus').one('webkitAnimationEnd', function () {
$("#menus").hide();
$('#prix-fix-menu').show().removeClass().addClass('animated fadeInRight');
});
});
$("a:not(a[href='#prix-fix'])").on('click', function () {
$('#prix-fix-menu').removeClass().addClass('animated fadeOutRight');
$('#prix-fix-menu').one('webkitAnimationEnd', function () {
$("#prix-fix-menu").hide();
$('#menus').show().removeClass().addClass('animated fadeInLeft');
});
});
In CODE2, remove only animationend event. It seems to work fine. But it has still problem when you click repeatedly same button. To work CODE2, you have to filter for user to click repeatedly same button using if-statement.

Toggle close outside click

I am using toggle class for dd & dt it is working fine but my problem is if user click out side i want to close that toggle. How to achive this ?
<script type="text/javascript" src="https://code.jquery.com/jquery-1.11.3.js"></script>
<script type="text/javascript">
jQuery=$.noConflict();
jQuery(document).ready(function() {
jQuery('.navigation dd').hide();
jQuery('.navigation dt').click(function(){
jQuery(this).next('dd').slideToggle('slow');
jQuery(this).toggleClass('glace_navigationlayer-collapsed');
});
});
</script>
You can also try -- what DBS has is proper, ( he posted in a comment ), imho.
//clicking on the body, closes the toggleitem
jQuery(document.body).bind('click.closedd', function() {
jQuery('.navigation dd').hide();
});
// clicking on the toggle item, won't trigger the click on the body
// that we set up above.
jQuery('.navigation dd').bind('click.closedd', function(e) {
e.stopPropagation();
});
You need to listen for clicks on the <body> and then check to make sure the target is outside of the navigation component.
jQuery('body').on('click', function(evt) {
var $target = $(evt.target);
if ( !$target.closest('.navigation').length ) {
// code to collpase the nav menu
}
});
I would also recommend refactoring your nav code to use separate expand and collapse functions instead of relying on slideToggle and toggleClass.
Good luck!
Not too sure about the requirements, but you can try achieving the above on mouseenter and mouseleave events.
However, if you have to implement the above functionality only on click, then you will have to wire the click event on a container that covers the entire html body.

Bootstrap dropdown-menu doesn't get close when clicking an element

I'm firing the bootstrap dropdown menu by using the javascript function dropdown('toggle') as stated in their docs.
Usually dropdowns would hide whenever you click outside them or you select one of their options.
This doesn't happen when firing it through javascript.
In this reproduction you'll see two menus:
One which works as expected as its fired using the "components" trigger
And another one, using the right click, which doesn't work as expected. (it doesn't get closed on click outside or even on element click)
I was able to "manually" get rid of the dropdown menu when clicking outside it by using the following:
$('body').removeClass('open');
But I'm not quite sure why dropdown menus don't work in the same way as when you fire them by the normal procedure.
And having to manually hide them doesn't seem like the best solution...
I got an answer from boostrap issues forum in which they explained how to deal with it:
B. You're missing a <div class="dropdown"> around the <ul class="dropdown-menu">
C. You're missing an element with data-toggle="dropdown" (Not explicitly documented, but followed by all the examples and related to the warning in http://getbootstrap.com/javascript/#callout-dropdowns-data-required )
Here's a reproduction of the solution. (right anywhere click to see the dropdown menu)
HTML markup
<div class="wrapper">
<span data-toggle="dropdown"></span>
<ul class="dropdown-menu" id="menu">
<li>Download file</li>
<li>Upload file</li>
</ul>
</div>
Javascript
//context menu for orders table
$(document).on("contextmenu", "body", function (event) {
//we won't show the default context menu
event.preventDefault();
//showing it close to our cursor
$('#menu').dropdown('toggle').css({
top: (event.pageY) + "px",
left: (event.pageX) + "px"
});
});
Opening in Javascript does not work well with data-toggles. I once used this code to activate the dropdown from Javascript:
$(document).on("contextmenu", "body", function (event) {
//we won't show the default context menu
event.preventDefault();
//showing it close to our cursor
$('#menu').css({
top: (event.pageY) + "px",
left: (event.pageX) + "px"
}).show();
$(document).on('click.contextmenu', function () {
$('#menu').hide();
$(document).off('click.contextmenu');
});
});
I have added a listener on the mouse up to close it without closing it when you click inside, so it closes just when you want or when you click outside it: http://jsfiddle.net/q6po6jzh/1/
$(document).mousedown(function (e) {
var container = $("#menu");
if (!container.is(e.target) && container.has(e.target).length === 0 && container.parent().hasClass('open')) {
container.dropdown('toggle')
container.parent().removeClass('open');
}
});
But if you want to be closed as well when clicked in it I guess #wero answer is probably better.
This will solve your issue. It will close all opened dropdowns.
$('.in,.open').removeClass('in open');
Reference:
https://stackoverflow.com/a/22632931/6488619

Click event when not clicking on an element

Maybe I'm, just tired but I have a right click menu that I want to close when the user clicks anywhere else than the menu. But I cant figure out how to make it disappear when the user clicks on something else.
Here is the code for showing the menu:
// If mouse click on was pressed
$('#content_list table tr').bind('mouseup', function(event) {
// if right click
if(event.which == 3) {
showRightClickMenu(event);
}
event.preventDefault();
event.stopPropagation();
});
And this is what I got for hiding the menu
// If mouse click outside document
$(document).bind('blur', function(event) {
hideRightClickMenu();
event.stopPropagation();
});
// If mouse click not on the menu
$('*:not(#rightClickMenu *)').bind('mousedown keydown', function(event) {
hideRightClickMenu();
event.stopPropagation();
});
The first bind checks if the user clicks outside the document and the second bind checks if the user clicks on everything except the menu.
But the second one doesn't really work. If I click on the menu the menu is hidden.
Shouldn't be so hard imo... Anyone got any ideas?
Try it this way
$(document.body).bind('click', function() {
hideRightClickMenu();
});
$(window).bind('blur', function() {
hideRightClickMenu();
});
Try with focusout() event and using on() instead of old bind(). This will work even with multiple submenus.
http://jsfiddle.net/elclanrs/jhaF3/1/
// Something like this...
$('#menu li').on('click', function() {
$(this).find('ul').show();
}).find('ul').on('focusout', function(){
$(this).hide();
});

Categories