diff options
author | timmywil <timmywillisn@gmail.com> | 2011-09-12 19:40:14 -0400 |
---|---|---|
committer | timmywil <timmywillisn@gmail.com> | 2011-09-19 15:42:30 -0400 |
commit | 92405d4f5ffe9ec1f26f280303783014948438c5 (patch) | |
tree | cce9fc8dbb2c13cea02c87a172eb8a0ff0b66440 | |
parent | 269899a50646e9c1a4f58f1fe91a8a7262fd77e7 (diff) | |
download | jquery-92405d4f5ffe9ec1f26f280303783014948438c5.tar.gz jquery-92405d4f5ffe9ec1f26f280303783014948438c5.zip |
Override Sizzle attribute retrieval with jQuery.attr. Fixes #5637, #7128, #9261, #9570, #10178.
Bug fixed on the side: $(window).is('a') was throwing an exception. Fixes #10178.
-rw-r--r-- | src/attributes.js | 12 | ||||
m--------- | src/sizzle | 0 | ||||
-rw-r--r-- | src/sizzle-jquery.js | 3 | ||||
-rw-r--r-- | test/data/selector.html | 132 | ||||
-rw-r--r-- | test/data/testinit.js | 3 | ||||
-rw-r--r-- | test/index.html | 3 | ||||
-rw-r--r-- | test/unit/attributes.js | 15 | ||||
-rw-r--r-- | test/unit/selector.js | 133 | ||||
-rw-r--r-- | test/unit/traversing.js | 8 |
9 files changed, 293 insertions, 16 deletions
diff --git a/src/attributes.js b/src/attributes.js index dd985b494..6380b7d6c 100644 --- a/src/attributes.js +++ b/src/attributes.js @@ -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; }, diff --git a/src/sizzle b/src/sizzle -Subproject 93cc46709e03173288111f8b60d4192fea2ec8d +Subproject 3375f91f7c0503da1cd34ae9b6bbbd7be07bf64 diff --git a/src/sizzle-jquery.js b/src/sizzle-jquery.js index f15b08252..7090e1c63 100644 --- a/src/sizzle-jquery.js +++ b/src/sizzle-jquery.js @@ -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 index 000000000..b62e1fa4e --- /dev/null +++ b/test/data/selector.html @@ -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> diff --git a/test/data/testinit.js b/test/data/testinit.js index c478390d5..c30747d09 100644 --- a/test/data/testinit.js +++ b/test/data/testinit.js @@ -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 = ""; diff --git a/test/index.html b/test/index.html index 4bbef54cd..05a444e01 100644 --- a/test/index.html +++ b/test/index.html @@ -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> @@ -136,7 +137,7 @@ <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"> diff --git a/test/unit/attributes.js b/test/unit/attributes.js index 2af36f1c2..6565d5db8 100644 --- a/test/unit/attributes.js +++ b/test/unit/attributes.js @@ -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 index 000000000..441258008 --- /dev/null +++ b/test/unit/selector.js @@ -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"]); +}); diff --git a/test/unit/traversing.js b/test/unit/traversing.js index 37ac20e82..4cbcee912 100644 --- a/test/unit/traversing.js +++ b/test/unit/traversing.js @@ -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() { |