Skip to content

Commit 0db243a

Browse files
westonruterscottgonzalez
authored andcommitted
Core: Remove ancestor visibility requirement from :focusable selector
* Check computed visibility in addition to :visible * Add tests for nested visibility override Fixes #14596 Closes gh-1583
1 parent 6308a26 commit 0db243a

File tree

3 files changed

+16
-14
lines changed

3 files changed

+16
-14
lines changed

tests/unit/core/core.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@
8080
<div id="visibilityHiddenAncestor" style="visibility: hidden;">
8181
<input id="visibilityHiddenAncestor-input">
8282
<span tabindex="1" id="visibilityHiddenAncestor-span">.</span>
83+
84+
<span id="nestedVisibilityOverrideAncestor" style="visibility: visible">
85+
<input id="nestedVisibilityOverrideAncestor-input">
86+
<span tabindex="1" id="nestedVisibilityOverrideAncestor-span">.</span>
87+
</span>
8388
</div>
8489

8590
<span tabindex="1" id="displayNone-span" style="display: none;">.</span>

tests/unit/core/selector.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,14 +125,17 @@ test( "focusable - disabled elements", function() {
125125
} );
126126

127127
test( "focusable - hidden styles", function() {
128-
expect( 8 );
128+
expect( 10 );
129129

130130
isNotFocusable( "#displayNoneAncestor-input", "input, display: none parent" );
131131
isNotFocusable( "#displayNoneAncestor-span", "span with tabindex, display: none parent" );
132132

133133
isNotFocusable( "#visibilityHiddenAncestor-input", "input, visibility: hidden parent" );
134134
isNotFocusable( "#visibilityHiddenAncestor-span", "span with tabindex, visibility: hidden parent" );
135135

136+
isFocusable( "#nestedVisibilityOverrideAncestor-input", "input, visibility: visible parent but visibility: hidden grandparent" );
137+
isFocusable( "#nestedVisibilityOverrideAncestor-span", "span with tabindex, visibility: visible parent but visibility: hidden grandparent " );
138+
136139
isNotFocusable( "#displayNone-input", "input, display: none" );
137140
isNotFocusable( "#visibilityHidden-input", "input, visibility: hidden" );
138141

@@ -210,14 +213,17 @@ test( "tabbable - disabled elements", function() {
210213
} );
211214

212215
test( "tabbable - hidden styles", function() {
213-
expect( 8 );
216+
expect( 10 );
214217

215218
isNotTabbable( "#displayNoneAncestor-input", "input, display: none parent" );
216219
isNotTabbable( "#displayNoneAncestor-span", "span with tabindex, display: none parent" );
217220

218221
isNotTabbable( "#visibilityHiddenAncestor-input", "input, visibility: hidden parent" );
219222
isNotTabbable( "#visibilityHiddenAncestor-span", "span with tabindex, visibility: hidden parent" );
220223

224+
isTabbable( "#nestedVisibilityOverrideAncestor-input", "input, visibility: visible parent but visibility: hidden grandparent" );
225+
isTabbable( "#nestedVisibilityOverrideAncestor-span", "span with tabindex, visibility: visible parent but visibility: hidden grandparent " );
226+
221227
isNotTabbable( "#displayNone-input", "input, display: none" );
222228
isNotTabbable( "#visibilityHidden-input", "input, visibility: hidden" );
223229

ui/focusable.js

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,26 +34,17 @@ $.ui.focusable = function( element, hasTabindex ) {
3434
if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
3535
return false;
3636
}
37-
img = $( "img[usemap='#" + mapName + "']" )[ 0 ];
38-
return !!img && visible( img );
37+
img = $( "img[usemap='#" + mapName + "']" );
38+
return img.length > 0 && img.is( ":visible" );
3939
}
4040
return ( /^(input|select|textarea|button|object)$/.test( nodeName ) ?
4141
!element.disabled :
4242
"a" === nodeName ?
4343
element.href || hasTabindex :
4444
hasTabindex ) &&
45-
46-
// The element and all of its ancestors must be visible
47-
visible( element );
45+
$( element ).is( ":visible" ) && $( element ).css( "visibility" ) === "visible";
4846
};
4947

50-
function visible( element ) {
51-
return $.expr.filters.visible( element ) &&
52-
!$( element ).parents().addBack().filter( function() {
53-
return $.css( this, "visibility" ) === "hidden";
54-
} ).length;
55-
}
56-
5748
$.extend( $.expr[ ":" ], {
5849
focusable: function( element ) {
5950
return $.ui.focusable( element, $.attr( element, "tabindex" ) != null );

0 commit comments

Comments
 (0)