Currently working on a piece of code written by someone else for event handling. there are parts of the code that I am unsure about and if possible would like to get an explanation of what it all means.
if (div.addEventListener) {
div.addEventListener("click", handleClick, false);
} else if (div.attachEvent) {
div.attachEvent("click", handleClick);
};
function handleClick(e) {
var evt = e || window.event;
var target;
if (evt.target) {
target = evt.target;
} else {
target = evt.srcElement;
}
alert("You clicked on " + target.id);
};
I understand that it is checking to see if there is an event occurring, but why does it addEventListener twice as well as attachEvent twice? Also why does one return a false value and the other does not?
I am unsure as to what the second part of code is doing altogether, any possible explanation? I know it is the code that is called once the event(s) occur.
"addEventListener" doesn't works in IE(older Versions), in IE(older Versions) "attachEvent" works, So here check is maintained that which function is available, so it would be used.
if (div.addEventListener) {
div.addEventListener("click", handleClick, false);
} else if (div.attachEvent) {
div.attachEvent("click", handleClick);
};
Only one of these will happen (it's an IF statement). It first checks if the browser supports addEventListener.
The false parameter it passes in is the useCapture argument:
"The third, now optional, parameter in addEventListener is “useCapture”, which literally means fire the event at “capture” and not “bubble”. When an event is attached to an element, it will fire once the event has fully bubbled up to the document. However, if you set the useCapture flag to true, the event will fire on capture, and not wait for the event to fully bubble up the DOM tree." - from http://benhowdle.im/useCapture-demystified.html
The second part of the code is ran when the user clicks the DIV. It's again, checking to see if the browser supports certain event properties, to get the right element that was clicked.
why does it addEventListener twice as well as attachEvent twice?
It doesn't.
It tests to see if the property has a value, and if it does it calls the function stored in the property.
Also why does one return a false value and the pother does not?
Neither returns a false value. One has false passed in as the third argument. They are different functions and don't work the same way.
Related
Is there anyway to remove an event listener added like this:
element.addEventListener(event, function(){/* do work here */}, false);
Without replacing the element?
There is no way to cleanly remove an event handler unless you stored a reference to the event handler at creation.
I will generally add these to the main object on that page, then you can iterate and cleanly dispose of them when done with that object.
You could remove the event listener like this:
element.addEventListener("click", function clicked() {
element.removeEventListener("click", clicked, false);
}, false);
Anonymous bound event listeners
The easiest way to remove all event listeners for an element is to assign its outerHTML to itself. What this does is send a string representation of the HTML through the HTML parser and assign the parsed HTML to the element. Because no JavaScript is passed, there will be no bound event listeners.
document.getElementById('demo').addEventListener('click', function(){
alert('Clickrd');
this.outerHTML = this.outerHTML;
}, false);
<a id="demo" href="javascript:void(0)">Click Me</a>
Anonymous delegated event listeners
The one caveat is delegated event listeners, or event listeners on a parent element that watch for every event matching a set of criteria on its children. The only way to get past that is to alter the element to not meet the criteria of the delegated event listener.
document.body.addEventListener('click', function(e){
if(e.target.id === 'demo') {
alert('Clickrd');
e.target.id = 'omed';
}
}, false);
<a id="demo" href="javascript:void(0)">Click Me</a>
Old Question, but here is a solution.
Strictly speaking you can’t remove an anonymous event listener unless you store a reference to the function. Since the goal of using an anonymous function is presumably not to create a new variable, you could instead store the reference in the element itself:
element.addEventListener('click',element.fn=function fn() {
// Event Code
}, false);
Later, when you want to remove it, you can do the following:
element.removeEventListener('click',element.fn, false);
Remember, the third parameter (false) must have the same value as for adding the Event Listener.
However, the question itself begs another: why?
There are two reasons to use .addEventListener() rather than the simpler .onsomething() method:
First, it allows multiple event listeners to be added. This becomes a problem when it comes to removing them selectively: you will probably end up naming them. If you want to remove them all, then #tidy-giant’s outerHTML solution is excellent.
Second, you do have the option of choosing to capture rather than bubble the event.
If neither reason is important, you may well decide to use the simpler onsomething method.
Yes you can remove an anonymous event listener:
const controller = new AbortController();
document.addEventListener(
"click",
() => {
// do function stuff
},
{ signal: controller.signal }
);
You then remove the event listener like this:
controller.abort();
You may try to overwrite element.addEventListener and do whatever you want.Something like:
var orig = element.addEventListener;
element.addEventListener = function (type, listener) {
if (/dontwant/.test(listener.toSource())) { // listener has something i dont want
// do nothing
} else {
orig.apply(this, Array.prototype.slice.apply(arguments));
}
};
ps.: it is not recommended, but it will do the trick (haven't tested it)
Assigning event handlers with literal functions is tricky- not only is there no way to remove them, without cloning the node and replacing it with the clone- you also can inadvertantly assign the same handler multiple times, which can't happen if you use a reference to a handler. Two functions are always treated as two different objects, even if they are character identical.
Edit: As Manngo suggested per comment, you should use .off() instead of .unbind() as .unbind() is deprecated as of jQuery 3.0 and superseded since jQuery 1.7.
Even though this an old question and it does not mention jQuery I will post my answer here as it is the first result for the searchterm 'jquery remove anonymous event handler'.
You could try removing it using the .off() function.
$('#button1').click(function() {
alert('This is a test');
});
$('#btnRemoveListener').click(function() {
$('#button1').off('click');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="button1">Click me</button>
<hr/>
<button id="btnRemoveListener">Remove listener</button>
However this only works if you've added the listener using jQuery - not .addEventListener
Found this here.
If you're using jQuery try off method
$("element").off("event");
Jquery .off() method removes event handlers that were attached with .on()
With ECMAScript2015 (ES2015, ES6) language specification, it is possible to do with this nameAndSelfBind function that magically turns an anonymous callback into a named one and even binds its body to itself, allowing the event listener to remove itself from within as well as it to be removed from an outer scope (JSFiddle):
(function()
{
// an optional constant to store references to all named and bound functions:
const arrayOfFormerlyAnonymousFunctions = [],
removeEventListenerAfterDelay = 3000; // an auxiliary variable for setTimeout
// this function both names argument function and makes it self-aware,
// binding it to itself; useful e.g. for event listeners which then will be able
// self-remove from within an anonymous functions they use as callbacks:
function nameAndSelfBind(functionToNameAndSelfBind,
name = 'namedAndBoundFunction', // optional
outerScopeReference) // optional
{
const functionAsObject = {
[name]()
{
return binder(...arguments);
}
},
namedAndBoundFunction = functionAsObject[name];
// if no arbitrary-naming functionality is required, then the constants above are
// not needed, and the following function should be just "var namedAndBoundFunction = ":
var binder = function()
{
return functionToNameAndSelfBind.bind(namedAndBoundFunction, ...arguments)();
}
// this optional functionality allows to assign the function to a outer scope variable
// if can not be done otherwise; useful for example for the ability to remove event
// listeners from the outer scope:
if (typeof outerScopeReference !== 'undefined')
{
if (outerScopeReference instanceof Array)
{
outerScopeReference.push(namedAndBoundFunction);
}
else
{
outerScopeReference = namedAndBoundFunction;
}
}
return namedAndBoundFunction;
}
// removeEventListener callback can not remove the listener if the callback is an anonymous
// function, but thanks to the nameAndSelfBind function it is now possible; this listener
// removes itself right after the first time being triggered:
document.addEventListener("visibilitychange", nameAndSelfBind(function(e)
{
e.target.removeEventListener('visibilitychange', this, false);
console.log('\nEvent listener 1 triggered:', e, '\nthis: ', this,
'\n\nremoveEventListener 1 was called; if "this" value was correct, "'
+ e.type + '"" event will not listened to any more');
}, undefined, arrayOfFormerlyAnonymousFunctions), false);
// to prove that deanonymized functions -- even when they have the same 'namedAndBoundFunction'
// name -- belong to different scopes and hence removing one does not mean removing another,
// a different event listener is added:
document.addEventListener("visibilitychange", nameAndSelfBind(function(e)
{
console.log('\nEvent listener 2 triggered:', e, '\nthis: ', this);
}, undefined, arrayOfFormerlyAnonymousFunctions), false);
// to check that arrayOfFormerlyAnonymousFunctions constant does keep a valid reference to
// formerly anonymous callback function of one of the event listeners, an attempt to remove
// it is made:
setTimeout(function(delay)
{
document.removeEventListener('visibilitychange',
arrayOfFormerlyAnonymousFunctions[arrayOfFormerlyAnonymousFunctions.length - 1],
false);
console.log('\nAfter ' + delay + 'ms, an event listener 2 was removed; if reference in '
+ 'arrayOfFormerlyAnonymousFunctions value was correct, the event will not '
+ 'be listened to any more', arrayOfFormerlyAnonymousFunctions);
}, removeEventListenerAfterDelay, removeEventListenerAfterDelay);
})();
//get Event
let obj = window; //for example
let eventStr= "blur"; //for example
let index= 0; //you can console.log(getEventListeners(obj)[eventStr]) and check index
let e = getEventListeners(obj)[eventStr][index];
//remove this event
obj .removeEventListener(eventStr,e.listener,e.useCapture);
THE END :)
i test in chrome 92, worked
How I used options parameter for my customEvent
options Optional
An object that specifies characteristics about the event listener. The available options are:
...
**once**
A boolean value indicating that the listener should be invoked at most once after being added. If true, the listener would be automatically removed when invoked.
for my custom function that I created, it worked quite nicely.
const addItemOpenEventListener = (item, openItem) => {
document.addEventListener('order:open', ({detail}) => {
if(detail.id === item.id) {
openItem();
}
}, {once: true})
};
el.addItemOpenEventListener(item, () => dispatch(itemOpen)()));
checked my console, seems like it worked (any feedback appreciated!)
The following worked well enough for me. The code handles the case where another event triggers the listener's removal from the element. No need for function declarations beforehand.
myElem.addEventListener("click", myFunc = function() { /*do stuff*/ });
/*things happen*/
myElem.removeEventListener("click", myFunc);
All of this is happening for IE8.
Due to script import orders, I'm having a bit of code being executed before JQuery is loaded where I need to fire a custom event.
This event will be picked up later in another bit of code when I'm sure JQuery will have been loaded. So I'd like to use JQuery to pick up this event.
I saw this previously asked question: How to trigger a custom javascript event in IE8? and applied the answer, which worked, but when I'm trying to pick up the Event via JQuery, then nothing happens.
Here's what I've tried:
function Event() {}
Event.listen = function(eventName, callback) {
if (document.addEventListener) {
document.addEventListener(eventName, callback, false);
} else {
document.documentElement.attachEvent('onpropertychange', function(e) {
if (e.propertyName == eventName) {
callback();
}
});
}
};
Event.trigger = function(eventName) {
if (document.createEvent) {
var event = document.createEvent('Event');
event.initEvent(eventName, true, true);
document.dispatchEvent(event);
} else {
document.documentElement[eventName] ++;
}
};
Event.listen('myevent', function() {
document.getElementById('mydiv-jquery').innerText = "myevent jquery";
});
$(document).on('myevent', function() {
document.getElementById('mydiv-vanilla').innerText = "myevent vanilla";
});
Event.trigger('myevent');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="mydiv-jquery">Nothing</div>
<div id="mydiv-vanilla">Nothing</div>
PS: The snippet doesn't seem to work properly in IE. Here's a jsfiddle that should be working.
There are a few problems with this code.
You shadow the built-in window.Event without checking if it exists; this could cause problems for other scripts.
You don't preserve the this binding when calling the callback from your onpropertychange listener. You should apply the callback to the document rather than calling it directly so the behavior will be as close as possible to addEventListener.
You attempt to increment document.documentElement[eventName] while it is undefined. The first call will change the value to NaN, so onpropertychange should pick it up, but on subsequent calls it will remain NaN.
You make no attempt to have .on() recognize your Event.listen function, so naturally the code in Event.listen will never be executed from a listener attached with .on().
Have you tried using Andrea Giammarchi's CustomEvent shim?
Just as the title says I'm curious if I'm guaranteed to get an event object inside of a Javscript event handler. The main reason I'm asking is that I've seen onClick event handlers that look like this.
function(e) {
if(e && e.target) {
//Code in here
}
}
Which seems wrong to me, but I know Javascript can have minor variances across browsers. Is there some time at which it's appropriate to check for the event object? Or the event target? It seems like you'd have to have a target to fire off an event.
No. Older versions of windows don't pass the event argument to the event handler. They have it in a global variable window.eventand the target is in .srcElement. Other than that exception, you should always get an event structure.
A work-around for the older versions of IE is this:
function(e) {
if (!e) {
e = window.event;
e.target = e.srcElement;
}
// code that uses e here
}
But, usually, this is addressed at a higher level by the function that you use to install event handlers. For example:
// add event cross browser
function addEvent(elem, event, fn) {
if (elem.addEventListener) {
elem.addEventListener(event, fn, false);
} else {
elem.attachEvent("on" + event, function() {
// set the this pointer same as addEventListener when fn is called
window.event.target = window.event.srcElement;
return(fn.call(elem, window.event));
});
}
}
Depending on the browser compatibility they are looking to achieve, this may be an acceptable solution. However, for older version of IE, the event object is a part of the global window object. In order to get the target in that case you would want window.event.srcElement, as there is no target.
More info here on the event object for IE.
I'm a newbie at JavaScript trying to learn event handlers. Looking at this fiddle http://jsfiddle.net/mjmitche/uV4kv/ can anyone tell me why the pop up is not appearing when the link is clicked?
I've also copied the code below
click me
function addEventHandler(oNode, sEvt, fFunc, bCaptures){
if (typeof(window.event) != "undefined")
oNode.attachEvent("on"+sEvt, fFunc);
else
oNode.addEventListener(sEvt,fFunc,bCaptures);
}
function onLinkClicked(e) {
alert("you clicked the link");
}
function setUpClickHandler(){
addEventHanlder(document.getElementById('clickLink'), "click", onLinkClicked, false);
}
addEventHandler(window,"load",setUpClickHandler,false);
There are basically three problems:
You have some typos, e.g. "Hanlder" instead of "Handler" and "Sevt" instead of "sEvt".
The code is already run after page load, so the event handler you add to window will never be called. Change in jsFiddle from onLoad to no wrap (head).
You "IE detection" does not work. I get this error in Chrome:
Uncaught TypeError: Object http://fiddle.jshell.net/_display/# has no method 'attachEvent'.
Better would be to test whether the function window.attachEvent exists. I also think that window.event is only available when an event is raised.
If this is corrected, your code will run (DEMO).
Further notes:
Testing which method is supported (i.e. attachEvent or addEventListener) on every call of your function is unnecessary. It won't change during the life of the page. Better is to test only once at the beginning. Example:
var addEventHandler = (function() {
if(window.attachEvent) {
return function(oNode, sEvt, fFunc) {
oNode.attachEvent("on"+sEvt, fFunc);
};
}
else {
return function(oNode, sEvt, fFunc, bCaptures) {
oNode.addEventListener(sEvt,fFunc,bCaptures);
};
}
}());
This assigns a function the supported function to addEventHandler.
A couple of problems:
You have a recurring typo, "Hanlder" instead of "Handler". There are at least two, one where you do your setUpClickHanlder (sic) function, and one within it (addEventHanlder).
Also, you have "Sevt" where you mean "sEvt". (JavaScript is case sensitive.)
Let tools help you. Use a browser that gives you a console showing errors. Use a debugger for single-stepping through code, looking at variable values at runtime with inspectors, etc.
Your addEventHandler needs adustment:
function addEventHandler(oNode, sEvt, fFunc, bCaptures) {
oNode.attachEvent ? oNode.attachEvent ("on" + sEvt, fFunc) :
oNode.addEventListener (sEvt, fFunc, bCaptures);
}
It is in event handlers themselves that you need to check for window.event
There are 24 div-objects waiting/listening for a mouse-click. After click on one div-object, I want to remove the EventListener from all 24 div-objects.
for (var i=1;i<=24;i++){
document.getElementById('div'+i).addEventListener('click',function(event){
for (var z=1;z<=24;z++){
document.getElementById('div'+z).removeEventListener()//Problem lies here
}
//Some other code to be run after mouseclick
},false);
}
The problem is that the removeEventListener is nested in addEventListener and I need to define type, listener, caption as attributes to the removeEventListener method. And I think it is impossible to define the listener because of nesting.
I also tried to define a function name, but it didn't worked:
for (var i=1;i<=24;i++){
document.getElementById('div'+i).addEventListener('click',function helpme(event){
for (var z=1;z<=24;z++){
document.getElementById('div'+z).removeEventListener('click',helpme,false);
}
//Some other code to be run after mouseclick
},false);
}
You can tell the event listener to simply fire just once:
document.addEventListener("click", (e) => {
// function which to run on event
}, { once: true });
The documentation says:
once:
A boolean value indicating that the listener should be invoked at most once after being added. If true, the listener would be automatically removed when invoked.
It should work with a named function. If your second approach does not work properly, try storing the initial listener into a variable, like this:
var handler = function(event) {
for(...) {
removeEventListener('click', handler, false);
}
};
addEventListener('click', handler, false);
Ps. if you care about speed, you may wish to consider using just one event handler. You can put the handler into the parent element of the divs, and then delegate the event from there. With 24 handlers your current approach probably doesn't have a very big performance hit, but this is something you should keep in mind if it ever feels slow.
For those who needs to remove after a certain condition (or even inside a loop too), one alternative is using AbortController and AbortSignal:
const abortController = new AbortController();
let handler = function(event) {
if(...) {
abortController.abort();
}
};
addEventListener('click', handler, {signal: abortController.signal});
The same answer:
element.addEventListener("click", (e) => {
// function which to run on event
}, { once: true });
You can read more here: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener