@@ -176,8 +176,7 @@ $.widget( "ui.tabs", {
176176 } ,
177177
178178 _tabKeydown : function ( event ) {
179- var focusedAnchor = $ ( this . document [ 0 ] . activeElement ) . closest ( "a" ) ,
180- focusedTab = focusedAnchor . closest ( "li" ) ,
179+ var focusedTab = $ ( this . document [ 0 ] . activeElement ) . closest ( "li" ) ,
181180 selectedIndex = this . tabs . index ( focusedTab ) ,
182181 goingForward = true ;
183182
@@ -203,12 +202,14 @@ $.widget( "ui.tabs", {
203202 break ;
204203 case $ . ui . keyCode . SPACE :
205204
205+ // Activate only, no collapsing
206206 event . preventDefault ( ) ;
207207 clearTimeout ( this . activating ) ;
208208 this . _activate ( selectedIndex ) ;
209209 return ;
210210 case $ . ui . keyCode . ENTER :
211211
212+ // Toggle (cancel delayed activation, allow collapsing)
212213 event . preventDefault ( ) ;
213214 clearTimeout ( this . activating ) ;
214215
@@ -227,8 +228,11 @@ $.widget( "ui.tabs", {
227228 // Navigating with control/command key will prevent automatic activation
228229 if ( ! event . ctrlKey && ! event . metaKey ) {
229230
230- focusedAnchor . attr ( "aria-selected" , "false" ) ;
231- this . anchors . eq ( selectedIndex ) . attr ( "aria-selected" , "true" ) ;
231+ // Update aria-selected immediately so that AT think the tab is already selected.
232+ // Otherwise AT may confuse the user by stating that they need to activate the tab,
233+ // but the tab will already be activated by the time the announcement finishes.
234+ focusedTab . attr ( "aria-selected" , "false" ) ;
235+ this . tabs . eq ( selectedIndex ) . attr ( "aria-selected" , "true" ) ;
232236
233237 this . activating = this . _delay ( function ( ) {
234238 this . option ( "active" , selectedIndex ) ;
@@ -282,7 +286,7 @@ $.widget( "ui.tabs", {
282286
283287 _focusNextTab : function ( index , goingForward ) {
284288 index = this . _findNextTab ( index , goingForward ) ;
285- this . anchors . eq ( index ) . trigger ( "focus" ) ;
289+ this . tabs . eq ( index ) . trigger ( "focus" ) ;
286290 return index ;
287291 } ,
288292
@@ -407,32 +411,42 @@ $.widget( "ui.tabs", {
407411 }
408412 } ) ;
409413
410- this . tabs = this . tablist . find ( "> li:has(a[href])" )
414+ this . tabs = this . tablist . find ( "> li:has(a[href]) > a " )
411415 . attr ( {
412- role : "presentation"
416+ role : "tab" ,
417+ tabindex : - 1
413418 } ) ;
414419 this . _addClass ( this . tabs , "ui-tabs-tab" , "ui-state-default" ) ;
415-
416- this . anchors = this . tabs . map ( function ( ) {
417- return $ ( "a" , this ) [ 0 ] ;
418- } )
420+ this . tablist . find ( "> li:has(a[href])" )
419421 . attr ( {
420- role : "tab" ,
421- tabIndex : - 1
422+ role : "presentation"
422423 } ) ;
424+
425+ this . anchors = this . tabs ;
426+ this . _addClass ( this . tabs , "ui-tabs-tab" , "ui-state-default" ) ;
423427 this . _addClass ( this . anchors , "ui-tabs-anchor" ) ;
424428
425429 this . panels = $ ( ) ;
426430
427431 this . anchors . each ( function ( i , anchor ) {
428432 var selector , panel , panelId ,
429433 anchorId = $ ( anchor ) . uniqueId ( ) . attr ( "id" ) ,
430- tab = $ ( anchor ) . closest ( "li" ) ,
434+ tab = $ ( anchor ) ,
431435 originalAriaControls = tab . attr ( "aria-controls" ) ;
432436
433437 // Inline tab
434438 if ( that . _isLocal ( anchor ) ) {
435439
440+ // The "scrolling to a fragment" section of the HTML spec:
441+ // https://html.spec.whatwg.org/#scrolling-to-a-fragment
442+ // uses a concept of document's indicated part:
443+ // https://html.spec.whatwg.org/#the-indicated-part-of-the-document
444+ // Slightly below there's an algorithm to compute the indicated
445+ // part:
446+ // https://html.spec.whatwg.org/#the-indicated-part-of-the-document
447+ // First, the algorithm tries the hash as-is, without decoding.
448+ // Then, if one is not found, the same is attempted with a decoded
449+ // hash. Replicate this logic.
436450 selector = anchor . hash ;
437451 panelId = selector . substring ( 1 ) ;
438452 panel = that . element . find ( "#" + CSS . escape ( panelId ) ) ;
@@ -441,8 +455,11 @@ $.widget( "ui.tabs", {
441455 panel = that . element . find ( "#" + CSS . escape ( panelId ) ) ;
442456 }
443457
458+ // remote tab
444459 } else {
445460
461+ // If the tab doesn't already have aria-controls,
462+ // generate an id by using a throw-away element
446463 panelId = tab . attr ( "aria-controls" ) || $ ( { } ) . uniqueId ( ) [ 0 ] . id ;
447464 selector = "#" + panelId ;
448465 panel = that . element . find ( selector ) ;
@@ -537,7 +554,7 @@ $.widget( "ui.tabs", {
537554 this . _on ( this . tabs , { keydown : "_tabKeydown" } ) ;
538555 this . _on ( this . panels , { keydown : "_panelKeydown" } ) ;
539556
540- this . _focusable ( this . anchors ) ;
557+ this . _focusable ( this . tabs ) ;
541558 this . _hoverable ( this . tabs ) ;
542559 } ,
543560
@@ -580,7 +597,7 @@ $.widget( "ui.tabs", {
580597 var options = this . options ,
581598 active = this . active ,
582599 anchor = $ ( event . currentTarget ) ,
583- tab = anchor . closest ( "li" ) ,
600+ tab = anchor ,
584601 clickedIsActive = tab [ 0 ] === active [ 0 ] ,
585602 collapsing = clickedIsActive && options . collapsible ,
586603 toShow = collapsing ? $ ( ) : this . _getPanelForTab ( tab ) ,
@@ -641,7 +658,7 @@ $.widget( "ui.tabs", {
641658 }
642659
643660 function show ( ) {
644- that . _addClass ( eventData . newTab . closest ( "li" ) , "ui-tabs-active" , "ui-state-active" ) ;
661+ that . _addClass ( eventData . newTab , "ui-tabs-active" , "ui-state-active" ) ;
645662
646663 if ( toShow . length && that . options . show ) {
647664 that . _show ( toShow , that . options . show , complete ) ;
@@ -654,12 +671,12 @@ $.widget( "ui.tabs", {
654671 // Start out by hiding, then showing, then completing
655672 if ( toHide . length && this . options . hide ) {
656673 this . _hide ( toHide , this . options . hide , function ( ) {
657- that . _removeClass ( eventData . oldTab . closest ( "li" ) ,
674+ that . _removeClass ( eventData . oldTab ,
658675 "ui-tabs-active" , "ui-state-active" ) ;
659676 show ( ) ;
660677 } ) ;
661678 } else {
662- this . _removeClass ( eventData . oldTab . closest ( "li" ) ,
679+ this . _removeClass ( eventData . oldTab ,
663680 "ui-tabs-active" , "ui-state-active" ) ;
664681 toHide . hide ( ) ;
665682 show ( ) ;
@@ -818,7 +835,7 @@ $.widget( "ui.tabs", {
818835 index = this . _getIndex ( index ) ;
819836 var that = this ,
820837 tab = this . tabs . eq ( index ) ,
821- anchor = tab . find ( ".ui-tabs-anchor" ) ,
838+ anchor = tab ,
822839 panel = this . _getPanelForTab ( tab ) ,
823840 eventData = {
824841 tab : tab ,
@@ -894,3 +911,4 @@ if ( $.uiBackCompat === true ) {
894911return $ . ui . tabs ;
895912
896913} ) ;
914+
0 commit comments