Skip to content

Commit dd77de0

Browse files
committed
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 jquery/jquery#4453 Ref jquery/jquery#4454 Ref jquery/jquery#4332 Ref gh-405
1 parent cd0239b commit dd77de0

File tree

5 files changed

+96
-22
lines changed

5 files changed

+96
-22
lines changed

dist/sizzle.js

+27-10
Original file line numberDiff line numberDiff line change
@@ -326,24 +326,30 @@ function Sizzle( selector, context, results, seed ) {
326326
// Thanks to Andrew Dupont for this technique.
327327
if ( nodeType === 1 && rdescend.test( selector ) ) {
328328

329-
// Capture the context ID, setting it first if necessary
330-
if ( ( nid = context.getAttribute( "id" ) ) ) {
331-
nid = nid.replace( rcssescape, fcssescape );
332-
} else {
333-
context.setAttribute( "id", ( nid = expando ) );
329+
// Expand context for sibling selectors
330+
newContext = rsibling.test( selector ) && testContext( context.parentNode ) ||
331+
context;
332+
333+
// We can use :scope instead of the ID hack if the browser
334+
// supports it & if we're not changing the context.
335+
if ( newContext !== context || !support.scope ) {
336+
337+
// Capture the context ID, setting it first if necessary
338+
if ( ( nid = context.getAttribute( "id" ) ) ) {
339+
nid = nid.replace( rcssescape, fcssescape );
340+
} else {
341+
context.setAttribute( "id", ( nid = expando ) );
342+
}
334343
}
335344

336345
// Prefix every selector in the list
337346
groups = tokenize( selector );
338347
i = groups.length;
339348
while ( i-- ) {
340-
groups[ i ] = "#" + nid + " " + toSelector( groups[ i ] );
349+
groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " +
350+
toSelector( groups[ i ] );
341351
}
342352
newSelector = groups.join( "," );
343-
344-
// Expand context for sibling selectors
345-
newContext = rsibling.test( selector ) && testContext( context.parentNode ) ||
346-
context;
347353
}
348354

349355
try {
@@ -631,6 +637,17 @@ setDocument = Sizzle.setDocument = function( node ) {
631637
}
632638
}
633639

640+
// Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only,
641+
// Safari 4 - 5 only, Opera <=11.6 - 12.x only
642+
// IE/Edge & older browsers don't support the :scope pseudo-class.
643+
// Support: Safari 6.0 only
644+
// Safari 6.0 supports :scope but it's an alias of :root there.
645+
support.scope = assert( function( el ) {
646+
docElem.appendChild( el ).appendChild( document.createElement( "div" ) );
647+
return typeof el.querySelectorAll !== "undefined" &&
648+
!el.querySelectorAll( ":scope fieldset div" ).length;
649+
} );
650+
634651
/* Attributes
635652
---------------------------------------------------------------------- */
636653

0 commit comments

Comments
 (0)