diff options
author | Michał Gołębiowski-Owczarek <m.goleb@gmail.com> | 2019-08-19 18:41:03 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-08-19 18:41:03 +0200 |
commit | df6a7f7f0f615149266b1a51064293b748b29900 (patch) | |
tree | 3a738ca53fe3c9eabc5fd4db4d8d4ec8c93217aa /src | |
parent | 7bdf307b51e4d4a891b123a96d4899e31bfba024 (diff) | |
download | jquery-df6a7f7f0f615149266b1a51064293b748b29900.tar.gz jquery-df6a7f7f0f615149266b1a51064293b748b29900.zip |
Selector: Leverage the :scope pseudo-class where possible
The `:scope` pseudo-class[1] has surprisingly good browser support: Chrome,
Firefox & Safari have supported if for a long time; only IE & Edge lack support.
This commit leverages this pseudo-class to get rid of the ID hack in most cases.
Adding a temporary ID may cause layout thrashing which was reported a few times
in [the past.
We can't completely eliminate the ID hack in modern browses as sibling selectors
require us to change context to the parent and then `:scope` stops applying to
what we'd like. But it'd still improve performance in the vast majority of
cases.
[1] https://developer.mozilla.org/en-US/docs/Web/CSS/:scope
Fixes gh-4453
Closes gh-4454
Ref gh-4332
Ref jquery/sizzle#405
Diffstat (limited to 'src')
-rw-r--r-- | src/selector.js | 29 | ||||
-rw-r--r-- | src/selector/support.js | 17 |
2 files changed, 35 insertions, 11 deletions
diff --git a/src/selector.js b/src/selector.js index 913f7486f..3e187a159 100644 --- a/src/selector.js +++ b/src/selector.js @@ -4,12 +4,13 @@ define( [ "./var/indexOf", "./var/pop", "./var/push", + "./selector/support", // The following utils are attached directly to the jQuery object. "./selector/contains", "./selector/escapeSelector", "./selector/uniqueSort" -], function( jQuery, document, indexOf, pop, push ) { +], function( jQuery, document, indexOf, pop, push, support ) { "use strict"; @@ -230,24 +231,30 @@ function find( selector, context, results, seed ) { // Thanks to Andrew Dupont for this technique. if ( nodeType === 1 && rdescend.test( selector ) ) { - // Capture the context ID, setting it first if necessary - if ( ( nid = context.getAttribute( "id" ) ) ) { - nid = jQuery.escapeSelector( nid ); - } else { - context.setAttribute( "id", ( nid = expando ) ); + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + + // We can use :scope instead of the ID hack if the browser + // supports it & if we're not changing the context. + if ( newContext !== context || !support.scope ) { + + // Capture the context ID, setting it first if necessary + if ( ( nid = context.getAttribute( "id" ) ) ) { + nid = jQuery.escapeSelector( nid ); + } else { + context.setAttribute( "id", ( nid = expando ) ); + } } // Prefix every selector in the list groups = tokenize( selector ); i = groups.length; while ( i-- ) { - groups[ i ] = "#" + nid + " " + toSelector( groups[ i ] ); + groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " + + toSelector( groups[ i ] ); } newSelector = groups.join( "," ); - - // Expand context for sibling selectors - newContext = rsibling.test( selector ) && testContext( context.parentNode ) || - context; } try { diff --git a/src/selector/support.js b/src/selector/support.js new file mode 100644 index 000000000..86cd2d9ae --- /dev/null +++ b/src/selector/support.js @@ -0,0 +1,17 @@ +define( [ + "../var/document", + "../var/support" +], function( document, support ) { + +"use strict"; + +// Support: IE 9 - 11+, Edge 12 - 18+ +// IE/Edge don't support the :scope pseudo-class. +try { + document.querySelectorAll( ":scope" ); + support.scope = true; +} catch ( e ) {} + +return support; + +} ); |