Remove event listener in ES6 - javascript

I have following code:
document.getElementsByClassName('drag')[i].addEventListener('mousedown', (e) => {
console.log('mousedown' + i);
});
It would be easy, if I would be able to name the function inside listener. But it's not possible in my case.
It would look like:
e.currentTarget.removeEventListener(e.type, nameFunction);
Is there any way to make it work?
Thank you.

Yes you can write like this.
document.getElementsByClassName('drag')[i].addEventListener('mousedown', mouseDownFun);
function mouseDownFun(e){
console.log('mousedown' + i);
}
e.currentTarget.removeEventListener(e.type, mouseDownFun);
So whenever mouse down event will be triggered it will listen in mouseDownFun.

It would be easy, if I would be able to name the function inside listener. But it's not possible in my case.
Don't use an arrow function if it doesn't allow you to do what you want.
document.getElementsByClassName('drag')[i].addEventListener('mousedown', function handler(e) {
console.log('mousedown' + i);
e.currentTarget.removeEventListener(e.type, handler);
});

There are two ways
When you define a function, then add and remove the listener when you want:
// Define a function
const handler = () => {
console.log('Click event...');
}
// Select the element and add event listener
const img = document.querySelector('img');
img.addEventListener('click', handler, false);
// ...
// Remove the event listener
img.removeEventListener('click', handler, false);
When you add an event listener and remove it on event:
// Select the element and add event listener
const img = document.querySelector('img');
img.addEventListener('click', function handler(event) {
console.log('Click event...');
// On event, remove the event listener
event.currentTarget.removeEventListener(event.type, handler);
});

Related

Dispatch event to element bypassing capturing and bubbling

Since dispatchEvent, as per the docs, will apply the:
normal event processing rules (including the capturing and optional
bubbling phase)
I'm looking for something similar but with a way to skip this process and trigger the event directly on the element. To trigger the default element event behavior while bypassing the processing stage.
As in, to capture the event at window level (before it reaches the other capture triggers) and pass it straight to the component (text area) invoking it directly.
(For example to trigger the default keydown of a text area without going through the hierarchy)
I've been trying to do it like this but if there is another event at window level this will not work:
window.addEventListener("keydown", this.keyDown, true);
keyDown = (event) => {
event.preventDefault();
event.nativeEvent && event.nativeEvent.stopImmediatePropagation();
event.stopImmediatePropagation && event.stopImmediatePropagation();
event.stopPropagation();
// Pass event straight to the element
return false;
};
I'm looking to trigger the default element event behavior while bypassing the processing
There may well be a more elegant way to do this, but one option is to remove the element from the DOM first, dispatch the event to it, then put it back into the DOM:
document.body.addEventListener('keydown', () => {
console.log('body keydown capturing');
}, true);
document.body.addEventListener('keydown', () => {
console.log('body keydown bubbling');
});
const input = document.querySelector('input');
input.addEventListener('keydown', () => {
console.log('input keydown');
});
const node = document.createTextNode('');
input.replaceWith(node);
input.dispatchEvent(new Event('keydown'));
node.replaceWith(input);
<input>
Since the element isn't in the DOM when the event is dispatched, the elements which used to be its ancestors won't see the event.
Note that events dispatched to detached elements do not capture/bubble regardless, not even to parents or children of element the event was dispatched to.
Without removing the element from the DOM entirely beforehand, if the input can exist in a shadow DOM, you can also dispatch an event to it there, and the event won't capture down or bubble up (though user input, not being custom events, will propagate through):
document.body.addEventListener('keydown', () => {
console.log('body keydown capturing');
}, true);
document.body.addEventListener('keydown', () => {
console.log('body keydown bubbling');
});
outer.attachShadow({mode: 'open'});
const input = document.createElement('input');
outer.shadowRoot.append(input);
input.addEventListener('keydown', () => {
console.log('input keydown');
});
input.dispatchEvent(new Event('keydown'));
<div id="outer"></div>
Another approach would be to call stopPropagation and stopImmediatePropagation in the capturing phase, at the very beginning, when the event is at the window, and then manually call the listener function you want. Make sure to attach your window listener before any other scripts on the page run, to make sure none of the page's listeners can see the event first:
// Your script:
const input = document.body.appendChild(document.createElement('input'));
input.className = 'custom-extension-element';
const handler = (e) => {
setTimeout(() => {
console.log(e.target.value);
});
};
window.addEventListener(
'keydown',
(e) => {
if (e.target.closest('.custom-extension-element')) {
e.stopImmediatePropagation(); // Stop other capturing listeners on window from seeing event
e.stopPropagation(); // Stop all other listeners
handler(e);
}
},
true // add listener in capturing phase
);
// Example page script
// which tries to attach listener to window in capturing phase:
window.addEventListener(
'keydown',
(e) => {
console.log('page sees keydown');
},
true
);
document.body.addEventListener('keydown', () => {
console.log('body keydown capturing');
}, true);
document.body.addEventListener('keydown', () => {
console.log('body keydown bubbling');
});
The best way around propagation issues is to just execute the function:
function tester(){
console.log("Just fire the function! Don't dispatchEvent!");
}
tester();
document.getElementById('test').onkeyup = function(e){
e.stopPropagation();
console.log(this.id); console.log(e.target); tester();
}
document.body.onkeyup = ()=>{
console.log("This shouldn't fire when on #test");
}
<input id='test' type='text' value='' />

Adding and Removing Event Listeners Dynamically

