My code -
hits.on('rowSelected', function (evt) {
setTimeout(function() { alert('hi'); }, 5000);
});
so I have a hits table. When a row is selected within that table, this event gets fired. The issue is I want to not be able to select a row again for 5 seconds. IMO, this isn't working because the event on the table gets fired every time a row is selected without giving the setTimeout time to delay.
Is there a better way?
The most elegant solution I could come up with is below, using a button to simulate the behavior.
It works as follows:
Bind a named event handler (handler) to the element, and use jQuery's one() to only execute it once.
In the event handler, handle the event.
Use setTimeout() to wait 5 seconds before re-binding the event handler for the next execution.
let button = $('#button');
button.one('click', function handler() {
console.log('clicked'); // handle event here
setTimeout(() => $(this).one('click', handler), 5000);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="button">Click me!</button>
This approach has the following benefits:
Does not require external variables to hold the state of the event handler (i.e. whether it's active or not).
Does not require unbinding of the event handler, as every time it's only bound for only a single execution.
Does not pollute the outer scope with a named event handler.
What I would recommend is making use of a variable to store whether the element has been clicked or not.
Run your setTimeout() inside an if conditional that checks against this variable. Once the element is clicked, update the variable so that it cannot be clicked again. Then, once the timeout has resolved, enable the click again.
This can be seen in the following:
var hits = $("p");
var clickable = true; // It's clickable by default
hits.on('click', function(evt) {
if (clickable) { // Check whether it can be clicked
clickable = false; // Once clicked, don't allow further clicks
setTimeout(function() {
alert('hi');
clickable = true; // Make it clickable again
}, 5000);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Click this paragraph.</p>
Hope this helps! :)
Related
On the first click myClick() has been called again in itself of myLoad() then at the second click, this myClick() will execute two times
=> The following causes two subsequent execution of
click event of #myBtn by one click
How to avoid or stop this? Please anybody suggest me new logical method or which way to stop this.
$(function() {
myLoad()
})
function myClick() {
$("#myBtn").click(function() {
myLoad(); //load new every click
});
}
function myLoad() {
$("#myCnt").load('ajax.php', {
"data": "some"
}, function() {
myClick() //to live the click event works after ajax load
})
}
Problem with your implementation is that in each call to myClick() an new event handler is attached to button.
You can use .off() to remove existing event handler attached using .on().
function myClick(){
$("#myBtn").off('click').on('click', function(){
myLoad();//load new every click
});
}
A better approach would be to use .on() method with Event Delegation approach, when generating elements dynamically.
General Syntax
$(document).on('event','selector',callback_function)
In place of document you should use closest static container.
The delegated events have the advantage that they can process events from descendant elements that are added to the document at a later time. By picking an element that is guaranteed to be present at the time the delegated event handler is attached, we can use delegated events to bind the click event to dynamically created elements and also to avoid the need to frequently attach and remove event handlers.
A good read Direct and delegated events
Modify you code as
$(function() {
myLoad();
$(document).on("click", "#myBtn", function() {
myLoad(); //load new every click
});
})
function myLoad() {
$("#myCnt").load('ajax.php', { "data": "some"}, function() {
//No need to call my click
})
}
Issues is in the flow of your code
First time when you call myClick() it binds the click event with #myBtn,
On second time it binds that event again , so it will be called twice , remove that event binding from there,
Or else $("#myBtn").click will be bind each time you call the myClick function.
As solution please try this code :
$(function(){
myLoad()
})
$("body").on( "click" , "#myBtn" , function(){
myLoad();//load new every click
});
function myLoad(){
$("#myCnt").load('ajax.php', {"data":"some"}, function(){
$("#myBtn").trigger("click");
})
}
I have a class method which defines the event listeners. Let us use the following code snippet for simplicity.
function bindEvents() {
document.querySelector('button').addEventListener('click', (e) => {
console.log('clicked!');
});
}
// Initial event binding
bindEvents();
// Rebind events at some point for dynamically created elements
bindEvents();
<button type="button">Click</button>
Everything works fine when using bindEvents() only once, however for example calling it again in ajax callback results in listener executed twice. So this means after second bindEvents(), clicking the button will console.log() twice and so on. Is there a way I can get around this behavior?
I know I can bind the events "dynamically" on the document and check with e.target, but there is a situation where I need mouseenter/mouseleave events and I don't think it's a good idea to always have those eventListeners on the document.
I've read somewhere the following, but it seems false...
The .addEventListener method ensures that the same function reference
won't be bound more than once to the same element/event/captures
combination.
Also I have played with the options parameter from here https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener, but without success.
Excuse me if this was answered somewhere, but I failed to find answer in SO and the search engines.
UPDATE: Is there a way to overwrite existing eventListeners or old ones should be removed with removeEventListener like kcp suggested below? Is there more elegant solution to this problem at all?
The .addEventListener method ensures that the same function reference won't be bound more than once to the same element/event/captures combination.
In your case, each time you execute bindEvents() a completely new handler is passed to the click event listener since you define new function (no matter it looks the same, it is different object). To use the same handler each time you must define it outside bindEvents and pass it by name (by reference). This works as expexted:
function clickHandler(e){
alert('clicked!');
}
function bindEvents() {
document.querySelector('button').addEventListener('click', clickHandler);
}
// Initial event binding
bindEvents();
// Rebind events at some point for dynamically created elements
bindEvents();
<button>click</button>
However with jQuery I use the following approach which allows me to specify that only elements in a specific container (context or ctx) will be bound:
$.fn.bindEvents = function bindEvents(ctx){
ctx = ctx || this;
$('button', ctx).on('click', function(event){
alert(1);
});
};
$('body').bindEvents();
$('div').bindEvents();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>Click</button>
<div><button>in div </button></div>
In the example above bindEvents is executed twice but the first button is bound only once since it's not in a div. While if you click on the second button it alerts twice because satisfies both contexts.
addEventListener does not overwrite existing event listeners, it simply adds a new one as the method name implies. Existing listeners must be removed using the removeEventListener method.
function onClick($event) {
console.log('clicked!');
}
function bindEvents() {
/** Remove event listener first **/
document.querySelector('button').removeEventListener('click', onClick);
document.querySelector('button').addEventListener('click', onClick);
}
removeEventListener docs
Apart from removeEventListener, You can also use Event delegation. Using this mechanism event is handler by attaching event listener to parent element.
var elem = document.querySelector('div');
elem.addEventListener('click', function(e) {
e = e || event
var target = e.target;
if (target.nodeName != 'BUTTON')
return;
console.log('clicked ' + target.textContent);
});
//Simulate addition of dynamic elements
setTimeout(function() {
var newButton = document.createElement('button');
newButton.innerHTML = 'Click 2';
elem.appendChild(newButton)
}, 2000)
<div>
<button type="button">Click</button>
</div>
I want to create a one-time event triggered by clicking anywhere. This event is created by clicking a button. I do not want the event to trigger upon clicking the button, only any subsequent clicks anywhere (including the button).
So say I've got some html like the following:
<body>
<div id="someparent">
<div id="btn"></div>
</div>
</body>
And the following javascript (jquery):
$('#btn').click( function() {
$(document).one('click', function() {
console.log('triggered');
});
});
$('#someparent').click(function() {
// this must always be triggered
});
I want to avoid stopping event propagation, but in the above example, the event is bound to document, the event then bubbles up, and the event is triggered.
One way to fix this seems to be to wrap the event creation in a timeout:
$('#btn').click( function() {
setTimeout(function() {
$(document).one('click', function() {
console.log('triggered');
});
}, 1);
});
$('#someparent').click(function() {
// this must always be triggered
});
Now, this works fine, but I'm wondering whether this is safe. Does some notion of order of execution guarantee that this will always work, or is it just working by chance? I know there are other solutions (another nested .one() event for instance), but I'm specifically looking for an answer to how setTimeout and event propagation interoperates.
The following fiddle shows two divs. The first one has the wrong behaviour (the document event is triggered immediately). Clicking the second div, and then anywhere on document (white area) illustrates the wanted behaviour:
https://jsfiddle.net/Lwocuuf8/7/
Event bubbling means that, after an event triggers on the deepest possible element, it then triggers on parents in nesting order.
From: https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#Adding_messages
Adding messages
In web browsers, messages are added any time an event occurs and there
is an event listener attached to it. If there is no listener, the
event is lost. So a click on an element with a click event handler
will add a message--likewise with any other event.
Calling setTimeout will add a message to the queue after the time
passed as second argument. If there is no other message in the queue,
the message is processed right away; however, if there are messages,
the setTimeout message will have to wait for other messages to be
processed. For that reason the second argument indicates a minimum
time and not a guaranteed time.
So, the behaviour you have, is not by chance, you are guaranteed that the messages in the queue will be processed before processing the message you add after a timeout.
Here is a workaround by using a flag instead of a second event :
var documentWaitingClick = false;
$(function() {
$(document).on('click', function(e) {
if (documentWaitingClick) {
//simulate the "one"
documentWaitingClick = false;
console.log('document click');
} else if (e.target.tagName === 'BUTTON') {
documentWaitingClick = true;
console.log('button click')
}
});
});
My understanding of what you want: after clicking an "activation" button, the next click anywhere on the page (including on the "activation" button) should trigger a special event. This is easily handled with a flag:
var activationFlag = false;
$('#btn').click(function(event){
event.preventDefault();
if(!activationFlag) {
event.stopPropagation();
console.log('Global event activated');
}
activationFlag = true;
});
$('#someparent').click(function(event){
if(activationFlag) {
console.log('Time for a special event');
} else {
event.preventDefault();
event.stopPropagation();
}
});
I have an input element with 2 events attached: focus and click. They both fire off the same helper function.
When I tab to the input, the focus event fires and my helper is run once. No problems there.
When the element already has focus, and I click on it again, the click event fires and my helper runs once. No problems there either.
But when the element does not have focus, and I click on it, BOTH events fire, and my helper is run TWICE. How can I keep this helper only running once?
I saw a couple similar questions on here, but didn't really follow their answers. I also discovered the .live jQuery handler, which seems like it could work if I had it watch a status class. But seems like there should be a simpler way. The .one handler would work, except I need this to work more than once.
Thanks for any help!
The best answer here would be to come up with a design that isn't trying to trigger the same action on two different events that can both occur on the same user action, but since you haven't really explained the overall problem you're coding, we can't really help you with that approach.
One approach is to keep a single event from triggering the same thing twice is to "debounce" the function call and only call the function from a given element if it hasn't been called very recently (e.g. probably from the same user event). You can do this by recording the time of the last firing for this element and only call the function if the time has been longer than some value.
Here's one way you could do that:
function debounceMyFunction() {
var now = new Date().getTime();
var prevTime = $(this).data("prevActionTime");
$(this).data("prevActionTime", now);
// only call my function if we haven't just called it (within the last second)
if (!prevTime || now - prevTime > 1000) {
callMyFunction();
}
}
$(elem).focus(debounceMyFunction).click(debounceMyFunction);
This worked for me:
http://jsfiddle.net/cjmemay/zN8Ns/1/
$('.button').on('mousedown', function(){
$(this).data("mouseDown", true);
});
$('.button').on('mouseup', function(){
$(this).removeData("mouseDown");
});
$('.button').on('focus', function(){
if (!$(this).data("mouseDown"))
$(this).trigger('click.click');
});
$(".button").on('click.click',evHandler);
Which I stole directly from this:
https://stackoverflow.com/a/9440580/264498
You could use a timeout which get's cleared and set. This would introduce a slight delay but ensures only the last event is triggered.
$(function() {
$('#field').on('click focus', function() {
debounce(function() {
// Your code goes here.
console.log('event');
});
});
});
var debounceTimeout;
function debounce(callback) {
clearTimeout(debounceTimeout);
debounceTimeout = setTimeout(callback, 500);
}
Here's the fiddle http://jsfiddle.net/APEdu/
UPDATE
To address a comment elsewhere about use of a global, you could make the doubleBounceTimeout a collection of timeouts with a key passed in the event handler. Or you could pass the same timeout to any methods handling the same event. This way you could use the same method to handle this for any number of inputs.
Live demo (click).
I'm just simply setting a flag to gate off the click when the element is clicked the first time (focus given). Then, if the element gets focus from tabbing, the flag is also removed so that the first click will work.
var $foo = $('#foo');
var flag = 0;
$foo.click(function() {
if (flag) {
flag = 0;
return false;
}
console.log('clicked');
});
$foo.focus(function() {
flag = 1;
console.log('focused');
});
$(document).keyup(function(e) {
if (e.which === 9) {
var $focused = $('input:focus');
if ($focused.is($foo)) {
flag = 0;
}
}
});
It seems to me that you don't actually need the click handler. It sounds like this event is attached to an element which when clicked gains focus and fires the focus handler. So clicking it is always going to fire your focus handler, so you only need the focus handler.
If this is not the case then unfortunately no, there is no easy way to achieve what you are asking. Adding/removing a class on focus and only firing the click when the class isn't present is about the only way I can think of.
I have it - 2 options
1 - bind the click handler to the element in the focus callback
2 - bind the focus and the click handler to a different class, and use the focus callback to add the click class and use blur to remove the click class
Thanks for the great discussion everybody. Seems like the debouncing solution from #jfriend00, and the mousedown solution from Chris Meyers, are both decent ways to handle it.
I thought some more, and also came up with this solution:
// add focus event
$myInput.focus(function() {
myHelper();
// while focus is active, add click event
setTimeout(function() {
$myInput.click(function() {
myHelper();
});
}, 500); // slight delay seems to be required
});
// when we lose focus, unbind click event
$myInput.blur(function() {
$myInput.off('click');
});
But seems like those others are slightly more elegant. I especially like Chris' because it doesn't involve dealing with the timing.
Thanks again!!
Improving on #Christopher Meyers solution.
Some intro: Before the click event fires, 2 events are preceding it, mousedown & mouseup, if the mousedown is fired, we know that probably the mouseup will fire.
Therefore we probably wouldn't like that the focus event handler would execute its action. One scenario in which the mouseup wouldn't fire is if the user starts clicking the button then drags the cursor away, for that we use the blur event.
let mousedown = false;
const onMousedown = () => {
mousedown = true;
};
const onMouseup = () => {
mousedown = false;
// perform action
};
const onFocus = () => {
if (mousedown) return;
// perform action
};
const onBlur = () => {
mousedown = false;
// perform action if wanted
};
The following events would be attached:
const events = [
{ type: 'focus', handler: onFocus },
{ type: 'blur', handler: onBlur },
{ type: 'mousedown', handler: onMousedown },
{ type: 'mouseup', handler: onMouseup }
];
There are a couple of things that really trouble me with regards to how jQuery handles nested functions (not to the point that I can't sleep but it's getting there) and I wish a jQuery expert could explain how things work to bring me piece of mind.
Let's say you have the below HTML code:
<button id="first">click me first</button>
<button id="second">click me next</button>
And the following jQuery code:
$(document).ready(function() {
$('#first').click(function() {
$('#second').click(function() {
alert('test');
});
});
});
A dialog box will popup if you click the first button and then the second button.
I understand jQuery instantiates the $('#first').click() function when the DOM is ready and calls it when someone clicks on the first button.
However what I am puzzled with is the following:
[Q1] is the $('#second').click() function also instantiated on DOM ready or only when $('#one').click() is called?
Now, when you look at the jQuery code, there is nothing that "keeps us" in the $('#first').click() function, that is once the user clicks on the first button, the $('#second').click() function should be instantiated and we should exit the $('#one').click() function straight away. However after clicking the first button, jquery must somehow keep $('#second').click() indefinitely in memory in case the user clicks on the second button.
[Q2] how does jquery know to keep the $('#second').click() function in memory until the user clicks on the second button after clicking the first button?
Finally let's say you wanted to modify your code so that the user had to click the second button within 10 seconds of clicking the first button for the dialog box to appear:
[Q3] how would you implement this so that jQuery would know to stop listening for click events on the second button after 10 seconds?
Q1 - JS will simply load function definitions. It won't run it unless they are explicitly triggered/called. In this case, it will simply attach the event handler to #first and wait until someone clicks the button to fire the event. This will make the second function attach itself to the second button.
Q2 Again, it's not jQuery, it's JavaScript doing all the work. The method is simply attached to the DOM element and is triggered on the event it is attached to. JS is like any programming language and will keep all methods and variables in its memory.
The second click function isn't actually attached to the second button until after someone clicks on the first button. This is because, when the first button is clicked, JS knows to trigger the first method which does all the work of attaching the second method to the second button.
Q3 You could use setTimeout to unbind that method from the DOM element.
$(document).ready(function() {
$('#first').click(function() {
$('#second').click(function() {
alert('test');
setTimeout(function(){$('#second').unbind('click');}, 10000);
});
});
});
Note This unbinds all click event handlers from this DOM element. You can also unbind that particular method by passing it as a parameter. Check out the API docs for usage.
setTimeout : https://developer.mozilla.org/en/DOM/window.setTimeout
unbind : http://api.jquery.com/unbind/
[A1] The second function is only instantiated when #first is clicked as it is part of the execution of the first method. This also means that if you click #first n times you should get n alerts for every click on #second.
[A2] The function is rooted by the #second element. So long as that element is alive javascript knows to keep the function around.
[A3] You would need to save off the function pointer and do a setTimeout to clear it.
$(document).ready(function() {
$('#first').click(function() {
var secondFunction = function() {
alert('test');
};
$('#second').click(secondFunction);
setTimeout(function(){ $('#second').unbind('click', secondFunction); }, 10000);
});
});
A better implementation is probably something like:
$(document).ready(function() {
var enabled = false;
$('#first').click(function() {
enabled = true;
setTimeout(function(){ enabled = false; }, 10000);
});
$('#second').click(function() {
if(enabled) {
alert('test');
};
});
});
The answer to your first question: Yes, the second button will bind to click event only when a user clicks on the first button.
The second question: I'm not sure what you're asking.
The third one: Assuming the button one has nothing to do except bind the event to second button once clicked, you can set a timeout on document ready for 10 seconds. Now when the timer expires it must unbind the button one's click event hence blocking second button's event. I guess you understand now. e.g.
$(document).ready(function(){
setTimeout(removeEvent, 10000);
$('#first').click(function() {
$('#second').click(function() {
alert('test');
});
});
});
function removeEvent(){
$('#first').unbind('click');
}