Merge branch 'attrhooks.1.6'

Conflicts:
	src/core.js
	src/support.js
This commit is contained in:
jeresig 2011-04-10 15:49:17 -04:00
commit 7fb95ebe8f
7 changed files with 401 additions and 201 deletions

View file

@ -3,44 +3,56 @@
var rclass = /[\n\t\r]/g, var rclass = /[\n\t\r]/g,
rspaces = /\s+/, rspaces = /\s+/,
rreturn = /\r/g, rreturn = /\r/g,
rspecialurl = /^(?:href|src|style)$/,
rtype = /^(?:button|input)$/i, rtype = /^(?:button|input)$/i,
rfocusable = /^(?:button|input|object|select|textarea)$/i, rfocusable = /^(?:button|input|object|select|textarea)$/i,
rclickable = /^a(?:rea)?$/i, rclickable = /^a(?:rea)?$/i,
rradiocheck = /^(?:radio|checkbox)$/i; rradiocheck = /^(?:radio|checkbox)$/i,
formHook;
jQuery.props = {
"for": "htmlFor",
"class": "className",
readonly: "readOnly",
maxlength: "maxLength",
cellspacing: "cellSpacing",
rowspan: "rowSpan",
colspan: "colSpan",
tabindex: "tabIndex",
usemap: "useMap",
frameborder: "frameBorder"
};
jQuery.fn.extend({ jQuery.fn.extend({
attr: function( name, value ) { attr: function( name, value ) {
return jQuery.access( this, name, value, true, jQuery.attr ); return jQuery.access( this, name, value, true, jQuery.attr );
}, },
removeAttr: function( name, fn ) { removeAttr: function( name ) {
return this.each(function(){ return this.each(function() {
jQuery.attr( this, name, "" ); jQuery.removeAttr( this, name );
if ( this.nodeType === 1 ) { });
this.removeAttribute( name ); },
}
prop: function( name, value ) {
return jQuery.access( this, name, value, true, jQuery.prop );
},
removeProp: function( name ) {
return this.each(function() {
// try/catch handles cases where IE balks (such as removing a property on window)
try {
this[ name ] = undefined;
delete this[ name ];
} catch( e ) {}
});
},
prop: function( name, value ) {
return jQuery.access( this, name, value, true, jQuery.prop );
},
removeProp: function( name ) {
return this.each(function() {
// try/catch handles cases where IE balks (such as removing a property on window)
try {
this[ name ] = undefined;
delete this[ name ];
} catch( e ) {}
}); });
}, },
addClass: function( value ) { addClass: function( value ) {
if ( jQuery.isFunction(value) ) { if ( jQuery.isFunction( value ) ) {
return this.each(function(i) { return this.each(function(i) {
var self = jQuery(this); var self = jQuery(this);
self.addClass( value.call(this, i, self.attr("class")) ); self.addClass( value.call(this, i, self.attr("class") || "") );
}); });
} }
@ -276,114 +288,244 @@ jQuery.extend({
offset: true offset: true
}, },
attrFix: {
// Always normalize to ensure hook usage
tabindex: "tabIndex",
readonly: "readOnly"
},
attr: function( elem, name, value, pass ) { attr: function( elem, name, value, pass ) {
var nType = elem.nodeType;
// don't get/set attributes on text, comment and attribute nodes // don't get/set attributes on text, comment and attribute nodes
if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || elem.nodeType === 2 ) { if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
return undefined; return undefined;
} }
if ( pass && name in jQuery.attrFn ) { if ( pass && name in jQuery.attrFn ) {
return jQuery(elem)[name](value); return jQuery( elem )[ name ]( value );
} }
var notxml = elem.nodeType !== 1 || !jQuery.isXMLDoc( elem ), var ret, hooks,
// Whether we are setting (or getting) notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
set = value !== undefined;
// Try to normalize/fix the name // Normalize the name if needed
name = notxml && jQuery.props[ name ] || name; name = notxml && jQuery.attrFix[ name ] || name;
// Only do all the following if this is a node (faster for style) // Get the appropriate hook, or the formHook
if ( elem.nodeType === 1 ) { // if getSetAttribute is not supported and we have form objects in IE6/7
// These attributes require special treatment hooks = jQuery.attrHooks[ name ] || ( elem.nodeName === "FORM" && formHook );
var special = rspecialurl.test( name );
// Safari mis-reports the default selected property of an option if ( value !== undefined ) {
// Accessing the parent's selectedIndex property fixes it
if ( name === "selected" && !jQuery.support.optSelected ) {
var parent = elem.parentNode;
if ( parent ) {
parent.selectedIndex;
// Make sure that it also works with optgroups, see #5701 if ( value === null ) {
if ( parent.parentNode ) { jQuery.removeAttr( elem, name );
parent.parentNode.selectedIndex; return undefined;
}
} } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) {
return ret;
} else {
elem.setAttribute( name, "" + value );
return value;
} }
// If applicable, access the attribute via the DOM 0 way } else {
// 'in' checks fail in Blackberry 4.7 #6931
if ( (name in elem || elem[ name ] !== undefined) && notxml && !special ) {
if ( set ) {
// We can't allow the type property to be changed (since it causes problems in IE)
if ( name === "type" && rtype.test( elem.nodeName ) && elem.parentNode ) {
jQuery.error( "type property can't be changed" );
}
if ( value === null ) { if ( hooks && "get" in hooks && notxml ) {
if ( elem.nodeType === 1 ) { return hooks.get( elem, name );
elem.removeAttribute( name );
}
} else { } else {
elem[ name ] = value;
} ret = elem.getAttribute( name );
// Non-existent attributes return null, we normalize to undefined
return ret === null ?
undefined :
ret;
}
}
},
removeAttr: function( elem, name ) {
if ( elem.nodeType === 1 ) {
name = jQuery.attrFix[ name ] || name;
if ( jQuery.support.getSetAttribute ) {
// Use removeAttribute in browsers that support it
elem.removeAttribute( name );
} else {
jQuery.attr( elem, name, "" );
elem.removeAttributeNode( elem.getAttributeNode( name ) );
}
}
},
attrHooks: {
type: {
set: function( elem, value ) {
// We can't allow the type property to be changed (since it causes problems in IE)
if ( rtype.test( elem.nodeName ) && elem.parentNode ) {
jQuery.error( "type property can't be changed" );
} }
}
// browsers index elements by id/name on forms, give priority to attributes. },
if ( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) { tabIndex: {
return elem.getAttributeNode( name ).nodeValue; get: function( elem ) {
}
// elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
if ( name === "tabIndex" ) { var attributeNode = elem.getAttributeNode("tabIndex");
var attributeNode = elem.getAttributeNode( "tabIndex" );
return attributeNode && attributeNode.specified ? return attributeNode && attributeNode.specified ?
attributeNode.value : parseInt( attributeNode.value, 10 ) :
rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
0 : 0 :
undefined; undefined;
} }
}
},
propFix: {},
prop: function( elem, name, value ) {
var nType = elem.nodeType;
// don't get/set properties on text, comment and attribute nodes
if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
return undefined;
}
var ret, hooks,
notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
// Try to normalize/fix the name
name = notxml && jQuery.propFix[ name ] || name;
hooks = jQuery.propHooks[ name ];
if ( value !== undefined ) {
if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
return ret;
} else {
return (elem[ name ] = value);
}
} else {
if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== undefined ) {
return ret;
} else {
return elem[ name ]; return elem[ name ];
} }
if ( !jQuery.support.style && notxml && name === "style" ) {
if ( set ) {
elem.style.cssText = "" + value;
}
return elem.style.cssText;
}
if ( set ) {
// convert the value to a string (all browsers do this but IE) see #1070
elem.setAttribute( name, "" + value );
}
// Ensure that missing attributes return undefined
// Blackberry 4.7 returns "" from getAttribute #6938
if ( !elem.attributes[ name ] && (elem.hasAttribute && !elem.hasAttribute( name )) ) {
return undefined;
}
var attr = !jQuery.support.hrefNormalized && notxml && special ?
// Some attributes require a special call on IE
elem.getAttribute( name, 2 ) :
elem.getAttribute( name );
// Non-existent attributes return null, we normalize to undefined
return attr === null ? undefined : attr;
} }
// Handle everything which isn't a DOM element node },
if ( set ) {
elem[ name ] = value; propHooks: {}
}
return elem[ name ];
}
}); });
// IE6/7 do not support getting/setting some attributes with get/setAttribute
if ( !jQuery.support.getSetAttribute ) {
jQuery.attrFix = jQuery.extend( jQuery.attrFix, {
"for": "htmlFor",
"class": "className",
maxlength: "maxLength",
cellspacing: "cellSpacing",
rowspan: "rowSpan",
colspan: "colSpan",
usemap: "useMap",
frameborder: "frameBorder"
});
// Use this for any attribute on a form in IE6/7
// And the name attribute
formHook = jQuery.attrHooks.name = {
get: function( elem, name ) {
var ret = elem.getAttributeNode( name );
// Return undefined if not specified instead of empty string
return ret && ret.specified ?
ret.nodeValue :
undefined;
},
set: function( elem, value, name ) {
// Check form objects in IE (multiple bugs related)
// Only use nodeValue if the attribute node exists on the form
var ret = elem.getAttributeNode( name );
if ( ret ) {
ret.nodeValue = value;
return value;
}
}
};
// Set width and height to auto instead of 0 on empty string( Bug #8150 )
// This is for removals
jQuery.each([ "width", "height" ], function( i, name ) {
jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
set: function( elem, value ) {
if ( value === "" ) {
elem.setAttribute( name, "auto" );
return value;
}
}
});
});
}
// Remove certain attrs if set to false
jQuery.each([ "selected", "checked", "readOnly", "disabled" ], function( i, name ) {
jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
set: function( elem, value ) {
if ( value === false ) {
jQuery.removeAttr( elem, name );
return value;
}
}
});
});
// Some attributes require a special call on IE
if ( !jQuery.support.hrefNormalized ) {
jQuery.each([ "href", "src", "width", "height", "list" ], function( i, name ) {
jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
get: function( elem ) {
var ret = elem.getAttribute( name, 2 );
return ret === null ? undefined : ret;
}
});
});
}
if ( !jQuery.support.style ) {
jQuery.attrHooks.style = {
get: function( elem ) {
// Return undefined in the case of empty string
// Normalize to lowercase since IE uppercases css property names
return elem.style.cssText.toLowerCase() || undefined;
},
set: function( elem, value ) {
return (elem.style.cssText = "" + value);
}
};
}
// Safari mis-reports the default selected property of an option
// Accessing the parent's selectedIndex property fixes it
if ( !jQuery.support.optSelected ) {
jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
get: function( elem ) {
var parent = elem.parentNode;
if ( parent ) {
parent.selectedIndex;
// Make sure that it also works with optgroups, see #5701
if ( parent.parentNode ) {
parent.parentNode.selectedIndex;
}
}
}
});
}
})( jQuery ); })( jQuery );

