// Fallback to prop when attributes are not supported
if ( typeof elem.getAttribute === "undefined" ) {
return jQuery.prop( elem, name, value );
// Attribute hooks are determined by the lowercase version
// Grab necessary hook if one is defined
if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
hooks = jQuery.attrHooks[ name.toLowerCase() ] ||
( jQuery.expr.match.bool.test( name ) ? boolHook : undefined );
if ( value !== undefined ) {
jQuery.removeAttr( elem, name );
if ( hooks && "set" in hooks &&
( ret = hooks.set( elem, value, name ) ) !== undefined ) {
elem.setAttribute( name, value + "" );
if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
ret = jQuery.find.attr( elem, name );
// Non-existent attributes return null, we normalize to undefined
return ret == null ? undefined : ret;
set: function( elem, value ) {
if ( !support.radioValue && value === "radio" &&
nodeName( elem, "input" ) ) {
elem.setAttribute( "type", value );
removeAttr: function( elem, value ) {
// Attribute names can contain non-HTML whitespace characters
// https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
attrNames = value && value.match( rnothtmlwhite );
if ( attrNames && elem.nodeType === 1 ) {
while ( ( name = attrNames[ i++ ] ) ) {
elem.removeAttribute( name );
// Hooks for boolean attributes
set: function( elem, value, name ) {
// Remove boolean attributes when set to false
jQuery.removeAttr( elem, name );
elem.setAttribute( name, name );
jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name ) {
var getter = attrHandle[ name ] || jQuery.find.attr;
attrHandle[ name ] = function( elem, name, isXML ) {
lowercaseName = name.toLowerCase();
// Avoid an infinite loop by temporarily removing this function from the getter
handle = attrHandle[ lowercaseName ];
attrHandle[ lowercaseName ] = ret;
ret = getter( elem, name, isXML ) != null ?
attrHandle[ lowercaseName ] = handle;
var rfocusable = /^(?:input|select|textarea|button)$/i,
rclickable = /^(?:a|area)$/i;
prop: function( name, value ) {
return access( this, jQuery.prop, name, value, arguments.length > 1 );
removeProp: function( name ) {
return this.each( function() {
delete this[ jQuery.propFix[ name ] || name ];
prop: function( elem, name, value ) {
// Don't get/set properties on text, comment and attribute nodes
if ( nType === 3 || nType === 8 || nType === 2 ) {
if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
// Fix name and attach hooks
name = jQuery.propFix[ name ] || name;
hooks = jQuery.propHooks[ name ];
if ( value !== undefined ) {
if ( hooks && "set" in hooks &&
( ret = hooks.set( elem, value, name ) ) !== undefined ) {
return ( elem[ name ] = value );
if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
// Support: IE <=9 - 11 only
// elem.tabIndex doesn't always return the
// correct value when it hasn't been explicitly set
// https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
// Use proper attribute retrieval(#12072)
var tabindex = jQuery.find.attr( elem, "tabindex" );
return parseInt( tabindex, 10 );
rfocusable.test( elem.nodeName ) ||
rclickable.test( elem.nodeName ) &&
// Accessing the selectedIndex property
// forces the browser to respect setting selected
// The getter ensures a default option is selected
// eslint rule "no-unused-expressions" is disabled for this code
// since it considers such accessions noop
if ( !support.optSelected ) {
jQuery.propHooks.selected = {
/* eslint no-unused-expressions: "off" */
var parent = elem.parentNode;
if ( parent && parent.parentNode ) {
parent.parentNode.selectedIndex;
/* eslint no-unused-expressions: "off" */
var parent = elem.parentNode;
if ( parent.parentNode ) {
parent.parentNode.selectedIndex;
jQuery.propFix[ this.toLowerCase() ] = this;
// Strip and collapse whitespace according to HTML spec
// https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace
function stripAndCollapse( value ) {
var tokens = value.match( rnothtmlwhite ) || [];
return tokens.join( " " );
function getClass( elem ) {
return elem.getAttribute && elem.getAttribute( "class" ) || "";
function classesToArray( value ) {
if ( Array.isArray( value ) ) {
if ( typeof value === "string" ) {
return value.match( rnothtmlwhite ) || [];
addClass: function( value ) {
var classes, elem, cur, curValue, clazz, j, finalValue,
if ( isFunction( value ) ) {
return this.each( function( j ) {
jQuery( this ).addClass( value.call( this, j, getClass( this ) ) );
classes = classesToArray( value );
while ( ( elem = this[ i++ ] ) ) {
curValue = getClass( elem );
cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
while ( ( clazz = classes[ j++ ] ) ) {
if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
// Only assign if different to avoid unneeded rendering.
finalValue = stripAndCollapse( cur );
if ( curValue !== finalValue ) {
elem.setAttribute( "class", finalValue );
removeClass: function( value ) {
var classes, elem, cur, curValue, clazz, j, finalValue,
if ( isFunction( value ) ) {
return this.each( function( j ) {
jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) );
if ( !arguments.length ) {
return this.attr( "class", "" );
classes = classesToArray( value );
while ( ( elem = this[ i++ ] ) ) {
curValue = getClass( elem );
// This expression is here for better compressibility (see addClass)
cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
while ( ( clazz = classes[ j++ ] ) ) {
// Remove *all* instances
while ( cur.indexOf( " " + clazz + " " ) > -1 ) {
cur = cur.replace( " " + clazz + " ", " " );
// Only assign if different to avoid unneeded rendering.
finalValue = stripAndCollapse( cur );
if ( curValue !== finalValue ) {
elem.setAttribute( "class", finalValue );
toggleClass: function( value, stateVal ) {
isValidValue = type === "string" || Array.isArray( value );
if ( typeof stateVal === "boolean" && isValidValue ) {
return stateVal ? this.addClass( value ) : this.removeClass( value );
if ( isFunction( value ) ) {
return this.each( function( i ) {
jQuery( this ).toggleClass(
value.call( this, i, getClass( this ), stateVal ),
return this.each( function() {
var className, i, self, classNames;
// Toggle individual class names
classNames = classesToArray( value );
while ( ( className = classNames[ i++ ] ) ) {
// Check each className given, space separated list
if ( self.hasClass( className ) ) {
self.removeClass( className );
self.addClass( className );
// Toggle whole class name
} else if ( value === undefined || type === "boolean" ) {
className = getClass( this );
// Store className if set
dataPriv.set( this, "__className__", className );
// If the element has a class name or if we're passed `false`,
// then remove the whole classname (if there was one, the above saved it).
// Otherwise bring back whatever was previously saved (if anything),
// falling back to the empty string if nothing was stored.
if ( this.setAttribute ) {
this.setAttribute( "class",
className || value === false ?
dataPriv.get( this, "__className__" ) || ""
hasClass: function( selector ) {
className = " " + selector + " ";
while ( ( elem = this[ i++ ] ) ) {
if ( elem.nodeType === 1 &&
( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) {
var hooks, ret, valueIsFunction,
if ( !arguments.length ) {
hooks = jQuery.valHooks[ elem.type ] ||
jQuery.valHooks[ elem.nodeName.toLowerCase() ];
( ret = hooks.get( elem, "value" ) ) !== undefined
// Handle most common string cases
if ( typeof ret === "string" ) {
return ret.replace( rreturn, "" );
// Handle cases where value is null/undef or number
return ret == null ? "" : ret;
valueIsFunction = isFunction( value );
return this.each( function( i ) {
if ( this.nodeType !== 1 ) {
val = value.call( this, i, jQuery( this ).val() );
// Treat null/undefined as ""; convert numbers to string
} else if ( typeof val === "number" ) {
} else if ( Array.isArray( val ) ) {
val = jQuery.map( val, function( value ) {
return value == null ? "" : value + "";
hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
// If set returns undefined, fall back to normal setting
if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) {