jQuery - Event delegation is occuring multiple times - javascript

I'm using event delegation in the pagination for my website. When you click the < or > buttons it moves the page up or down. The problem is that if you don't release the mouse button, in a split-second it will keep repeating the click handler.
How can I make it so that this event only occurs once per-click? Here's my code:
$(document).on('mousedown', '#prev', function(event) {
// Page is the value of the URL parameter 'page'
if (page != 1) {
page--;
var state = {
"thisIsOnPopState": true
};
history.replaceState(state, "a", "?page=" + page + "");
}
// Refresh is a custom function that loads new items on the page
refresh();
});

You should use "click" event instead of "mousedown" unless you have a unavoidable reason.
But "mousedown" or "touchstart" event occurs when a user start pressing the mouse button or screen and it will not be fired until you release the button and press it again.
So I assume you are using a chattering mouse or mouses which has macro software.
change event into "click" and see if it works and in the case "click" event is not gonna solve the issue,try using another mouse.
FYI,underscore methods _.throttle or _.debounce might help to support chattering mouses.
throttle_.throttle(function, wait, [options])
Creates and returns a new, throttled version of the passed function, that, when invoked repeatedly, will only actually call the original function at most once per every wait milliseconds. Useful for rate-limiting events that occur faster than you can keep up with.
debounce_.debounce(function, wait, [immediate])
Creates and returns a new debounced version of the passed function which will postpone its execution until after wait milliseconds have elapsed since the last time it was invoked. Useful for implementing behavior that should only happen after the input has stopped arriving. For example: rendering a preview of a Markdown comment, recalculating a layout after the window has stopped being resized, and so on.
http://underscorejs.org/

If you want to use a "delegated" event handler rather than a "direct" event handler to bubble up the event, try to use a more specific target selector than $(document) like $('.some-class') where some-class is the class name directly above the #prev element.
I would also use either the mouseup or click events instead to avoid the mousedown event firing while the mouse click is held down.
According to the API:
The majority of browser events bubble, or propagate, from the deepest,
innermost element (the event target) in the document where they occur
all the way up to the body and the document element.
Try this:
// delegated "click" listener using a more specific target selector
$('.some-class').on('click', '#prev', function(event) {})
You may want to check your HTML to see if you are using #prev multiple times. Usually, just creating the listener on the target ID element should work fine.
// direct "click" listener on an ID element
$('#prev').on('click', function(event) {})

I haven't found the answer to this question, but I have found a solution that fixes the problem. What I have done is added a conditional that only allows the click event to occur once-per-click:
var i = 0;
$(document).on('click', '#prev', function(event) {
if (page != 1 && i === 0) {
page--;
var state = {
"thisIsOnPopState": true
};
history.replaceState(state, "a", "?page=" + page + "");
i = 1;
refresh();
}
});
// Resets 'i' for the next click
$(document).on('mouseup', function() {
i = 0;
});

Related

How quickly can an element be doubleclicked multiple times?

If an element is clicked twice, the 'dblclick' event is fired. If the element continues to be clicked, the 'dblclick' event does not continue to be fired. The 'dblclick' event will only be fired once until a "cooloff" period is complete, eg. there is a time to wait before another 'dblclick' event can be fired no matter how many times the element is clicked.
I cannot find any documentation that specifies how long must elapse before another 'dblclick' can occur. Does anyone know what the 'dblclick' "cooloff" period is?
I have tried to test this by scripting the clicking of an element, but for some reason javascript-invoked clicks do not trigger 'dblclick' events. So I have tested manually and I can't get a double-click to occurred any sooner than ~400ms after another double-click has occurred. You can try yourself: https://jsfiddle.net/5v4pcx2k/8/
code
If you're wondering, this is basically what it seems like the browser is doing https://jsfiddle.net/b0y5ej2y/3/
There are quite a few bugs and inefficiencies in your jsfiddle.
h1 element has no end tag (you think your ending it with /hi)
you don't need jquery
the general advice around the webs, seem to be don't mix click and dblclick event listeners on the same element
the event already has a property on it that tells you when it fired, you don't need to ask for the current time again with Date.now()
Having said all that, I still couldn't manually click any faster than about ~700ms. I think what's happening is it's the window manager / OS / mouse driver is artificially holding back double clicks to some speed limit.
Here's my version of a jsfiddle test for dblclick speed, with a working programatic dblclick simulation which can dblclick as fast as every 4ms on my computer.
https://jsfiddle.net/stephencarmody/v0b3dpwc/
var lastOne;
foobar.addEventListener('dblclick', function (event) {
log.innerText = 'elapsed: ' + (event.timeStamp - lastOne);
lastOne = event.timeStamp;
});
function simulateClick () {
var event = new MouseEvent('dblclick', {
'view': window,
'bubbles': true,
'cancelable': true
});
foobar.dispatchEvent(event);
}
setInterval(simulateClick, 0);
Comment out the setInterval line to test manual dblclick events.

Is it possible to force event listeners to fire in a particular order in JavaScript?

