From 05184cc448f4ed7715ddd6a5d724e167882415f1 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Micha=C5=82=20Go=C5=82=C4=99biowski-Owczarek?= Date: Mon, 18 Nov 2019 22:10:55 +0100 Subject: [PATCH] Selector: Make empty attribute selectors work in IE again qSA in IE 11/Edge often (but not always) don't find elements with an empty name attribute selector (`[name=""]`). Detect that & fall back to Sizzle traversal. Interestingly, IE 10 & older don't seem to have the issue. Fixes gh-4435 Closes gh-4510 --- src/selector.js | 4 +--- src/selector/rbuggyQSA.js | 16 +++++++++++++++- src/selector/var/whitespace.js | 2 ++ test/data/qunit-fixture.html | 5 +++++ test/data/qunit-fixture.js | 2 +- test/unit/attributes.js | 4 ++-- test/unit/selector.js | 15 +++++++++++++-- 7 files changed, 39 insertions(+), 9 deletions(-) create mode 100644 src/selector/var/whitespace.js diff --git a/src/selector.js b/src/selector.js index 019f25d62..66abe7935 100644 --- a/src/selector.js +++ b/src/selector.js @@ -5,6 +5,7 @@ import documentElement from "./var/documentElement.js"; import indexOf from "./var/indexOf.js"; import pop from "./var/pop.js"; import push from "./var/push.js"; +import whitespace from "./selector/var/whitespace.js"; import rbuggyQSA from "./selector/rbuggyQSA.js"; import support from "./selector/support.js"; @@ -41,9 +42,6 @@ var i, // Regular expressions - // https://www.w3.org/TR/css3-selectors/#whitespace - whitespace = "[\\x20\\t\\r\\n\\f]", - // https://www.w3.org/TR/css-syntax-3/#ident-token-diagram identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+", diff --git a/src/selector/rbuggyQSA.js b/src/selector/rbuggyQSA.js index f638fc429..2c853a0f1 100644 --- a/src/selector/rbuggyQSA.js +++ b/src/selector/rbuggyQSA.js @@ -1,8 +1,10 @@ import document from "../var/document.js"; import isIE from "../var/isIE.js"; +import whitespace from "./var/whitespace.js"; var rbuggyQSA = [], - testEl = document.createElement( "div" ); + testEl = document.createElement( "div" ), + input = document.createElement( "input" ); testEl.innerHTML = ""; @@ -19,6 +21,18 @@ if ( isIE ) { rbuggyQSA.push( ":enabled", ":disabled" ); } +// Support: IE 11+, Edge 15 - 18+ +// IE 11/Edge don't find elements on a `[name='']` query in some cases. +// Adding a temporary attribute to the document before the selection works +// around the issue. +// Interestingly, IE 10 & older don't seem to have the issue. +input.setAttribute( "name", "" ); +testEl.appendChild( input ); +if ( !testEl.querySelectorAll( "[name='']" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" + + whitespace + "*(?:''|\"\")" ); +} + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) ); export default rbuggyQSA; diff --git a/src/selector/var/whitespace.js b/src/selector/var/whitespace.js new file mode 100644 index 000000000..dcac814c7 --- /dev/null +++ b/src/selector/var/whitespace.js @@ -0,0 +1,2 @@ +// https://www.w3.org/TR/css3-selectors/#whitespace +export default "[\\x20\\t\\r\\n\\f]"; diff --git a/test/data/qunit-fixture.html b/test/data/qunit-fixture.html index ba083ec55..f3c99dfd6 100644 --- a/test/data/qunit-fixture.html +++ b/test/data/qunit-fixture.html @@ -116,6 +116,11 @@ Z +
+
+ +
+
\n\t\n\t\n\n\t\n\t\n\t\n\n\t\n\t\n\n\t\n\t\n\n\t\n\n\t\n\n\t\n\t\n\t\n\t\n\t\n\n\t\n\t\t\n\t\t\n\t\n\n\t\n\t\n\t\"'台北Táiběi\"'\n\t\n\t\n\t\n\n\ttest element\n\nFloat test.\n\n
\n\t\n\t\n
\n
\n\n
\n\t\n\t\n\t\n\t\n
\n\n
\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\t\n\t\n
\n
\n\t
\n\t\t
\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t
\n\t
\n\t
hi there
\n\t
\n\t\t
hidden
\n\t
\n\t
\n\t\t
\n\t
\n\t
\n\t\t\n\t
\n\t
\n\t
\n\t\t\n\t\t\n\t
\n\t
\n\t\t\n\t\t\n\t\t
C
\n\t
\n\t
\n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t
\n
\n\n
\n\t
    \n\t\t
  1. Rice
  2. \n\t\t
  3. Beans
  4. \n\t\t
  5. Blinis
  6. \n\t\t
  7. Tofu
  8. \n\t
