I wanted to get a fuller understanding of how Javascript handles events. click() triggers a div to pop up, but once that div is closed it doesn't respond to the event again.
What is a good way to keep this event loop going?
$project.click(function() {
$popup = $(".popup");
$np.hide();
$popup.append($html);
// EXIT THE POPUP
$(document).bind('keydown',function(e) {
if (e.which == 27) {
$popup.hide();
$np.show("slow");
}
});
$(".exitbutton").click(function() {
$popup.hide();
$np.show("slow");
});
});
If .popup element is set to display:none at page load, can try using .toggle() at $project click handler. Also defined $popup , moved keydown event to outside of click handler, where appears to be added as event handler at each click of $project
var $popup = $(".popup");
$project.click(function() {
$np.toggle();
$popup.append($html).toggle();
});
// EXIT THE POPUP
$(document).bind("keydown",function(e) {
if (e.which == 27) {
$popup.hide();
$np.show("slow");
}
});
$(".exitbutton").click(function() {
$popup.hide();
$np.show("slow");
});
Related
I have a notification dropdown menu being fired by adding a CSS class via Javascript. However, I am certain that Turbolinks is causing it to not work properly, as it seems to only work on refresh.
There is a data-turbolinks="true" in the <body> tag of the document. If I change this to false I get normal operation.
If I put the data-turbolinks="false" in the <div> tag of the specific links it still does not work properly.
So I am thinking I will have to change the JaveScript so it is not affected by the Turbolinks. However, I am not sure how to go about this. This is for a Laravel 5.6 app.
/*--------------------------------------------------*/
/* Notification Dropdowns
/*--------------------------------------------------*/
$(".header-notifications").each(function() {
var userMenu = $(this);
var userMenuTrigger = $(this).find('.header-notifications-trigger a');
$(userMenuTrigger).on('click', function(event) {
event.preventDefault();
if ( $(this).closest(".header-notifications").is(".active") ) {
close_user_dropdown();
} else {
close_user_dropdown();
userMenu.addClass('active');
}
});
});
// Closing function
function close_user_dropdown() {
$('.header-notifications').removeClass("active");
}
// Closes notification dropdown on click outside the conatainer
var mouse_is_inside = false;
$( ".header-notifications" ).on( "mouseenter", function() {
mouse_is_inside=true;
});
$( ".header-notifications" ).on( "mouseleave", function() {
mouse_is_inside=false;
});
$("body").mouseup(function(){
if(! mouse_is_inside) close_user_dropdown();
});
// Close with ESC
$(document).keyup(function(e) {
if (e.keyCode == 27) {
close_user_dropdown();
}
});
I think the issue is that the script only selects the elements on the first page load, rather than on every page load. For example, calling $(".header-notifications") will attempt to find all the elements with a class of .header-notifications, however this is only run once, so when a new page is loaded with Turbolinks , the body gets replaced, and those selected elements no longer exist. Scripts are not executed again until a full page load, and so this script is only run once—.header-notifications elements are never reselected.
To fix this, the Turbolinks README recommends using event delegation:
When possible, avoid using the turbolinks:load event to add other event listeners directly to elements on the page body. Instead, consider using event delegation to register event listeners once on document or window.
So you add your event listeners to the document or window, then choose which elements it should run on with a selector, e.g.:
$(document).on(
"click", ".header-notifications-trigger a", function (event) { … }
)
This means that any time a .header-notifications-trigger a element is added to the page, the click event handler will be fired.
With that in mind, you may wish to update your script to something like:
/*--------------------------------------------------*/
/* Notification Dropdowns
/*--------------------------------------------------*/
// For convenience and to prevent unnecessary $() calls
var doc = $(document);
doc.on("click", ".header-notifications-trigger a", function (event) {
event.preventDefault();
var user_menu = $(this).closest(".header-notifications");
if (user_menu.is(".active")) {
close_user_dropdown();
} else {
close_user_dropdown();
user_menu.addClass('active');
}
});
// Closing function
function close_user_dropdown() {
$('.header-notifications').removeClass("active");
}
// Closes notification dropdown on click outside the container
var mouse_is_inside = false;
doc.on("mouseenter", ".header-notifications", function() {
mouse_is_inside = true;
});
doc.on("mouseleave", ".header-notifications", function() {
mouse_is_inside = false;
});
doc.on("mouseup", function(){
if(!mouse_is_inside) close_user_dropdown();
});
// Close with ESC
doc.on("keyup", function(e) {
if (e.keyCode == 27) {
close_user_dropdown();
}
});
I have a listener which runs when I click on document.
document.addEventListener('click', print);
function print(element)
{
doSomething();
}
It creates div id=panel, where I print some information.
When I run the print function I would like to detect whether I clicked outside of the div#panel (The panel exists when I click second time).
I wish not to use the mouseout event listener because I think it is redundant to use listener for mouse movements when the event click is already fired.
How to detect when I clicked out of div#panel?
You can check the target of jQuery's click event, which element it was:
$(document).click(function(e) {
var target = $(e.target);
if( !target.is("#panel") && target.closest("#panel").length === 0 ) {
// click was not on or inside #panel
}
});
Your event handler gets passed an event object, not an element. Since you are listening for the click event, the event will be of type MouseEvent and that event object will have a target property which you can use to check if the target element matches your desired element.
function handler(event) {
if (event.target == document.getElementById("panel")) {
// Do stuff
}
}
document.addEventListener('click', handler);
Edit: I intentionally gave the vanilla JS answer since your own code fragments don't use jQuery. But jQuery wouldn't change anything as its event handling API is almost just a thin wrapper over JS.
I am just using event from the click. Here it is
var elem=document.getElementById("elem");
var rects=elem.getBoundingClientRect();//get the bounds of the element
document.addEventListener('click', print);
function print(e)
{
//check if click position is inside or outside target element
if(e.pageX<= rects.left +rects.width && e.pageX>= rects.left && e.pageY<= rects.top +rects.height && e.pageY>= rects.top){
console.log("Inside element");
}
else{
console.log("Outside element");
}
}
JS Bin link : https://jsbin.com/pepilehigo/edit?html,js,console,output
A different approach, using only javascript is:
function print(evt) {
if (!(evt.target.tagName == 'DIV' && evt.target.classList.contains('myDiv'))) {
var div = document.createElement('div');
div.classList.add('myDiv');
div.textContent="new div";
document.body.appendChild(div);
}
}
window.onload = function() {
document.addEventListener('click', print);
}
.myDiv {
border:1px solid green;
}
I have this function:
this.div.click( function(e) {
...
});
I would like to listen for double clicks outside this element. I know that we can use blur() for clicks outside an element. But I would like to handle only double click events. What's the best way to do this?
You can use the .dblclick() event to listen to the double-click at the body level, and then use it's target attribute and .contains() to see if the click occurred within the div.
Something like this:
// div to check if dbl click did _not_ originate from
var mydiv = jQuery("#mydiv").get(0);
// listen to body for double clicks
$("body").dblclick(function(e) {
// if click target does not fall within #mydiv
if (mydiv !== e.target && $.contains(mydiv, e.target) !== true) {
console.log("outside of mydiv");
}
});
Here is a jsbin demo.
There is another way to do this, by modifying e.originalEvent:
$( "#mydiv" ).dblclick(function(e) {
e.originalEvent.inside = true;
});
$( "body" ).dblclick(function(e) {
if( e.originalEvent.inside ) {
console.log('inside');
} else {
console.log('outside');
};
});
I have updated Johnatan's Bin. Think it should be faster.
Here's a fiddle illustrating the problem. I am adding a jQuery one binding on the click of one element to the 'html' element. I am not expecting the 'one' event handler to fire until the next click, but it fires on the click that adds the binding. This seems to not be a problem if it is a more specific element that the 'one' event handler is added to, but it happens when I use 'html' or 'body' as the element, which is what I want to do.
This doesn't make sense to me, I'd think the first click would add the one for the next click and it wouldn't fire on the click on the link.
By the way, my actual problem could probably be solved in a better way, but I came across this and was curious why it didn't work as I expected.
Code:
html:
<div id='hello'>hello</div>
<a class="title" href="#">this example</a> is a test
js:
$(function() {
$('a.title').click(function() {
var htmlClickBind = function (e) {
console.log('clicked on html, e.target = ' + e.target);
console.log(e.target == '');
if (!$(e.target).is('a') ) {
console.log('cleared click event');
}
else {
$('html').one('click', htmlClickBind);
}
};
$('html').one('click', htmlClickBind);
});
});
The click event on the a.target element bubbles up to the html element, where your (just-added) handler sees it.
To prevent this, use event.stopPropgation in your a.target click handler (or return false, which does stopPropagation and preventDefault).
Updated code (see the comments): Live copy
$(function() {
// Accept the event arg ----v
$('a.title').click(function(e) {
// Stop propagation
e.stopPropagation();
var htmlClickBind = function (e) {
console.log('clicked on html, e.target = ' + e.target);
console.log(e.target == '');
if (!$(e.target).is('a') ) {
console.log('cleared click event');
}
else {
$('html').one('click', htmlClickBind);
}
};
$('html').one('click', htmlClickBind);
});
});
I have the following function to open an overlay menu:
$('.context-switch').click(function() {
$(".context-switch-menu").toggle();
});
To hide the menu, I would like the user to be able to click on any area outside ".context-switch-menu"
I am trying with :not() but with no success..
$('body').click(function(e) {
if ($(e.target).hasClass('context-switch')) {
return;
}
$(".context-switch-menu").hide();
});
$('.context-switch').click(function() {
$(".context-switch-menu").toggle();
return false;
});
The reason this can be difficult is because of event bubbling.
You can try something like this:
$('.context-switch').click(function(e) {
e.stopPropagation();
$(".context-switch-menu").toggle();
});
$(".context-switch-menu").click(function(e){
e.stopPropagation();
});
$("body").click(function(e){
$(".context-switch-menu").hide();
});
The e.stopPropagation() prevents the click event from bubbling to the body handlers. Without it, any click to .context-switch or .context-switch-menu would also trigger the body event handler, which you don't want, as it would nullify the effect of the .context-switch click half the time. (ie, if the state is hidden, and then you click to show, the event would bubble and trigger the body handler that would then hide the .context-switch-menu again.)
Without testing, would something like this work?:
$('.context-switch').click(function() {
$(".context-switch-menu").show();
});
$(document).click(function() {
$(".context-switch-menu").hide();
});
Instead of using document, 'html' or 'body' may work as well.
$(document).on('click', function(e) {
if (e.target.className !='context-switch-menu') {
$(".context-switch-menu").hide();
}
});
Just an idea here, based on what what others have suggested in the past:
$(document).click(function(e){
//this should give you the clicked element's id attribute
var elem = $(e.target).attr('classname');
if(elem !== 'context-switch-menu'){
$('.context-switch-menu').slideUp('slow');
//or however you want to hide it
}
});
try this, we don't want to call a function when you clicked on the element itself, and not when we click inside the element. That's why we need 2 checks.
You want to use e.target which is the element you clicked.
$("html").click(function(e){
if( !$(e.target).is(".context-switch-menu") &&
$(e.target).closest(".context-switch-menu").length == 0
)
{
alert("CLICKED OUTSIDE");
}
});
Live fiddle: http://jsfiddle.net/Xc25K/1/