8000 Selector: Backport jQuery selection context logic to selector-native · jquery/jquery@2e644e8 · GitHub
[go: up one dir, main page]

Skip to content

Commit 2e644e8

Browse files
authored
Selector: Backport jQuery selection context logic to selector-native
This makes: ```js $div.find("div > *") ``` no longer matching children of `$div`. Also, leading combinators now work, e.g.: ```js $div.find( "> *" ); ``` returns children of `$div`. As a result of that, a number of tests are no longer skipped in the `selector-native` mode. Also, rename `rcombinators` to `rleadingCombinator`. Fixes gh-5185 Closes gh-5186 Ref gh-5085
1 parent 7e7bd06 commit 2e644e8

23 files changed

+516
-414
lines changed

src/selector-native.js

Lines changed: 77 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,10 @@
88
* * Positional selectors (:first; :eq(n); :odd; etc.)
99
* * Type selectors (:input; :checkbox; :button; etc.)
1010
* * State-based selectors (:animated; :visible; :hidden; etc.)
11-
* * :has(selector)
12-
* * :not(complex selector)
11+
* * :has(selector) in browsers without native support
12+
* * :not(complex selector) in IE
1313
* * custom selectors via jQuery extensions
14-
* * Leading combinators (e.g., $collection.find("> *"))
1514
* * Reliable functionality on XML fragments
16-
* * Requiring all parts of a selector to match elements under context
17-
* (e.g., $div.find("div > *") now matches children of $div)
1815
* * Matching against non-elements
1916
* * Reliable sorting of disconnected nodes
2017
* * querySelectorAll bug fixes (e.g., unreliable :focus on WebKit)
@@ -26,20 +23,35 @@
2623

2724
import jQuery from "./core.js";
2825
import document from "./var/document.js";
29-
import documentElement from "./var/documentElement.js";
3026
import whitespace from "./var/whitespace.js";
3127

3228
// The following utils are attached directly to the jQuery object.
3329
import "./selector/escapeSelector.js";
3430
import "./selector/uniqueSort.js";
31+
import isIE from "./var/isIE.js";
32+
import booleans from "./selector/var/booleans.js";
33+
import rleadingCombinator from "./selector/var/rleadingCombinator.js";
34+
import rdescend from "./selector/var/rdescend.js";
35+
import rsibling from "./selector/var/rsibling.js";
36+
import matches from "./selector/var/matches.js";
37+
import testContext from "./selector/testContext.js";
38+
import filterMatchExpr from "./selector/filterMatchExpr.js";
39+
import preFilter from "./selector/preFilter.js";
40+
import tokenize from "./selector/tokenize.js";
41+
import toSelector from "./selector/toSelector.js";
3542

36-
// Support: IE 9 - 11+
37-
// IE requires a prefix.
38-
var matches = documentElement.matches || documentElement.msMatchesSelector;
43+
var matchExpr = jQuery.extend( {
44+
bool: new RegExp( "^(?:" + booleans + ")$", "i" ),
45+
needsContext: new RegExp( "^" + whitespace + "*[>+~]" )
46+
}, filterMatchExpr );
3947

4048
jQuery.extend( {
4149
find: function( selector, context, results, seed ) {
42-
var elem, nodeType,
50+
var elem, nid, groups, newSelector,
51+
newContext = context && context.ownerDocument,
52+
53+
// nodeType defaults to 9, since context defaults to document
54+
nodeType = context ? context.nodeType : 9,
4355
i = 0;
4456

4557
results = results || [];
@@ -51,7 +63,7 @@ jQuery.extend( {
5163
}
5264

5365
// Early return if context is not an element, document or document fragment
54-
if ( ( nodeType = context.nodeType ) !== 1 && nodeType !== 9 && nodeType !== 11 ) {
66+
if ( nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {
5567
return [];
5668
}
5769

@@ -62,18 +74,65 @@ jQuery.extend( {
6274
}
6375
}
6476
} else {
65-
jQuery.merge( results, context.querySelectorAll( selector ) );
77+
78+
newSelector = selector;
79+
newContext = context;
80+
81+
// qSA considers elements outside a scoping root when evaluating child or
82+
// descendant combinators, which is not what we want.
83+
// In such cases, we work around the behavior by prefixing every selector in the
84+
// list with an ID selector referencing the scope context.
85+
// The technique has to be used as well when a leading combinator is used
86+
// as such selectors are not recognized by querySelectorAll.
87+
// Thanks to Andrew Dupont for this technique.
88+
if ( nodeType === 1 &&
89+
( rdescend.test( selector ) || rleadingCombinator.test( selector ) ) ) {
90+
91+
// Expand context for sibling selectors
92+
newContext = rsibling.test( selector ) &&
93+
testContext( context.parentNode ) ||
94+
context;
95+
96+
// Outside of IE, if we're not changing the context we can
97+
// use :scope instead of an ID.
98+
if ( newContext !== context || isIE ) {
99+
100+
// Capture the context ID, setting it first if necessary
101+
if ( ( nid = context.getAttribute( "id" ) ) ) {
102+
nid = jQuery.escapeSelector( nid );
103+
} else {
104+
context.setAttribute( "id", ( nid = jQuery.expando ) );
105+
}
106+
}
107+
108+
// Prefix every selector in the list
109+
groups = tokenize( selector );
110+
i = groups.length;
111+
while ( i-- ) {
112+
groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " +
113+
toSelector( groups[ i ] );
114+
}
115+
newSelector = groups.join( "," );
116+
}
117+
118+
try {
119+
jQuery.merge( results, newContext.querySelectorAll( newSelector ) );
120+
} finally {
121+
if ( nid === jQuery.expando ) {
122+
context.removeAttribute( "id" );
123+
}
124+
}
66125
}
67126

68127
return results;
69128
},
70129
expr: {
71-
attrHandle: {},
72-
match: {
73-
bool: new RegExp( "^(?:checked|selected|async|autofocus|autoplay|controls|defer" +
74-
"|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped)$", "i" ),
75-
needsContext: new RegExp( "^" + whitespace + "*[>+~]" )
76-
}
130+
131+
// Can be adjusted by the user
132+
cacheLength: 50,
133+
134+
match: matchExpr,
135+
preFilter: preFilter
77136
}
78137
} );
79138

0 commit comments

Comments
 (0)
0