Changeset 9233

Show
Ignore:
Timestamp:
06/20/07 13:40:58 (2 years ago)
Author:
becky
Message:

refs #2505 better handling of modality; use onblur/deactivate event to determine last focusable item in dialog - use that when shift-tabbing to cycle focus to all items; Escape key closes dialog;
save and restore focus when dialog closes - fixes #2421

Location:
dijit/trunk/layout
Files:
2 modified

Legend:

Unmodified
Added
Removed
  • dijit/trunk/layout/Dialog.js

    r9230 r9233  
    9393 
    9494                _duration: 400, 
    95  
     95                 
     96                _lastFocusItem:null, 
     97                                 
    9698                postCreate: function(){ 
    9799                        dijit.layout.Dialog.superclass.postCreate.apply(this, arguments); 
     
    100102 
    101103                startup: function(){ 
    102                         var closeNode = dojo.byId(this.closeNode); 
    103                         this.connect(closeNode, "onclick", "hide"); 
     104                        if(this.closeNode){ 
     105                                var closeNode = dojo.byId(this.closeNode); 
     106                                this.connect(closeNode, "onclick", "hide"); 
     107                        } 
    104108                }, 
    105109 
     
    109113                        this._position(); 
    110114                        dijit.layout.Dialog.superclass.onLoad.call(this); 
    111                 }, 
    112  
    113                 _trapTabs: function(/*Event*/ e){ 
    114                         // summary: callback on focus 
    115                         if(e.target == this.tabStartOuter){ 
    116                                 if(this._fromTrap){ 
    117                                         this.tabStart.focus(); 
    118                                         this._fromTrap = false; 
    119                                 }else{ 
    120                                         this._fromTrap = true; 
    121                                         this.tabEnd.focus(); 
    122                                 } 
    123                         }else if(e.target == this.tabStart){ 
    124                                 if(this._fromTrap){ 
    125                                         this._fromTrap = false; 
    126                                 }else{ 
    127                                         this._fromTrap = true; 
    128                                         this.tabEnd.focus(); 
    129                                 } 
    130                         }else if(e.target == this.tabEndOuter){ 
    131                                 if(this._fromTrap){ 
    132                                         this.tabEnd.focus(); 
    133                                         this._fromTrap = false; 
    134                                 }else{ 
    135                                         this._fromTrap = true; 
    136                                         this.tabStart.focus(); 
    137                                 } 
    138                         }else if(e.target == this.tabEnd){ 
    139                                 if(this._fromTrap){ 
    140                                         this._fromTrap = false; 
    141                                 }else{ 
    142                                         this._fromTrap = true; 
    143                                         this.tabStart.focus(); 
    144                                 } 
    145                         } 
    146                 }, 
    147  
    148                 _clearTrap: function(/*Event*/ e){ 
    149                         // summary: callback on blur 
    150                         setTimeout(dojo.hitch(this, function(){ 
    151                                 this._fromTrap = false; 
    152                         }), 100); 
    153115                }, 
    154116 
     
    214176                        style.top = (viewport.t + (viewport.h - mb.h)/2) + "px"; 
    215177                }, 
     178                 
     179                _findLastFocus: function(/*Event*/ evt){ 
     180                        // summary:  called from onblur of dialog container to determine the last focusable item  
     181                        this._lastFocused = evt.target; 
     182                }, 
     183                 
     184                _cycleFocus: function(/*Event*/ evt){ 
     185                        // summary: when tabEnd receives focus, advance focus around to titleBar 
     186                         
     187                        // on first focus to tabEnd, store the last focused item in dialog 
     188                        if(!this._lastFocusItem){ 
     189                                this._lastFocusItem = this._lastFocused; 
     190                        } 
     191                        this.titleBar.focus(); 
     192                }, 
    216193 
    217194                _onKey: function(/*Event*/ evt){ 
    218195                        if(evt.keyCode){ 
    219                                 // see if the key is for the dialog 
    220196                                var node = evt.target; 
    221                                 while(node){ 
    222                                         if(node == this.domNode){ 
    223                                                 return; // yes, so just let it go 
     197                                // see if we are shift-tabbing from titleBar 
     198                                if(node == this.titleBar && evt.shiftKey && evt.keyCode == dojo.keys.TAB){ 
     199                                        if (this._lastFocusItem){ 
     200                                                this._lastFocusItem.focus(); // send focus to last item in dialog if known 
    224201                                        } 
    225                                         node = node.parentNode; 
    226                                 } 
    227                                 // this key is for the disabled document window 
    228                                 if (evt.keyCode != dojo.keys.TAB){ // allow tabbing into the dialog for a11y 
    229202                                        dojo.stopEvent(evt); 
    230                                 // opera won't tab to a div 
    231                                 }else if (!dojo.isOpera){ 
    232                                         try{ 
    233                                                 this.tabStart.focus(); 
    234                                         }catch(e){/*squelch*/} 
     203                                }else{ 
     204                                        // see if the key is for the dialog 
     205                                        while (node){ 
     206                                                if(node == this.domNode){ 
     207                                                        if (evt.keyCode == dojo.keys.ESCAPE){ 
     208                                                                this.hide();  
     209                                                        }else{ 
     210                                                                return; // just let it go 
     211                                                        } 
     212                                                } 
     213                                                node = node.parentNode; 
     214                                        } 
     215                                        // this key is for the disabled document window 
     216                                        if (evt.keyCode != dojo.keys.TAB){ // allow tabbing into the dialog for a11y 
     217                                                dojo.stopEvent(evt); 
     218                                        // opera won't tab to a div 
     219                                        }else if (!dojo.isOpera){ 
     220                                                try{ 
     221                                                        this.titleBar.focus(); 
     222                                                }catch(e){/*squelch*/} 
     223                                        } 
    235224                                } 
    236225                        } 
     
    252241                        this._modalconnects.push(dojo.connect(window, "onscroll", this, "layout")); 
    253242                        this._modalconnects.push(dojo.connect(document.documentElement, "onkeypress", this, "_onKey")); 
    254  
     243                         
     244                        // IE doesn't bubble onblur events - use ondeactivate instead 
     245                        var ev = typeof(document.ondeactivate) == "object" ? "ondeactivate" : "onblur"; 
     246                        this._modalconnects.push(dojo.connect(this.containerNode, ev, this, "_findLastFocus")); 
     247                         
     248                         
    255249                        dojo.style(this.domNode, "opacity", 0); 
    256250                        this.domNode.style.display="block"; 
     
    258252                        this._position(); 
    259253 
    260                         this._fromTrap = false; 
    261  
    262254                        this._fadeIn.play(); 
    263  
     255                         
     256                        dijit.util.focus.save(this); 
     257                         
    264258                        // set timeout to allow the browser to render dialog 
    265259                        setTimeout(dojo.hitch(this, function(){ 
     
    289283                        dojo.forEach(this._modalconnects, dojo.disconnect); 
    290284                        this._modalconnects = []; 
    291  
     285                         
     286                        dijit.util.focus.restore(this); 
    292287                }, 
    293288 
  • dijit/trunk/layout/templates/Dialog.html

    r9220 r9233  
    11<div class="dijitDialog"> 
    2         <span dojoAttachPoint="tabStartOuter" dojoAttachEvent="onfocus:_trapTabs;onblur:_clearTrap" tabindex="0"></span> 
    3         <span dojoAttachPoint="tabStart" dojoAttachEvent="onfocus:_trapTabs;onblur:_clearTrap" tabindex="0"></span> 
    42                <div dojoAttachPoint="titleBar" class="dijitDialogTitleBar" tabindex="0" waiRole="dialog" title="${title}"> 
    53                <span dojoAttachPoint="titleNode" class="dijitDialogTitle">${title}</span> 
    64                <span dojoAttachPoint="closeButtonNode" class="dijitDialogCloseIcon" dojoAttachEvent="onclick: hide"></span> 
    75        </div> 
    8         <div dojoAttachPoint="containerNode" class="dijitTitlePaneContent"></div> 
    9         <span dojoAttachPoint="tabEnd" dojoAttachEvent="onfocus:_trapTabs;onblur:_clearTrap;" tabindex="0"></span> 
    10         <span dojoAttachPoint="tabEndOuter" dojoAttachEvent="onfocus:_trapTabs;onblur:_clearTrap" tabindex="0"></span> 
     6                <div dojoAttachPoint="containerNode" class="dijitTitlePaneContent"></div> 
     7        <span dojoAttachPoint="tabEnd" dojoAttachEvent="onfocus:_cycleFocus;" tabindex="0"></span> 
    118</div>