I have an event "pointerdown" but I want it to cancel the event call when a certain condition is met in one of the callbacks. So, all the next callbacks should not be called.
I have tried evt.preventDefault(); but that does not work, I have also tried evt.stopPropagation(); but that does not work.
const pointer = getMousePos(evt);
if (inBounds(pointer)) {
evt.preventDefault();
evt.stopPropagation();
}
The inBounds function returns true as expected, but the next callbacks of the event are still called. This event is added first, before the other events I wish to cancel but they are not.
If your listeners are attached on the same element, you will need to use stopImmediatePropagation() instead of stopPropagation()
The stopImmediatePropagation() method of the Event interface prevents other listeners of the same event from being called.
If several listeners are attached to the same element for the same event type, they are called in the order in which they were added. If stopImmediatePropagation() is invoked during one such call, no remaining listeners will be called.
https://developer.mozilla.org/en-US/docs/Web/API/Event/stopImmediatePropagation
You can also find a little description of the difference between both methods here:
stopPropagation vs. stopImmediatePropagation
Here a little demonstration of how you can use it. In this case, the second listener will never be called when the counter is a even number.
let counter = 0
const button = document.getElementById('TheButton')
button.addEventListener('click', e => {
counter++
console.log(`first listener: ${counter}`)
if (counter % 2 === 0) e.stopImmediatePropagation()
})
button.addEventListener('click', e => {
console.log(`second listener: ${counter}`)
})
<button id="TheButton">
OK
</button>
Use a global variable that you toggle to indicate whether the other event code should run.
let doBFunction = true;
element.addEventListener("pointerdown", function(evt) {
const pointer = getMousePos(evt);
if (inBounds(pointer)) {
doBFunction = false;
} else {
doBFunction = true;
}
// rest of code
});
element.addEventListner("pointerdown", function(evt) {
if (!doBfunction) {
return;
}
// rest of code
});
Related
Is there any way to pause all the "click" event Listeners so that the new click event listener that I add will alone work ? I need to resume all the pre-existing event listeners later.
Thank You
Edit:
I added used stopPropogation() method add the end of new click event listener. And to resume all the pre-existing event listener I just deleted the new click listener. It worked perfectly !
Thanks for the support !
You could route all your listeners through a central function that qualified if they should continue or not.
let elems = document.querySelectorAll(".thing");
elems.forEach(el => el.addEventListener('click', function(e) {
if (okForMeToRun(e)) {
console.log('I am allowed')
} else {
console.log('I am not allowed');
}
}));
function okForMeToRun(e) {
// in case we need to examine the event that wants to fire...
console.log(e.target.innerText + ' wants to fire ')
if (e.target.classList.contains('goodtogo')) return true;
return false;
}
<button class='thing goodtogo'>Test me </button> <button class='thing'>Then test me </button>
let textList = document.getElementsByClassName('click');
let testFun = () => {
for (let element of textList) {
element.addEventListener('click', e => {
console.log(e.target.textContent);
if (e.target.textContent === 'TEXT3') {
console.log('WIN!');
return; // After this line I want the function to stop, but it still works. Why? And how to stop it?
}
});
}
};
testFun();
<div>
<p class="click">TEXT1</p>
<p class="click">TEXT2</p>
<p class="click">TEXT3</p>
<p class="click">TEXT4</p>
</div>
It is clear to me that this is the basics of JavaScript, but I have not come to a solution for hours.
The application I am writing is based on the manipulation of DOM elements. I need a listener for several elements at the same time and for a hit on the right element, I want the function to stop working.
Also, the break keyword doesn't work either because I'm in a function inside addEventListener.
So how do I solve this problem? Is there a mistake in the logic of the program?
return is returning from the event handler. No code in the event handler will run until after the testFun call has finished, if and when the click event occurs in one of the elements. Since testFun has already finished its work, there's nothing the code in the event handler can do to prevent that — it can't go back in time. :-)
You could remove the event handler from the elements (or just some of them?), though, so that they don't respond to clicks after the first click:
let testFun = () => {
// Create the handler once
const handler = e => {
console.log(e.target.textContent);
if (e.target.textContent === "TEXT3") {
console.log("WIN!");
// Remove it from the elements
for (let element of textList) {
element.removeEventListener("click", handler);
}
}
};
for (let element of textList) {
element.addEventListener("click", handler);
}
};
I want avoid that double click also fire a single click event.
A simple solution i found is to delay the click with a timer and destroy the timer if a double click is fired.
var pendingClick;
function myclick(){
clearTimeout(pendingClick);
pendingClick = setTimeout(function(){
console.log('click');
}, 500);
}
function mydblclick(){
clearTimeout(pendingClick);
console.log('double click');
}
<div onclick="myclick()" ondblclick="mydblclick()">Double Click Me!</div>
But this solution is based on timing, if the double click is too slow (>500ms) it also fire a single click.
There is a stable solution for handle both click and double click?
Double-clicking in itself is "based on timing", even in the standard implementation of dblclick / ondblclick. There will always be the issue of a single-click being fired if the double-click is "too slow". What is "too slow"? 300ms? 500ms? 1000ms? Your double-clicks may be only 50ms apart, while my mom's double-clicks are 1-2 seconds apart...
You can get the event and cancel it with the addEventListener like this:
document.addEventListener('dblclick', (event) => {
event.preventDefault();
event.stopPropagation();
}, true); // With this true, you are cancelling the dblclick event
let pendingClick;
function myclick(){
clearTimeout(pendingClick);
pendingClick = setTimeout(function (){
console.log('click');
}, 500);
}
function mydblclick(){
clearTimeout(pendingClick);
console.log('double click');
}
<div onclick="myclick()" ondblclick="mydblclick()">Double Click Me!</div>
Only work with the 'onclick' function to check if it was one or two clicks and use a variable to count the number of clicks in a given time interval.
Example:
var pendingClick;
var clicked = 0;
var time_dbclick = 500 // 500ms
function myclick(){
clicked++;
if(clicked >= 2){
mydblclick()
clearTimeout(pendingClick)
clicked = 0;
return;
}
clearTimeout(pendingClick)
pendingClick = setTimeout(() => {
console.log('One click!')
clicked = 0;
}, time_dbclick);
}
function mydblclick(){
console.log('double click');
}
<div onclick="myclick()">Double Click Me!</div>
Custom Events instead of inline event handlers
If one prefers to use .addEventListener and .removeEventListener instead of HTML inline-eventhandlers, I would suggest another approach based on Custom Events. That means one would not make use of the standard implementation of "click" and "dblclick", but create own event handling for both:
let lastLeftClick = document.dispatchEvent(new Event("click"));
let doubleclickLength = 300;
function leftClickHandler (e) {
if (e.button != 0) return; // only left clicks shall be handled;
let delaySinceLastClick = e.timeStamp - lastLeftClick.timeStamp;
let eIsDoubleClick = delaySinceLastClick < doubleclickLength;
if (eIsDoubleClick) {
let doubleclickEvt = new CustomEvent("doubleclick", e);
lastLeftClick = lastLeftClick = doubleclickEvt;
document.dispatchEvent(doubleclickEvt);
} else {
let singleClickEvt = new CustomEvent("singleclick", e);
lastLeftClick = singleClickEvt;
document.dispatchEvent(lastLeftClick);
}
}
// adding above click event implementation:
document.addEventListener("click", leftClickHandler);
using the new custom events:
document.addEventListener("singleclick", e=>console.log("single click"));
document.addEventListener("doubleclick", e=>console.log("double click"));
I'm trying to create a horizontal scrolling container. In a precise case i need to revert e.preventDefault(); from a click.
I tried a lot of options, changing 'window.location.href' in the else statement seems to be a great option.
But i can't figure how to grab the href from the link clicked.
Any idea can help to achieve my goal. :)
slider.addEventListener('mouseup', () => {
isDown = false;
// Disable click event (for ever unfortunately)
if(moved === true) {
this.addEventListener('click', (e) => {
e.preventDefault();
});
} else {
// trying to reset click function
}
You can conditionally prevent a click event from firing on your slider by registering a click event listener that shares the moved variable with your mousedown and mousemove event listeners.
The { passive: true } option indicates that the listener does not call event.preventDefault(), and saves a lot CPU time particularly for the mousemove event which can fire several times per second.
The true parameter indicates that the event listener should be called before the event starts to bubble up from the target element. This allows it to prevent propagation even to listeners that were already added on the same element, as long as they didn't also set useCapture to true.
const slider = document.querySelector('input[type="range"]');
// prevent this if mousemove occurred between mousedown and mouseup
slider.addEventListener('click', () => {
console.log('click event fired on slider');
});
// fires just before click event
slider.addEventListener('mouseup', () => {
console.log('mouseup event fired on slider');
});
let moved = false;
// reset for each potential click
slider.addEventListener('mousedown', () => {
moved = false;
});
// indicate cancellation should occur for click
slider.addEventListener('mousemove', () => {
moved = true;
}, { passive: true });
// prevents click event if mousemove occurred between mousedown and mouseup
slider.addEventListener('click', event => {
if (moved) {
event.preventDefault();
event.stopImmediatePropagation();
}
}, true);
<input type="range" />
You should remove the event listener containing the event.preventDefault();.
In order to do that you have to save your function reference into a variable like so:
const preventClickHandler = (e) => e.preventDefault;
slider.addEventListener('mouseup', () => {
isDown = false;
// Disable click event (for ever unfortunately)
if(moved === true) {
this.addEventListener('click', preventClickHandler);
} else {
this.removeEventListener('click', preventClickHandler);
}
})
I want to remove only the mouseup event listners from a selected HTML element.
I used the below code but it will remove all listners.
var old_element = divs[d];
var new_element = old_element.cloneNode(true);
old_element.parentNode.replaceChild(new_element, old_element);
this is how i attach event listners.
var divs = document.getElementsByTagName('body');// to enhance the preformance
for(var d in divs) {
try{
if (divs[d].addEventListener) {
divs[d].addEventListener('mouseup',callHighlight);
} else {
divs[d].attachEvent('mouseup', callHighlight);
}
}catch(err){
//alert(err.message);
}
}
You should use removeEventListener instead of replacechild which will obviously remove all events.
old_element.removeEventListener('mouseup', handler);
When cloning an element, listeners added using addEventListener or by direct property assignment (element.onclick = fn;) are removed, but in–line listeners and those added using IE's attachEvent are not.
In your scenario where listeners are added by reference and also possibly using attachEvent, you are best to remove them using removeEventListener and detachEvent. So you might like to create add and remove functions like:
function addEvent(element, event, fn) {
if (element.addEventListener) {
element.addEventListener(event, fn, false);
} else if (element.attachEvent) {
element.attachEvent('on' + event, fn);
}
}
function removeEvent(element, event, fn) {
if (element.removeEventListener) {
element.removeEventListener(event, fn);
} else if (element.detachEvent) {
element.detachEvent('on' + event, fn);
}
}
Note that there are some significant differences between addEventListener and attachEvent, the most important are that in the latter, this is not set to the element whose handler is calling the function and a reference to the event isn't passed as the first argument to the listener. So the listener function ends up looking like:
function foo(evt) {
evt = evt || window.event;
var target = evt.target || evt.srcElement;
...
}
There are ways around this, but they introduce more issues. Keep it simple if you can.
By default all event listeners are null, so simply just reset it. Problem is that all your mouseup events are registered to the body, so therefore you won't be able to drop the event without first stopping the event from bubbling to the body. You can solve that problem with, stopPropagation()
old_element.onmouseup = function (e) {
// event won't go up to the body tag
e.stopPropagation();
return null;
};
or
function kill (e) {
e.stopPropagation();
return null;
}
old_element.onmouseup = kill;
second_element.onmouseup = kill;
JSFIDDLE