Event listener and button click conflict - javascript

I have a menu that needs to obey the following behavior:
Open with an external button press
Close when there is a click outside it's boundaries
The problem is, that the button to open is also outside the boundaries of the menu and so currently, the button press is opening the editor menu, and then the click listener is immediately closing it again. I've tried using variables and element data so that the click listener only activates if the menu is already open, but the event listener is slower than the button click and so the menu has already been expanded (as far as the listener knows) by the time it is activated. I know I can solve this using timeout so the data isn't changed to "expanded = true" until after the click listener has activated, but this seems kind of clunky and I'm wondering if there is a better option.
Here is a code snippet to demonstrate the problem.
And the js code that accompanies it:
document.addEventListener("click", (event) => {
if (!document.getElementById("menu").contains(event.target) && document.getElementById("menu").dataset.open) {
closeMenu();
}
});
//Expand menu
function openMenu() {
document.getElementById("menu").dataset.open = true;
document.getElementById("menu").style.height = "80vh";
console.log("open");
}
//Collapse menu
function closeMenu() {
document.getElementById("menu").dataset.open = false;
document.getElementById("menu").style.height = "0";
console.log("close");
}
Thanks for your help!

You can have the button prevent its parent element from registering the click by taking the event parameter in openMenu, and calling the stopPropogation method on it.
function openMenu(e) {
e.stopPropogation()
document.getElementById("menu").dataset.open = true;
document.getElementById("menu").style.height = "80vh";
console.log("open");
}
How do I prevent a parent's onclick event from firing when a child anchor is clicked?

Thanks for the answers! Here's my solution based on #Addison Schmidt's answer that fixes a couple of errors:
function openMenu(e) {
if (!event) var e = window.event
e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
document.getElementById("menu").dataset.open = true;
document.getElementById("menu").style.height = "80vh";
console.log("open");
}
Source: Event.stopPropagation Not Working

Related

How to prevent custom function execution when another function is executions

Here's my situation:
I have a custom menu on right mouse click for my project. Here, I have document.addEventListener on click, that makes this menu invisible, like this:
var i = document.getElementById("menu").style;
document.addEventListener('click', function(e) {
i.opacity = "0";
setTimeout(function() {
i.visibility = "hidden";
}, 100);
}, false);
And it's great, works well, but I'm implementing a dropdown submenu that should be opened when you click on a certain element, like so:
$('#change_color').click(function(){
if($('#back_color').hasClass('back_color')){
$('#back_color').removeClass('back_color')
}else{
$('#back_color').addClass('back_color')
}
})
The thing is that when I click on that #change_color then addEventListener is firing, which is obvious.
The question is – how can I prevent that listener function to execute when I click on #change_color?
You can prevent further propagation of the current event in the capturing and bubbling phases using event.stopPropagation() and for simplicity in your code use jQuery.toggleClass().
Code example:
$('#change_color').click(function (e) {
$('#back_color').toggleClass('back_color');
e.preventDefault();
e.stopPropagation();
});

Cannot hide the windows with 2nd click

I click on the button in red rectangle to show the windows. Now, if want want to close the windows, i just click on other part of the grey bar. What I want to do is to modify the code to click the button in red rectangle 2nd time to close the windows, but it does not work.
I have put the html and related files here.
The main html is chat.html, where the main javascript lies in
assets\plugins\emojiarea\jquery.emojiarea.js
Following is portion of the code:
EmojiMenu.prototype.hide = function(callback) {
if (this.emojiarea) {
this.emojiarea.menu = null;
this.emojiarea.$button.removeClass('on');
this.emojiarea = null;
}
this.visible = false;
this.$menu.hide();
};
EmojiMenu.prototype.show = function(emojiarea) {
if (this.emojiarea && this.emojiarea === emojiarea) return;
this.emojiarea = emojiarea;
this.emojiarea.menu = this;
this.reposition();
this.$menu.show();
this.visible = true;
};
I try to use this.visible to detect the whether the windows has been opened, if yes, then close it, but it does not work out. Is there a possibility to make the windows closed when I click the button in red rectangle 2nd time?
So I went through the plugin. This piece of code:
$body.on('mouseup', function() {
self.hide();
});
is why you couldn't use this.visible to check if it was already open or not,because every time you click on the button too this mouseup is triggered effectively hiding and then showing the popup.
Right after this:$button.on('click', function(e) {
EmojiMenu.show(self);
e.stopPropagation();
});Add this:$button.on('mouseup', function(e) {
e.stopPropagation();
});This will prevent the bubbling of the mouseup event from the button itself.Now you can use "this.visible to detect the whether the windows has been opened, if yes, then close it."

