Skip to content
Browse files

Add QSA tests. If QSA is buggy on certain selectors, use a regex to p…

…redict and skip QSA. Remove outdated Safari 3.2 QSA test. Fix TAG regex to include '*' on matches.
  • Loading branch information...
1 parent fbef036 commit f0bcea07e90aeeba82a4408e3a06fe18d106582d @timmywil timmywil committed May 28, 2012
Showing with 57 additions and 26 deletions.
  1. +44 −18 sizzle.js
  2. +13 −8 test/unit/selector.js
View
62 sizzle.js
@@ -25,8 +25,11 @@ var document = window.document,
// Used for testing something on an element
assert = function( fn ) {
- var div = document.createElement("div"),
+ var pass = false,
+ div = document.createElement("div");
+ try {
pass = fn( div );
+ } catch (e) {}
// release memory in IE
div = null;
return pass;
@@ -781,11 +784,14 @@ var Expr = Sizzle.selectors = {
},
disabled: function( elem ) {
- return elem.disabled === true;
+ return !!elem.disabled;
@gibson042
jQuery Foundation member
gibson042 added a note May 28, 2012

Warning: this will now fail on form elements containing inputs named "disabled".

@timmywil
jQuery Foundation member
timmywil added a note May 28, 2012

Good call, I couldn't think of a reason, but that's it right there.

@staabm
staabm added a note May 28, 2012

Should this case be covered by a test?

@timmywil
jQuery Foundation member
timmywil added a note May 28, 2012

It was, I missed a failing test in IE.

@scottgonzalez
jQuery Foundation member

This is worthy of a comment in the code :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
},
checked: function( elem ) {
- return elem.checked === true;
+ // In CSS3, :checked should return both checked and selected elements
+ // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+ var nodeName = elem.nodeName.toLowerCase();
+ return (nodeName === "input" && !! elem.checked) || (nodeName === "option" && !!elem.selected);
},
selected: function( elem ) {
@@ -1077,7 +1083,7 @@ var characterEncoding = "(?:[-\\w]|[^\\x00-\\xa0]|\\\\.)",
ID: new RegExp("#(" + characterEncoding + "+)"),
CLASS: new RegExp("\\.(" + characterEncoding + "+)"),
NAME: new RegExp("\\[name=['\"]*(" + characterEncoding + "+)['\"]*\\]"),
- TAG: new RegExp("^(" + characterEncoding + "+)"),
+ TAG: new RegExp("^(" + characterEncoding.replace( "[-", "[-\\*" ) + "+)"),
ATTR: new RegExp("\\[\\s*(" + characterEncoding + "+)\\s*(?:(\\S?=)\\s*(?:(['\"])(.*?)\\3|(#?" + characterEncoding + "*)|)|)\\s*\\]"),
CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,
PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/,
@@ -1197,22 +1203,45 @@ if ( docElem.compareDocumentPosition ) {
if ( document.querySelectorAll ) {
(function(){
var oldSelect = select,
- div = document.createElement("div"),
id = "__sizzle__",
rrelativeHierarchy = /^\s*[+~]/,
- rapostrophe = /'/g;
+ rapostrophe = /'/g,
+ rbuggyQSA = [];
+
+ // Build QSA regex
+ // Regex strategy adopted from Diego Perini
+ assert(function( div ) {
+
+ // Webkit/Opera - :checked should return selected option elements
+ // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+ div.innerHTML = "<select><option selected='selected'></option></select>";
+ if ( !div.querySelectorAll( ":checked" ).length ) {
+ rbuggyQSA.push(":checked");
+ }
- div.innerHTML = "<p class='TEST'></p>";
+ // assertQSAAttrEmptyValue
+ // Opera 10/IE - ^= $= *= and empty values
+ div.innerHTML = "<p class=''></p>";
+ // Should not select anything
+ if ( !!div.querySelectorAll("[class^='']").length ) {
+ rbuggyQSA.push("[*^$]=[\\x20\\t\\n\\r\\f]*(?:\"\"|'')");
+ }
- // Safari can't handle uppercase or unicode characters when
- // in quirks mode.
- if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
- return;
- }
+ // assertQSAHiddenEnabled
+ // IE8 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
+ div.innerHTML = "<input type='hidden'>";
+ if ( !div.querySelectorAll(":enabled").length ) {
+ rbuggyQSA.push(":enabled", ":disabled");
+ }
+
+ rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
+ });
select = function( selector, context, results, seed, contextXML ) {
- // Only use querySelectorAll on non-XML documents
- if ( !seed && !contextXML ) {
+ // Only use querySelectorAll when not filtering,
+ // when this is not xml,
+ // and when no QSA bugs apply
+ if ( !seed && !contextXML && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
if ( context.nodeType === 9 ) {
try {
return makeArray( context.querySelectorAll(selector), results );
@@ -1241,7 +1270,7 @@ if ( document.querySelectorAll ) {
if ( !relativeHierarchySelector || parent ) {
return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + selector ), results );
}
- } catch(pseudoError) {
+ } catch(qsaError) {
} finally {
if ( !old ) {
oldContext.removeAttribute( "id" );
@@ -1252,9 +1281,6 @@ if ( document.querySelectorAll ) {
return oldSelect( selector, context, results, seed, contextXML );
};
-
- // release memory in IE
- div = null;
})();
}
View
21 test/unit/selector.js
@@ -4,11 +4,13 @@ test("element", function() {
expect(21);
QUnit.reset();
- ok( jQuery("*").size() >= 30, "Select all" );
- var all = jQuery("*"), good = true;
- for ( var i = 0; i < all.length; i++ )
- if ( all[i].nodeType == 8 )
+ ok( Sizzle("*").length >= 30, "Select all" );
+ var all = Sizzle("*"), good = true;
+ for ( var i = 0; i < all.length; i++ ) {
+ if ( all[i].nodeType == 8 ) {
good = false;
+ }
+ }
ok( good, "Select all elements, no comment nodes" );
t( "Element Selector", "#qunit-fixture p", ["firstp","ap","sndp","en","sap","first"] );
t( "Element Selector", "body", ["body"] );
@@ -54,7 +56,7 @@ test("XML Document Selectors", function() {
});
test("broken", function() {
- expect(18);
+ expect(19);
function broken(name, selector) {
try {
@@ -81,7 +83,7 @@ test("broken", function() {
// Sigh. WebKit thinks this is a real selector in qSA
// They've already fixed this and it'll be coming into
// current browsers soon.
- //broken( "Nth-child", ":nth-child(asdf)", [] );
+ broken( "Nth-child", ":nth-child(asdf)", [] );
broken( "Nth-child", ":nth-child(2n+-0)", [] );
broken( "Nth-child", ":nth-child(2+0)", [] );
broken( "Nth-child", ":nth-child(- 1n)", [] );
@@ -267,7 +269,7 @@ test("child and adjacent", function() {
});
test("attributes", function() {
- expect(45);
+ expect(46);
t( "Attribute Exists", "a[title]", ["google"] );
t( "Attribute Exists", "*[title]", ["google"] );
@@ -314,6 +316,7 @@ test("attributes", function() {
ok( match( opt, "[id*=option1][type!=checkbox]" ), "Attribute Is Not Equal Matches" );
ok( match( opt, "[id*=option1]" ), "Attribute With No Quotes Contains Matches" );
ok( match( opt, "[test=]" ), "Attribute With No Quotes No Content Matches" );
+ ok( !match( opt, "[test^='']" ), "Attribute with empty string value does not match startsWith selector (^=)" );
ok( match( opt, "[id=option1a]" ), "Attribute With No Quotes Equals Matches" );
ok( match( document.getElementById("simon1"), "a[href*=#]" ), "Attribute With No Quotes Href Contains Matches" );
@@ -560,7 +563,7 @@ test("pseudo - visibility", function() {
}
test("pseudo - form", function() {
- expect(8);
+ expect(10);
var implied = jQuery('<input id="impliedText"/>').appendTo("#form");
@@ -573,6 +576,8 @@ test("pseudo - form", function() {
t( "Form element :radio:checked, :checkbox:checked", "#form :radio:checked, #form :checkbox:checked", ["radio2", "check1"] );
t( "Selected Option Element", "#form option:selected", ["option1a","option2d","option3b","option3c","option4b","option4c","option4d","option5a"] );
+ t( "Selected Option Element are also :checked", "#form option:checked", ["option1a","option2d","option3b","option3c","option4b","option4c","option4d","option5a"] );
+ t( "Hidden inputs should be treated as enabled. See QSA test.", "#hidden1:enabled", ["hidden1"] );
implied.remove();
});

0 comments on commit f0bcea0

Please sign in to comment.
Something went wrong with that request. Please try again.