Bug Tracker

Changeset 5990

Show
Ignore:
Timestamp:
12/22/08 04:59:34 (6 months ago)
Author:
jeresig
Message:

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");

Location:
trunk/jquery
Files:
2 modified

Legend:

Unmodified
Added
Removed
  • trunk/jquery/src/event.js

    r5988 r5990  
    5454        jQuery.each(types.split(/\s+/), function(index, type) { 
    5555            // Namespaced event handlers 
    56             var parts = type.split("."); 
    57             type = parts.shift(); 
    58             handler.type = parts.sort().join("."); 
     56            var namespaces = type.split("."); 
     57            type = namespaces.shift(); 
     58            handler.type = namespaces.slice().sort().join("."); 
    5959 
    6060            // Get the current list of functions bound to this event 
    6161            var handlers = events[type]; 
     62             
     63            if ( jQuery.event.specialAll[type] ) 
     64                jQuery.event.specialAll[type].setup.call(elem, data, namespaces); 
    6265 
    6366            // Init the event handler queue 
     
    6871                // Only use addEventListener/attachEvent if the special 
    6972                // events handler returns false 
    70                 if ( !jQuery.event.special[type] || jQuery.event.special[type].setup.call(elem,data) === false ) { 
     73                if ( !jQuery.event.special[type] || jQuery.event.special[type].setup.call(elem, data, namespaces) === false ) { 
    7174                    // Bind the global event handler to the element 
    7275                    if (elem.addEventListener) 
     
    115118                jQuery.each(types.split(/\s+/), function(index, type){ 
    116119                    // Namespaced event handlers 
    117                     var namespace = type.split("."); 
    118                     type = namespace.shift(); 
    119                     namespace = RegExp("(^|\\.)" + namespace.sort().join(".*\\.") + "(\\.|$)"); 
     120                    var namespaces = type.split("."); 
     121                    type = namespaces.shift(); 
     122                    var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)"); 
    120123 
    121124                    if ( events[type] ) { 
     
    130133                                if ( namespace.test(events[type][handler].type) ) 
    131134                                    delete events[type][handler]; 
     135                                     
     136                        if ( jQuery.event.specialAll[type] ) 
     137                            jQuery.event.specialAll[type].teardown.call(elem, namespaces); 
    132138 
    133139                        // remove generic event handler if no more handlers exist 
    134140                        for ( ret in events[type] ) break; 
    135141                        if ( !ret ) { 
    136                             if ( !jQuery.event.special[type] || jQuery.event.special[type].teardown.call(elem) === false ) { 
     142                            if ( !jQuery.event.special[type] || jQuery.event.special[type].teardown.call(elem, namespaces) === false ) { 
    137143                                if (elem.removeEventListener) 
    138144                                    elem.removeEventListener(type, jQuery.data(elem, "handle"), false); 
     
    158164    }, 
    159165 
    160     trigger: function(type, data, elem, donative, extra) { 
     166    trigger: function(type, data, elem, donative, extra, dohandlers) { 
    161167        // Clone the incoming data, if any 
    162168        data = jQuery.makeArray(data); 
     
    204210                data[0].exclusive = true; 
    205211 
    206             // Trigger the event, it is assumed that "handle" is a function 
    207             var handle = jQuery.data(elem, "handle"); 
    208             if ( handle ) 
    209                 val = handle.apply( elem, data ); 
    210  
    211             // Handle triggering native .onfoo handlers (and on links since we don't call .click() for links) 
    212             if ( (!fn || (jQuery.nodeName(elem, 'a') && type == "click")) && elem["on"+type] && elem["on"+type].apply( elem, data ) === false ) 
    213                 val = false; 
     212            if ( dohandlers !== false ) { 
     213                // Trigger the event, it is assumed that "handle" is a function 
     214                var handle = jQuery.data(elem, "handle"); 
     215                if ( handle ) 
     216                    val = handle.apply( elem, data ); 
     217 
     218                // Handle triggering native .onfoo handlers (and on links since we don't call .click() for links) 
     219                if ( (!fn || (jQuery.nodeName(elem, 'a') && type == "click")) && elem["on"+type] && elem["on"+type].apply( elem, data ) === false ) 
     220                    val = false; 
     221            } 
    214222 
    215223            if ( donative !== false && val !== false ) { 
     
    249257    handle: function(event) { 
    250258        // returned undefined or false 
    251         var val, ret, namespace, all, handlers; 
     259        var val, ret, all, handlers; 
    252260 
    253261        event = arguments[0] = jQuery.event.fix( event || window.event ); 
    254262 
    255263        // Namespaced event handlers 
    256         namespace = event.type.split("."); 
    257         event.type = namespace.shift(); 
     264        var namespaces = event.type.split("."); 
     265        event.type = namespaces.shift(); 
    258266 
    259267        // Cache this now, all = true means, any handler 
    260         all = !namespace.length && !event.exclusive; 
     268        all = !namespaces.length && !event.exclusive; 
    261269         
    262         namespace = RegExp("(^|\\.)" + namespace.sort().join(".*\\.") + "(\\.|$)"); 
     270        var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)"); 
    263271 
    264272        handlers = ( jQuery.data(this, "events") || {} )[event.type]; 
     
    381389            setup: bindReady, 
    382390            teardown: function() {} 
     391        } 
     392    }, 
     393     
     394    specialAll: { 
     395        live: { 
     396            setup: function( selector, namespaces ){ 
     397                jQuery.event.add( this, namespaces[0], liveHandler ); 
     398            }, 
     399            teardown:  function( namespaces ){ 
     400                if ( namespaces.length ) { 
     401                    var remove = 0, name = RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)"); 
     402                     
     403                    jQuery.each( (jQuery.data(this, "events").live || {}), function(){ 
     404                        if ( name.test(this.type) ) 
     405                            remove++; 
     406                    }); 
     407                     
     408                    if ( remove <= 1 ) 
     409                        jQuery.event.remove( this, namespaces[0], liveHandler ); 
     410                } 
     411            } 
    383412        } 
    384413    } 
     
    494523 
    495524        return this; 
     525    }, 
     526     
     527    live: function( type, fn ){ 
     528        jQuery(document).bind( liveConvert(type, this.selector), this.selector, fn ); 
     529        return this; 
     530    }, 
     531     
     532    die: function( type, fn ){ 
     533        jQuery(document).unbind( liveConvert(type, this.selector), fn ); 
     534        return this; 
    496535    } 
    497536}); 
     537 
     538function liveHandler( event ){ 
     539    var check = RegExp("(^|\\.)" + event.type + "(\\.|$)"); 
     540    jQuery.each(jQuery.data(this, "events").live || [], function(i, fn){ 
     541        if ( check.test(fn.type) ) { 
     542            var elem = jQuery(event.target).closest(fn.data)[0]; 
     543            if ( elem ) 
     544                jQuery.event.trigger( event.type, fn.data, elem, false, fn, false ); 
     545        } 
     546    }); 
     547} 
     548 
     549function liveConvert(type, selector){ 
     550    return ["live", type, selector.replace(/\./g, "_")].join("."); 
     551} 
    498552 
    499553jQuery.extend({ 
  • trunk/jquery/test/unit/event.js

    r5989 r5990  
    419419    ok( !data, "Unbinding one function from toggle unbinds them all"); 
    420420}); 
     421 
     422test(".live()/.die()", function() { 
     423    expect(28); 
     424 
     425    var submit = 0, div = 0, livea = 0, liveb = 0; 
     426 
     427    jQuery("div").live("submit", function(){ submit++; return false; }); 
     428    jQuery("div").live("click", function(){ div++; }); 
     429    jQuery("div#nothiddendiv").live("click", function(){ livea++; }); 
     430    jQuery("div#nothiddendivchild").live("click", function(){ liveb++; }); 
     431 
     432    // Nothing should trigger on the body 
     433    jQuery("body").trigger("click"); 
     434    equals( submit, 0, "Click on body" ); 
     435    equals( div, 0, "Click on body" ); 
     436    equals( livea, 0, "Click on body" ); 
     437    equals( liveb, 0, "Click on body" ); 
     438 
     439    // This should trigger two events 
     440    jQuery("div#nothiddendiv").trigger("click"); 
     441    equals( submit, 0, "Click on div" ); 
     442    equals( div, 1, "Click on div" ); 
     443    equals( livea, 1, "Click on div" ); 
     444    equals( liveb, 0, "Click on div" ); 
     445 
     446    // This should trigger three events (w/ bubbling) 
     447    jQuery("div#nothiddendivchild").trigger("click"); 
     448    equals( submit, 0, "Click on inner div" ); 
     449    equals( div, 2, "Click on inner div" ); 
     450    equals( livea, 2, "Click on inner div" ); 
     451    equals( liveb, 1, "Click on inner div" ); 
     452 
     453    // This should trigger one submit 
     454    jQuery("div#nothiddendivchild").trigger("submit"); 
     455    equals( submit, 1, "Submit on div" ); 
     456    equals( div, 2, "Submit on div" ); 
     457    equals( livea, 2, "Submit on div" ); 
     458    equals( liveb, 1, "Submit on div" ); 
     459 
     460    // Make sure no other events were removed in the process 
     461    jQuery("div#nothiddendivchild").trigger("click"); 
     462    equals( submit, 1, "die Click on inner div" ); 
     463    equals( div, 3, "die Click on inner div" ); 
     464    equals( livea, 3, "die Click on inner div" ); 
     465    equals( liveb, 2, "die Click on inner div" ); 
     466 
     467    // Now make sure that the removal works 
     468    jQuery("div#nothiddendivchild").die("click"); 
     469    jQuery("div#nothiddendivchild").trigger("click"); 
     470    equals( submit, 1, "die Click on inner div" ); 
     471    equals( div, 4, "die Click on inner div" ); 
     472    equals( livea, 4, "die Click on inner div" ); 
     473    equals( liveb, 2, "die Click on inner div" ); 
     474 
     475    // Make sure that the click wasn't removed too early 
     476    jQuery("div#nothiddendiv").trigger("click"); 
     477    equals( submit, 1, "die Click on inner div" ); 
     478    equals( div, 5, "die Click on inner div" ); 
     479    equals( livea, 5, "die Click on inner div" ); 
     480    equals( liveb, 2, "die Click on inner div" ); 
     481 
     482    jQuery("div#nothiddendiv").die("click"); 
     483    jQuery("div").die("click"); 
     484    jQuery("div").die("submit"); 
     485}); 
     486 
    421487/* 
    422488test("jQuery(function($) {})", function() {