Skip to content
Permalink
Browse files

Make sure that jQuery.data( elem ) always returns a data object, fixe…

…s #5971. Improve the performance of .bind() and .unbind(), fixes #5972.
  • Loading branch information
jeresig committed Jan 28, 2010
1 parent 942f8f7 commit eed69eccc54d010889b5d8495320538d7ceb4e51
Showing with 92 additions and 58 deletions.
  1. +7 −10 src/data.js
  2. +79 −41 src/event.js
  3. +6 −7 test/unit/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;
}

@@ -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,14 +148,21 @@ 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
if ( types === undefined || (typeof types === "string" && types.charAt(0) === ".") ) {
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" );

0 comments on commit eed69ec

Please sign in to comment.
You can’t perform that action at this time.