I have a page which has a keydown event listener, to listen for the Escape key, to navigate back. I also have a simple modal class, which also listens for the Escape key to close it. The main page listener checks if a modal is open, and if so, returns without doing anything.
window.addEventListener("keydown", function (ev) {
if (modal_is_open) { return; }
ev = ev || window.event;
if (ev.key == "Escape") { history.go(-1); }
});
modal_div.addEventListener("keydown",function (ev) {
ev = ev || window.event;
ev.stopPropagation();
ev.preventDefault();
ev.cancelBubble = true;
if (ev.key == "Escape") { close_the_modal(); }
return false;
});
My problem is, if a modal is open, the Escape key closes it, but still bubbles up to the main page handler and navigates back. How can I stop this?
I finally found the solution, replace stopPropagation with stopImmediatePropagation, and the window keydown handler no longer fires if the modal is open.
I am implementing a website and want to stop users go to the previous page when they scroll with two fingers on their touchpad. I tried to add scroll event and stop propagation but it doesn't work.
document.addEventListener('scroll', (e) => {e.stopPropagation()});
document.addEventListener('touchstart', (e) => {e.stopPropagation()});
document.addEventListener('touchend', (e) => {e.stopPropagation()});
Is there any way to solve this issue?
I have tried to disable touch-action in css but it still doesn't work:
touch-action: none;
I use below code to solve the issue:
export const disableScroll = () => {
windowOnweel = window.onwheel;
window.onwheel = e => {
e = e || window.event;
if (e.preventDefault) e.preventDefault();
e.returnValue = false;
};
};
but you need to remember to restore the onwheel method when you don't need that.
I have an event handler attached to touchstart and I want to call preventDefault as soon as touchmove occurs. I have this code currently.
link.addEventListener("click", function () {
console.log("clicked");
});
link.addEventListener("touchstart", function (touchStartEvent) {
var mouseMoveHandler = function () {
console.log("moved.");
touchStartEvent.preventDefault(); // This does not work.
link.removeEventListener('touchmove', arguments.callee);
};
link.addEventListener("touchmove", mouseMoveHandler);
});
http://jsfiddle.net/682VP/
I'm calling preventDefault for touchstart within an event handler for touchmove. This does not seem to work because the click event handler is always invoked.
What am I doing wrong here?
When you call preventDefault it works for touchstart event, not for click event.
You can add some property for the link object indicating the moving state and check it inside click handler itself (and stop it either by preventDefault or return false)
var link = document.getElementById("link");
link.addEventListener("click", function () {
if (this.moving) {
this.moving = false;
return false;
}
console.log("clicked");
});
link.addEventListener("touchstart", function (touchStartEvent) {
var mouseMoveHandler = function () {
console.log("moved.");
this.moving = true;
link.removeEventListener('touchmove', arguments.callee);
};
link.addEventListener("touchmove", mouseMoveHandler);
});
On Mac browsers, javascript does not receive keyup events for most keys (other modifier keys seem to be an exception) when the metakey is down. Use this jsfiddle to demonstrate (focus the result area and try something like cmd + x, the x will not receive a keyup event):
http://jsfiddle.net/mUEaV/
I've reproduced this in stable releases for Chrome, FF, Safari and Opera. The same thing does not seem to happen with the control key in Windows 7.
Is the OS hijacking the keyup event? This seems especially strange since commands that use the metakey such as save, find, cut, copy, etcetera all activate on keydown not on keyup, and can be hijacked by the javascript just fine.
It's simply not possible to get the onKeyUp events when meta is used, I learned today. Very unfortunate and difficult to work around. You'll have to emulate them some other way.
Edit: To clarify, this is only on Mac and occurs due to OS level handling of the event. It cannot be overridden. Sorry to be the bearer of bad news.
Although event.metaKey returns false, event.keyCode and event.key are still populated.
document.addEventListener('keyup', function(e) {
console.log(e.metaKey || e.key);
});
Click here then press the Command, Control, or Option keys.
Is the browser window retaining the focus when you press those keys? In windows you can get similar result when pressing windows+R or CTRL+ESC and similar key combinations that make browser to loose focus and that results in missed events.
While keyup events are indeed not available when the meta key is pressed, you can still get keydown events for all keys, as well as keyup events for the meta key itself.
This allows us to just simply keep track of the state of the meta key ourselves, like so:
let metaKeyDown = false;
window.addEventListener("keydown", event => {
if (event.key == 'Meta') { metaKeyDown = true; }
});
window.addEventListener("keyup", event => {
if (event.key == 'Meta') { metaKeyDown = false; }
});
By now additionally checking for the main key, plus cancelling the default behavior with Event.preventDefault() we can easily listen for key combinations (like here e.g. CMD+K) and prevent the browser from handling them:
let metaKeyDown = false;
window.addEventListener("keydown", event => {
if (event.key == 'Meta') { metaKeyDown = true; }
if (event.key == 'k' && metaKeyDown) {
event.preventDefault();
console.log('CMD+K pressed!');
}
});
window.addEventListener("keyup", event => {
if (event.key == 'Meta') { metaKeyDown = false; }
});
(Note the observation of the k key taking place already on keydown.)
Also, please be aware that when used incorrectly, this can break standard browser functionality (e.g. like CMD+C or CMD+R), and lead to poor user experience.
You can create an artificial keyup event by waiting for a certain period after the last keydown event. The only caveat is people will have different repeat rates on their os.
https://jsfiddle.net/u7t43coz/10/
const metaKeyCodes = ["MetaLeft", "MetaRight"];
const shiftKeyCodes = ["ShiftLeft", "ShiftRight"];
const ctrlKeyCodes = ["ControlLeft", "ControlRight"];
const altKeyCodes = ["AltLeft", "AltRight"];
const modifierKeyCodes = [
...metaKeyCodes,
...shiftKeyCodes,
...ctrlKeyCodes,
...altKeyCodes
];
// record which keys are down
const downKeys = new Set()
const artificialKeyUpTimes = {}
function onKeydown(e) {
downKeys.add(e.code);
// do other keydown stuff here
console.log("meta", e.metaKey, e.code, "down")
// check if metaKey is down
if (metaKeyCodes.some(k => downKeys.has(k))) {
downKeys.forEach(dk => {
// we want to exclude modifier keys has they dont repeat
if (!modifierKeyCodes.includes(dk)) {
// fire artificial keyup on timeout
if (!artificialKeyUpTimes[dk])
setTimeout(
() => fireArtificialKeyUp(dk, e),
500
);
artificialKeyUpTimes[dk] = Date.now();
}
});
}
}
function fireArtificialKeyUp(code, e) {
// if enough time has passed fire keyup
if (Date.now() - artificialKeyUpTimes[code] > 100) {
delete artificialKeyUpTimes[code];
//if key is still down, fire keyup
if (downKeys.has(code)) {
const eCode = isNaN(code) ? { code: code } : { keyCode: code };
document.dispatchEvent(
new KeyboardEvent("keyup", { ...e, ...eCode })
);
}
} else {
setTimeout(() => fireArtificialKeyUp(code, e), 100);
}
}
function onKeyup(e) {
downKeys.delete(e.code);
// do keyup stuff here
console.log("meta", e.metaKey, e.code, "up")
}
document.addEventListener("keydown", onKeydown)
document.addEventListener("keyup", onKeyup)
I would like to stop my event after sayHello1
var sayHello1 = function(e) {
console.log("hello1");
e.stopMe = true;
e.preventDefault(); // doesn't work
e.stopPropagation(); // doesn't work
return false; // doesn't work
};
var sayHello2 = function(e) {
console.log("hello2"); // Still fired !
if (e.stopMe ) console.log("stop hello2"); // works
};
document.addEventListener("click", sayHello1);
document.addEventListener("click", sayHello2);
"e.stopMe" cant help to stop sayHello2, but there is no way to do that ! (imagine firefox & Co using the name "stopMe" on their browser !)
You want to use e.stopImmediatePropagation() which prevents other listeners of the same event from being called.
var sayHello1 = function(e) {
console.log("hello1");
e.stopImmediatePropagation(); //keeps any event listener that is bound after this from firing
e.preventDefault(); // prevents the default action from happening
e.stopPropagation(); // prevents ancestors from getting the event
return false; // works like preventDefaut
};
var sayHello2 = function(e) {
console.log("hello2"); // Still fired !
};
document.addEventListener("click", sayHello1);
document.addEventListener("click", sayHello2);
<h1>Test</h1>