I have the following code that returns 'haha' when the user clicks up and down. I am having trouble with the amount of times this gets retuned though. The first time I click up/down, 'haha' only gets returned once. The second time I go through this, it gets printed twice. The third time, 'haha' gets printed 3 times and so on. Any advice on why this is happening? I only want 'haha' to be printed once for each up/down.
$(".test").mousedown(function(){
$(".test").mouseup(function(){
console.log('haha');
});
});
You could use one, so that the event handling is unbound after the mouseup :
$(".test").mousedown(function(){
$(".test").one('mouseup', function(){
console.log('haha');
});
});
But it seems simpler to simply use click :
$(".test").click(function(){ console.log('haha') });
You're adding a new mouseup event handler every time you click mouse down. You may want to separate the two, or just use the mouseup handler.
This is because each time the mousedown event is triggered, a new mouseup event is added to the target element.
Remove the mouseup event immediately again on the firing of mouseup event and your problem will be solved.
The problem is that in your code you are adding the "mouseup" handler every time the user clicks. You don't need to wait for the mousedown event in order to add this handler; you should just be able to add it from scratch. If you really want to make sure the mousedown has been triggered first, use a boolean, like so:
var mouseClicked = false;
$(".test").mousedown(function(){
mouseClicked = truel
}).mouseup(function(){
if (mouseClicked ==true){
console.log('haha');
}
});
Related
I have following simple JS code (https://stackblitz.com/edit/web-platform-ueq5aq?file=script.js):
const baton = document.querySelector('button');
baton.addEventListener('mousedown', (e) => {
console.log('baton');
baton.addEventListener('click', (e) => {
console.log('baton click');
});
});
When I click a button, I get 'baton' and 'baton click' logged to console. Now my question is what exactly happens here? As I understand it, the moment script is executed, handler mousedown is added to even queue. When I actually click button, this handler is run, so it's taken from event queue, added to call stack and it is executed. When it is executed, handler "click" is added to event queue.
How actually event onClick is triggered after onMouseDown? How is that related to event queue? Why onMouseDown handler is run before click event happens? I'm asking because I have a lot more complex code where result is different in different scenarios.
When user navigates to page in SPA which contains similiar script, and then clicks button 'baton' order is:
mousedown event -> handler mousedown -> handler click -> click event
And when user reloads page, so SPA is loaded right on that page, and clicks button 'baton' order is:
mousedown event -> click event -> handler mousedown
I am seeking answer and truth. Any help will be greatly appreciated.
Ps. Unfortunately I'm not able to reproduce this error in example repository - it happens in quite complex web app which production code I can't share here for obvious reasons.
Ps2. Just to clarify, because probably it isn't stated clearly enough: I'm not asking "why mousedown event is triggered before click event", but "why mousedown HANDLER is run before click event". This is NOT obvious, because handlers are not run immediately. In order of handler to be run, it first have to wait to call stack to be empty, so event queue can be processed by JS engine.
The browser tracks the element you clicked the moused down on. Then it tracks the element you lifted the mouse button on. If the element you lifted the mouse button on is the same element or a child element of the target element. Then a click event is dispatched to the last element you lifted the mouse on. The event then propagates up the element chain to every parent element unless the event is told to stop propagating.
If you click down on element A and mouse up on element B. Then A gets mouse down event, and B gets mouse up event, but neither get a click event. Same thing if you navigate the browser to another page in between the mouse down and mouse up.
From MDN Web Docs
An element receives a click event when a pointing device button (such as a mouse's primary mouse button) is both pressed and released while the pointer is located inside the element.
So there is a mouseup event and then the click event.
EDIT after question edit:
"why mousedown HANDLER is run before click event?"
Your already executing mousedown handler registers the click handler so how should the click handler run before it?
All click handlers registered in all previous mousedown handlers will run after the mousedown and mouseup events too.
Perhaps we should start by clarifying a few things.
Events in the browser, are modeled more like a "nesting hierarchy", then a queue -- How it works is referred to as Event Bubbling -- [Wikipedia][1]
But, essentially what you are doing, when adding an EventListener, is hooking into one or more points of the DOM, and saying hey, when X Event passes through here, use function Y to handle it, before passing it along up the stack.
Once an EventListener has been "added" it remains active waiting to be given an event. What exactly it does is defined in its handler function.
let myYFunction = function( e ) { ... }
let myXListener = baton.addEventListern('X event', myYFunction );
// at this point, anytime 'X event' happens to baton, myYFunction will
// be called to handle it...
Now let's take a look at your examples, lets break things down a little,
const baton = document.querySelector('button');
This first line, is simply querying the DOM, to find the first element of type 'button' in the page. Right... This is "where" we want to insert our event handler. We could add them to any element, anywhere in the DOM, we could even hook into the 'body' element if we wanted to.
Ok, then you have this bit,
baton.addEventListener('mousedown', (e) => {
console.log('baton');
baton.addEventListener('click', (e) => {
console.log('baton click');
});
});
Which is "nesting" the creation of the 'click' Event Listener, but only after a 'mousedown' event has been "handled". There is no real reason the 'click' event had to be registered within the function body of the mousedown handler.
If we re-write it a bit, it may be clearer what is actually going on.
baton.addEventListener('mousedown', (e) => {
console.log('baton mousedown');
}
baton.addEventListener('click', (e) => {
console.log('baton click');
});
Additionally I would also point out, that how it is being done currently "works" -- but it is actually hiding a tiny bit of sloppy coding... you see every time the 'mousedown' event is triggered a new 'click' eventListener is being registered... so eventually you may end up with many, many, many click handlers responding to a single 'click' event... Check out MDN to learn more about [this][2]
I hope this answers your initial questions as to what is going on.
To your question "When I click a button, I get 'baton' and 'baton click' logged to console. Now my question is what exactly happens here?" -- To me, it would look something like this:
a 'mousedown' eventListener is added, however nothing "executes"
a 'mousedown' event takes place, now your 'mousedown' listener executes its function, which in turn logs out to the console, and registers a new 'click' handler -- but again, does not execute.
Moving forward, steps 1 and 2 are repeated for every 'mousedown' seen by baton. Additionally, for any 'click' event passed through baton --- which happens after every 'mousedown' on baton:
A 'click' event occurs, your 'click' handler is then executed and logs out to the console.
SPA event handling strategies
When working with SPAs, where multiple "pages" are displayed, in the same page load... it can get messy, all these event listeners hanging around piling up on one another. If you are going to employ eventListeners between "Pages" of your SPA, you might want to look into how to "remove" them too. - [MDN][3]
That way, you only have eventListeners active for the current "Page" of your SPA.
Also, consider "generalizing" your handlers, and attaching them higher up in the DOM... This would allow you to have only a few event listeners which "route" events to their "logical" handlers.
Random/Different Behaviors
With the steps outlines above, 1, 2 and 3 and how they don't all happen at the same time. You will see what appears to be random output to the console... try and run something like this, to get a proper sense of things:
let cCount = 0;
let mCount = 0;
let tCount = 0;
const baton = document.querySelector('button');
baton.addEventListener('mousedown', (e) => {
console.log('mousedown # ' + (mCount++) + ' order:' + tCount++);
baton.addEventListener('click', (e) => {
console.log('click # ' + (cCount++) + ' order:' + tCount++);
});
});
[1]: https://en.wikipedia.org/wiki/Event_bubbling#:~:text=Event%20bubbling%20is%20a%20type,Provided%20the%20handler%20is%20initialized).
[2]: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
[3]: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener
I have a bug in my jQuery/animate.css code. The problem is an event handler that is called twice but it should be executed only one. You can find the code here, the important line is below:
JSFiddle
$('#tiles').addClass(tiles_in).one(animation_end, function() {
// called twice
log('tiles in');
});
In order to reproduce the bug, follow these steps:
Wait for the animation to end, which will output these logs
change_tiles
tiles out
tiles in
Click on the yellow div, here are the new logs
tile out // normal
tiles in // why ?
Tiles in should not be called again as the event is one(). And the documentation says "The .replaceWith() method removes all data and event handlers associated with the removed nodes." So I don't know.
why ?
A call to .one() is within first .one() event handler where the the event is attached to the same selector #tiles
I have found it myself after logging the event that was fired.
https://jsfiddle.net/uhz6Ly0s/8/
$('#tiles').addClass(tiles_in).one(animation_end, function(event) {
log('tiles in, event=' + event.type);
});
The first event is animationend, and the second is webkitAnimationEnd. For some reasons, the second one is fired at the end of the click event.
I’m running into this issue where a single action by the user is supposed to trigger two events but it only triggers the first one.
The scenario:
A user enters some text into a special field that modifies the layout on focusout , after entering the text, without leaving the field, they click a button.
What’s happening?
I have a focusout event on a text box and click event on a button. What I see is the focusout event gets fired but the click event never does.
I’ve encapsulated this in a jsfiddle:
http://jsfiddle.net/fCz6X/13/
$('#theText').focusout(function (){
$("#focusevent").text("Focusevent");
console.log("focus");
});
$('#theButton').click(function (){
$("#clickevent").text("Clickevent");
console.log("click");
});
So if you click in the text field then click the button I’d expect both events to fire, but we only see the focus out event.
I put in a temporary fix for this by having the mousedown event fire the button instead of a click event (this fires before the focusout event) but that is causing some other behaviors and issues that I don’t want to see. Due to those I think optimal solution is finding a way to get the focusout and click events to both fire. Does anyone have thoughts on how to fix this problem?
Edit: After seeing initial responses I dug a little deeper, the issue here is that the focusout event is changing the page layout which very slightly pushes the location of the button down. The click event triggers after the focusout is done but since the button is no longer in the exact same location, nothing happens.
Here is an updated fiddle that shows my problem
http://jsfiddle.net/fCz6X/11/
It's because you're calling alert - the focusout event fires, but before the browser recognizes you've clicked the button, the alert box blocks it.
Change your event handler to console.log or something else that's non-obtrusive and you'll be ok.
It is the Alert that is blocking.
Some browser security prevents firing too many window.alert at the time.
When trying with other triggers, it looks. You may try console.log()
$('#theText').on("focusout",function (){
$("#theText").val($("#theText").val()+"flb");
});
$('#theButton').on("click",function (){
$("#theText").val($("#theText").val()+"but");
});
I believe this is because the focusout event fires first, executing the handler, and the alert then prevents the browser from recognizing the click.
Try again with console.log instead of alert - it's less invasive.
As Joe said, the blocking alert call is what is breaking the event. Using a non-blocking call you will see both events.
If you really need to perform an alert like this, though, you can defer calling 'alert' until later using setTimeout()
$('#theText').focusout(function (){
setTimeout(function() { // alert after all events have resolved
alert("focus left text box");
}, 0);
});
Edit: In your updated fiddle the reason the click event never fires is because no click event occurs. If you move the button out from under the mouse on mousedown, there is no followup mouseup which is what initiates the 'click' event.
You may need to reconsider other aspects of your design. Your solution of using 'mousedown' is the best you can achieve because it's the only event that actually occurs.
Is there any way to know, in a jQuery onmouseup handler, if the event is going to be followed by a click event for the same element?
I have an event handler for a menu hyperlink which unbinds itself when the user either clicks on an element or "drops" (as in drag-n-drop) on an element. I want to avoid prematurely unbinding the handler on mouseup if a click is coming next.
I realize I can track mousedown and mouseup events myself or otherwise hack up a solution (e.g. wait 50 msecs to see if a click comes soon), but I was hoping to avoid rolling my own implementation if there's something built-in for this purpose.
There is nothing built-in because it's really specific to your needs. Thus, there would kilometers of code and documentation to maintain if jQuery would handle any combination of clicks, long clicks, moves, etc.
It's also hard to give you a snippet that satisfies your needs, but a setTimeout is usually the first step to take, with something like that :
obj.mouseup = function (){
obj.click = action; // do action
setTimeout ( function() {
obj.click = functionOrigin // after 500 ms, disable the click interception
}, 500);
};
you can use $(selector).data('events') for that
$('div').mouseup(function(){
if($(this).data('events').click){
console.log('Has a click event handler')
}
});
I want to load an image and some other actions after I click a certain DOM element, but I want to load them AFTER the clicking action finished.
Here is a code example:
$("#message_link").click(function(){
if (some_conditions...){
$("#header").append("<div><img alt=\"Loader\"src=\"/images/ajax-loader.gif\" /></div>");
}
});
The problem is that the if condition executes before the click action have finished(Or at least that is my impression). I need that the If condition executes after the click action has finished. Can some one please tell me a solution?
Thanks :)
setTimeout may help out here
$("#message_link").click(function(){
setTimeout(function() {
if (some_conditions...){
$("#header").append("<div><img alt=\"Loader\"src=\"/images/ajax-loader.gif\" /></div>");
}
}, 100);
});
That will cause the div to be appended ~100ms after the click event occurs, if some_conditions are met.
If I've understood your question correctly, then you are looking for the mouseup event, rather than the click event:
$("#message_link").mouseup(function() {
//Do stuff here
});
The mouseup event fires when the mouse button is released, and does not take into account whether the mouse button was pressed on that element, whereas click takes into account both mousedown and mouseup.
However, click should work fine, because it won't actually fire until the mouse button is released.
you can write events on elements like chain,
$(element).on('click',function(){
//action on click
}).on('mouseup',function(){
//action on mouseup (just before click event)
});
i've used it for removing cart items. same object, doing some action, after another action