View file

@ -893,4 +893,3 @@ function doScrollCheck() {
return jQuery; return jQuery;
})(); })();

View file

@ -377,7 +377,7 @@ function cloneCopyEvent( src, dest ) {
} }
} }
function cloneFixAttributes(src, dest) { function cloneFixAttributes( src, dest ) {
// We do not need to do anything for non-Elements // We do not need to do anything for non-Elements
if ( dest.nodeType !== 1 ) { if ( dest.nodeType !== 1 ) {
return; return;
@ -549,7 +549,8 @@ jQuery.extend({
// Return the cloned set // Return the cloned set
return clone; return clone;
}, },
clean: function( elems, context, fragment, scripts ) { clean: function( elems, context, fragment, scripts ) {
context = context || document; context = context || document;

View file

@ -72,6 +72,9 @@ jQuery.support = (function() {
// (WebKit defaults to false instead of true, IE too, if it's in an optgroup) // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
optSelected: opt.selected, optSelected: opt.selected,
// Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
getSetAttribute: div.className !== "t",
// Will be defined later // Will be defined later
submitBubbles: true, submitBubbles: true,
changeBubbles: true, changeBubbles: true,

View file

@ -203,6 +203,10 @@ Z</textarea>
<select name="D4" disabled="disabled"> <select name="D4" disabled="disabled">
<option selected="selected" value="NO">NO</option> <option selected="selected" value="NO">NO</option>
</select> </select>
<input id="list-test" type="text" />
<datalist id="datalist">
<option value="option"></option>
</datalist>
</form> </form>
<div id="moretests"> <div id="moretests">
<form> <form>

View file

@ -3,38 +3,81 @@ module("attributes", { teardown: moduleTeardown });
var bareObj = function(value) { return value; }; var bareObj = function(value) { return value; };
var functionReturningObj = function(value) { return (function() { return value; }); }; var functionReturningObj = function(value) { return (function() { return value; }); };
test("jQuery.props: itegrity test", function() {
expect(1); test("jQuery.attrFix integrity test", function() {
expect(1);
// This must be maintained and equal jQuery.props // This must be maintained and equal jQuery.attrFix when appropriate
// Ensure that accidental or erroneous property // Ensure that accidental or erroneous property
// overwrites don't occur // overwrites don't occur
// This is simply for better code coverage and future proofing. // This is simply for better code coverage and future proofing.
var propsShouldBe = { var propsShouldBe;
"for": "htmlFor", if ( !jQuery.support.getSetAttribute ) {
"class": "className", propsShouldBe = {
readonly: "readOnly", tabindex: "tabIndex",
maxlength: "maxLength", readonly: "readOnly",
cellspacing: "cellSpacing", "for": "htmlFor",
rowspan: "rowSpan", "class": "className",
colspan: "colSpan", maxlength: "maxLength",
tabindex: "tabIndex", cellspacing: "cellSpacing",
usemap: "useMap", rowspan: "rowSpan",
frameborder: "frameBorder" colspan: "colSpan",
}; usemap: "useMap",
frameborder: "frameBorder"
};
} else {
propsShouldBe = {
tabindex: "tabIndex",
readonly: "readOnly"
};
}
same(propsShouldBe, jQuery.props, "jQuery.props passes integrity check"); same(propsShouldBe, jQuery.attrFix, "jQuery.attrFix passes integrity check");
});
test("prop(String, Object)", function() {
expect(19);
equals( jQuery('#text1').prop('value'), "Test", 'Check for value attribute' );
equals( jQuery('#text1').prop('value', "Test2").prop('defaultValue'), "Test", 'Check for defaultValue attribute' );
equals( jQuery('#select2').prop('selectedIndex'), 3, 'Check for selectedIndex attribute' );
equals( jQuery('#foo').prop('nodeName').toUpperCase(), 'DIV', 'Check for nodeName attribute' );
equals( jQuery('#foo').prop('tagName').toUpperCase(), 'DIV', 'Check for tagName attribute' );
equals( jQuery("<option/>").prop("selected"), false, "Check selected attribute on disconnected element." );
var body = document.body, $body = jQuery( body );
ok( $body.prop('nextSibling') === null, 'Make sure a null expando returns null' );
body.foo = 'bar';
equals( $body.prop('foo'), 'bar', 'Make sure the expando is preferred over the dom attribute' );
body.foo = undefined;
ok( $body.prop('foo') === undefined, 'Make sure the expando is preferred over the dom attribute, even if undefined' );
var select = document.createElement("select"), optgroup = document.createElement("optgroup"), option = document.createElement("option");
optgroup.appendChild( option );
select.appendChild( optgroup );
equals( jQuery(option).prop("selected"), true, "Make sure that a single option is selected, even when in an optgroup." );
equals( jQuery(document).prop("nodeName"), "#document", "prop works correctly on document nodes (bug #7451)." );
var attributeNode = document.createAttribute("irrelevant"),
commentNode = document.createComment("some comment"),
textNode = document.createTextNode("some text"),
obj = {};
jQuery.each( [document, attributeNode, commentNode, textNode, obj, "#firstp"], function( i, ele ) {
strictEqual( jQuery(ele).prop("nonexisting"), undefined, "prop works correctly for non existing attributes (bug #7500)." );
});
var obj = {};
jQuery.each( [document, obj], function( i, ele ) {
var $ele = jQuery( ele );
$ele.prop( "nonexisting", "foo" );
equal( $ele.prop("nonexisting"), "foo", "prop(name, value) works correctly for non existing attributes (bug #7500)." );
});
jQuery( document ).removeProp("nonexisting");
}); });
test("attr(String)", function() { test("attr(String)", function() {
expect(37); expect(32);
// This one sometimes fails randomly ?!
equals( jQuery('#text1').attr('value'), "Test", 'Check for value attribute' );
equals( jQuery('#text1').attr('value', "Test2").attr('defaultValue'), "Test", 'Check for defaultValue attribute' );
equals( jQuery('#text1').attr('type'), "text", 'Check for type attribute' ); equals( jQuery('#text1').attr('type'), "text", 'Check for type attribute' );
equals( jQuery('#radio1').attr('type'), "radio", 'Check for type attribute' ); equals( jQuery('#radio1').attr('type'), "radio", 'Check for type attribute' );
equals( jQuery('#check1').attr('type'), "checkbox", 'Check for type attribute' ); equals( jQuery('#check1').attr('type'), "checkbox", 'Check for type attribute' );
@ -46,60 +89,58 @@ test("attr(String)", function() {
equals( jQuery('#name').attr('name'), "name", 'Check for name attribute' ); equals( jQuery('#name').attr('name'), "name", 'Check for name attribute' );
equals( jQuery('#text1').attr('name'), "action", 'Check for name attribute' ); equals( jQuery('#text1').attr('name'), "action", 'Check for name attribute' );
ok( jQuery('#form').attr('action').indexOf("formaction") >= 0, 'Check for action attribute' ); ok( jQuery('#form').attr('action').indexOf("formaction") >= 0, 'Check for action attribute' );
// Temporarily disabled. See: #4299 equals( jQuery('#form').attr('blah', 'blah').attr('blah'), 'blah', 'Set non-existant attribute on a form' );
// ok( jQuery('#form').attr('action','newformaction').attr('action').indexOf("newformaction") >= 0, 'Check that action attribute was changed' ); equals( jQuery('#foo').attr('height'), undefined, 'Non existent height attribute should return undefined' );
// [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]' );
// 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();
equals( jQuery('#text1').attr('maxlength'), '30', 'Check for maxlength attribute' ); equals( jQuery('#text1').attr('maxlength'), '30', 'Check for maxlength attribute' );
equals( jQuery('#text1').attr('maxLength'), '30', 'Check for maxLength attribute' ); equals( jQuery('#text1').attr('maxLength'), '30', 'Check for maxLength attribute' );
equals( jQuery('#area1').attr('maxLength'), '30', 'Check for maxLength attribute' ); equals( jQuery('#area1').attr('maxLength'), '30', 'Check for maxLength attribute' );
equals( jQuery('#select2').attr('selectedIndex'), 3, 'Check for selectedIndex attribute' );
equals( jQuery('#foo').attr('nodeName').toUpperCase(), 'DIV', 'Check for nodeName attribute' );
equals( jQuery('#foo').attr('tagName').toUpperCase(), 'DIV', 'Check for tagName attribute' );
// using innerHTML in IE causes href attribute to be serialized to the full path // using innerHTML in IE causes href attribute to be serialized to the full path
jQuery('<a/>').attr({ 'id': 'tAnchor5', 'href': '#5' }).appendTo('#main'); jQuery('<a/>').attr({ 'id': 'tAnchor5', 'href': '#5' }).appendTo('#main');
equals( jQuery('#tAnchor5').attr('href'), "#5", 'Check for non-absolute href (an anchor)' ); equals( jQuery('#tAnchor5').attr('href'), "#5", 'Check for non-absolute href (an anchor)' );
equals( jQuery("<option/>").attr("selected"), false, "Check selected attribute on disconnected element." ); // list attribute is readonly by default in browsers that support it
jQuery('#list-test').attr('list', 'datalist');
equals( jQuery('#list-test').attr('list'), 'datalist', 'Check setting list attribute' );
// Related to [5574] and [5683] // Related to [5574] and [5683]
var body = document.body, $body = jQuery(body); var body = document.body, $body = jQuery(body);
ok( $body.attr('foo') === undefined, 'Make sure that a non existent attribute returns undefined' ); strictEqual( $body.attr('foo'), undefined, 'Make sure that a non existent attribute returns undefined' );
ok( $body.attr('nextSibling') === null, 'Make sure a null expando returns null' );
body.setAttribute('foo', 'baz'); body.setAttribute('foo', 'baz');
equals( $body.attr('foo'), 'baz', 'Make sure the dom attribute is retrieved when no expando is found' ); equals( $body.attr('foo'), 'baz', 'Make sure the dom attribute is retrieved when no expando is found' );
body.foo = 'bar';
equals( $body.attr('foo'), 'bar', 'Make sure the expando is preferred over the dom attribute' );
$body.attr('foo','cool'); $body.attr('foo','cool');
equals( $body.attr('foo'), 'cool', 'Make sure that setting works well when both expando and dom attribute are available' ); equals( $body.attr('foo'), 'cool', 'Make sure that setting works well when both expando and dom attribute are available' );
body.foo = undefined;
ok( $body.attr('foo') === undefined, 'Make sure the expando is preferred over the dom attribute, even if undefined' );
body.removeAttribute('foo'); // Cleanup body.removeAttribute('foo'); // Cleanup
var select = document.createElement("select"), optgroup = document.createElement("optgroup"), option = document.createElement("option"); var select = document.createElement("select"), optgroup = document.createElement("optgroup"), option = document.createElement("option");
optgroup.appendChild( option ); optgroup.appendChild( option );
select.appendChild( optgroup ); select.appendChild( optgroup );
equals( jQuery(option).attr("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="http://static.jquery.com/files/rocker/images/logo_jquery_215x53.gif"/>').appendTo('body');
equals( $img.attr('width'), "215", "Retrieve width attribute an an element with display:none." );
equals( $img.attr('height'), "53", "Retrieve height attribute an an element with display:none." );
// Check for style support
ok( !!~jQuery('#dl').attr('style').indexOf('position'), 'Check style attribute getter, also normalize css props to lowercase' );
ok( !!~jQuery('#foo').attr('style', 'position:absolute;').attr('style').indexOf('position'), 'Check style setter' );
ok( jQuery("<div/>").attr("doesntexist") === undefined, "Make sure undefined is returned when no attribute is found." ); ok( jQuery("<div/>").attr("doesntexist") === undefined, "Make sure undefined is returned when no attribute is found." );
ok( jQuery().attr("doesntexist") === undefined, "Make sure undefined is returned when no element is there." ); ok( jQuery().attr("doesntexist") === undefined, "Make sure undefined is returned when no element is there." );
equals( jQuery(document).attr("nodeName"), "#document", "attr works correctly on document nodes (bug #7451)." );
var attributeNode = document.createAttribute("irrelevant"),
commentNode = document.createComment("some comment"),
textNode = document.createTextNode("some text"),
obj = {};
jQuery.each( [document, attributeNode, commentNode, textNode, obj, "#firstp"], function( i, ele ) {
strictEqual( jQuery(ele).attr("nonexisting"), undefined, "attr works correctly for non existing attributes (bug #7500)." );
});
}); });
if ( !isLocal ) { if ( !isLocal ) {
@ -116,8 +157,8 @@ if ( !isLocal ) {
test("attr(String, Function)", function() { test("attr(String, Function)", function() {
expect(2); expect(2);
equals( jQuery('#text1').attr('value', function() { return this.id ;})[0].value, "text1", "Set value from id" ); equals( jQuery('#text1').attr('value', function() { return this.id; })[0].value, "text1", "Set value from id" );
equals( jQuery('#text1').attr('title', function(i) { return i }).attr('title'), "0", "Set value with an index"); equals( jQuery('#text1').attr('title', function(i) { return i; }).attr('title'), "0", "Set value with an index");
}); });
test("attr(Hash)", function() { test("attr(Hash)", function() {
@ -133,7 +174,7 @@ test("attr(Hash)", function() {
}); });
test("attr(String, Object)", function() { test("attr(String, Object)", function() {
expect(30); expect(29);
var div = jQuery("div").attr("foo", "bar"), var div = jQuery("div").attr("foo", "bar"),
fail = false; fail = false;
@ -153,7 +194,7 @@ test("attr(String, Object)", function() {
jQuery("#name").attr('name', 'something'); jQuery("#name").attr('name', 'something');
equals( jQuery("#name").attr('name'), 'something', 'Set name attribute' ); equals( jQuery("#name").attr('name'), 'something', 'Set name attribute' );
jQuery("#name").attr('name', null); jQuery("#name").attr('name', null);
equals( jQuery("#name").attr('title'), '', 'Remove name attribute' ); equals( jQuery("#name").attr('name'), undefined, 'Remove name attribute' );
jQuery("#check2").attr('checked', true); jQuery("#check2").attr('checked', true);
equals( document.getElementById('check2').checked, true, 'Set checked attribute' ); equals( document.getElementById('check2').checked, true, 'Set checked attribute' );
jQuery("#check2").attr('checked', false); jQuery("#check2").attr('checked', false);
@ -163,28 +204,22 @@ test("attr(String, Object)", function() {
jQuery("#text1").attr('readonly', false); jQuery("#text1").attr('readonly', false);
equals( document.getElementById('text1').readOnly, false, 'Set readonly attribute' ); equals( document.getElementById('text1').readOnly, false, 'Set readonly attribute' );
jQuery("#name").attr('maxlength', '5'); jQuery("#name").attr('maxlength', '5');
equals( document.getElementById('name').maxLength, '5', 'Set maxlength attribute' ); equals( document.getElementById('name').maxLength, 5, 'Set maxlength attribute' );
jQuery("#name").attr('maxLength', '10'); jQuery("#name").attr('maxLength', '10');
equals( document.getElementById('name').maxLength, '10', 'Set maxlength attribute' ); equals( document.getElementById('name').maxLength, 10, 'Set maxlength attribute' );
var $p = jQuery('#firstp').attr('nonexisting', 'foo');
equals( $p.attr('nonexisting'), 'foo', "attr(name, value) works correctly for non existing attributes (bug #7500).");
$p.removeAttr('nonexisting');
var attributeNode = document.createAttribute("irrelevant"), var attributeNode = document.createAttribute("irrelevant"),
commentNode = document.createComment("some comment"), commentNode = document.createComment("some comment"),
textNode = document.createTextNode("some text"), textNode = document.createTextNode("some text");
obj = {};
jQuery.each( [document, obj, "#firstp"], function( i, ele ) {
var $ele = jQuery( ele );
$ele.attr( "nonexisting", "foo" );
equal( $ele.attr("nonexisting"), "foo", "attr(name, value) works correctly for non existing attributes (bug #7500)." );
});
jQuery.each( [commentNode, textNode, attributeNode], function( i, ele ) { jQuery.each( [commentNode, textNode, attributeNode], function( i, ele ) {
var $ele = jQuery( ele ); var $ele = jQuery( ele );
$ele.attr( "nonexisting", "foo" ); $ele.attr( "nonexisting", "foo" );
strictEqual( $ele.attr("nonexisting"), undefined, "attr(name, value) works correctly on comment and text nodes (bug #7500)." ); strictEqual( $ele.attr("nonexisting"), undefined, "attr(name, value) works correctly on comment and text nodes (bug #7500)." );
}); });
//cleanup
jQuery.each( [document, "#firstp"], function( i, ele ) {
jQuery( ele ).removeAttr("nonexisting");
});
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>"), 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>"),
td = table.find('td:first'); td = table.find('td:first');
@ -193,15 +228,15 @@ test("attr(String, Object)", function() {
td.attr("colspan", "2"); td.attr("colspan", "2");
equals( td[0].colSpan, 2, "Check colspan is correctly set" ); equals( td[0].colSpan, 2, "Check colspan is correctly set" );
table.attr("cellspacing", "2"); table.attr("cellspacing", "2");
equals( table[0].cellSpacing, 2, "Check cellspacing is correctly set" ); equals( table[0].cellSpacing, "2", "Check cellspacing is correctly set" );
// for #1070 // for #1070
jQuery("#name").attr('someAttr', '0'); jQuery("#name").attr('someAttr', '0');
equals( jQuery("#name").attr('someAttr'), '0', 'Set attribute to a string of "0"' ); equals( jQuery("#name").attr('someAttr'), '0', 'Set attribute to a string of "0"' );
jQuery("#name").attr('someAttr', 0); jQuery("#name").attr('someAttr', 0);
equals( jQuery("#name").attr('someAttr'), 0, 'Set attribute to the number 0' ); equals( jQuery("#name").attr('someAttr'), '0', 'Set attribute to the number 0' );
jQuery("#name").attr('someAttr', 1); jQuery("#name").attr('someAttr', 1);
equals( jQuery("#name").attr('someAttr'), 1, 'Set attribute to the number 1' ); equals( jQuery("#name").attr('someAttr'), '1', 'Set attribute to the number 1' );
// using contents will get comments regular, text, and comment nodes // using contents will get comments regular, text, and comment nodes
var j = jQuery("#nonnodes").contents(); var j = jQuery("#nonnodes").contents();
@ -212,6 +247,7 @@ test("attr(String, Object)", function() {
QUnit.reset(); QUnit.reset();
// Type
var type = jQuery("#check2").attr('type'); var type = jQuery("#check2").attr('type');
var thrown = false; var thrown = false;
try { try {
@ -251,6 +287,13 @@ test("attr(String, Object)", function() {
} }
ok( thrown, "Exception thrown when trying to change type property" ); ok( thrown, "Exception thrown when trying to change type property" );
equals( "button", button.attr('type'), "Verify that you can't change the type of a button element" ); equals( "button", button.attr('type'), "Verify that you can't change the type of a button element" );
// Setting attributes on svg elements (bug #3116)
var $svg = jQuery('<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" baseProfile="full" width="3000" height="3000">'
+ '<circle cx="200" cy="200" r="150" />'
+ '</svg>').appendTo('body');
equals( $svg.attr('cx', 100).attr('cx'), "100", "Set attribute on svg element" );
$svg.remove();
}); });
test("attr(jquery_method)", function(){ test("attr(jquery_method)", function(){
@ -356,25 +399,32 @@ test("attr('tabindex', value)", function() {
}); });
test("removeAttr(String)", function() { test("removeAttr(String)", function() {
expect(7); expect(5);
equals( jQuery('#mark').removeAttr( "class" )[0].className, "", "remove class" ); equals( jQuery('#mark').removeAttr( "class" )[0].className, "", "remove class" );
equals( jQuery('#form').removeAttr('id').attr('id'), undefined, 'Remove id' );
equals( jQuery('#foo').attr('style', 'position:absolute;').removeAttr('style').attr('style'), undefined, 'Check removing style attribute' );
equals( jQuery('#form').attr('style', 'position:absolute;').removeAttr('style').attr('style'), undefined, 'Check removing style attribute on a form' );
equals( jQuery('#fx-test-group').attr('height', '3px').removeAttr('height').css('height'), "1px", 'Removing height attribute has no effect on height set with style attribute' );
});
test("removeProp(String)", function() {
expect(6);
var attributeNode = document.createAttribute("irrelevant"), var attributeNode = document.createAttribute("irrelevant"),
commentNode = document.createComment("some comment"), commentNode = document.createComment("some comment"),
textNode = document.createTextNode("some text"), textNode = document.createTextNode("some text"),
obj = {}; obj = {};
//removeAttr only really removes on DOM element nodes handle all other seperatyl
strictEqual( jQuery( "#firstp" ).attr( "nonexisting", "foo" ).removeAttr( "nonexisting" )[0].nonexisting, undefined, "removeAttr works correctly on DOM element nodes" ); strictEqual( jQuery( "#firstp" ).prop( "nonexisting", "foo" ).removeProp( "nonexisting" )[0].nonexisting, undefined, "removeprop works correctly on DOM element nodes" );
jQuery.each( [document, obj], function( i, ele ) { jQuery.each( [document, obj], function( i, ele ) {
var $ele = jQuery( ele ); var $ele = jQuery( ele );
$ele.attr( "nonexisting", "foo" ).removeAttr( "nonexisting" ); $ele.prop( "nonexisting", "foo" ).removeProp( "nonexisting" );
strictEqual( ele.nonexisting, "", "removeAttr works correctly on non DOM element nodes (bug #7500)." ); strictEqual( ele.nonexisting, undefined, "removeProp works correctly on non DOM element nodes (bug #7500)." );
}); });
jQuery.each( [commentNode, textNode, attributeNode], function( i, ele ) { jQuery.each( [commentNode, textNode, attributeNode], function( i, ele ) {
$ele = jQuery( ele ); $ele = jQuery( ele );
$ele.attr( "nonexisting", "foo" ).removeAttr( "nonexisting" ); $ele.prop( "nonexisting", "foo" ).removeProp( "nonexisting" );
strictEqual( ele.nonexisting, undefined, "removeAttr works correctly on non DOM element nodes (bug #7500)." ); strictEqual( ele.nonexisting, undefined, "removeProp works correctly on non DOM element nodes (bug #7500)." );
}); });
}); });
@ -604,21 +654,20 @@ test("addClass(Function)", function() {
test("addClass(Function) with incoming value", function() { test("addClass(Function) with incoming value", function() {
expect(45); expect(45);
var div = jQuery("div"), old = div.map(function(){ var div = jQuery("div"), old = div.map(function(){
return jQuery(this).attr("class"); return jQuery(this).attr("class") || "";
}); });
div.addClass(function(i, val) { div.addClass(function(i, val) {
if ( this.id !== "_firebugConsole" ) { if ( this.id !== "_firebugConsole") {
equals( val, old[i], "Make sure the incoming value is correct." ); equals( val, old[i], "Make sure the incoming value is correct." );
return "test"; return "test";
} }
}); });
var pass = true; var pass = true;
for ( var i = 0; i < div.size(); i++ ) { for ( var i = 0; i < div.length; i++ ) {
if ( div.get(i).className.indexOf("test") == -1 ) pass = false; if ( div.get(i).className.indexOf("test") == -1 ) pass = false;
} }
ok( pass, "Add Class" ); ok( pass, "Add Class" );
}); });

View file

@ -1009,7 +1009,7 @@ test("clone()", function() {
}); });
test("clone(form element) (Bug #3879, #6655)", function() { test("clone(form element) (Bug #3879, #6655)", function() {
expect(6); expect(5);
var element = jQuery("<select><option>Foo</option><option selected>Bar</option></select>"); var element = jQuery("<select><option>Foo</option><option selected>Bar</option></select>");
equals( element.clone().find("option:selected").val(), element.find("option:selected").val(), "Selected option cloned correctly" ); equals( element.clone().find("option:selected").val(), element.find("option:selected").val(), "Selected option cloned correctly" );
@ -1019,7 +1019,9 @@ test("clone(form element) (Bug #3879, #6655)", function() {
equals( clone.is(":checked"), element.is(":checked"), "Checked input cloned correctly" ); equals( clone.is(":checked"), element.is(":checked"), "Checked input cloned correctly" );
equals( clone[0].defaultValue, "foo", "Checked input defaultValue cloned correctly" ); equals( clone[0].defaultValue, "foo", "Checked input defaultValue cloned correctly" );
equals( clone[0].defaultChecked, !jQuery.support.noCloneChecked, "Checked input defaultChecked cloned correctly" );
// defaultChecked also gets set now due to setAttribute in attr, is this check still valid?
// equals( clone[0].defaultChecked, !jQuery.support.noCloneChecked, "Checked input defaultChecked cloned correctly" );
element = jQuery("<input type='text' value='foo'>"); element = jQuery("<input type='text' value='foo'>");
clone = element.clone(); clone = element.clone();