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.
Related
I have a function that fires only when we press the spacebar or the isJumping event is already happening
If we press any other key, nothing happens.
function onJump(e) {
if (e.code !== "Space" || isJumping) return
yVelocity = JUMP_SPEED
isJumping = true
}
The question is, can I add to this function so that, in addition to the space, the mouse click event also fires?
That is, if we press the spacebar or click on the mouse, then the function works
The mouse click event is not key code, so I'm a little confused on how to do it right
If the function is called when you press a key then that is because somewhere in the code you didn't show us you have some code which registers that function (or another function which calls the first) as an event handler that triggers when a key is pressed (e.g. keyup or keypress).
If you want to call it when something is clicked then you also need to register it as an event handler for that kind of event (e.g. click or mousedown).
MDN has a tutorial on event handling.
I assume onJump is attached to a keyboard event handler, presumably on document, something like this:
document.addEventListener("keydown", onJump);
If so, you can also attach it to click:
document.addEventListener("keydown", onJump);
document.addEventListener("click", onJump);
...and then modify the function so it checks which type of event it got and handles it accordingly:
function onJump(e) {
if (isJumping || (e.type === "keydown" && e.code !== "Space")) return;
yVelocity = JUMP_SPEED;
isJumping = true;
}
That will only check e.code if the event is the keydown event, not the click event. (Adjust the event name as necessary).
USE EVENT.TYPE
Use the event.type property when using a single handler for multiple events. In your case, you want the same function to handle keyboard and mouse events. View and run the code snippet below to see how it works.
window.addEventListener("keydown", onJump);
window.addEventListener("mousedown", onJump);
const JUMP_SPEED = 100;
let isJumping = false;
function onJump(e) {
if (e.type === "mousedown") {
// add event handler here
}
else if (e.type === "keydown" && e.code === "Space" && !isJumping) {
yVelocity = JUMP_SPEED
isJumping = true
}
console.log("event: " + e.type + ", isJumping: " + isJumping + ", keycode: " + e.code);
}
<h3>Click the mouse or press spacebar</h3>
<img src="https://cdn.icon-icons.com/icons2/567/PNG/512/packman_icon-icons.com_54382.png" style="height:100px;width:auto"/>
You can use a event listener, like this:
document.body.addEventListener('click', function(){
console.log('hi');
});
<body>
<p>This is supposed to be your elements. Click here!</p>
</body>
In your case, you can make it
document.addEventListener("keydown", onJump);
document.addEventListener("click", onJump);
You can change your function correspondingly, so that it works with the event listeners:
function onJump(e) {
if (isJumping || (e.type == "keydown" && e.code !== "space")) return;
yVelocity = JUMP_SPEED;
isJumping = true;
}
I am trying to listen for a click on the "open link in new tab" contextmenu item on the browsers default contextmenu (NOT a custom contextmenu). I want to do some javascript right before the page opens in a new tab. Or even stop the default behavior, do my javascript and then window.open() the link myself.
I've seen how I can listen for the contextmenu like this...
document.addEventListener('contextmenu', e => {});
But that won't allow me to figure out if a specific item is clicked.
That's not possible. You can't listen to every user interaction, but you can guess some. If you have to do actions before you open a new window, use a custom contextmenu.
You can do some kind of workaround, like listening for window.onblur and/or window.mousemove after the contextmenu was opened. I had several situations where i had to handle similar challenges to yours, but not everything can be captured.
For example the mousemove doesn't trigger, while the contextmenu is open but document.hasFocus() is true. As soon as the user clicks inside the contextmenu and if the mouse is still above the current document, mousemove is triggered twice at the exact same spot. If combined with a click event, the user closed the contextmenu with a left-click into the DOM. If the user closed the contextmenu with ESC, keyup is triggered, but not if the user selects something with keyboard.
/*
auxclick can be any mouse key except primary
contextmenu may also trigger, if you use the contextmenu key on the keyboard
but it fires a MouseEvent with values from a KeyboardEvent
*/
["contextmenu", /*"mousemove", */ "click", "auxclick", "keydown", "keyup"].forEach(name => {
document.addEventListener(name, function(e) {
if (document.visibilityState != "hidden") {
console.log(name, document.hasFocus(), document.activeElement, e.key || e.screenX, e.key || e.screenY);
}
});
});
document.addEventListener("visibilitychange", function(e) {
console.log("visibilitychange", document.hasFocus(), document.visibilityState, window.screenX, window.screenY);
if (window.screenX == window.screenY && window.screenX == -32000 && document.visibilityState == 'visible') {
console.log("restored");
}
});
/*
this won't trigger if the user switches from outside
the window directly into an iframe or between multiple
iframes see https://crbug.com/967430
monitor document.hasFocus() changes and fire a custom focus/blur event on change.
*/
window.addEventListener("focus", function(e) {
console.log("focus", document.hasFocus());
});
window.addEventListener("blur", function(e) {
console.log("blur", document.hasFocus(), document.visibilityState, window.screenX, window.screenY);
setTimeout(() => {
if (document.visibilityState == "hidden") {
if (window.screenX == window.screenY && window.screenX == -32000) {
console.log("minimized");
} else {
console.log("maybe new or other tab with focus or view source?");
}
} else if (!document.hasFocus()) {
if (window == window.top) {
console.log("maybe new window or dev tools");
} else {
console.log("maybe new window, dev tools or I'm an iframe and my parent got selected");
}
} else if (document.hasFocus()) {
console.log("Maybe entered an iframe or canvas?", document.activeElement);
}
}, 1);
});
setInterval(() => console.log("interval", document.hasFocus(), document.activeElement), 2000);
on down arrow keypress , click event is getting fired, event.keycode is undefined
$(".dropdown:not(.li-search) a.dropdown-toggle", ".navbar-collapse").on("click", function(event) {
var target = $(this).attr("target");
if (event.keyCode !== '40'){
if (!$(".li-menu").is(":visible") && target === undefined) {
location.href=this.href;
} else {
window.open(this.href, '_blank');
}
}
});
in this code i am trying to open main menu in new tab , but external link is getting open on down arrow keypress
call preventDefault() function.
$(".dropdown:not(.li-search) a.dropdown-toggle", ".navbar-collapse").on("click", function(event) {
event.preventDefault();
var target = $(this).attr("target");
if(event.keyCode!=='40'){
if (!$(".li-menu").is(":visible") && target===undefined) {
location.href=this.href;
}
else {
window.open(this.href,'_blank');
}
}
});
See the keycode for the reference https://css-tricks.com/snippets/javascript/javascript-keycodes/
in order to configure your app for particular key event
Looking at the classes dropdown-toggle, navbar-collapse, I'm guessing that you are using Bootstrap library.
If that is the case, the behaviour you are seeing is reasonable. Let's break down the issues:
on down arrow keypress , click event is getting fired
Q: You have only bind the handler on click event so why are it is being triggered on keypress?
A: Because this is a feature of bootstrap dropdown. To have better accessibilty, bootstrap triggers click event on the keydown of up, down, esc and space keys.
event.keycode is undefined
Since it is a click event handler and not some keyboard event handler like keydown or keypress, event.keyCode should be undefined
Note: You are using a strict equality in the following condition
if (event.keyCode !== '40')
This will check both the type and value of the operands. Now, event.keyCode always return a Number while '40' is a string, hence the above condtion will yield false even if keyCode is 40. You should correct it to:
if (event.keyCode !== 40)
Now, if you want to stop the redirect on down key, you should check whether the event triggered is an original event or was triggered by some js logic. For this, you may choose jQuery's event.isTrigger or event.originalEvent
Here's a code snippet:
$(".dropdown:not(.li-search) a.dropdown-toggle", ".navbar-collapse").on("click", function(event) {
var target = $(this).attr("target");
// Check if NOT an triggered event
if (!event.isTrigger) {
if (!$(".li-menu").is(":visible") && target === undefined) {
location.href = this.href;
} else {
window.open(this.href, '_blank');
}
}
});
<a> tags will fire the click event when you press enter on them. However you will not have a keyCode on the event because it is not a Key* event. If you want to know the keyCode add a keyDown or keyUp handler as well. You could also handle both by doing something like the following:
$(".dropdown:not(.li-search) a.dropdown-toggle", ".navbar-collapse").on("click keydown", function(event) {
var target = $(this).attr("target");
if(event.type === 'keydown' && event.keyCode!=='40'){
if (!$(".li-menu").is(":visible") && target===undefined) {
location.href=this.href;
}
else {
window.open(this.href,'_blank');
}
}
});
You'll probably also want to add an event.preventDefault(); in there if you wish to prevent default browser behaviour from taking place.
I have a listener for keyup which will unload my javascript slideshow if an escape key is pressed.
window.addEventListener("keyup", this.escapekeyfn, false);
However, within the slideshow is the option to print an image using window.print()- but if the user presses escape while in that dialog, it is passed down to my listener and the slideshow exits.
Any way to prevent this in pure js? I've tried to find any event properties which can help me determine where the escape came from. originalTarget looked promising, but once a dialog is called, the originalTarget is changed to BODY ever more.
The Listeners
The escape key handling (the essential code for this)
slideshow.prototype.initslideshow = function(){
_this = this
....
this.escapekeyfn = function(e){ _this.escapekeycheck(e, _this)};
window.addEventListener("keyup", this.escapekeyfn, false);
....
}
The print button:
slideshow.prototype.printlink = function(){
var _this = this;
var a = document.createElement("a");
var img = document.createElement("img");
a.onclick = function(e){e.stopPropagation();window.print();return false};
....
}
slideshow.prototype.escapekeycheck = function(e, _this){
if (e.stopPropagation) e.stopPropagation();
if (e.keyCode == 27){
_this.ssclose(e);
}
}
The close function:
slideshow.prototype.ssclose = function(e) {
if (e.stopPropagation) e.stopPropagation();
// stop listening for escape key
window.removeEventListener("keyup", this.escapekeyfn);
return false;
}
I finally came across a way to solve this problem. I changed keyup to keypress
window.addEventListener("keypress", keypressFn, false);
For some reason, the keypress event is not sent to the window object whereas the keyup one is while the print dialog is open.
I wanted to make modal dialog accessible . I added two hidden focusable elements
Dialog Start
Some focussable Dialog Elements
Dialog end
function onblurevent(){
document.getElementById("dialog-start").focus();
}
When ever dialog-end element blur event happens i tried to move focus to dialog-start element calling focus() method
but the focus is moving to address bar .dialog start and end anchor tags are hidden by using below style
#dialog-start{
height:1px;
left:-9999px;
overflow:hidden;
position:absolute;
top:0;
width:1px;
}
Iam not sure if anchor styles are the reason or is the only way to make sure focus is inside the dialog is to get list of focusable elments and call focus() method in a keydown event handler on container.
The problem occurs because you don't handle your keydown event. When you pressing Tab on last link browser automatically switches focus to address bar. So you just need to preventDefault() default browser behavior if Tab pressed.
The following code will work:
window.onload = function() {
var firstAnchor = document.getElementById("dialog-start"),
lastAnchor = document.getElementById("dialog-end");
function keydownHandler(e) {
var evt = e || window.event;
var keyCode = evt.which || evt.keyCode;
if(keyCode === 9) { // TAB pressed
if(evt.preventDefault) evt.preventDefault();
else evt.returnValue = false;
firstAnchor.focus();
}
}
if(lastAnchor.addEventListener) lastAnchor.addEventListener('keydown', keydownHandler, false);
else if(lastAnchor.attachEvent) lastAnchor.attachEvent('onkeydown', keydownHandler);
}
(note that you dont need onblurevent function anymore)
$(document).ready(function () {
//set focus on first field in Bootstrap modal when loaded
$("#yourModal").on('shown.bs.modal', function () {
$(this).find('#yourField').focus();
});
});