trapping focus for a modal dialog - javascript

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();
});
});

Related

Javascript - stop a modal keyboard event from bubbling / propagating

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.

Capturing Tabbed Focus/Blur properly

So for accessibility purposes I am trying to captured tabbed focus and blur events to capture tab order within a modal.
For some reason I am running into some odd browser behavior.
Inside my component I have the following code:
// On Init
ngOnInit (){
// Get all of the buttons and anchors in the modal
var buttons = this.modal.nativeElement.querySelectorAll("button, a");
// Get the number of buttons
var numButtons = buttons.length;
// Get the last button
var lastButton = buttons[numButtons - 1];
// When the last button loses focus
lastButton.addEventListener("blur", (e: any) => {
// Prevent Default
e.preventDefault();
// Focus on the modals close button
this.focusCloseButton();
})
}
And TECHNICALLY this works perfectly. If I log out the active element after the call to this.focusCloseButton, I indeed get a reference to the close button.
HOWEVER, the tab actually moves to the browser itself into whatever the first element is. For Chrome this is the "View site information" button to the left of the URL bar. In Firefox this is the first tab in the list of tabs.
How can I capture this properly so that the browser is not hijacking the tab press?
Apparently the blur event happens too late to be captured before the browser takes over.
Instead I used keybinding to detect when the tab-key was pressed and did the capturing from there.
// The OnInit function handles capturing tab order
ngOnInit (){
// All of the buttons and links in the modal
var buttons = this.modal.nativeElement.querySelectorAll("button, a");
// The first button or link in the modal
var firstButton = buttons[0];
// The last button or link in the modal
var lastButton = buttons[buttons.length - 1];
// Listen for keydown events on the modal
this.modal.nativeElement.addEventListener("keydown", (e: any)=> {
// If the key pressed was the tab button
if ( e.keyCode === 9 && !e.shiftKey ) {
// If the currently active element is the last button
if (document.activeElement == lastButton){
// Prevent default action
e.preventDefault();
// Put focus on the close button
this.focusCloseButton();
}
} else if ( e.keyCode === 9 && e.shiftKey === true ){
// If the key pressed was shift+tab
// And the currently active button is the close button
if ( document.activeElement == firstButton ){
// Prevent Default
e.preventDefault();
// Focus the last button
lastButton.focus();
}
}
})
}
for those wondering about what this.focusCloseButton does:
// Puts focus on the close button
focusCloseButton: Function = function(){
this.closeButton.nativeElement.focus();
};
The reference to closeButton is created by ViewChild:
// Reference to the close button
#ViewChild("closeButton") closeButton: ElementRef;
Which ties into the dom with the marked element:
<button #closeButton></button>

Prevent escape key in dialog box from being passed to underlying window

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.

Hide a popup div on ESC key press with Javascript

//Function To Display Popup
function div_show(x) {
document.getElementById(x).style.display = "block";
div_hide(x);
}
//Function to Hide Popup
function div_hide(x){
window.onkeyup = function (event){
if(event.keyCode == 27)
document.getElementById(x).style.display = "none"};
}
I want to hide the popup I created, but the div_hide function doesn't work. I even tried an alert message and it results that the event keycode is undefined. Basically I can't catch the esc key.
Don't pass event as an argument. You will still be able to use event.keyCode inside your event handler.

Why is tab keypress causing focus change also triggering keyup event?

Pressing the tab key which triggers a focus change is also received by the input receiving the focus as a keyup.
a: <input type='text'/><br/>
b: <input type='text' onkeyup='alert("wtf?")'/><br/>
http://jsfiddle.net/59SnP/
As my control also uses tab (not in the example), I would want the focus related keyup event being consumed (but I want to receive other non-focus-change related tab events). I tried to research the rationale behind the current behavior but found nothing. The question: Where is this current behavior specified (event not consumed by focus change), and what would be a cross-browser workaround to force consuming it. Thx.
You can try this. I changed your keyup event in your input :
<input type='text' onkeyup="if(!tabPressed){ alert('This is it !'); }"/>
And I added a little event handler which will raise a flag when the tab button is pressed :
var tabPressed = false;
document.addEventListener('keydown', function (e) {
if(e.keyCode == 9) {
tabPressed = true;
} else {
tabPressed = false;
}
}, false);
Based on Nathan's insight, here is a fully working example:
// First part of Nathan's HACK (set a sentinel when a focus changing tab has happened)
var tabPressed = false;
// remove this listener to break the functionality
$(document).on("keydown", function (e) {
if(e.keyCode == 9) {
tabPressed = true;
} else {
tabPressed = false;
}
});
// The listener on the client input that would kill the keyup tab event upon focus change
$("#magic").on("keyup", function(e) {
if (tabPressed && e.keyCode==9) {
tabPressed = false; // reset the sentinel
e.stopImmediatePropagation()
e.preventDefault()
}
})
And here is the second part, which is a simple skeleton of something meaningful. We disable TAB inside the input, and log it as we do with other keyups:
$("#magic").on("keydown", function(e) {
if (e.keyCode==9) {
e.preventDefault()
e.stopPropagation()
}
})
$("#magic").on("keyup", function(e) {
$(this).val($(this).val() + " " + e.keyCode)
e.stopPropagation()
e.preventDefault()
})
The HTML backing the story is as simple as:
a: <input type='text'/><br/>
b: <input type='text'/><br/>
c: <input type='text' id='magic'/><br/>
If you want to play with it, here it is on jsfiddle
NOTE: This still is not the perfect solution, the sentinel is just reset inside the control, so if a tabpress moving the focus does not activate our input, the sentinel stucks, and the first event will be swallowed.. So here is an example of wrong behaviour:
Click on input A
Press TAB (focus moves to input B, tabPressed becomes true)
Click on input C
Press TAB (it is eaten up as sentinel is true)
Press TAB (now it goes through)
Still it is slightly better to have to press TAB twice as to have something happening automatically, wo user control...

Categories

Resources