Make sure that jQuery.data( elem ) always returns a data object, fixes #5971. Improve the performance of .bind() and .unbind(), fixes #5972.
This commit is contained in:
parent
942f8f7f75
commit
eed69eccc5
3 changed files with 92 additions and 58 deletions
17
src/data.js
17
src/data.js
|
@ -1,5 +1,4 @@
|
|||
var expando = "jQuery" + now(), uuid = 0, windowData = {};
|
||||
var emptyObject = {};
|
||||
|
||||
jQuery.extend({
|
||||
cache: {},
|
||||
|
@ -25,8 +24,7 @@ jQuery.extend({
|
|||
|
||||
var id = elem[ expando ], cache = jQuery.cache, thisCache;
|
||||
|
||||
// Handle the case where there's no name immediately
|
||||
if ( !name && !id ) {
|
||||
if ( !id && typeof name === "string" && data === undefined ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -40,17 +38,16 @@ jQuery.extend({
|
|||
if ( typeof name === "object" ) {
|
||||
elem[ expando ] = id;
|
||||
thisCache = cache[ id ] = jQuery.extend(true, {}, name);
|
||||
} else if ( cache[ id ] ) {
|
||||
thisCache = cache[ id ];
|
||||
} else if ( typeof data === "undefined" ) {
|
||||
thisCache = emptyObject;
|
||||
} else {
|
||||
thisCache = cache[ id ] = {};
|
||||
|
||||
} else if ( !cache[ id ] ) {
|
||||
elem[ expando ] = id;
|
||||
cache[ id ] = {};
|
||||
}
|
||||
|
||||
thisCache = cache[ id ];
|
||||
|
||||
// Prevent overriding the named cache with undefined values
|
||||
if ( data !== undefined ) {
|
||||
elem[ expando ] = id;
|
||||
thisCache[ name ] = data;
|
||||
}
|
||||
|
||||
|
|
120
src/event.js
120
src/event.js
|
@ -42,8 +42,16 @@ jQuery.event = {
|
|||
}
|
||||
|
||||
// Init the element's event structure
|
||||
var events = jQuery.data( elem, "events" ) || jQuery.data( elem, "events", {} ),
|
||||
handle = jQuery.data( elem, "handle" ), eventHandle;
|
||||
var elemData = jQuery.data( elem );
|
||||
|
||||
// If no elemData is found then we must be trying to bind to one of the
|
||||
// banned noData elements
|
||||
if ( !elemData ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var events = elemData.events || (elemData.events = {}),
|
||||
handle = elemData.handle, eventHandle;
|
||||
|
||||
if ( !handle ) {
|
||||
eventHandle = function() {
|
||||
|
@ -54,13 +62,7 @@ jQuery.event = {
|
|||
undefined;
|
||||
};
|
||||
|
||||
handle = jQuery.data( elem, "handle", eventHandle );
|
||||
}
|
||||
|
||||
// If no handle is found then we must be trying to bind to one of the
|
||||
// banned noData elements
|
||||
if ( !handle ) {
|
||||
return;
|
||||
handle = elemData.handle = eventHandle;
|
||||
}
|
||||
|
||||
// Add elem as a property of the handle function
|
||||
|
@ -70,15 +72,11 @@ jQuery.event = {
|
|||
|
||||
// Handle multiple events separated by a space
|
||||
// jQuery(...).bind("mouseover mouseout", fn);
|
||||
types = types.split( /\s+/ );
|
||||
types = types.split(" ");
|
||||
|
||||
var type, i = 0;
|
||||
var type, i = 0, namespaces;
|
||||
|
||||
while ( (type = types[ i++ ]) ) {
|
||||
// Namespaced event handlers
|
||||
var namespaces = type.split(".");
|
||||
type = namespaces.shift();
|
||||
|
||||
if ( i > 1 ) {
|
||||
handler = jQuery.proxy( handler );
|
||||
|
||||
|
@ -87,7 +85,16 @@ jQuery.event = {
|
|||
}
|
||||
}
|
||||
|
||||
handler.type = namespaces.slice(0).sort().join(".");
|
||||
// Namespaced event handlers
|
||||
if ( type.indexOf(".") > -1 ) {
|
||||
namespaces = type.split(".");
|
||||
type = namespaces.shift();
|
||||
handler.type = namespaces.slice(0).sort().join(".");
|
||||
|
||||
} else {
|
||||
namespaces = [];
|
||||
handler.type = "";
|
||||
}
|
||||
|
||||
// Get the current list of functions bound to this event
|
||||
var handlers = events[ type ],
|
||||
|
@ -104,6 +111,7 @@ jQuery.event = {
|
|||
// Bind the global event handler to the element
|
||||
if ( elem.addEventListener ) {
|
||||
elem.addEventListener( type, handle, false );
|
||||
|
||||
} else if ( elem.attachEvent ) {
|
||||
elem.attachEvent( "on" + type, handle );
|
||||
}
|
||||
|
@ -140,7 +148,13 @@ jQuery.event = {
|
|||
return;
|
||||
}
|
||||
|
||||
var events = jQuery.data( elem, "events" ), ret, type, fn;
|
||||
var elemData = jQuery.data( elem );
|
||||
|
||||
if ( !elemData ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var events = elemData.events, ret, type, fn;
|
||||
|
||||
if ( events ) {
|
||||
// Unbind all events for the element
|
||||
|
@ -148,6 +162,7 @@ jQuery.event = {
|
|||
for ( type in events ) {
|
||||
this.remove( elem, type + (types || "") );
|
||||
}
|
||||
|
||||
} else {
|
||||
// types is actually an event object here
|
||||
if ( types.type ) {
|
||||
|
@ -157,16 +172,24 @@ jQuery.event = {
|
|||
|
||||
// Handle multiple events separated by a space
|
||||
// jQuery(...).unbind("mouseover mouseout", fn);
|
||||
types = types.split(/\s+/);
|
||||
var i = 0;
|
||||
types = types.split(" ");
|
||||
|
||||
var i = 0, all, namespaces, namespace;
|
||||
|
||||
while ( (type = types[ i++ ]) ) {
|
||||
// Namespaced event handlers
|
||||
var namespaces = type.split(".");
|
||||
type = namespaces.shift();
|
||||
var all = !namespaces.length,
|
||||
cleaned = jQuery.map( namespaces.slice(0).sort(), fcleanup ),
|
||||
namespace = new RegExp("(^|\\.)" + cleaned.join("\\.(?:.*\\.)?") + "(\\.|$)"),
|
||||
special = this.special[ type ] || {};
|
||||
all = type.indexOf(".") < 0;
|
||||
namespaces = null;
|
||||
|
||||
if ( !all ) {
|
||||
// Namespaced event handlers
|
||||
namespaces = type.split(".");
|
||||
type = namespaces.shift();
|
||||
|
||||
namespace = new RegExp("(^|\\.)" +
|
||||
jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)")
|
||||
}
|
||||
|
||||
var special = this.special[ type ] || {};
|
||||
|
||||
if ( events[ type ] ) {
|
||||
// remove the given handler for the given type
|
||||
|
@ -185,21 +208,23 @@ jQuery.event = {
|
|||
}
|
||||
|
||||
if ( special.remove ) {
|
||||
special.remove.call( elem, namespaces, fn);
|
||||
special.remove.call( elem, namespaces || [], fn);
|
||||
}
|
||||
|
||||
// remove generic event handler if no more handlers exist
|
||||
for ( ret in events[ type ] ) {
|
||||
|
||||
break;
|
||||
}
|
||||
if ( !ret ) {
|
||||
if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
|
||||
if ( elem.removeEventListener ) {
|
||||
elem.removeEventListener( type, jQuery.data( elem, "handle" ), false );
|
||||
elem.removeEventListener( type, elemData.handle, false );
|
||||
} else if ( elem.detachEvent ) {
|
||||
elem.detachEvent( "on" + type, jQuery.data( elem, "handle" ) );
|
||||
elem.detachEvent( "on" + type, elemData.handle );
|
||||
}
|
||||
}
|
||||
|
||||
ret = null;
|
||||
delete events[ type ];
|
||||
}
|
||||
|
@ -211,13 +236,19 @@ jQuery.event = {
|
|||
for ( ret in events ) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ( !ret ) {
|
||||
var handle = jQuery.data( elem, "handle" );
|
||||
var handle = elemData.handle;
|
||||
if ( handle ) {
|
||||
handle.elem = null;
|
||||
}
|
||||
jQuery.removeData( elem, "events" );
|
||||
jQuery.removeData( elem, "handle" );
|
||||
|
||||
delete elemData.events;
|
||||
delete elemData.handle;
|
||||
|
||||
if ( jQuery.isEmptyObject( elemData ) ) {
|
||||
jQuery.removeData( elem );
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -796,11 +827,16 @@ jQuery.each(["bind", "one"], function( i, name ) {
|
|||
return fn.apply( this, arguments );
|
||||
}) : fn;
|
||||
|
||||
return type === "unload" && name !== "one" ?
|
||||
this.one( type, data, fn ) :
|
||||
this.each(function() {
|
||||
jQuery.event.add( this, type, handler, data );
|
||||
});
|
||||
if ( type === "unload" && name !== "one" ) {
|
||||
this.one( type, data, fn );
|
||||
|
||||
} else {
|
||||
for ( var i = 0, l = this.length; i < l; i++ ) {
|
||||
jQuery.event.add( this[i], type, handler, data );
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -811,12 +847,14 @@ jQuery.fn.extend({
|
|||
for ( var key in type ) {
|
||||
this.unbind(key, type[key]);
|
||||
}
|
||||
return this;
|
||||
|
||||
} else {
|
||||
for ( var i = 0, l = this.length; i < l; i++ ) {
|
||||
jQuery.event.remove( this[i], type, fn );
|
||||
}
|
||||
}
|
||||
|
||||
return this.each(function() {
|
||||
jQuery.event.remove( this, type, fn );
|
||||
});
|
||||
return this;
|
||||
},
|
||||
trigger: function( type, data ) {
|
||||
return this.each(function() {
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
module("data");
|
||||
|
||||
test("expando", function(){
|
||||
expect(7);
|
||||
expect(6);
|
||||
|
||||
equals("expando" in jQuery, true, "jQuery is exposing the expando");
|
||||
|
||||
var obj = {};
|
||||
jQuery.data(obj);
|
||||
equals( jQuery.expando in obj, false, "jQuery.data did not add an expando to the object" );
|
||||
|
||||
jQuery.data(obj, true);
|
||||
equals( jQuery.expando in obj, false, "jQuery.data did not add an expando to the object" );
|
||||
|
||||
equals( jQuery.expando in obj, true, "jQuery.data adds an expando to the object" );
|
||||
|
||||
obj = {};
|
||||
jQuery.data(obj, 'test');
|
||||
equals( jQuery.expando in obj, false, "jQuery.data did not add an expando to the object" );
|
||||
|
||||
|
||||
obj = {};
|
||||
jQuery.data(obj, "foo", "bar");
|
||||
equals( jQuery.expando in obj, true, "jQuery.data added an expando to the object" );
|
||||
|
||||
|
|
Loading…
Reference in a new issue