aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichał Gołębiowski-Owczarek <m.goleb@gmail.com>2019-08-26 19:15:53 +0200
committerGitHub <noreply@github.com>2019-08-26 19:15:53 +0200
commit29a9544a4fb743491a42f827a6cf8627b7b99e0f (patch)
tree7c700ac6a669717e3245b5292ab46be8eefdfe86
parentabdc89ac2e581392b800c0364e0f5f2b6a82cdc6 (diff)
downloadjquery-29a9544a4fb743491a42f827a6cf8627b7b99e0f.tar.gz
jquery-29a9544a4fb743491a42f827a6cf8627b7b99e0f.zip
Selector: reduce size, simplify setDocument
With new selector code doing less convoluted support tests, it was possible to extract a lot of logic out of setDocument & also reduce size. This commit also backports jquery/sizzle#439 that was reverted by mistake during a switch from JSHint + JSCS to ESLint. Closes gh-4462 Ref jquery/sizzle#442 Ref jquery/sizzle#439
-rw-r--r--src/selector.js210
-rw-r--r--src/selector/rbuggyQSA.js30
-rw-r--r--test/unit/selector.js18
3 files changed, 128 insertions, 130 deletions
diff --git a/src/selector.js b/src/selector.js
index 3e187a159..847d3a448 100644
--- a/src/selector.js
+++ b/src/selector.js
@@ -1,20 +1,23 @@
define( [
"./core",
"./var/document",
+ "./var/documentElement",
"./var/indexOf",
"./var/pop",
"./var/push",
+ "./selector/rbuggyQSA",
"./selector/support",
// The following utils are attached directly to the jQuery object.
"./selector/contains",
"./selector/escapeSelector",
"./selector/uniqueSort"
-], function( jQuery, document, indexOf, pop, push, support ) {
+], function( jQuery, document, documentElement, indexOf, pop, push, rbuggyQSA, support ) {
"use strict";
-var preferredDoc = document;
+var preferredDoc = document,
+ matches = documentElement.matches || documentElement.msMatchesSelector;
( function() {
@@ -24,10 +27,8 @@ var i,
// Local document vars
document,
- docElem,
+ documentElement,
documentIsHTML,
- rbuggyQSA,
- matches,
// Instance-specific data
expando = jQuery.expando,
@@ -86,20 +87,20 @@ var i,
ridentifier = new RegExp( "^" + identifier + "$" ),
matchExpr = {
- "ID": new RegExp( "^#(" + identifier + ")" ),
- "CLASS": new RegExp( "^\\.(" + identifier + ")" ),
- "TAG": new RegExp( "^(" + identifier + "|[*])" ),
- "ATTR": new RegExp( "^" + attributes ),
- "PSEUDO": new RegExp( "^" + pseudos ),
- "CHILD": new RegExp(
+ ID: new RegExp( "^#(" + identifier + ")" ),
+ CLASS: new RegExp( "^\\.(" + identifier + ")" ),
+ TAG: new RegExp( "^(" + identifier + "|[*])" ),
+ ATTR: new RegExp( "^" + attributes ),
+ PSEUDO: new RegExp( "^" + pseudos ),
+ CHILD: new RegExp(
"^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" +
whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" +
whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
- "bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
+ bool: new RegExp( "^(?:" + booleans + ")$", "i" ),
// For use in libraries implementing .is()
// We use this for POS matching in `select`
- "needsContext": new RegExp( "^" + whitespace +
+ needsContext: new RegExp( "^" + whitespace +
"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace +
"*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
},
@@ -190,10 +191,8 @@ function find( selector, context, results, seed ) {
if ( nodeType === 9 ) {
if ( ( elem = context.getElementById( m ) ) ) {
results.push( elem );
- return results;
- } else {
- return results;
}
+ return results;
// Element context
} else {
@@ -420,20 +419,19 @@ function testContext( context ) {
/**
* Sets document-related variables once based on the current document
* @param {Element|Object} [node] An element or document object to use to set the document
- * @returns {Object} Returns the current document
*/
function setDocument( node ) {
var subWindow,
doc = node ? node.ownerDocument || node : preferredDoc;
// Return early if doc is invalid or already selected
- if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
- return document;
+ if ( doc === document || doc.nodeType !== 9 ) {
+ return;
}
// Update global variables
document = doc;
- docElem = document.documentElement;
+ documentElement = document.documentElement;
documentIsHTML = !jQuery.isXMLDoc( document );
// Support: IE 9 - 11+, Edge 12 - 18+
@@ -444,77 +442,6 @@ function setDocument( node ) {
// Support: IE 9 - 11+, Edge 12 - 18+
subWindow.addEventListener( "unload", unloadHandler );
}
-
- // ID filter and find
- Expr.filter.ID = function( id ) {
- var attrId = id.replace( runescape, funescape );
- return function( elem ) {
- return elem.getAttribute( "id" ) === attrId;
- };
- };
- Expr.find.ID = function( id, context ) {
- if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
- var elem = context.getElementById( id );
- return elem ? [ elem ] : [];
- }
- };
-
- // Tag
- Expr.find.TAG = function( tag, context ) {
- if ( typeof context.getElementsByTagName !== "undefined" ) {
- return context.getElementsByTagName( tag );
-
- // DocumentFragment nodes don't have gEBTN
- } else {
- return context.querySelectorAll( tag );
- }
- };
-
- // Class
- Expr.find.CLASS = function( className, context ) {
- if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) {
- return context.getElementsByClassName( className );
- }
- };
-
- /* QSA/matchesSelector
- ---------------------------------------------------------------------- */
-
- // QSA and matchesSelector support
-
- rbuggyQSA = [];
-
- var testEl = document.createElement( "fieldset" );
-
- testEl.innerHTML = "<a href='' disabled='disabled'></a>" +
- "<select disabled='disabled'><option/></select>";
-
- // Support: Windows 8 Native Apps
- // The type and name attributes are restricted during .innerHTML assignment
- var input = document.createElement( "input" );
- input.setAttribute( "type", "hidden" );
- testEl.appendChild( input ).setAttribute( "name", "D" );
-
- // Support: Chrome 74+
- // :enabled/:disabled and hidden elements (hidden elements are still enabled)
- if ( testEl.querySelectorAll( ":enabled" ).length !== 2 ) {
- rbuggyQSA.push( ":enabled", ":disabled" );
- }
-
- // Support: IE 9 - 11+
- // IE's :disabled selector does not pick up the children of disabled fieldsets
- docElem.appendChild( testEl ).disabled = true;
- if ( testEl.querySelectorAll( ":disabled" ).length !== 2 ) {
- rbuggyQSA.push( ":enabled", ":disabled" );
- }
-
- docElem.removeChild( testEl );
-
- matches = docElem.matches || docElem.msMatchesSelector;
-
- rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) );
-
- return document;
}
find.matches = function( expr, elements ) {
@@ -551,7 +478,30 @@ Expr = jQuery.expr = {
match: matchExpr,
- find: {},
+ find: {
+ ID: function( id, context ) {
+ if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
+ var elem = context.getElementById( id );
+ return elem ? [ elem ] : [];
+ }
+ },
+
+ TAG: function( tag, context ) {
+ if ( typeof context.getElementsByTagName !== "undefined" ) {
+ return context.getElementsByTagName( tag );
+
+ // DocumentFragment nodes don't have gEBTN
+ } else {
+ return context.querySelectorAll( tag );
+ }
+ },
+
+ CLASS: function( className, context ) {
+ if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) {
+ return context.getElementsByClassName( className );
+ }
+ }
+ },
relative: {
">": { dir: "parentNode", first: true },
@@ -561,7 +511,7 @@ Expr = jQuery.expr = {
},
preFilter: {
- "ATTR": function( match ) {
+ ATTR: function( match ) {
match[ 1 ] = match[ 1 ].replace( runescape, funescape );
// Move the given value to match[3] whether quoted or unquoted
@@ -575,7 +525,7 @@ Expr = jQuery.expr = {
return match.slice( 0, 4 );
},
- "CHILD": function( match ) {
+ CHILD: function( match ) {
/* matches from matchExpr["CHILD"]
1 type (only|nth|...)
@@ -612,7 +562,7 @@ Expr = jQuery.expr = {
return match;
},
- "PSEUDO": function( match ) {
+ PSEUDO: function( match ) {
var excess,
unquoted = !match[ 6 ] && match[ 2 ];
@@ -644,8 +594,14 @@ Expr = jQuery.expr = {
},
filter: {
+ ID: function( id ) {
+ var attrId = id.replace( runescape, funescape );
+ return function( elem ) {
+ return elem.getAttribute( "id" ) === attrId;
+ };
+ },
- "TAG": function( nodeNameSelector ) {
+ TAG: function( nodeNameSelector ) {
var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
return nodeNameSelector === "*" ?
function() {
@@ -656,7 +612,7 @@ Expr = jQuery.expr = {
};
},
- "CLASS": function( className ) {
+ CLASS: function( className ) {
var pattern = classCache[ className + " " ];
return pattern ||
@@ -672,7 +628,7 @@ Expr = jQuery.expr = {
} );
},
- "ATTR": function( name, operator, check ) {
+ ATTR: function( name, operator, check ) {
return function( elem ) {
var result = jQuery.attr( elem, name );
@@ -712,7 +668,7 @@ Expr = jQuery.expr = {
};
},
- "CHILD": function( type, what, _argument, first, last ) {
+ CHILD: function( type, what, _argument, first, last ) {
var simple = type.slice( 0, 3 ) !== "nth",
forward = type.slice( -4 ) !== "last",
ofType = what === "of-type";
@@ -822,7 +778,7 @@ Expr = jQuery.expr = {
};
},
- "PSEUDO": function( pseudo, argument ) {
+ PSEUDO: function( pseudo, argument ) {
// pseudo-class names are case-insensitive
// http://www.w3.org/TR/selectors/#pseudo-classes
@@ -864,7 +820,7 @@ Expr = jQuery.expr = {
pseudos: {
// Potentially complex pseudos
- "not": markFunction( function( selector ) {
+ not: markFunction( function( selector ) {
// Trim the selector passed to compile
// to avoid treating leading and trailing
@@ -896,13 +852,13 @@ Expr = jQuery.expr = {
};
} ),
- "has": markFunction( function( selector ) {
+ has: markFunction( function( selector ) {
return function( elem ) {
return find( selector, elem ).length > 0;
};
} ),
- "contains": markFunction( function( text ) {
+ contains: markFunction( function( text ) {
text = text.replace( runescape, funescape );
return function( elem ) {
return ( elem.textContent || jQuery.text( elem ) ).indexOf( text ) > -1;
@@ -916,7 +872,7 @@ Expr = jQuery.expr = {
// The matching of C against the element's language value is performed case-insensitively.
// The identifier C does not have to be a valid language name."
// http://www.w3.org/TR/selectors/#lang-pseudo
- "lang": markFunction( function( lang ) {
+ lang: markFunction( function( lang ) {
// lang value must be a valid identifier
if ( !ridentifier.test( lang || "" ) ) {
@@ -939,26 +895,26 @@ Expr = jQuery.expr = {
} ),
// Miscellaneous
- "target": function( elem ) {
+ target: function( elem ) {
var hash = window.location && window.location.hash;
return hash && hash.slice( 1 ) === elem.id;
},
- "root": function( elem ) {
- return elem === docElem;
+ root: function( elem ) {
+ return elem === documentElement;
},
- "focus": function( elem ) {
+ focus: function( elem ) {
return elem === document.activeElement &&
- ( !document.hasFocus || document.hasFocus() ) &&
+ document.hasFocus() &&
!!( elem.type || elem.href || ~elem.tabIndex );
},
// Boolean properties
- "enabled": createDisabledPseudo( false ),
- "disabled": createDisabledPseudo( true ),
+ enabled: createDisabledPseudo( false ),
+ disabled: createDisabledPseudo( true ),
- "checked": function( elem ) {
+ checked: function( elem ) {
// In CSS3, :checked should return both checked and selected elements
// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
@@ -967,7 +923,7 @@ Expr = jQuery.expr = {
( nodeName === "option" && !!elem.selected );
},
- "selected": function( elem ) {
+ selected: function( elem ) {
// Support: IE <=11+
// Accessing the selectedIndex property
@@ -982,7 +938,7 @@ Expr = jQuery.expr = {
},
// Contents
- "empty": function( elem ) {
+ empty: function( elem ) {
// http://www.w3.org/TR/selectors/#empty-pseudo
// :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
@@ -996,43 +952,43 @@ Expr = jQuery.expr = {
return true;
},
- "parent": function( elem ) {
+ parent: function( elem ) {
return !Expr.pseudos.empty( elem );
},
// Element/input types
- "header": function( elem ) {
+ header: function( elem ) {
return rheader.test( elem.nodeName );
},
- "input": function( elem ) {
+ input: function( elem ) {
return rinputs.test( elem.nodeName );
},
- "button": function( elem ) {
+ button: function( elem ) {
var name = elem.nodeName.toLowerCase();
return name === "input" && elem.type === "button" || name === "button";
},
- "text": function( elem ) {
+ text: function( elem ) {
return elem.nodeName.toLowerCase() === "input" &&
elem.type === "text";
},
// Position-in-collection
- "first": createPositionalPseudo( function() {
+ first: createPositionalPseudo( function() {
return [ 0 ];
} ),
- "last": createPositionalPseudo( function( _matchIndexes, length ) {
+ last: createPositionalPseudo( function( _matchIndexes, length ) {
return [ length - 1 ];
} ),
- "eq": createPositionalPseudo( function( _matchIndexes, length, argument ) {
+ eq: createPositionalPseudo( function( _matchIndexes, length, argument ) {
return [ argument < 0 ? argument + length : argument ];
} ),
- "even": createPositionalPseudo( function( matchIndexes, length ) {
+ even: createPositionalPseudo( function( matchIndexes, length ) {
var i = 0;
for ( ; i < length; i += 2 ) {
matchIndexes.push( i );
@@ -1040,7 +996,7 @@ Expr = jQuery.expr = {
return matchIndexes;
} ),
- "odd": createPositionalPseudo( function( matchIndexes, length ) {
+ odd: createPositionalPseudo( function( matchIndexes, length ) {
var i = 1;
for ( ; i < length; i += 2 ) {
matchIndexes.push( i );
@@ -1048,7 +1004,7 @@ Expr = jQuery.expr = {
return matchIndexes;
} ),
- "lt": createPositionalPseudo( function( matchIndexes, length, argument ) {
+ lt: createPositionalPseudo( function( matchIndexes, length, argument ) {
var i;
if ( argument < 0 ) {
@@ -1065,7 +1021,7 @@ Expr = jQuery.expr = {
return matchIndexes;
} ),
- "gt": createPositionalPseudo( function( matchIndexes, length, argument ) {
+ gt: createPositionalPseudo( function( matchIndexes, length, argument ) {
var i = argument < 0 ? argument + length : argument;
for ( ; ++i < length; ) {
matchIndexes.push( i );
@@ -1672,8 +1628,6 @@ setDocument();
jQuery.find = find;
-/* eslint-enable */
-
} )();
} );
diff --git a/src/selector/rbuggyQSA.js b/src/selector/rbuggyQSA.js
new file mode 100644
index 000000000..eee151afb
--- /dev/null
+++ b/src/selector/rbuggyQSA.js
@@ -0,0 +1,30 @@
+define( [
+ "../var/document",
+ "../var/isIE"
+], function( document, isIE ) {
+
+"use strict";
+
+var rbuggyQSA = [],
+ testEl = document.createElement( "div" );
+
+testEl.innerHTML = "<a href=''></a>";
+
+// Support: Chrome 38 - 77 only
+// Chrome considers anchor elements with href to match ":enabled"
+// See https://bugs.chromium.org/p/chromium/issues/detail?id=993387
+if ( testEl.querySelectorAll( ":enabled" ).length ) {
+ rbuggyQSA.push( ":enabled" );
+}
+
+// Support: IE 9 - 11+
+// IE's :disabled selector does not pick up the children of disabled fieldsets
+if ( isIE ) {
+ rbuggyQSA.push( ":enabled", ":disabled" );
+}
+
+rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) );
+
+return rbuggyQSA;
+
+} );
diff --git a/test/unit/selector.js b/test/unit/selector.js
index 779b554e7..f015fc933 100644
--- a/test/unit/selector.js
+++ b/test/unit/selector.js
@@ -1408,8 +1408,6 @@ QUnit.test( "pseudo - fieldset:(dis|en)abled", function( assert ) {
QUnit.test( "pseudo - :disabled by ancestry", function( assert ) {
assert.expect( 1 );
- // Don't test for presence of select
- // IE6 doesn't visibly or functionally disable them when the fieldset is disabled
assert.t(
"Inputs inherit disabled from fieldset",
"#disabled-fieldset :disabled",
@@ -1418,6 +1416,22 @@ QUnit.test( "pseudo - :disabled by ancestry", function( assert ) {
);
} );
+QUnit.test( "pseudo - a:(dis|en)abled", function( assert ) {
+ assert.expect( 2 );
+
+ var enabled, disabled,
+ container = jQuery( "<div/>" ),
+ anchor = jQuery( "<a href='#'>Link</a>" );
+
+ container.appendTo( "#qunit-fixture" );
+
+ enabled = container.find( "a:enabled" );
+ disabled = container.find( "a:disabled" );
+
+ assert.strictEqual( enabled.length, 0, ":enabled doesn't match anchor elements" );
+ assert.strictEqual( disabled.length, 0, ":disabled doesn't match anchor elements" );
+} );
+
QUnit.test( "pseudo - :target and :root", function( assert ) {
assert.expect( 2 );