I am having issues dynamically adding and removing eventListeners. I want to be able to add an event listener to all child nodes of an element. Then at a later time remove them, and add them back.
Note: I am aware of EventListener options.once however this doesn't exactly solve my case.
Here is some sample code of what I am trying to do:
var cont;
window.onload = function() {
cont = document.querySelector(".container");
[...cont.children].forEach(c => {
c.addEventListener("click", clicked.bind(c))
});
}
function clicked() {
console.log(`removing event listener from ${this}`);
this.removeEventListener("click", clicked); //Not actually removing - why?
}
Thanks everyone!
The problem is that .bind creates a new function. The function passed to addEventListener can only be removed if that exact same function is passed to removeEventListener. A bound function is not the same as the original unbound function, so removeEventListener won't work if you pass it the unbound function.
In your situation, one possibility would be to use a Map indexed by the HTML elements, whose value is the bound listener for that element, so that they can then be removed later:
const listenerMap = new Map();
const cont = document.querySelector(".container");
[...cont.children].forEach(c => {
const listener = clicked.bind(c);
listenerMap.set(c, listener);
c.addEventListener("click", listener);
});
function clicked() {
console.log(`removing event listener from ${this}`);
this.removeEventListener("click", listenerMap.get(this));
}
<div class="container">
<div>one</div>
<div>two</div>
<div>three</div>
</div>

Javascript Vanilla - How to attach event listener on element inside ajax content?

If in Jquery we can simply delegate the event in <body> like this:
$('body').on('click', 'a[data-method]', function () {
// event action here
});
But how we do that in Vanilla Javascript?
Note: <a data-method=""> is created inside ajax content
Thanks.
You can add an event listener to body, and then check to see if the event.target.matches the desired selector:
document.body.addEventListener('click', (event) => {
if (!event.target.matches('a[data-method]')) return;
console.log('click');
});
setTimeout(() => {
const a = document.body.appendChild(document.createElement('a'));
a.setAttribute('data-method', 'foo');
a.textContent = 'aaa';
}, 100);

RemoveEventListener does not work on named function

I am twisting my mind and fingers finding why removeEventListenerwon't work. It should be a one time event, so I included the removeEventListener in the callback function, but the event fires every time.
var group = ...//some div's ID
var img_button = document.createElement("SPAN");
img_button.setAttribute("id","imgbutton_"+group);
if (figures.addEventListener){
figures.addEventListener(
'click', function(e){if (!e) e = window.event;e.stopPropagation();}, false);
img_button.addEventListener(
'mouseover', loadImg(group), false);
}else{...};
And the callback loadImg:
function loadImg(nodeId){//loading images when needed
return function(e){
if (!e) e = window.event;
[...]
//remove eventlistener when executed once
var sp = (e.target ? e.target : e.srcElement);
if (sp.removeEventListener){
sp.removeEventListener(
'mouseover', loadImg);
} else {...};
};
The anonymous function can stay but the loadImg I only need once. What did I forget?
loadImg is not the name of the listener function. You've attached an anonymous function which is returned from loadImg().
To solve this problem, you can give a name to the actual event handler function:
return function handler (e){
:
this.removeEventListener('mouseover', handler, false);
:
}
A working demo at jsFiddle.
this in event handler refers automatically to the element to which the event was attached.

dojo how to overwrite event handler

Currently dojo uses on method to connect event to handler.
btn = new Button();
btn.on('click', function () {console.log('do something');});
this will call the attached function when the button gets clicked.
however, according to the documents, removing existing handlers should be done in the following way
handler = btn.on('click', function () {console.log('do something');});
handler.remove();
this is not the way I want to remove event handler.
I do not store the handler reference anywhere. But I want to add a new 'click' event by doing
btn.on('click', function () {console.log('do something different');});
so that it replaces the existing 'click' event handler and add a new one.
Is there any way to achieve what I want?
Thanks!
That's not possible, the framework tells you to do it in the way by creating a reference to the event handler. This is similar to how other frameworks like jQuery work.
jQuery has of course a mechanism to remove all event handlers by using the off() function, but that's not available in Dojo either. Like Chris Hayes suggested in the comments, you can implement such a feature by yourself, either by wrapping it inside another module, or by using aspects on the dojo/on module.
For example, you can wrap it inside a new module:
// Saving the event handlers
var on2 = function(dom, event, callback) {
on2.handlers = [];
if (on2.handlers[event] === undefined) {
on2.handlers[event] = [];
}
var handler = on(dom, event, callback);
on2.handlers[event].push({
node: dom,
handler: handler
});
return handler;
};
// Off functionality
lang.mixin(on2, on, {
off: function(dom, event) {
if (this.handlers[event] !== undefined) {
array.forEach(this.handlers[event], function(handler) {
if (handler.node === dom) {
handler.handler.remove();
}
});
}
}
});
And then you can use it:
on2(dom.byId("test"), "click", function() {
console.log("test 1 2 3"); // Old event handler
});
on2.off(dom.byId("test"), "click"); // Remove old event handlers
on2(dom.byId("test"), "click", function() {
console.log("test 4 5 6"); // New event handler
});
This should work fine, as you can see in this fiddle: http://jsfiddle.net/X7H3F/
btn = new Button();
btn.attr('id','myButton');
query("#myButton").on('click', function () {console.log('do something');});
Do the same thing when you want to replace your handler. Like,
query("#myButton").on('click', function () {console.log('do something different');});
Hope that helps :)

Categories