Added a new liveQuery/event delegation hybrid method: .live and .die. Easily adapts event delegation to the jQuery style. $("div").live("click", fn); $("div > #foo").live("submit", fn); $("div").die("click");
This commit is contained in:
parent
67ded9a36a
commit
c03a280155
2 changed files with 141 additions and 21 deletions
82
src/event.js
82
src/event.js
|
@ -53,13 +53,16 @@ jQuery.event = {
|
||||||
// jQuery(...).bind("mouseover mouseout", fn);
|
// jQuery(...).bind("mouseover mouseout", fn);
|
||||||
jQuery.each(types.split(/\s+/), function(index, type) {
|
jQuery.each(types.split(/\s+/), function(index, type) {
|
||||||
// Namespaced event handlers
|
// Namespaced event handlers
|
||||||
var parts = type.split(".");
|
var namespaces = type.split(".");
|
||||||
type = parts.shift();
|
type = namespaces.shift();
|
||||||
handler.type = parts.sort().join(".");
|
handler.type = namespaces.slice().sort().join(".");
|
||||||
|
|
||||||
// Get the current list of functions bound to this event
|
// Get the current list of functions bound to this event
|
||||||
var handlers = events[type];
|
var handlers = events[type];
|
||||||
|
|
||||||
|
if ( jQuery.event.specialAll[type] )
|
||||||
|
jQuery.event.specialAll[type].setup.call(elem, data, namespaces);
|
||||||
|
|
||||||
// Init the event handler queue
|
// Init the event handler queue
|
||||||
if (!handlers) {
|
if (!handlers) {
|
||||||
handlers = events[type] = {};
|
handlers = events[type] = {};
|
||||||
|
@ -67,7 +70,7 @@ jQuery.event = {
|
||||||
// Check for a special event handler
|
// Check for a special event handler
|
||||||
// Only use addEventListener/attachEvent if the special
|
// Only use addEventListener/attachEvent if the special
|
||||||
// events handler returns false
|
// events handler returns false
|
||||||
if ( !jQuery.event.special[type] || jQuery.event.special[type].setup.call(elem,data) === false ) {
|
if ( !jQuery.event.special[type] || jQuery.event.special[type].setup.call(elem, data, namespaces) === false ) {
|
||||||
// Bind the global event handler to the element
|
// Bind the global event handler to the element
|
||||||
if (elem.addEventListener)
|
if (elem.addEventListener)
|
||||||
elem.addEventListener(type, handle, false);
|
elem.addEventListener(type, handle, false);
|
||||||
|
@ -114,9 +117,9 @@ jQuery.event = {
|
||||||
// jQuery(...).unbind("mouseover mouseout", fn);
|
// jQuery(...).unbind("mouseover mouseout", fn);
|
||||||
jQuery.each(types.split(/\s+/), function(index, type){
|
jQuery.each(types.split(/\s+/), function(index, type){
|
||||||
// Namespaced event handlers
|
// Namespaced event handlers
|
||||||
var namespace = type.split(".");
|
var namespaces = type.split(".");
|
||||||
type = namespace.shift();
|
type = namespaces.shift();
|
||||||
namespace = RegExp("(^|\\.)" + namespace.sort().join(".*\\.") + "(\\.|$)");
|
var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");
|
||||||
|
|
||||||
if ( events[type] ) {
|
if ( events[type] ) {
|
||||||
// remove the given handler for the given type
|
// remove the given handler for the given type
|
||||||
|
@ -130,10 +133,13 @@ jQuery.event = {
|
||||||
if ( namespace.test(events[type][handler].type) )
|
if ( namespace.test(events[type][handler].type) )
|
||||||
delete events[type][handler];
|
delete events[type][handler];
|
||||||
|
|
||||||
|
if ( jQuery.event.specialAll[type] )
|
||||||
|
jQuery.event.specialAll[type].teardown.call(elem, namespaces);
|
||||||
|
|
||||||
// remove generic event handler if no more handlers exist
|
// remove generic event handler if no more handlers exist
|
||||||
for ( ret in events[type] ) break;
|
for ( ret in events[type] ) break;
|
||||||
if ( !ret ) {
|
if ( !ret ) {
|
||||||
if ( !jQuery.event.special[type] || jQuery.event.special[type].teardown.call(elem) === false ) {
|
if ( !jQuery.event.special[type] || jQuery.event.special[type].teardown.call(elem, namespaces) === false ) {
|
||||||
if (elem.removeEventListener)
|
if (elem.removeEventListener)
|
||||||
elem.removeEventListener(type, jQuery.data(elem, "handle"), false);
|
elem.removeEventListener(type, jQuery.data(elem, "handle"), false);
|
||||||
else if (elem.detachEvent)
|
else if (elem.detachEvent)
|
||||||
|
@ -157,7 +163,7 @@ jQuery.event = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
trigger: function(type, data, elem, donative, extra) {
|
trigger: function(type, data, elem, donative, extra, dohandlers) {
|
||||||
// Clone the incoming data, if any
|
// Clone the incoming data, if any
|
||||||
data = jQuery.makeArray(data);
|
data = jQuery.makeArray(data);
|
||||||
|
|
||||||
|
@ -203,6 +209,7 @@ jQuery.event = {
|
||||||
if ( exclusive )
|
if ( exclusive )
|
||||||
data[0].exclusive = true;
|
data[0].exclusive = true;
|
||||||
|
|
||||||
|
if ( dohandlers !== false ) {
|
||||||
// Trigger the event, it is assumed that "handle" is a function
|
// Trigger the event, it is assumed that "handle" is a function
|
||||||
var handle = jQuery.data(elem, "handle");
|
var handle = jQuery.data(elem, "handle");
|
||||||
if ( handle )
|
if ( handle )
|
||||||
|
@ -211,6 +218,7 @@ jQuery.event = {
|
||||||
// Handle triggering native .onfoo handlers (and on links since we don't call .click() for links)
|
// Handle triggering native .onfoo handlers (and on links since we don't call .click() for links)
|
||||||
if ( (!fn || (jQuery.nodeName(elem, 'a') && type == "click")) && elem["on"+type] && elem["on"+type].apply( elem, data ) === false )
|
if ( (!fn || (jQuery.nodeName(elem, 'a') && type == "click")) && elem["on"+type] && elem["on"+type].apply( elem, data ) === false )
|
||||||
val = false;
|
val = false;
|
||||||
|
}
|
||||||
|
|
||||||
if ( donative !== false && val !== false ) {
|
if ( donative !== false && val !== false ) {
|
||||||
var parent = elem.parentNode || elem.ownerDocument;
|
var parent = elem.parentNode || elem.ownerDocument;
|
||||||
|
@ -248,18 +256,18 @@ jQuery.event = {
|
||||||
|
|
||||||
handle: function(event) {
|
handle: function(event) {
|
||||||
// returned undefined or false
|
// returned undefined or false
|
||||||
var val, ret, namespace, all, handlers;
|
var val, ret, all, handlers;
|
||||||
|
|
||||||
event = arguments[0] = jQuery.event.fix( event || window.event );
|
event = arguments[0] = jQuery.event.fix( event || window.event );
|
||||||
|
|
||||||
// Namespaced event handlers
|
// Namespaced event handlers
|
||||||
namespace = event.type.split(".");
|
var namespaces = event.type.split(".");
|
||||||
event.type = namespace.shift();
|
event.type = namespaces.shift();
|
||||||
|
|
||||||
// Cache this now, all = true means, any handler
|
// Cache this now, all = true means, any handler
|
||||||
all = !namespace.length && !event.exclusive;
|
all = !namespaces.length && !event.exclusive;
|
||||||
|
|
||||||
namespace = RegExp("(^|\\.)" + namespace.sort().join(".*\\.") + "(\\.|$)");
|
var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)");
|
||||||
|
|
||||||
handlers = ( jQuery.data(this, "events") || {} )[event.type];
|
handlers = ( jQuery.data(this, "events") || {} )[event.type];
|
||||||
|
|
||||||
|
@ -381,6 +389,27 @@ jQuery.event = {
|
||||||
setup: bindReady,
|
setup: bindReady,
|
||||||
teardown: function() {}
|
teardown: function() {}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
specialAll: {
|
||||||
|
live: {
|
||||||
|
setup: function( selector, namespaces ){
|
||||||
|
jQuery.event.add( this, namespaces[0], liveHandler );
|
||||||
|
},
|
||||||
|
teardown: function( namespaces ){
|
||||||
|
if ( namespaces.length ) {
|
||||||
|
var remove = 0, name = RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)");
|
||||||
|
|
||||||
|
jQuery.each( (jQuery.data(this, "events").live || {}), function(){
|
||||||
|
if ( name.test(this.type) )
|
||||||
|
remove++;
|
||||||
|
});
|
||||||
|
|
||||||
|
if ( remove <= 1 )
|
||||||
|
jQuery.event.remove( this, namespaces[0], liveHandler );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -493,9 +522,34 @@ jQuery.fn.extend({
|
||||||
jQuery.readyList.push( function() { return fn.call(this, jQuery); } );
|
jQuery.readyList.push( function() { return fn.call(this, jQuery); } );
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
live: function( type, fn ){
|
||||||
|
jQuery(document).bind( liveConvert(type, this.selector), this.selector, fn );
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
die: function( type, fn ){
|
||||||
|
jQuery(document).unbind( liveConvert(type, this.selector), fn );
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function liveHandler( event ){
|
||||||
|
var check = RegExp("(^|\\.)" + event.type + "(\\.|$)");
|
||||||
|
jQuery.each(jQuery.data(this, "events").live || [], function(i, fn){
|
||||||
|
if ( check.test(fn.type) ) {
|
||||||
|
var elem = jQuery(event.target).closest(fn.data)[0];
|
||||||
|
if ( elem )
|
||||||
|
jQuery.event.trigger( event.type, fn.data, elem, false, fn, false );
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function liveConvert(type, selector){
|
||||||
|
return ["live", type, selector.replace(/\./g, "_")].join(".");
|
||||||
|
}
|
||||||
|
|
||||||
jQuery.extend({
|
jQuery.extend({
|
||||||
isReady: false,
|
isReady: false,
|
||||||
readyList: [],
|
readyList: [],
|
||||||
|
|
|
@ -418,6 +418,72 @@ test("toggle(Function, Function, ...)", function() {
|
||||||
var data = jQuery.data( $div[0], 'events' );
|
var data = jQuery.data( $div[0], 'events' );
|
||||||
ok( !data, "Unbinding one function from toggle unbinds them all");
|
ok( !data, "Unbinding one function from toggle unbinds them all");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test(".live()/.die()", function() {
|
||||||
|
expect(28);
|
||||||
|
|
||||||
|
var submit = 0, div = 0, livea = 0, liveb = 0;
|
||||||
|
|
||||||
|
jQuery("div").live("submit", function(){ submit++; return false; });
|
||||||
|
jQuery("div").live("click", function(){ div++; });
|
||||||
|
jQuery("div#nothiddendiv").live("click", function(){ livea++; });
|
||||||
|
jQuery("div#nothiddendivchild").live("click", function(){ liveb++; });
|
||||||
|
|
||||||
|
// Nothing should trigger on the body
|
||||||
|
jQuery("body").trigger("click");
|
||||||
|
equals( submit, 0, "Click on body" );
|
||||||
|
equals( div, 0, "Click on body" );
|
||||||
|
equals( livea, 0, "Click on body" );
|
||||||
|
equals( liveb, 0, "Click on body" );
|
||||||
|
|
||||||
|
// This should trigger two events
|
||||||
|
jQuery("div#nothiddendiv").trigger("click");
|
||||||
|
equals( submit, 0, "Click on div" );
|
||||||
|
equals( div, 1, "Click on div" );
|
||||||
|
equals( livea, 1, "Click on div" );
|
||||||
|
equals( liveb, 0, "Click on div" );
|
||||||
|
|
||||||
|
// This should trigger three events (w/ bubbling)
|
||||||
|
jQuery("div#nothiddendivchild").trigger("click");
|
||||||
|
equals( submit, 0, "Click on inner div" );
|
||||||
|
equals( div, 2, "Click on inner div" );
|
||||||
|
equals( livea, 2, "Click on inner div" );
|
||||||
|
equals( liveb, 1, "Click on inner div" );
|
||||||
|
|
||||||
|
// This should trigger one submit
|
||||||
|
jQuery("div#nothiddendivchild").trigger("submit");
|
||||||
|
equals( submit, 1, "Submit on div" );
|
||||||
|
equals( div, 2, "Submit on div" );
|
||||||
|
equals( livea, 2, "Submit on div" );
|
||||||
|
equals( liveb, 1, "Submit on div" );
|
||||||
|
|
||||||
|
// Make sure no other events were removed in the process
|
||||||
|
jQuery("div#nothiddendivchild").trigger("click");
|
||||||
|
equals( submit, 1, "die Click on inner div" );
|
||||||
|
equals( div, 3, "die Click on inner div" );
|
||||||
|
equals( livea, 3, "die Click on inner div" );
|
||||||
|
equals( liveb, 2, "die Click on inner div" );
|
||||||
|
|
||||||
|
// Now make sure that the removal works
|
||||||
|
jQuery("div#nothiddendivchild").die("click");
|
||||||
|
jQuery("div#nothiddendivchild").trigger("click");
|
||||||
|
equals( submit, 1, "die Click on inner div" );
|
||||||
|
equals( div, 4, "die Click on inner div" );
|
||||||
|
equals( livea, 4, "die Click on inner div" );
|
||||||
|
equals( liveb, 2, "die Click on inner div" );
|
||||||
|
|
||||||
|
// Make sure that the click wasn't removed too early
|
||||||
|
jQuery("div#nothiddendiv").trigger("click");
|
||||||
|
equals( submit, 1, "die Click on inner div" );
|
||||||
|
equals( div, 5, "die Click on inner div" );
|
||||||
|
equals( livea, 5, "die Click on inner div" );
|
||||||
|
equals( liveb, 2, "die Click on inner div" );
|
||||||
|
|
||||||
|
jQuery("div#nothiddendiv").die("click");
|
||||||
|
jQuery("div").die("click");
|
||||||
|
jQuery("div").die("submit");
|
||||||
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
test("jQuery(function($) {})", function() {
|
test("jQuery(function($) {})", function() {
|
||||||
stop();
|
stop();
|
||||||
|
|
Loading…
Add table
Reference in a new issue