Mobile navigation click out jquery

I have a function that when a user clicks outside of the mobile navigation close the mobile menu. I don't think I've got the code just right as when I click the mobile menu it opens and closes straight away.
What I want to happen is when the user clicks outside of the nav slideToggle the menu back.
Current code is:
// Responsive menu
$('.mobile-menu').click(function(e) {
e.preventDefault();
$('nav').slideToggle('slow');
});
// Close out the menu on click outside
window.addEventListener('mouseup', function(event) {
var box = document.getElementById('nav');
if(event.target != box && event.target.parentNode != box) {
$('nav').slideToggle('slow');
}
});
I think it's somewhere to do with mouseup however I don't see a mouseclick in the available parameters.
Thanks
I can give you a cleaner approach to this.
The window's listener should only be bound after the menu is expanded. When it's collapsed, the listener should be removed. This will prevent a lot of hassle and possible bug due to asynchronous execution of 2 event listeners.
With that in mind:
// First we define a callback function to be called after the nav complete showing, which will close the nav when a user tap outside.
var bindEventListener = function(event) {
var box = document.getElementById('nav');
if(event.target != box && event.target.parentNode != box) {
$('nav').slideUp('slow', function(){
// remove the window's event listener after the nav is closed
window.removeEventListener('mouseup', bindEventListener);
});
}
});
// Now we set the button's onclick event and bind the above defined callback for mouseup event.
$('.mobile-menu').click(function(e) {
e.preventDefault();
$('nav').slideDown('slow', function(){
window.addEventListener('mouseup', bindEventListener);
});
});
The target of the click event is not necessarily the nav or it's first level child, so if there is a click event on the nav you will slide it open and then slide it close.
You need to check if the nav contains the target element or equals to it, so inside the event listener, change your condition to this:
if(!(event.target == box || $.contains(box, event.target))) {
...
The problem is that when you mousedown on .mobile-menu it shows the nav, then when you mouseup on anything outside the nav, including .mobile-menu, it hides the nav again. You should check that the click event's target also isn't .mobile-menu. Try the following code:
// Responsive menu
$('.mobile-menu').click(function(e) {
e.preventDefault();
$('nav').slideToggle('slow');
});
// Close out the menu on click outside
$(window).on('mouseup', function(event) {
if(!$(event.target).is("nav") && $.contains(event.target, $("nav")) && !$(event.target).is('.mobile-menu')) {
$('nav').slideToggle('slow');
}
});

Clicking 'Submit' on form closes modal window

I have a modal window that allows users to submit a form as one of the call-to-actions. However, clicking 'Submit' closes the modal window but I do not want it to. Here is the page: infowest.com/residential-internet.
To activate the modal window, the user clicks the yellow-orange "Get started" button on any of the "cards" of Internet options. Then to fill out and submit a form, the user clicks the "Request a callback" button. Actually, come to find, clicking ANYWHERE accept the "Request a callback" button closes the modal window, not just clicking the 'Submit' button.
I tried using this code:
if ( $(event.target).is('.modal-window-content') ) {
return false;
}
if ( $(event.target).is('input') ) {
return false;
}
to keep the modal from closing, which worked but didn't allow the form to submit or even try to submit. The form uses Ajax. It is the WP Contact Form 7 plugin.
I am using jQuery that I'm just making up as I go but I've never really been taught jQuery so I'm assuming I'm making errors in the js code. Here is my code:
var pagePosition;
$('.js--activate-cta-modal').click(function() {
if ( (!$(this).hasClass('.active')) && (!$('body').hasClass('modal-active')) ) {
pagePosition = $(window).scrollTop();
$('.cta-modal-window').css( "margin-top", pagePosition );
$(this).addClass("active");
$('body').addClass("modal-active");
$('body').css( "top", -pagePosition );
return false;
}
if($(this).hasClass('active')) {
$(this).removeClass('active');
$('body').removeClass('modal-active')
$('body').css( "top", "0" );
$('body').scrollTop(pagePosition);
}
});
$('.js--activate-cta-modal').click(function (e) {
e.stopPropagation();
});
$('.modal-overlay').click(function() {
$('body').removeClass('modal-active');
$('.js--activate-cta-modal').removeClass('active');
$('body').css( "top", "0" );
$('body').scrollTop(pagePosition);
});
$('.cta-modal-window').click(function() {
// if ( $(event.target).is('.modal-window-content') ) {
// return false;
// }
// if ( $(event.target).is('input') ) {
// return false;
// }
$('body').removeClass('modal-active');
$('.js--activate-cta-modal').removeClass('active');
$('body').css( "top", "0" );
$('body').scrollTop(pagePosition);
});
$('.close-modal-window').click(function() {
$('body').removeClass('modal-active');
$('.js--activate-cta-modal').removeClass('active');
$('body').css( "top", "0" );
$('body').scrollTop(pagePosition);
});
I greatly appreciate any and all help! Thank you!
It's important not to confuse Event.stopPropagation() and Event.preventDefault(), and to understand exactly what they do.
Event.stopPropagation() will prevent events from bubbling up through the DOM. Normally if you click on an element its event will attempt to fire, and then the parent element will attempt to fire its version of the event, and so on up the tree. This is used to contain events to a certain level on the DOM.
Event.preventDefault() stops the default action that elements might have from triggering. The most common one is using this on a form to stop it from submitting. This is used to cancel out the default behavior of elements with certain events.
Here is an example of a common modal staging technique. http://codepen.io/Oka/pen/xGKJVJ
(If you're new to CodePen, click the eyeballs to view any compiled code. I've used Jade and SCSS here. The page is also running Normalize.css)
With this technique we only need one Event.stopPropagation() to stop similar events from bubbling up from the modal to its container. Clicking anywhere on the modal's container will close the modal, and so will the small close button. You can expand this to suit your needs, the key is the modal should never bubble to its container.
JS
var open = $('.modal-open'),
close = $('.modal-close'),
modal = $('.modal'),
container = $('.modal-container');
function openModal (e) {
container.addClass('active');
}
function closeModal (e) {
container.removeClass('active');
}
open.on('mouseup', openModal);
close.on('mouseup', closeModal);
container.on('mouseup', closeModal);
modal.on('mouseup', function (e) {
e.stopPropagation();
});
(Keep in mind some of this stuff is IE9+. Make sure to check browser compatibility if you have legacy needs.)
I now understand a little more of what
$('element').click(function (e) {
e.stopPropagation();
});
is for. This is used to not perform the click function of the targeted element. I'll need to do some more reading on this but I solved the issue by using this code to target the div.modal-window-content.

Javascript onclick dropdown menu

I'm struggling with this javascript at the moment.
$(document).ready(function () {
var visible = false;
var body = false;
$("body").mouseup(function () {
if (visible) {
$(this).parent().find("ul.subnav").slideUp('slow');
visible = false;
$(this).removeClass("clicked-background");
body = true;
}
});
$("ul.topnav li a").click(function () { //When trigger is clicked...
var menu = $(this).parent().find('ul.subnav');
if (!visible && !body) {
$(this).parent().find("ul.subnav").slideDown('fast').show();
visible = true;
$(this).addClass("clicked-background");
}
// else if (visible)
//{
// $(this).parent().find("ul.subnav").slideUp('slow');
// visible = false;
// $(this).removeClass("clicked-background");
// }
body = false;
});
});
I wanted to add the feature, so if you clicked outside the menu/navigation the dropdown would hide.
The current problem with this code is, that if you click the menu and then click outside the menu - you have to double click the menu again to get it showen. This is caused by the body variable is set too 'True' ofc.
I made the body variable trying to fix the problem if you clicked the menu - and then clicked the same link again. The menu would first open correctly, and then close and open again.
Soo main problem is. My navigation open -> closes -> open
Don't use global variables. Check if the actual element is visible by checking
.is(':visible');
You can use that on the various selectors you have now.
I would be tempted to use onmouseout of the 'now visible' menu as the event of choice..
I dont think that running events off of the body tag is the good way to go.
the flow should be..
click (menu button or link)
show menu
set onmouseout for button and menu on click
onmouseout, remove onmouseout events

Categories