I'm attempting to get an event to fire on both click
and focus
, however I only want it to fire once. When I click inside an input, it fires twice (clicking and focusing). How can I prevent this?
$('input').on('focus click', function(){
console.log('fired');
});
You can use .one
instead. That will only allow the event to fire once, but will also remove the bind once it has been fired:
$('input').one('focus click', function(){
console.log('fired');
});
If you need to keep the bind you will have to keep track of the state of the mouse button and the current target that triggered the mousedown
:
var mouseDown, currentTarget;
$('input').on({
"mousedown mouseup": function (e) {
mouseDown = e.type === "mousedown";
currentTarget = e.target;
},
"focus click": function (e) {
if (mouseDown && currentTarget === e.target) return;
console.log('fired');
}
});
See test case on jsFiddle.
A little hysteresis might be an option. Basically record the last time you responded to either event and ignore subsequent events within a guard time.
You could use jQuery's data
for this (example at end of answer), but I prefer this: A general-purpose debouncer:
Live Example using click
and focus
| Live Source
$("#field").on("click focus", debounce(100, function(e) {
// Event occurred, but not with 100ms of the previous one
}));
The debouncer
function:
// debounce - debounces a function call
//
// Usage: var f = debounce([guardTime, ] func);
//
// Where `guardTime` is the interval during which to suppress
// repeated calls, and `func` in the function to call.
// You use the returned function instead of `func` to get
// debouncing;
//
// Example: Debouncing a jQuery `click` event so if it happens
// more than once within a second (1,000ms), subsequent ones
// are ignored:
//
// $("selector").on("click", debounce(1000, function(e) {
// // Click occurred, but not within 1000ms of previous
// });
//
// Both `this` and arguments are passed through.
function debounce(guardTime, func) {
var last = 0;
if (typeof guardTime === "function") {
func = guardTime;
guardTime = 100;
}
if (!guardTime) {
throw "No function given to debounce";
}
if (!func) {
throw "No func given to debounce";
}
return function() {
var now = +new Date();
if (!last || (now - last) > guardTime) {
last = now;
return func.apply(this, arguments);
}
};
}
(The name "debouncer" is a common term for something that uses hysteresis to rate-limit inputs. IIRC, it comes from "switch debouncer" which is a (very) simple circuit used to avoid triggering an action hundreds of times as a mechanical-throw electric switch transitions from being open to being closed and vice-versa, since when the contacts get in proximity, there can be a lot of closed/open/closed/open/closed/open chatter before the switch hits steady-state. This chatter is called "bouncing," hence, "debouncer.")
The approach just using jQuery's data
:
$('input').on('focus click', function(){
var $this = $(this);
var now = +new Date();
var lastClicked = $this.data("lastClicked");
if (lastClicked && (now - lastClicked) < 100) {
// Don't do anything
return;
}
$this.data("lastClicked", now);
// Do the work
});
This is an old question, but I couldn't find any other answers like what solved my problem. So I'm posting it here for anyone who happens to run into this problem in 2015.
$('#menu-button').on('click focus', function() {
if(!$(this).is(':focus')) { // 1
// Do stuff once
}
else {
$this.blur(); // 2
}
});
This fires the event on click only. I'm not sure what's going on behind the scenes, so maybe someone can explain this to me, but tabbing and focus appear untouched and working 100%.
This un-focuses the selected object, but it sets the focus path back to the top of the document. I leave this here so I can click the selected element again to disable a menu. I'm still looking for a fix to keep the focus path.
Edit: Better way:
$('#menu-button').on('click focus', function(event) {
if(event.type === 'focus') { // 1
// Do stuff once
}
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With