I have an introduction on my page which shall disappear when a key is pressed or a certain elements is clicked on. I'll use the same function for both events but as the page is heavily modified the event shall fire only once regardless which way it was triggered.
My function:
function start() {
$('.intro').remove();
$('.something-else').show();
}
How I bind the events:
$('body').keypress(start);
$('.intro').click(start);
If the events were the same I could say
$('body, .intro').one('click', start);
If both events were to happen to the same element I could say:
$('.intro').one('click keypress', start);
How to combine both things: having different elements and different events and the function must only be called once?
The simplest solution would be to store whether your function has been called or not.
var started = false;
function start() {
if(started) return;
$('.intro').remove();
$('.something-else').show();
started = true;
}
Then your function can only be called once, every other call will be ignored.
I don't think this solution is a clean as an explicit approach, but is done using a combination of .one() and .trigger(). Because you call $(".intro").remove() on one of the bound items the event handler is implicitly unbound. Sample Fiddle
Excluding the html seen in fiddle, this is the code.
// Your event handler
function start() {
$("#intro").remove();
$("#main").show();
console.log("start handler called");
}
// Handle the keypress event
$("body").one( "keypress", start );
// Allow user to click through the intro, but trigger the keypress handler on the body which is a one-time event handler)
$("#intro").one( "click", function () { $("body").trigger( "keypress" ); });
Use two lines of code and be done ;)
$('body').one('keypress', start);
$('.intro').one('click', start);
Edit:
You should manually unbind the other callback, lest it runs later, unexpectedly.
Related
I want to overwrite event handler for click event. This is the event handler I have attached initially.
document.querySelector("[data-id='start-btn']")
.addEventListener("click", function (evt) {
//some code
});
Again after some condition, I want to over write this handler and attach new for 'click' event.
//removing
document.querySelector("[data-id='start-btn']")
.removeEventListener("click", function (evt) {
//some code
}, false);
//attaching new
document.querySelector("[data-id='start-btn']")
.addEventListener("click", function (evt) {
//code
});
But still it is executing the previous event handler.I have used removeEventListener(but I guess, its not working).
Guide me where I am going wrong.
The only way to remove a handler added with addEventListener is to use removeEventListener with exactly the same arguments. That means you'll need a reference to the original function:
var handler = function (evt) {
//some code
};
document.querySelector("[data-id='start-btn']").addEventListener("click", handler);
then to remove
document.querySelector("[data-id='start-btn']").removeEventListener("click", handler);
removeEventListener makes sense really only when using function references rather than passing an entire function body to both it and addEventListener, which would potentially mean mass duplication of code (and, as you've found, doesn't work anyway.)
So, prepare a reference to your function:
function my_func() { /* code */ }
And pass it as the handler argument to add/removeEventListener
document.querySelector('query').addEventListener('click', my_func);
document.querySelector('query').removeEventListener('click', my_func);
There is an easier way that utilises an older coding standard. If you specifically want only one event handler for a given type and element, you can use the DOM-zero onclick.
document.querySelector('query').onClick = my_func;
document.querySelector('query').onClick = my_func2; /* my_func() will no longer fire */
As you can see from some other answers, removing an event listener can be kind of a nightmare. Thankfully in certain circumstances there is an easier way: add another event listener that fires earlier and cancels out the rest.
In my case there was a click event handler I wanted to override and I was able to add another event handler with useCapture=true (among other things) to override it.
document.body.addEventListener('click',function (e) {
if (e.target.innerHTML.toLowerCase() == 'regular') {
e.target.insertAdjacentHTML('afterend','<div>One we do <strong>not want</strong></div>');
e.preventDefault();
}
});
document.body.addEventListener('click',function (e) {
if (e.target.innerHTML.toLowerCase() == 'fixed') {//or any event or other pre-/evaluations/conditions here
e.target.insertAdjacentHTML('afterend','<div>One we <strong>do want</strong></div>');
e.preventDefault();
e.stopPropagation();
}
//document.querySelector('.somethingelse').click();//etc.
},true);
<div>Regular</div>
<div>Fixed</div>
I was building a chrome extension and for some reason removeEventListener was not working as expected. The solution I came up with was to use the cloneNode method.
The documentation says
Cloning a node copies all of its attributes and their values,
including intrinsic (inline) listeners. It does not copy event
listeners added using addEventListener() or those assigned to element
properties.
What I did was created a clone for my actual element and replaced it with the cloned one. This removes all event listeners from the element. A simple example will be like
let newClonedElem = myActualElem.cloneNode(true);
myActualElem.parentNode.replaceChild(newClonedElem, myActualElem);
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;
});
I have a site that uses AJAX to navigate. I have two pages that I use a click and drag feature using:
$(".myDragArea").mousedown(function(){
do stuff...
mouseDrag = true; // mouseDrag is global.
});
$("body").mousemove(function(){
if (mouseDrag) {
do stuff...
}
});
$("body").mouseup(function(){
if (mouseDrag) {
do stuff...
mouseDrag = false;
}
});
I just type that out, so excuse any incidental syntax errors. Two parts of the site use almost identical code, with the only difference being what is inside the $("body").mouseup() function. However, if I access the first part, then navigate to the second part, the code that runs on mouseup doesn't change. I have stepped through the code with Firebug, and no errors or thrown when $("body").mouseup() is run when the second part loads.
So, why doesn't the event handler change when I run $("body").mouseup() the second time?
Using $("body").mouseup( ... ) will add an event handler for the body that is triggered at mouseup.
If you want to add another event handler that would conflict with current event handler(s) then you must first remove the current conflicting event handler(s).
You have 4 options to do this with .unbind(). I'll list them from the least precise to the most precise options:
Nuclear option - Remove all event handlers from the body
$("body").unbind();
This is pretty crude. Let's try to improve.
The elephant gun - Remove all mouseup event handlers from the body
$("body").unbind('mouseup');
This is a little better, but we can still be more precise.
The surgeon's scalpel - Remove one specific event handler from the body
$("body").unbind('mouseup', myMouseUpV1);
Of course for this version you must set a variable to your event handler. In your case this would look something like:
myMouseUpV1 = function(){
if (mouseDrag) {
do stuff...
mouseDrag = false;
}
}
$("body").mouseup(myMouseUpV1);
$("body").unbind('mouseup', myMouseUpV1);
$("body").mouseup(myMouseUpV2); // where you've defined V2 somewhere
Scalpel with anesthesia (ok, the analogy's wearing thin) - You can create namespaces for the event handlers you bind and unbind. You can use this technique to bind and unbind either anonymous functions or references to functions. For namespaces, you have to use the .bind() method directly instead of one of the shortcuts ( like .mouseover() ).
To create a namespace:
$("body").bind('mouseup.mySpace', function() { ... });
or
$("body").bind('mouseup.mySpace', myHandler);
Then to unbind either of the previous examples, you would use:
$("body").unbind('mouseup.mySpace');
You can unbind multiple namespaced handlers at once by chaining them:
$("body").unbind('mouseup.mySpace1.mySpace2.yourSpace');
Finally, you can unbind all event handlers in a namespace irrespective of the event type!
$("body").unbind('.mySpace')
You cannot do this with a simple reference to a handler. $("body").unbind(myHandler) will not work, since with a simple reference to a handler you must specify the event type ( $("body").unbind('mouseup', myHandler) )!
PS: You can also unbind an event from within itself using .unbind(event). This could be useful if you want to trigger an event handler only a limited number of times.
var timesClicked = 0;
$('input').bind('click', function(event) {
alert('Moar Cheezburgerz!');
timesClicked++;
if (timesClicked >= 2) {
$('input').unbind(event);
$('input').val("NO MOAR!");
}
});
Calling $("body").mouseup(function) will add an event handler.
You need to remove the existing handler by writing $("body").unbind('mouseup');.
jQUery doesn't "replace" event handlers when you wire up handlers.
If you're using Ajax to navigate, and not refreshing the overall DOM (i.e. not creating an entirely new body element on each request), then executing a new line like:
$("body").mouseup(function(){
is just going to add an additional handler. Your first handler will still exist.
You'll need to specifically remove any handlers by calling
$("body").unbind("mouseUp");
I have an ajax app that will run functions on every interaction. I'd like to be able to run my setup function each time so all my setup code for that function remains encapsulated. However, binding elements more than once means that the handler will run more than once, which is obviously undesirable. Is there an elegant way in jQuery to call bind on an element more than once without the handler being called more than once?
User jQuery one function like Tom said, but unbind the handler each time before binding again. It helps to have the event handler assigned to a variable than using an anonymous function.
var handler = function(e) { // stuff };
$('#element').unbind('click', handler).one('click', handler);
//elsewhere
$('#element').unbind('click', handler).one('click', handler);
You can also do .unbind('click') to remove all click handlers attached to an element.
You could attach the event to document with the one() function:
$(document).one('click', function(e) {
// initialization here
});
Once run, this event handler is removed again so that it will not run again. However, if you need the initialization to run before the click event of some other element, we will have to think of something else. Using mousedown instead of click might work then, as the mousedown event is fired before the click event.
You can also use .off() if unbind doesn't do the trick. Make sure the selector and event given to .off exactly match the ones initially provided to .on():
$("div.selector").off("click", "a.another_selector");
$("div.selector").on("click", "a.another_selector", function(e){
This is what worked for me in resolving the same ajax reloading problem.
The answer from Chetan Sastry is what you want. Basically just call a $(element).unbind(event); before every event.
So if you have a function like loadAllButtonClicks() that contains all the
$(element).on("click", function (){});
methods for each button on your page, and you run that every time a button is clicked, this will obviously produce more than one event for each button. To solve this just add
$(element).unbind(event);
before every
$(element).on("click", function (){});
and it will unbind all events to that element, then add the one click event.
I know how to bind multiple events and all that stuff. What I want to do is have multiple events occur to trigger a function.
Like
$(this).click and $(this).mousemove triggers a function
Is there a way to do this? Is it possible or am I just dreaming.
With a better understanding now, one thing you could do is have one event bind and unbind the other:
Example: http://jsfiddle.net/ZMeUv/
$(myselector).mousedown( function() {
$(document).mousemove(function() {
// do something
});
$(document).mouseup(function() {
$(this).unbind(); // unbind events from document
});
});
This prevents the mousemove from constantly firing when you have no need for it.
You can use jQuery's special events to package everything nicely and optimize things in the process. A mousedown and mousemove combo also commonly goes by the name "drag", so here's an example of creating a drag event that you can bind to elements. Note, that this code is specific to jQuery 1.4.2
One of the advantages to using this is that you only bind the mousemove, mouseout, and mousedown handlers once each element, no matter how many times that element is bound to the drag event. Now this isn't the most optimal way of doing it, and you can setup just 3 handlers on the document and manage everything with it, which is equally easy to do with the special events API. It just provides a nicely packaged way of building complex interactions than would be possible with just native events or custom events, in the jQuery sense.
$("..").bind("drag", function() {
...
});
I will try and add more documentation on what's actually going on, as it looks pretty unintuitive, I must confess. Checkout another nice article on the topic.
See an example of this here. To create this custom special event, use:
jQuery.event.special.drag = {
// invoked each time we bind drag to an element
add: function(obj) {
var originalHandler = obj.handler;
obj.handler = function(event) {
var el = jQuery(this);
if(el.data('mousePressed')) {
return originalHandler.apply(this, arguments);
}
};
},
// invoked only the first time drag is bound per element
setup: function(data, namespaces) {
var el = jQuery(this);
el.data('mousePressed', false);
el.bind('mousedown', function() {
jQuery(this).data('mousePressed', true);
});
jQuery(document).bind('mouseup', function() {
el.data('mousePressed', false);
});
el.bind('mousemove', jQuery.event.special.drag.handler);
},
// invoked when all drag events are removed from element
teardown: function(namespaces) {
var el = jQuery(this);
jQuery.removeData(this, 'mousePressed');
el.unbind('mousedown');
el.unbind('mouseup');
},
// our wrapper event is bound to "mousemove" and not "bind"
// change event type, so all attached drag handlers are fired
handler: function(event) {
event.type = 'drag';
jQuery.event.handle.apply(this, arguments);
}
};
Try something like this?
var isDown = false;
$(sel).mousedown(function() {
isDown = true;
});
$(sel).mouseup(function() {
isDown = false;
});
$(sel).mousemove(function() {
if (isDown) {
// Mouse is clicked and is moving.
}
});
If I'm reading your question correctly, you're asking about requiring the combination of multiple events to trigger a single function. It's possible to achieve this sort of thing, but I think it will depend greatly on the specific events and the logic or illogic of their combination. For example, the mousemove event:
...is triggered whenever the mouse
pointer moves, even for a pixel. This
means that hundreds of events can be
generated over a very small amount of
time.
Contrast that with the mousedown event, which is -- well, one per click. How to combine? The jQuery API goes on to state:
A common pattern is to bind the
mousemove handler from within a
mousedown hander [sic], and to unbind it
from a corresponding mouseup handler.
If implementing this sequence of
events, remember that the mouseup
event might be sent to a different
HTML element than the mousemove event
was. To account for this, the mouseup
handler should typically be bound to
an element high up in the DOM tree,
such as <body>.
Perhaps another approach would be to create a primitive finite state machine to ingest as inputs the various relevant events you have in mind, update its state accordingly, and then trigger a custom event when appropriate states are achieved. This all smells a little bit like reinventing the wheel, but maybe your requirement is very specific or unusual.
References: jQuery API: mousemove()
Alright, thanks for your idea Patrick. It reminded of a way I had done something like this in Java.
var m_down = false;
$(this).mousedown(function() {
m_down = true;
});
$(this).mouseup(function() {
m_down = false;
});
$(this).mousemove(function() {
// Code to occur here
});