\n\n\t
I'm hungry. I should...
\n\t...Eat lots of food... |\n\t...Eat a little food... |\n\t...Eat no food...\n\t...Eat a burger...\n\t...Eat some funyuns...\n\t...Eat some funyuns...\n\t\n\t\n\t\n\t\n\t\t\n\t\n
\n\n
\n\t\n\t\n
\n\n
\n\t
\n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t\tNeither enabled nor disabled\n\t
\n\t
\n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t\tNeither enabled nor disabled\n\t
\n\t\n\t\n
\n\n
\n\t1\n\t2\n\t\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t\n\t\n
\n
\n\t
\n\t\t
fadeIn
fadeIn
\n\t\t
fadeOut
fadeOut
\n\n\t\t
show
show
\n\t\t
hide
hide
\n\t\t
hide
hide
\n\n\t\t
togglein
togglein
\n\t\t
toggleout
toggleout
\n\t\t
toggleout
toggleout
\n\n\t\t
slideUp
slideUp
\n\t\t
slideDown
slideDown
\n\t\t
slideUp
slideUp
\n\n\t\t
slideToggleIn
slideToggleIn
\n\t\t
slideToggleOut
slideToggleOut
\n\n\t\t
fadeToggleIn
fadeToggleIn
\n\t\t
fadeToggleOut
fadeToggleOut
\n\n\t\t
fadeTo
fadeTo
\n\t
\n\n\t
\n\t\n
\n
\n"; +QUnit.config.fixture = "

See this blog entry for more information.

\n

\n\tHere are some [links] in a normal paragraph: Google,\n\tGoogle Groups (Link).\n\tThis link has class=\"blog\":\n\tdiveintomark\n\n

\n
\n\t

Everything inside the red border is inside a div with id=\"foo\".

\n\t

This is a normal link: Yahoo

\n\t

This link has class=\"blog\": Simon Willison's Weblog

\n\n
\n
\n\t
\n
\n\n

Try them out:

