Fixes #7340. Use a single capturing handler to simulate bubbling focusin/focusout...
authorDave Methvin <dave.methvin@gmail.com>
Sat, 5 Mar 2011 02:16:40 +0000 (21:16 -0500)
committerDave Methvin <dave.methvin@gmail.com>
Sat, 5 Mar 2011 02:16:40 +0000 (21:16 -0500)
src/event.js
test/unit/event.js

index f7e0a08..61c8a93 100644 (file)
@@ -70,10 +70,10 @@ jQuery.event = {
                }
 
                if ( !eventHandle ) {
-                       elemData.handle = eventHandle = function() {
+                       elemData.handle = eventHandle = function( e ) {
                                // Handle the second event of a trigger and when
                                // an event is called after a page has unloaded
-                               return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
+                               return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ?
                                        jQuery.event.handle.apply( eventHandle.elem, arguments ) :
                                        undefined;
                        };
@@ -380,7 +380,7 @@ jQuery.event = {
                                                        target[ "on" + targetType ] = null;
                                                }
 
-                                               jQuery.event.triggered = true;
+                                               jQuery.event.triggered = event.type;
                                                target[ targetType ]();
                                        }
 
@@ -391,7 +391,7 @@ jQuery.event = {
                                        target[ "on" + targetType ] = old;
                                }
 
-                               jQuery.event.triggered = false;
+                               jQuery.event.triggered = undefined;
                        }
                }
        },
@@ -868,19 +868,33 @@ function trigger( type, elem, args ) {
 // Create "bubbling" focus and blur events
 if ( document.addEventListener ) {
        jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+       
+               // Attach a single capturing handler while someone wants focusin/focusout
+               var attaches = 0;
+               
                jQuery.event.special[ fix ] = {
                        setup: function() {
-                               this.addEventListener( orig, handler, true );
+                               if ( attaches++ === 0 ) {
+                                       document.addEventListener( orig, handler, true );
+                               }
                        },
                        teardown: function() {
-                               this.removeEventListener( orig, handler, true );
+                               if ( --attaches === 0 ) {
+                                       document.removeEventListener( orig, handler, true );
+                               }
                        }
                };
 
-               function handler( e ) {
-                       e = jQuery.event.fix( e );
+               function handler( donor ) {
+                       // Donor event is always a native one; fix it and switch its type.
+                       // Let focusin/out handler cancel the donor focus/blur event.
+                       var e = jQuery.event.fix( donor );
                        e.type = fix;
-                       return jQuery.event.handle.call( this, e );
+                       e.originalEvent = {};
+                       jQuery.event.trigger( e, null, e.target );
+                       if ( e.isDefaultPrevented() ) {
+                               donor.preventDefault();
+                       }
                }
        });
 }
index b7b2604..d549879 100644 (file)
@@ -1966,6 +1966,31 @@ test("window resize", function() {
        ok( !jQuery._data(window, "__events__"), "Make sure all the events are gone." );
 });
 
+test("focusin bubbles", function() {
+       expect(4);
+       
+       var input = jQuery( '<input type="text" />' ).prependTo( "body" ), 
+               order = 0;
+
+       jQuery( "body" ).bind( "focusin.focusinBubblesTest", function(){
+               equals( 1, order++, "focusin on the body second" );
+       });
+
+       input.bind( "focusin.focusinBubblesTest", function(){
+               equals( 0, order++, "focusin on the element first" );
+       });
+
+       // DOM focus method
+       input[0].focus();
+       // jQuery trigger, which calls DOM focus
+       order = 0;
+       input[0].blur();
+       input.trigger( "focus" );
+
+       input.remove();
+       jQuery( "body" ).unbind( "focusin.focusinBubblesTest" );
+});
+
 /*
 test("jQuery(function($) {})", function() {
        stop();