Is there a way to be notified or perform some callback function once an event has finished propagating in JavaScript?
Equivalently, and more specifically: is there a way to 'prioritize' an event and make sure that it is called after every other event listener has been fired (similarly almost to the !important value in CSS).
For instance, if I have 3 event listeners - 2 attached to the window and 1 to some button element. Can I force a certain one of those events to be called LAST, regardless of where it lies in the DOM? I understand that there are event phases and the ability to attach a listener to the capture or bubbling phase but this still means there's a preset order.
edit: the specific problem:
I'm attempting to build components (in React JS) which are aware of a click being registered outside of themselves (i.e. anywhere on the window/document except themselves) - often as a way of closing/hiding the component. Each of these components will register a listener on the window object which fires a function belonging to that component.
The trouble is, when another component [B] (inherently lower down in the DOM than the window) is clicked to let's say toggle the display of [A], [B]'s event fires first and toggles the state 'showA', the event bubbles up and [A]'s window event listener kicks in and re-toggles the state 'showA' - so, [A] remains hidden after changing state twice. I can't use stopPropagation as other window events need to fire. I've tried to unbind listeners but this doesn't happen in time.
An example of what currently happens all in one go is:
'show thing' button clicked
add listener to window for closing 'thing'
'window but not thing' was clicked
remove listener to close 'thing'
If only I could wait until the click event had finished bubbling before adding the new listener, I'd have no issue
I did leave an answer to your original question but I see you've updated it. I wouldn't say this is React specific but a common implementation for components that need to close/de-activate when the document is clicked.
For instance, the following snippet is an implementation for a speed dial spin out button;
(function () {
var VISIBLE_CLASS = 'is-showing-options',
btn = document.getElementById('.btn'),
ctn = document.getElementById('.ctn'),
showOpts = function(e) {
var processClick = function (evt) {
if (e !== evt) {
ctn.classList.remove(VISIBLE_CLASS);
ctn.IS_SHOWING = false;
document.removeEventListener('click', processClick);
}
};
if (!ctn.IS_SHOWING) {
ctn.IS_SHOWING = true;
ctn.classList.add(VISIBLE_CLASS);
document.addEventListener('click', processClick);
}
};
btn.addEventListener('click', showOpts);
}.call(this));
When the user clicks the button, the container is shown for the speed dial options and an event listener is bound to the document. However, you need to make sure that the initial event that is fired is not the one that triggers the takedown straight away (this is sometime a gotcha). This check is made with if (e !== evt) .... For further clicks the event check is made and the relevant action taken ending in removal of the event listener from the document.
Of course in your particular case if you want to only close when the element isn't clicked then you could make relevant checks on the evt.target and evt.currentTarget in the callback (in the snippet case, processClick).
Hopefully, this can help you out with registering close down callbacks for your individual components.

document ready with ui Dilaog

Ok i have one page wich loads, with UI dialog one without. Problem is cuz i use document ready with keyup. In dialog keyup fires twice, and on page with out dialog normal once. If i remove document ready keyup function wont work, on page with out dialog
$(document).ready(function() {
is_draft_started = 0;
$(":input").keyup(function() {
alert(33232);
if(is_draft_started == 0) {
s2 = setInterval('draft("' + frm_name + '")', auto_save_time);
is_draft_started = 1;
});
});
Is it a way to solve this, if this code is in dialog it auto add one more document ready so key up fires twice
My concern here is that everytime the KeyUp Event fires,
You are binding a setInterval to execute every X seconds
So Eventually as Keyup is supposed to fire multiple times you will have generated a dozen calls to your backend for saving a draft ...
Are you sure the event is firing twice and not bubbling ??
you should pass the event variable in your function and use
event.bubbles = false;
AND
event.stopPropagation() to stop further propagation.

How to tell if a mouseup is going to be followed by a click event?

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')
}
});

jQuery Event Bubble

I have a mousedown event attached in an anchor element which does many stuffs.
I also have a mousedown event attached to the document, and because of bubbling this event is called whenever the event attached to anchor is triggered. This is not what I want.
Can I bind a event with delay?
I dont want to use stopPropagation.
$('a').mousedown ->
...
openWindow()
$(document).mousedown ->
...
closeWindow()
Edit
I create a hack
$.fn.onBubble = (events, selector, data, handler) ->
setTimeout =>
this.on events, selector, data, handler
, 0
Work but like very ugly
As one of the comments mentions, the only way to stop events from bubbling is with stopPropagation. That said, if there are both conditions where you do want to prevent bubbling and others where you do not, you can put event.stopPropagation() into an if-statement:
$(...).mousedown(function(event) {
if(/* some condition */) { event.stopPropagation(); }
});
Alternatively you can add a conditional to the event handler attached to the document. For example:
$(document).mousedown(function(event) {
if($(event.target).is("a")) {
return; // if the element that originally trigged this event
// (i.e. the target) is an anchor, then immediately return.
}
/** code that runs if event not from an anchor **/
});
This snippet uses $.fn.is to determine if the event was triggered by an anchor. If it is generated by an anchor, the code immediately returns, which in effect ignores the event bubble.
EDIT in response to comment:
If I understand correctly, you want to close the window, if the user clicks on anything that is not in the window. In that case try this:
function whenWindowOpens { // Called when the window is opened
var windowElement; // Holds actual window element (not jQuery object)
$(document).bind("mousedown", function(event) {
if($.contains(windowElement, event.target)) {
return; // Ignore mouse downs in window element
}
if($(event.target).is(windowElement)) {
return; // Ignore mouse downs on window element
}
/** close window **/
$(this).unbind(event); // detaches event handler from document
});
}
This is basically a variation on the second solution suggested above. The first two if-statements ensure the mouse down did not occur in (using $.contains) or on (using $.fn.is again) the windowElement. When both statements are false, we close the window and unbind the current event handler. Note that $.contains only takes raw DOM elements -- not jQuery objects. To get the raw DOM element from a jQuery object use $.fn.get.

Categories