\n\n
    \n
    \n\t\n\t\n\t\n\t\n\n\t\n\t\n\t\n\n\t\n\t\n\n\t\n\t\n\n\t\n\n\t\n\n\t\n\t\n\t\n\t\n\t\n\n\t\n\t\t\n\t\t\n\t\n\n\t\n\t\n\t\"'台北Táiběi\"'\n\t\n\t\n\t\n\n\ttest element\n
    \nFloat test.\n\n
    \n\t\n\t\n
    \n
    \n\n
    \n\t\n\t\n\t\n\t\n
    \n\n
    \n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t
    \n\t\t
    \n\t\t\t\n\t\t
    \n\t
    \n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\t\n\t\n
    \n
    \n\t
    \n\t\t
    \n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t
    \n\t
    \n\t
    hi there
    \n\t
    \n\t\t
    hidden
    \n\t
    \n\t
    \n\t\t
    \n\t
    \n\t
    \n\t\t\n\t
    \n\t
    \n\t
    \n\t\t\n\t\t\n\t
    \n\t
    \n\t\t\n\t\t\n\t\t
    C
    \n\t
    \n\t
    \n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t
    \n
    \n\n
    \n\t
      \n\t\t
    1. Rice
    2. \n\t\t
    3. Beans
    4. \n\t\t
    5. Blinis
    6. \n\t\t
    7. Tofu
    8. \n\t
    \n\n\t
    I'm hungry. I should...
    \n\t...Eat lots of food... |\n\t...Eat a little food... |\n\t...Eat no food...\n\t...Eat a burger...\n\t...Eat some funyuns...\n\t...Eat some funyuns...\n\t\n\t\n\t\n\t\n\t\t\n\t\n
    \n\n
    \n\t\n\t\n
    \n\n
    \n\t
    \n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t\tNeither enabled nor disabled\n\t
    \n\t
    \n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n\t\tNeither enabled nor disabled\n\t
    \n\t\n\t\n
    \n\n
    \n\t1\n\t2\n\t\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t\n\t\n
    \n
    \n\t
    \n\t\t
    fadeIn
    fadeIn
    \n\t\t
    fadeOut
    fadeOut
    \n\n\t\t
    show
    show
    \n\t\t
    hide
    hide
    \n\t\t
    hide
    hide
    \n\n\t\t
    togglein
    togglein
    \n\t\t
    toggleout
    toggleout
    \n\t\t
    toggleout
    toggleout
    \n\n\t\t
    slideUp
    slideUp
    \n\t\t
    slideDown
    slideDown
    \n\t\t
    slideUp
    slideUp
    \n\n\t\t
    slideToggleIn
    slideToggleIn
    \n\t\t
    slideToggleOut
    slideToggleOut
    \n\n\t\t
    fadeToggleIn
    fadeToggleIn
    \n\t\t
    fadeToggleOut
    fadeToggleOut
    \n\n\t\t
    fadeTo
    fadeTo
    \n\t
    \n\n\t
    \n\t\n
    \n
    \n"; diff --git a/test/unit/attributes.js b/test/unit/attributes.js index 1aca11482..41bdadb18 100644 --- a/test/unit/attributes.js +++ b/test/unit/attributes.js @@ -1255,7 +1255,7 @@ QUnit.test( "addClass(Array)", function( assert ) { } ); QUnit.test( "addClass(Function) with incoming value", function( assert ) { - assert.expect( 57 ); + assert.expect( 59 ); var pass, i, div = jQuery( "#qunit-fixture div" ), old = div.map( function() { @@ -1330,7 +1330,7 @@ QUnit.test( "removeClass(Array) - simple", function( assert ) { } ); QUnit.test( "removeClass(Function) with incoming value", function( assert ) { - assert.expect( 57 ); + assert.expect( 59 ); var $divs = jQuery( "#qunit-fixture div" ).addClass( "test" ), old = $divs.map( function() { return jQuery( this ).attr( "class" ); diff --git a/test/unit/selector.js b/test/unit/selector.js index 461339335..f5e15e8c2 100644 --- a/test/unit/selector.js +++ b/test/unit/selector.js @@ -720,7 +720,7 @@ QUnit.test( "attributes - special characters", function( assert ) { } ); QUnit.test( "attributes - others", function( assert ) { - assert.expect( 10 ); + assert.expect( 14 ); var div = document.getElementById( "foo" ); @@ -750,6 +750,17 @@ QUnit.test( "attributes - others", function( assert ) { assert.ok( !jQuery( "" ).prop( "checked", true ).is( "[checked]" ), "[checked] selects by attribute (negative)" ); + + assert.t( "empty name", "[name='']", [ "name-empty" ] ); + assert.t( "prefixed empty name", "#empty-name-parent [name='']", [ "name-empty" ] ); + + var emptyNameContainer = jQuery( ".empty-name-container" ); + assert.deepEqual( emptyNameContainer.find( "[name='']" ).get(), + q( "name-empty" ), + "empty name with context" ); + assert.deepEqual( emptyNameContainer.find( "#empty-name-parent [name='']" ).get(), + q( "name-empty" ), + "prefixed empty name with context" ); } ); QUnit.test( "pseudo - (parent|empty)", function( assert ) { @@ -1257,7 +1268,7 @@ QUnit[ QUnit.jQuerySelectorsPos ? "test" : "skip" ]( "pseudo - position", functi assert.t( "Check element position", "#qunit-fixture div div:eq(0)", [ "nothiddendivchild" ] ); assert.t( "Check element position", "#select1 option:eq(3)", [ "option1d" ] ); - assert.t( "Check element position", "#qunit-fixture div div:eq(10)", [ "names-group" ] ); + assert.t( "Check element position", "#qunit-fixture div div:eq(10)", [ "no-clone-exception" ] ); assert.t( "Check element position", "#qunit-fixture div div:first", [ "nothiddendivchild" ] ); assert.t( "Check element position", "#qunit-fixture div > div:first", [ "nothiddendivchild" ] ); assert.t( "Check element position", "#qunit-fixture div:first a:first", [ "yahoo" ] ); -- 2.39.5