]> source.dussan.org Git - jquery.git/commitdiff
Override Sizzle attribute retrieval with jQuery.attr. Fixes #5637, #7128, #9261,...
authortimmywil <timmywillisn@gmail.com>
Mon, 12 Sep 2011 23:40:14 +0000 (19:40 -0400)
committertimmywil <timmywillisn@gmail.com>
Mon, 19 Sep 2011 19:42:30 +0000 (15:42 -0400)
Bug fixed on the side: $(window).is('a') was throwing an exception. Fixes #10178.

src/attributes.js
src/sizzle
src/sizzle-jquery.js
test/data/selector.html [new file with mode: 0644]
test/data/testinit.js
test/index.html
test/unit/attributes.js
test/unit/selector.js [new file with mode: 0644]
test/unit/traversing.js

index dd985b4940538a18152eb75efac13ca4874474a7..6380b7d6ce4d08c19d89b30a089381ed42856f8b 100644 (file)
@@ -7,7 +7,7 @@ var rclass = /[\n\t\r]/g,
        rfocusable = /^(?:button|input|object|select|textarea)$/i,
        rclickable = /^a(?:rea)?$/i,
        rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
-       nodeHook, boolHook;
+       nodeHook, boolHook, fixSpecified;
 
 jQuery.fn.extend({
        attr: function( name, value ) {
@@ -519,15 +519,19 @@ boolHook = {
 
 // IE6/7 do not support getting/setting some attributes with get/setAttribute
 if ( !jQuery.support.getSetAttribute ) {
-       
+
+       fixSpecified = {
+               name: true,
+               id: true
+       };
+
        // Use this for any attribute in IE6/7
        // This fixes almost every IE6/7 issue
        nodeHook = jQuery.valHooks.button = {
                get: function( elem, name ) {
                        var ret;
                        ret = elem.getAttributeNode( name );
-                       // Return undefined if nodeValue is empty string
-                       return ret && ret.nodeValue !== "" ?
+                       return ret && (fixSpecified[ name ] ? ret.nodeValue !== "" : ret.specified) ?
                                ret.nodeValue :
                                undefined;
                },
index 93cc46709e03173288111f8b60d4192fea2ec8d0..3375f91f7c0503da1cd34ae9b6bbbd7be07bf64d 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 93cc46709e03173288111f8b60d4192fea2ec8d0
+Subproject commit 3375f91f7c0503da1cd34ae9b6bbbd7be07bf64d
index f15b08252c1c47a5143892bdb3f00d7bb40ac3a2..7090e1c636e59ad6094de48d68c8b2b9ecc44ceb 100644 (file)
@@ -1,3 +1,6 @@
+// Override sizzle attribute retrieval
+Sizzle.attr = jQuery.attr;
+Sizzle.selectors.attrMap = {};
 jQuery.find = Sizzle;
 jQuery.expr = Sizzle.selectors;
 jQuery.expr[":"] = jQuery.expr.filters;
diff --git a/test/data/selector.html b/test/data/selector.html
new file mode 100644 (file)
index 0000000..b62e1fa
--- /dev/null
@@ -0,0 +1,132 @@
+<!doctype html>
+<html>
+<head>
+       <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+       <title>jQuery selector</title>
+
+       <script src="../../src/core.js"></script>
+       <script src="../../src/deferred.js"></script>
+       <script src="../../src/support.js"></script>
+       <script src="../../src/data.js"></script>
+       <script src="../../src/queue.js"></script>
+       <script src="../../src/attributes.js"></script>
+       <script src="../../src/event.js"></script>
+       <script src="../../src/sizzle/sizzle.js"></script>
+       <script src="../../src/sizzle-jquery.js"></script>
+       <script src="../../src/traversing.js"></script>
+       <script src="../../src/manipulation.js"></script>
+       <script src="../../src/css.js"></script>
+       <script src="../../src/ajax.js"></script>
+       <script src="../../src/ajax/jsonp.js"></script>
+       <script src="../../src/ajax/script.js"></script>
+       <script src="../../src/ajax/xhr.js"></script>
+       <script src="../../src/effects.js"></script>
+       <script src="../../src/offset.js"></script>
+       <script src="../../src/dimensions.js"></script>
+
+       <script id="script1"
+                       defer
+                       async></script>
+
+       <script type="text/javascript">
+               document.createElement('video');
+               document.createElement('audio');
+               document.createElement('article');
+               document.createElement('details');
+       </script>
+</head>
+<body>
+       <img id="img1"
+                ismap>
+
+       <hr id="hr1"
+               noshade>
+
+       <form id="form1"
+                 name="formName"
+                 novalidate
+                 formnovalidate>
+               <input type="text" id="text1"
+                          tabindex="1"
+                          name="name"
+                          required
+                          autofocus
+                          readonly>
+               <textarea id="textarea1"
+                                 noresize></textarea>
+       </form>
+
+       <table>
+               <tr><td id="td1"
+                               nowrap></td></tr>
+       </table>
+
+       <iframe id="iframe1"
+                       src="iframe.html"
+                       seamless></iframe>
+
+       <style id="style1"
+                  scoped></style>
+
+       <ol id="ol1"
+               reversed></ol>
+
+       <article id="article1"
+                        pubdate></article>
+
+       <details id="details1"
+                        open></details>
+
+       <div id="div1"
+                nowrap
+                hidden
+                itemscope
+                draggable="true"
+                contenteditable="true"
+                aria-disabled="true">
+               <p>My name is <span id="span1"
+                                                       spellcheck="true"
+                                                       itemprop="name">Elizabeth</span>.</p>
+       </div>
+
+       <audio id="audio1"
+                  muted></audio>
+
+       <video id="video1"
+                  loop
+                  controls
+                  autoplay
+                  autobuffer></video>
+
+       <map id="map1">
+               <area id="area1"
+                         nohref
+                         shape="default">
+       </map>
+
+       <input id="check1"
+                  type="checkbox"
+                  disabled
+                  checked>
+
+       <select id="select1"
+                       multiple>
+               <option id="option1"
+                               selected
+                               value="blar">blar</option>
+       </select>
+
+       <dl id="dl"
+               compact>
+               <dt>Term</dt><dd>This is the first definition in compact format.</dd>
+               <dt>Term</dt><dd>This is the second definition in compact format.</dd>
+       </dl>
+
+       <object id="object1"
+                       declare></object>
+
+       <marquee id="marquee1"
+                        direction="up"
+                        truespeed>Scrolling text (non-standard)</marquee>
+</body>
+</html>
index c478390d5291ade65a60d19e7ce9606aec1b7a35..c30747d09ffe59f7e0b93389dd89a20aa62d1b1b 100644 (file)
@@ -20,8 +20,7 @@ function q() {
 
 /**
  * Asserts that a select matches the given IDs * @example t("Check for something", "//[a]", ["foo", "baar"]);
- * @result returns true if "//[a]" return two elements with the IDs 'foo' and 'baa
-r'
+ * @result returns true if "//[a]" return two elements with the IDs 'foo' and 'baar'
  */
 function t(a,b,c) {
        var f = jQuery(b).get(), s = "";
index 4bbef54cd217915a5b4784eeb831053ca2e38e49..05a444e01d309ef925aa33393c50b838993fa0a2 100644 (file)
@@ -41,6 +41,7 @@
        <script src="unit/attributes.js"></script>
        <script src="unit/event.js"></script>
        <script src="../src/sizzle/test/unit/selector.js"></script>
+       <script src="unit/selector.js"></script>
        <script src="unit/traversing.js"></script>
        <script src="unit/manipulation.js"></script>
        <script src="unit/css.js"></script>
                        <select name="select5" id="select5">
                                <option id="option5a" value="3">1</option>
                                <option id="option5b" value="2">2</option>
-                               <option id="option5c" value="1">3</option>
+                               <option id="option5c" value="1" data-attr="">3</option>
                        </select>
 
                        <object id="object1" codebase="stupid">
index 2af36f1c27db1de5193fbd9cd72f81909fce211b..6565d5db85dfc0239fe693660427875fc5127457 100644 (file)
@@ -55,10 +55,10 @@ test("attr(String)", function() {
 
        // [7472] & [3113] (form contains an input with name="action" or name="id")
        var extras = jQuery("<input name='id' name='name' /><input id='target' name='target' />").appendTo("#testForm");
-       equals( jQuery("#form").attr("action","newformaction").attr("action"), "newformaction", "Check that action attribute was changed" );
-       equals( jQuery("#testForm").attr("target"), undefined, "Retrieving target does not equal the input with name=target" );
-       equals( jQuery("#testForm").attr("target", "newTarget").attr("target"), "newTarget", "Set target successfully on a form" );
-       equals( jQuery("#testForm").removeAttr("id").attr("id"), undefined, "Retrieving id does not equal the input with name=id after id is removed [#7472]" );
+       equal( jQuery("#form").attr("action","newformaction").attr("action"), "newformaction", "Check that action attribute was changed" );
+       equal( jQuery("#testForm").attr("target"), undefined, "Retrieving target does not equal the input with name=target" );
+       equal( jQuery("#testForm").attr("target", "newTarget").attr("target"), "newTarget", "Set target successfully on a form" );
+       equal( jQuery("#testForm").removeAttr("id").attr("id"), undefined, "Retrieving id does not equal the input with name=id after id is removed [#7472]" );
        // Bug #3685 (form contains input with name="name")
        equals( jQuery("#testForm").attr("name"), undefined, "Retrieving name does not retrieve input with name=name" );
        extras.remove();
@@ -157,7 +157,7 @@ test("attr(Hash)", function() {
 });
 
 test("attr(String, Object)", function() {
-       expect(75);
+       expect(76);
 
        var div = jQuery("div").attr("foo", "bar"),
                fail = false;
@@ -177,8 +177,9 @@ test("attr(String, Object)", function() {
        equals( jQuery("#name").attr("name"), "something", "Set name attribute" );
        jQuery("#name").attr("name", null);
        equals( jQuery("#name").attr("name"), undefined, "Remove name attribute" );
-       var $input = jQuery("<input>", { name: "something" });
-       equals( $input.attr("name"), "something", "Check element creation gets/sets the name attribute." );
+       var $input = jQuery("<input>", { name: "something", id: "specified" });
+       equal( $input.attr("name"), "something", "Check element creation gets/sets the name attribute." );
+       equal( $input.attr("id"), "specified", "Check element creation gets/sets the id attribute." );
 
        jQuery("#check2").prop("checked", true).prop("checked", false).attr("checked", true);
        equals( document.getElementById("check2").checked, true, "Set checked attribute" );
diff --git a/test/unit/selector.js b/test/unit/selector.js
new file mode 100644 (file)
index 0000000..4412580
--- /dev/null
@@ -0,0 +1,133 @@
+/**
+ * This test page is for selector tests that address selector issues that have already been addressed in jQuery functions
+ *   and which only work because jQuery has hooked into Sizzle.
+ * These tests may or may not fail in an independent Sizzle.
+ */
+
+module("selector - jQuery only", { teardown: moduleTeardown });
+
+/**
+ * Loads an iframe for the selector context
+ * @param {String} fileName - Name of the html file to load
+ * @param {String} name - Test name
+ * @param {Function} fn - Test callback containing the tests to run
+ */
+var testIframe = function( fileName, name, fn ) {
+
+       var loadFixture = function() {
+
+               // Creates iframe with cache disabled
+               var src = "./data/" + fileName + ".html?" + parseInt( Math.random()*1000, 10 ),
+                       iframe = jQuery("<iframe />").css({
+                               width: 500, height: 500, position: "absolute", top: -600, left: -600, visibility: "hidden"
+                       }).appendTo("body")[0];
+               iframe.contentWindow.location = src;
+               return iframe;
+       };
+
+       test(name, function() {
+               // pause execution for now
+               stop();
+
+               // load fixture in iframe
+               var iframe = loadFixture(),
+                       win = iframe.contentWindow,
+                       interval = setInterval( function() {
+                               if ( win && win.jQuery && win.jQuery.isReady ) {
+                                       clearInterval( interval );
+                                       // continue
+                                       start();
+                                       // call actual tests passing the correct jQuery instance to use
+                                       fn.call( this, win.jQuery, win );
+                                       document.body.removeChild( iframe );
+                                       iframe = null;
+                               }
+                       }, 15 );
+       });
+};
+
+testIframe("selector", "attributes - jQuery.attr", function( jQuery, window ) {
+       expect(34);
+
+       var document = window.document;
+
+       /**
+        * Returns an array of elements with the given IDs, eg.
+        */
+       var q = function() {
+               var r = [];
+        
+               for ( var i = 0; i < arguments.length; i++ ) {
+                       r.push( document.getElementById( arguments[i] ) );
+               }
+        
+               return r;
+       };
+                          
+       /**
+        * Asserts that a select matches the given IDs * @example t("Check for something", "//[a]", ["foo", "baar"]);
+        * @param {String} a - Assertion name
+        * @param {String} b - Sizzle selector
+        * @param {String} c - Array of ids to construct what is expected
+        */
+       var t = function( a, b, c ) {
+               var f = jQuery(b).get(), s = "";
+               
+               for ( var i = 0; i < f.length; i++ ) {
+                       s += (s && ",") + '"' + f[i].id + '"';
+               }
+
+               deepEqual(f, q.apply( q, c ), a + " (" + b + ")");
+       };
+
+       // ====== All known boolean attributes, including html5 booleans ======
+       // autobuffer, autofocus, autoplay, async, checked,
+       // compact, controls, declare, defer, disabled,
+       // formnovalidate, hidden, indeterminate (property only),
+       // ismap, itemscope, loop, multiple, muted, nohref, noresize,
+       // noshade, nowrap, novalidate, open, pubdate, readonly, required,
+       // reversed, scoped, seamless, selected, truespeed, visible (skipping visible attribute, which is on a barprop object)
+
+       t( "Attribute Exists", "[autobuffer]",     ["video1"]);
+       t( "Attribute Exists", "[autofocus]",      ["text1"]);
+       t( "Attribute Exists", "[autoplay]",       ["video1"]);
+       t( "Attribute Exists", "[async]",          ["script1"]);
+       t( "Attribute Exists", "[checked]",        ["check1"]);
+       t( "Attribute Exists", "[compact]",        ["dl"]);
+       t( "Attribute Exists", "[controls]",       ["video1"]);
+       t( "Attribute Exists", "[declare]",        ["object1"]);
+       t( "Attribute Exists", "[defer]",          ["script1"]);
+       t( "Attribute Exists", "[disabled]",       ["check1"]);
+       t( "Attribute Exists", "[formnovalidate]", ["form1"]);
+       t( "Attribute Exists", "[hidden]",         ["div1"]);
+       t( "Attribute Exists", "[indeterminate]",  []);
+       t( "Attribute Exists", "[ismap]",          ["img1"]);
+       t( "Attribute Exists", "[itemscope]",      ["div1"]);
+       // t( "Attribute Exists", "[loop]",           ["video1"]); // IE 6/7 cannot differentiate here. loop is also used on img, input, and marquee tags as well as video/audio. getAttributeNode unfortunately only retrieves the property value.
+       t( "Attribute Exists", "[multiple]",       ["select1"]);
+       t( "Attribute Exists", "[muted]",          ["audio1"]);
+       // t( "Attribute Exists", "[nohref]",         ["area1"]); // IE 6/7 keep this set to false regardless of presence. The attribute node is not retrievable.
+       t( "Attribute Exists", "[noresize]",       ["textarea1"]);
+       t( "Attribute Exists", "[noshade]",        ["hr1"]);
+       t( "Attribute Exists", "[nowrap]",         ["td1", "div1"]);
+       t( "Attribute Exists", "[novalidate]",     ["form1"]);
+       t( "Attribute Exists", "[open]",           ["details1"]);
+       t( "Attribute Exists", "[pubdate]",        ["article1"]);
+       t( "Attribute Exists", "[readonly]",       ["text1"]);
+       t( "Attribute Exists", "[required]",       ["text1"]);
+       t( "Attribute Exists", "[reversed]",       ["ol1"]);
+       t( "Attribute Exists", "[scoped]",         ["style1"]);
+       t( "Attribute Exists", "[seamless]",       ["iframe1"]);
+       // t( "Attribute Exists", "[selected]",       ["option1"]); // IE8's querySelectorAll fails here. Redirecting to oldSizzle would work, but it would require an additional support test as well as a check for the selected attribute within the qsa logic
+       t( "Attribute Exists", "[truespeed]",      ["marquee1"]);
+
+       // Enumerated attributes (these are not boolean content attributes)
+       jQuery.each([ "draggable", "contenteditable", "aria-disabled" ], function( i, val ) {
+               t( "Enumerated attribute", "[" + val + "]", ["div1"]);
+       });
+       t( "Enumerated attribute", "[spellcheck]", ["span1"]);
+
+       // t( "tabindex selector does not retrieve all elements in IE6/7(#8473)", "form, [tabindex]", ["form1", "text1"]); // Uncomment this when the tabindex attrHook is deprecated
+
+       t( "Improperly named form elements do not interfere with form selections (#9570)", "form[name='formName']", ["form1"]);
+});
index 37ac20e826d10d2a33a7dcd6503fb961c07f186f..4cbcee912dcc28501053d98cd5cfbb9cefa6f6c3 100644 (file)
@@ -38,7 +38,7 @@ test("find(node|jQuery object)", function() {
 });
 
 test("is(String|undefined)", function() {
-       expect(27);
+       expect(29);
        ok( jQuery("#form").is("form"), "Check for element: A form must be a form" );
        ok( !jQuery("#form").is("div"), "Check for element: A form is not a div" );
        ok( jQuery("#mark").is(".blog"), "Check for class: Expected class 'blog'" );
@@ -69,6 +69,9 @@ test("is(String|undefined)", function() {
        ok( jQuery("#en").is("[lang=\"de\"],[lang=\"en\"]"), "Comma-seperated; Check for lang attribute: Expect en or de" );
        ok( jQuery("#en").is("[lang=\"en\"] , [lang=\"de\"]"), "Comma-seperated; Check for lang attribute: Expect en or de" );
        ok( jQuery("#en").is("[lang=\"de\"] , [lang=\"en\"]"), "Comma-seperated; Check for lang attribute: Expect en or de" );
+
+       ok( !jQuery(window).is('a'), "Checking is on a window does not throw an exception(#10178)" );
+       ok( !jQuery(document).is('a'), "Checking is on a document does not throw an exception(#10178)" );
 });
 
 test("is(jQuery)", function() {
@@ -325,13 +328,14 @@ test("andSelf()", function() {
 });
 
 test("siblings([String])", function() {
-       expect(5);
+       expect(6);
        same( jQuery("#en").siblings().get(), q("sndp", "sap"), "Check for siblings" );
        same( jQuery("#sndp").siblings(":has(code)").get(), q("sap"), "Check for filtered siblings (has code child element)" );
        same( jQuery("#sndp").siblings(":has(a)").get(), q("en", "sap"), "Check for filtered siblings (has anchor child element)" );
        same( jQuery("#foo").siblings("form, b").get(), q("form", "floatTest", "lengthtest", "name-tests", "testForm"), "Check for multiple filters" );
        var set = q("sndp", "en", "sap");
        same( jQuery("#en, #sndp").siblings().get(), set, "Check for unique results from siblings" );
+       deepEqual( jQuery("#option5a").siblings("option[data-attr]").get(), q("option5c"), "Has attribute selector in siblings (#9261)" );
 });
 
 test("children([String])", function() {