From 0e5a2e126ab4179f1ec83e1e4e773058b49e336d Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jo=CC=88rn=20Zaefferer?= Date: Wed, 2 Oct 2013 17:27:43 +0200 Subject: [PATCH] Dialog: Restore focus to the previously focused element when window regains focus. Fixes #9101 - Dialog: Track last focused element instead of always focusing the first tabbable element --- tests/unit/dialog/dialog_core.js | 48 ++++++++++++++++++++++---------- ui/jquery.ui.dialog.js | 33 ++++++++++++++++------ 2 files changed, 58 insertions(+), 23 deletions(-) diff --git a/tests/unit/dialog/dialog_core.js b/tests/unit/dialog/dialog_core.js index e85759dc9..062d44576 100644 --- a/tests/unit/dialog/dialog_core.js +++ b/tests/unit/dialog/dialog_core.js @@ -40,7 +40,7 @@ test("widget method", function() { }); asyncTest( "focus tabbable", function() { - expect( 5 ); + expect( 6 ); var element, options = { buttons: [{ @@ -50,6 +50,12 @@ asyncTest( "focus tabbable", function() { }; function checkFocus( markup, options, testFn, next ) { + + // Support: IE8 + // For some reason the focus doesn't get set properly if we don't + // focus the body first. + $( "body" ).focus(); + element = $( markup ).dialog( options ); setTimeout(function() { testFn(); @@ -59,43 +65,57 @@ asyncTest( "focus tabbable", function() { } function step1() { + element = $( "
" ).dialog( options ); + setTimeout(function() { + var input = element.find( "input:last" ).focus().blur(); + element.dialog( "instance" )._focusTabbable(); + setTimeout(function() { + equal( document.activeElement, input[ 0 ], + "1. an element that was focused previously." ); + element.remove(); + setTimeout( step2 ); + }); + }); + } + + function step2() { checkFocus( "
", options, function() { equal( document.activeElement, element.find( "input" )[ 1 ], - "1. first element inside the dialog matching [autofocus]" ); - }, step2 ); + "2. first element inside the dialog matching [autofocus]" ); + }, step3 ); } - function step2() { + function step3() { checkFocus( "
", options, function() { equal( document.activeElement, element.find( "input" )[ 0 ], - "2. tabbable element inside the content element" ); - }, step3 ); + "3. tabbable element inside the content element" ); + }, step4 ); } - function step3() { + function step4() { checkFocus( "
text
", options, function() { equal( document.activeElement, element.dialog( "widget" ).find( ".ui-dialog-buttonpane button" )[ 0 ], - "3. tabbable element inside the buttonpane" ); - }, step4 ); + "4. tabbable element inside the buttonpane" ); + }, step5 ); } - function step4() { + function step5() { checkFocus( "
text
", {}, function() { equal( document.activeElement, element.dialog( "widget" ).find( ".ui-dialog-titlebar .ui-dialog-titlebar-close" )[ 0 ], - "4. the close button" ); - }, step5 ); + "5. the close button" ); + }, step6 ); } - function step5() { + function step6() { element = $( "
text
" ).dialog({ autoOpen: false }); element.dialog( "widget" ).find( ".ui-dialog-titlebar-close" ).hide(); element.dialog( "open" ); setTimeout(function() { - equal( document.activeElement, element.parent()[ 0 ], "5. the dialog itself" ); + equal( document.activeElement, element.parent()[ 0 ], "6. the dialog itself" ); element.remove(); start(); }); diff --git a/ui/jquery.ui.dialog.js b/ui/jquery.ui.dialog.js index c5bd42ab5..2684b7615 100644 --- a/ui/jquery.ui.dialog.js +++ b/ui/jquery.ui.dialog.js @@ -118,6 +118,8 @@ $.widget( "ui.dialog", { } this._isOpen = false; + + this._trackFocus(); }, _init: function() { @@ -178,6 +180,7 @@ $.widget( "ui.dialog", { } this._isOpen = false; + this._focusedElement = null; this._destroyOverlay(); if ( !this.opener.filter(":focusable").focus().length ) { @@ -256,20 +259,24 @@ $.widget( "ui.dialog", { _focusTabbable: function() { // Set focus to the first match: - // 1. First element inside the dialog matching [autofocus] - // 2. Tabbable element inside the content element - // 3. Tabbable element inside the buttonpane - // 4. The close button - // 5. The dialog itself - var hasFocus = this.element.find("[autofocus]"); + // 1. An element that was focused previously + // 2. First element inside the dialog matching [autofocus] + // 3. Tabbable element inside the content element + // 4. Tabbable element inside the buttonpane + // 5. The close button + // 6. The dialog itself + var hasFocus = this._focusedElement; + if ( !hasFocus ) { + hasFocus = this.element.find( "[autofocus]" ); + } if ( !hasFocus.length ) { - hasFocus = this.element.find(":tabbable"); + hasFocus = this.element.find( ":tabbable" ); } if ( !hasFocus.length ) { - hasFocus = this.uiDialogButtonPane.find(":tabbable"); + hasFocus = this.uiDialogButtonPane.find( ":tabbable" ); } if ( !hasFocus.length ) { - hasFocus = this.uiDialogTitlebarClose.filter(":tabbable"); + hasFocus = this.uiDialogTitlebarClose.filter( ":tabbable" ); } if ( !hasFocus.length ) { hasFocus = this.uiDialog; @@ -552,6 +559,14 @@ $.widget( "ui.dialog", { .css( "position", position ); }, + _trackFocus: function() { + this._on( this.widget(), { + "focusin": function( event ) { + this._focusedElement = $( event.target ); + } + }); + }, + _minHeight: function() { var options = this.options; -- 2.39.5