rreturn = /\r/g,
rfocusable = /^(?:input|select|textarea|button|object)$/i,
rclickable = /^(?:a|area)$/i,
- rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
- getSetAttribute = jQuery.support.getSetAttribute;
+ rboolean = /^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,
+ ruseDefault = /^(?:checked|selected)$/i,
+ getSetAttribute = jQuery.support.getSetAttribute,
+ getSetInput = jQuery.support.input;
jQuery.fn.extend({
attr: function( name, value ) {
},
removeAttr: function( elem, value ) {
- var name, propName, isBool,
+ var name, propName,
i = 0,
attrNames = value && value.match( core_rnotwhite );
if ( attrNames && elem.nodeType === 1 ) {
while ( (name = attrNames[i++]) ) {
propName = jQuery.propFix[ name ] || name;
- isBool = rboolean.test( name );
+
+ // Boolean attributes get special treatment (#10870)
+ if ( rboolean.test( name ) ) {
+ // Set corresponding property to false for boolean attributes
+ // Also clear defaultChecked/defaultSelected (if appropriate) for IE<8
+ if ( !getSetAttribute && ruseDefault.test( name ) ) {
+ elem[ jQuery.camelCase( "default-" + name ) ] =
+ elem[ propName ] = false;
+ } else {
+ elem[ propName ] = false;
+ }
// See #9699 for explanation of this approach (setting first, then removal)
- // Do not do this for boolean attributes (see #10870)
- if ( !isBool ) {
+ } else {
jQuery.attr( elem, name, "" );
}
- elem.removeAttribute( getSetAttribute ? name : propName );
- // Set corresponding property to false for boolean attributes
- if ( isBool && propName in elem ) {
- elem[ propName ] = false;
- }
+ elem.removeAttribute( getSetAttribute ? name : propName );
}
}
},
// Hook for boolean attributes
boolHook = {
get: function( elem, name ) {
- // Align boolean attributes with corresponding properties
- // Fall back to attribute presence where some booleans are not supported
- var attrNode,
- property = jQuery.prop( elem, name );
- return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ?
+ var
+ // Use .prop to determine if this attribute is understood as boolean
+ prop = jQuery.prop( elem, name ),
+
+ // Fetch it accordingly
+ attr = typeof prop === "boolean" && elem.getAttribute( name ),
+ detail = typeof prop === "boolean" ?
+
+ getSetInput && getSetAttribute ?
+ attr != null :
+ // oldIE fabricates an empty string for missing boolean attributes
+ // and conflates checked/selected into attroperties
+ ruseDefault.test( name ) ?
+ elem[ jQuery.camelCase( "default-" + name ) ] :
+ !!attr :
+
+ // fetch an attribute node for properties not recognized as boolean
+ elem.getAttributeNode( name );
+
+ return detail && detail.value !== false ?
name.toLowerCase() :
undefined;
},
set: function( elem, value, name ) {
- var propName;
if ( value === false ) {
// Remove boolean attributes when set to false
jQuery.removeAttr( elem, name );
- } else {
- // value is true since we know at this point it's type boolean and not false
- // Set boolean attributes to the same name and set the DOM property
- propName = jQuery.propFix[ name ] || name;
- if ( propName in elem ) {
- // Only set the IDL specifically if it already exists on the element
- elem[ propName ] = true;
- }
+ } else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
+ // IE<8 needs the *property* name
+ elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name );
- elem.setAttribute( name, name.toLowerCase() );
+ // Use defaultChecked and defaultSelected for oldIE
+ } else {
+ elem[ jQuery.camelCase( "default-" + name ) ] = true;
}
+
return name;
}
};
// fix oldIE value attroperty
-if ( !getSetAttribute || !jQuery.support.valueAttribute ) {
+if ( !getSetInput || !getSetAttribute ) {
jQuery.attrHooks.value = {
get: function( elem, name ) {
var ret = elem.getAttributeNode( name );
ret.value = value += "";
- // Break association with cloned elements (#9646)
- if ( name !== "value" && value !== elem.getAttribute( name ) ) {
- elem.setAttribute( name, value );
- }
-
- return value;
+ // Break association with cloned elements by also using setAttribute (#9646)
+ return name === "value" || value === elem.getAttribute( name ) ?
+ value :
+ undefined;
}
};
optgroup.appendChild( option );
select.appendChild( optgroup );
- equal( jQuery( option ).attr("selected"), "selected", "Make sure that a single option is selected, even when in an optgroup." );
+ equal( jQuery( option ).prop("selected"), true, "Make sure that a single option is selected, even when in an optgroup." );
var $img = jQuery("<img style='display:none' width='215' height='53' src='data/1x1.jpg'/>").appendTo("body");
equal( $img.attr("width"), "215", "Retrieve width attribute an an element with display:none." );
});
test( "attr(String, Object)", function() {
- expect( 79 );
+ expect( 67 );
var div = jQuery("div").attr("foo", "bar"),
+ i = 0,
fail = false;
- for ( var i = 0; i < div.size(); i++ ) {
- if ( div.get( i ).getAttribute("foo") != "bar" ) {
+ for ( ; i < div.length; i++ ) {
+ if ( div[ i ].getAttribute("foo") !== "bar" ) {
fail = i;
break;
}
equal( jQuery("#name").attr("name"), "something", "Set name attribute" );
jQuery("#name").attr( "name", null );
equal( jQuery("#name").attr("name"), undefined, "Remove 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 );
- equal( document.getElementById("check2").checked, true, "Set checked attribute" );
- equal( jQuery("#check2").prop("checked"), true, "Set checked attribute" );
- equal( jQuery("#check2").attr("checked"), "checked", "Set checked attribute" );
- jQuery("#check2").attr( "checked", false );
- equal( document.getElementById("check2").checked, false, "Set checked attribute" );
- equal( jQuery("#check2").prop("checked"), false, "Set checked attribute" );
- equal( jQuery("#check2").attr("checked"), undefined, "Set checked attribute" );
- jQuery("#text1").attr( "readonly", true );
- equal( document.getElementById("text1").readOnly, true, "Set readonly attribute" );
- equal( jQuery("#text1").prop("readOnly"), true, "Set readonly attribute" );
- equal( jQuery("#text1").attr("readonly"), "readonly", "Set readonly attribute" );
- jQuery("#text1").attr( "readonly", false );
- equal( document.getElementById("text1").readOnly, false, "Set readonly attribute" );
- equal( jQuery("#text1").prop("readOnly"), false, "Set readonly attribute" );
- equal( jQuery("#text1").attr("readonly"), undefined, "Set readonly attribute" );
-
- jQuery("#check2").prop( "checked", true );
- equal( document.getElementById("check2").checked, true, "Set checked attribute" );
- equal( jQuery("#check2").prop("checked"), true, "Set checked attribute" );
- equal( jQuery("#check2").attr("checked"), "checked", "Set checked attribute" );
- jQuery("#check2").prop( "checked", false );
- equal( document.getElementById("check2").checked, false, "Set checked attribute" );
- equal( jQuery("#check2").prop("checked"), false, "Set checked attribute" );
- equal( jQuery("#check2").attr("checked"), undefined, "Set checked attribute" );
-
- jQuery("#check2").attr("checked", "checked");
- equal( document.getElementById("check2").checked, true, "Set checked attribute with 'checked'" );
- equal( jQuery("#check2").prop("checked"), true, "Set checked attribute" );
- equal( jQuery("#check2").attr("checked"), "checked", "Set checked attribute" );
-
- QUnit.reset();
+ // As of fixing #11115, we make no promises about the effect of .attr on boolean properties
+ $input = jQuery("#check2");
+ $input.prop( "checked", true ).prop( "checked", false ).attr( "checked", true );
+ equal( $input.attr("checked"), "checked", "Set checked (verified by .attr)" );
+ $input.prop( "checked", false ).prop( "checked", true ).attr( "checked", false );
+ equal( $input.attr("checked"), undefined, "Remove checked (verified by .attr)" );
+
+ $input = jQuery("#text1").prop( "readOnly", true ).prop( "readOnly", false ).attr( "readonly", true );
+ equal( $input.attr("readonly"), "readonly", "Set readonly (verified by .attr)" );
+ $input.prop( "readOnly", false ).prop( "readOnly", true ).attr( "readonly", false );
+ equal( $input.attr("readonly"), undefined, "Remove readonly (verified by .attr)" );
+
+ $input = jQuery("#check2").attr( "checked", true ).attr( "checked", false ).prop( "checked", true );
+ equal( $input[0].checked, true, "Set checked property (verified by native property)" );
+ equal( $input.prop("checked"), true, "Set checked property (verified by .prop)" );
+ equal( $input.attr("checked"), undefined, "Setting checked property doesn't affect checked attribute" );
+ $input.attr( "checked", false ).attr( "checked", true ).prop( "checked", false );
+ equal( $input[0].checked, false, "Clear checked property (verified by native property)" );
+ equal( $input.prop("checked"), false, "Clear checked property (verified by .prop)" );
+ equal( $input.attr("checked"), "checked", "Clearing checked property doesn't affect checked attribute" );
+
+ $input = jQuery("#check2").attr( "checked", false ).attr( "checked", "checked" );
+ equal( $input.attr("checked"), "checked", "Set checked to 'checked' (verified by .attr)" );
var $radios = jQuery("#checkedtest").find("input[type='radio']");
$radios.eq( 1 ).click();
equal( $radios.eq( 1 ).prop("checked"), true, "Second radio was checked when clicked" );
- equal( $radios.attr("checked"), $radios[ 0 ].checked ? "checked" : undefined, "Known booleans do not fall back to attribute presence (#10278)" );
-
- jQuery("#text1").prop( "readOnly", true );
- equal( document.getElementById("text1").readOnly, true, "Set readonly attribute" );
- equal( jQuery("#text1").prop("readOnly"), true, "Set readonly attribute" );
- equal( jQuery("#text1").attr("readonly"), "readonly", "Set readonly attribute" );
- jQuery("#text1").prop( "readOnly", false );
- equal( document.getElementById("text1").readOnly, false, "Set readonly attribute" );
- equal( jQuery("#text1").prop("readOnly"), false, "Set readonly attribute" );
- equal( jQuery("#text1").attr("readonly"), undefined, "Set readonly attribute" );
-
- jQuery("#name").attr( "maxlength", "5" );
- equal( document.getElementById("name").maxLength, 5, "Set maxlength attribute" );
- jQuery("#name").attr( "maxLength", "10" );
- equal( document.getElementById("name").maxLength, 10, "Set maxlength attribute" );
+ equal( $radios.eq( 0 ).attr("checked"), "checked", "First radio is still [checked]" );
+
+ $input = jQuery("#text1").attr( "readonly", false ).prop( "readOnly", true );
+ equal( $input[0].readOnly, true, "Set readonly property (verified by native property)" );
+ equal( $input.prop("readOnly"), true, "Set readonly property (verified by .prop)" );
+ $input.attr( "readonly", true ).prop( "readOnly", false );
+ equal( $input[0].readOnly, false, "Clear readonly property (verified by native property)" );
+ equal( $input.prop("readOnly"), false, "Clear readonly property (verified by .prop)" );
+
+ $input = jQuery("#name").attr( "maxlength", "5" );
+ equal( $input[0].maxLength, 5, "Set maxlength (verified by native property)" );
+ $input.attr( "maxLength", "10" );
+ equal( $input[0].maxLength, 10, "Set maxlength (verified by native property)" );
// HTML5 boolean attributes
var $text = jQuery("#text1").attr({
"autofocus": true,
"required": true
});
- equal( $text.attr("autofocus"), "autofocus", "Set boolean attributes to the same name" );
- equal( $text.attr( "autofocus", false ).attr("autofocus"), undefined, "Setting autofocus attribute to false removes it" );
- equal( $text.attr("required"), "required", "Set boolean attributes to the same name" );
+ equal( $text.attr("autofocus"), "autofocus", "Reading autofocus attribute yields 'autofocus'" );
+ equal( $text.attr( "autofocus", false ).attr("autofocus"), undefined, "Setting autofocus to false removes it" );
+ equal( $text.attr("required"), "required", "Reading required attribute yields 'required'" );
equal( $text.attr( "required", false ).attr("required"), undefined, "Setting required attribute to false removes it" );
var $details = jQuery("<details open></details>").appendTo("#qunit-fixture");
strictEqual( $elem.attr("nonexisting"), undefined, "attr(name, value) works correctly on comment and text nodes (bug #7500)." );
});
+ // Register the property name to avoid generating a new global when testing window
+ Globals.register("nonexisting");
jQuery.each( [ window, document, obj, "#firstp" ], function( i, elem ) {
- // use iframeCallback to avoid generating a new global when testing window
- var oldVal = elem.iframeCallback,
+ var oldVal = elem.nonexisting,
$elem = jQuery( elem );
strictEqual( $elem.attr("nonexisting"), undefined, "attr works correctly for non existing attributes (bug #7500)." );
- equal( $elem.attr( "iframeCallback", "foo" ).attr("iframeCallback"), "foo", "attr falls back to prop on unsupported arguments" );
- elem.iframeCallback = oldVal;
+ equal( $elem.attr( "nonexisting", "foo" ).attr("nonexisting"), "foo", "attr falls back to prop on unsupported arguments" );
+ elem.nonexisting = oldVal;
});
var table = jQuery("#table").append("<tr><td>cell</td></tr><tr><td>cell</td><td>cell</td></tr><tr><td>cell</td><td>cell</td></tr>"),