From 175e14f3fa56ac82400c9bd7cddf7c9bd528ec5f Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 4 Aug 2015 16:46:58 -0700 Subject: [PATCH] Core: Remove ancestor visibility requirement from :focusable selector * Check computed visibility in addition to :visible * Add tests for nested visibility override --- tests/unit/core/core.html | 5 +++++ tests/unit/core/selector.js | 10 ++++++++-- ui/focusable.js | 15 +++------------ 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/tests/unit/core/core.html b/tests/unit/core/core.html index 366eecebe78..5a089aa7b34 100644 --- a/tests/unit/core/core.html +++ b/tests/unit/core/core.html @@ -80,6 +80,11 @@ diff --git a/tests/unit/core/selector.js b/tests/unit/core/selector.js index c0a0f48880d..ffae7e024c4 100644 --- a/tests/unit/core/selector.js +++ b/tests/unit/core/selector.js @@ -125,7 +125,7 @@ test( "focusable - disabled elements", function() { } ); test( "focusable - hidden styles", function() { - expect( 8 ); + expect( 10 ); isNotFocusable( "#displayNoneAncestor-input", "input, display: none parent" ); isNotFocusable( "#displayNoneAncestor-span", "span with tabindex, display: none parent" ); @@ -133,6 +133,9 @@ test( "focusable - hidden styles", function() { isNotFocusable( "#visibilityHiddenAncestor-input", "input, visibility: hidden parent" ); isNotFocusable( "#visibilityHiddenAncestor-span", "span with tabindex, visibility: hidden parent" ); + isFocusable( "#nestedVisibilityOverrideAncestor-input", "input, visibility: visible parent but visibility: hidden grandparent" ); + isFocusable( "#nestedVisibilityOverrideAncestor-span", "span with tabindex, visibility: visible parent but visibility: hidden grandparent " ); + isNotFocusable( "#displayNone-input", "input, display: none" ); isNotFocusable( "#visibilityHidden-input", "input, visibility: hidden" ); @@ -210,7 +213,7 @@ test( "tabbable - disabled elements", function() { } ); test( "tabbable - hidden styles", function() { - expect( 8 ); + expect( 10 ); isNotTabbable( "#displayNoneAncestor-input", "input, display: none parent" ); isNotTabbable( "#displayNoneAncestor-span", "span with tabindex, display: none parent" ); @@ -218,6 +221,9 @@ test( "tabbable - hidden styles", function() { isNotTabbable( "#visibilityHiddenAncestor-input", "input, visibility: hidden parent" ); isNotTabbable( "#visibilityHiddenAncestor-span", "span with tabindex, visibility: hidden parent" ); + isTabbable( "#nestedVisibilityOverrideAncestor-input", "input, visibility: visible parent but visibility: hidden grandparent" ); + isTabbable( "#nestedVisibilityOverrideAncestor-span", "span with tabindex, visibility: visible parent but visibility: hidden grandparent " ); + isNotTabbable( "#displayNone-input", "input, display: none" ); isNotTabbable( "#visibilityHidden-input", "input, visibility: hidden" ); diff --git a/ui/focusable.js b/ui/focusable.js index a50598cd4ea..942f0fed362 100644 --- a/ui/focusable.js +++ b/ui/focusable.js @@ -34,26 +34,17 @@ $.ui.focusable = function( element, hasTabindex ) { if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) { return false; } - img = $( "img[usemap='#" + mapName + "']" )[ 0 ]; - return !!img && visible( img ); + img = $( "img[usemap='#" + mapName + "']" ); + return img.length > 0 && img.is( ":visible" ); } return ( /^(input|select|textarea|button|object)$/.test( nodeName ) ? !element.disabled : "a" === nodeName ? element.href || hasTabindex : hasTabindex ) && - - // The element and all of its ancestors must be visible - visible( element ); + $( element ).is( ":visible" ) && $( element ).css( "visibility" ) === "visible"; }; -function visible( element ) { - return $.expr.filters.visible( element ) && - !$( element ).parents().addBack().filter( function() { - return $.css( this, "visibility" ) === "hidden"; - } ).length; -} - $.extend( $.expr[ ":" ], { focusable: function( element ) { return $.ui.focusable( element, $.attr( element, "tabindex" ) != null );