How to overwrite event handler for 'click' event in javaScript? - javascript

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

Related

How can I watch Event Handlers being added to an Element?

I've got a bug that looks like it's caused by an Event handler being attached a click event:
mxpnl.track_links("#pagebody a", "Click.body");
I'd like to watch to see how that Element's event handler is added (and when)
I found it in the Chrome Debugger (Dev Tools) > Elements and choose Break on Attribute modifications. Debugger never Breaks.
I also selected it's Parent Div (that it is within) and set Debugger (right-click on Element) > break on subtree modifications. Again, it never breaks.
What am I doing wrong here?
Adding an event listener isn't an attribute change (often) - rather, generally it's a call to addEventListener or an on- assignment. So, listening to attribute changes won't work.
One option would be to monkeypatch addEventListener so that debugger runs when addEventListener is called with the matching arguments. For example:
// snippet might not actually enter the debugger due to embedded sandboxing issues
const nativeEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(...args) {
if (this.matches('div') && args[0] === 'click') {
console.log('listener is being added to a div');
debugger;
}
nativeEventListener.apply(this, args);
}
// then, when an event listener is added, you'll be able to intercept the call and debug it:
document.querySelector('div').addEventListener('click', () => {
console.log('clicked');
});
<div>click me</div>
In very simple term if explain then event listeners is just a function which add in an array with reference of string such as "click" and "on" etc..
So when you say.....
function myClick1(){
console.log('myclick1 called');
}
document.querySelector('mydiv').addEventListener('click', myClick1);
function myClick2(){
console.log('myclick2 called');
}
document.querySelector('mydiv').addEventListener('click', myClick2);
it add the function myClick1 and myClick2 in event listener array for click and execute in sequence as it added.
AND YOU CAN USE PROTOTYPE OF EVENT TARGET TO MONKEY PATCH THE EVENT LISTENER ARRAY.

Reattach a removed event handler doesn't work

I want to remove the click of an element when it is clicked (I'm using off() for this), and want to reattach the click when the element isn't clicked ('actived'). To indicate it is not clicked, I'm using a class named 'disabled'.
But when I remove, I cant add it again. It just doesn't attach the event again!
This is what I'm trying to do:
$('.my-element').on('click', function() {
$('.my-element').off('click');
});
var disabled = setInterval(function() {
if($('.my-element').hasClass('not-clicked')) {
$('.my-element').on();
}
}, 1000);
I'm using setInterval() to watch whether the element isn't clicked. If it isn't, it can be using on() again.
I have even tried to remove the event handler in the browser console:
$('.my-element').off();
But when I try to reattach the event handler...
$('.my-element').on();
It doesn't work, and will not repeat the behavior.
jQuery.on() doesn't remember removed events, but you can save the current events before removing them with:
var handlers;
$('.my-element').on('click', function() {
handlers = $.extend(true, {}, $._data( this, "events" ));
$('.my-element').off('click');
});
and then attach with:
$(".my-element").on("click", handlers.click[0].handler);
Using on and off without any parameters has no effect. Ideally you should pass the name of the event and it's handler to those methods.
var handler = function() {
// ...
}
$('.my-element').on('click.namespace', handler);
// ...
$('.my-element').off('click.namespace', handler);
If the handler should be called only once you could also consider using the one method. Also using setIntevarl here is a bad idea. You should bind the handler right after adding the class. In this case I'd suggest using event delegation technique:
$('#aStaticParent').on('click', '.my-element.not-clicked', function() {
// the handler is called only if the element has `not-clicked` className
$(this).removeClass('not-clicked');
});

jQuery .one() with same function for different events on different elements

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.

How do I replace an event handler with jQuery?

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");

stopPropagation not working for KeyListener in YUI 2.7

I have created a new YAHOO.util.KeyListener to attach to a specific element and have also created another new YAHOO.util.KeyListener to attach to the entire document. They are both associated with the enter key (keys:13).
In the handler function for the listener attached to the specific element, I have the following code:
getDetailsLocalnameInput = function(e) {
getDetails(localnameInput.value);
YAHOO.util.Event.preventDefault(e);
YAHOO.util.Event.stopPropagation(e);
};
Yet, the event from the keypress continues to propagate up to the key listener attached to the entire document. I do not want the handler for the key listener attached to the entire document to get kicked off. I am sure that both handlers are being called, but only want the handler attached to the specific element to run.
Is it correct to use YAHOO.util.Event.stopPropagation with YAHOO.util.KeyListener?
Is there a different way I should go about preventing the keypress event from being propagated?
I have also tried using the function YAHOO.util.Event.stopEvent and setting e.cancelBubble with no success.
I have been testing all of this with Firefox 3.5. I cannot get stopPropagation() to work at all.
Try this:
getDetailsLocalnameInput = function(e) {
getDetails(localnameInput.value);
if(window.event){
e.cancelBubble=true;//In IE
}else{
evt.stopPropagation();//in Others
}
//YAHOO.util.Event.preventDefault(e);
//YAHOO.util.Event.stopPropagation(e);
};

Categories