From ff39bed57a05ca060033187b8aecebafab357f78 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Scott=20Gonz=C3=A1lez?= Date: Thu, 14 Jun 2012 12:33:16 -0400 Subject: [PATCH] Widget: Added _off() for removing event handlers. Fixes #7795 - Widget: _on and _off. --- tests/unit/widget/widget_core.js | 77 ++++++++++++++++++++++++++++++++ ui/jquery.ui.accordion.js | 3 +- ui/jquery.ui.button.js | 32 ++++++------- ui/jquery.ui.dialog.js | 6 +-- ui/jquery.ui.menu.js | 4 +- ui/jquery.ui.tabs.js | 6 +-- ui/jquery.ui.tooltip.js | 2 +- ui/jquery.ui.widget.js | 5 +++ 8 files changed, 108 insertions(+), 27 deletions(-) diff --git a/tests/unit/widget/widget_core.js b/tests/unit/widget/widget_core.js index 24ae9c567..abfb0d9de 100644 --- a/tests/unit/widget/widget_core.js +++ b/tests/unit/widget/widget_core.js @@ -767,6 +767,83 @@ test( "_on() to common element", function() { $( document ).trigger( "customevent" ); }); +test( "_off() - single event", function() { + expect( 3 ); + + $.widget( "ui.testWidget", {} ); + var shouldTriggerWidget, shouldTriggerOther, + element = $( "#widget" ), + widget = element.testWidget().data( "testWidget" ); + widget._on( element, { foo: function() { + ok( shouldTriggerWidget, "foo called from _on" ); + }}); + element.bind( "foo", function() { + ok( shouldTriggerOther, "foo called from bind" ); + }); + shouldTriggerWidget = true; + shouldTriggerOther = true; + element.trigger( "foo" ); + shouldTriggerWidget = false; + widget._off( element, "foo" ); + element.trigger( "foo" ); +}); + +test( "_off() - multiple events", function() { + expect( 6 ); + + $.widget( "ui.testWidget", {} ); + var shouldTriggerWidget, shouldTriggerOther, + element = $( "#widget" ), + widget = element.testWidget().data( "testWidget" ); + widget._on( element, { + foo: function() { + ok( shouldTriggerWidget, "foo called from _on" ); + }, + bar: function() { + ok( shouldTriggerWidget, "bar called from _on" ); + } + }); + element.bind( "foo bar", function( event ) { + ok( shouldTriggerOther, event.type + " called from bind" ); + }); + shouldTriggerWidget = true; + shouldTriggerOther = true; + element.trigger( "foo" ); + element.trigger( "bar" ); + shouldTriggerWidget = false; + widget._off( element, "foo bar" ); + element.trigger( "foo" ); + element.trigger( "bar" ); +}); + +test( "_off() - all events", function() { + expect( 6 ); + + $.widget( "ui.testWidget", {} ); + var shouldTriggerWidget, shouldTriggerOther, + element = $( "#widget" ), + widget = element.testWidget().data( "testWidget" ); + widget._on( element, { + foo: function() { + ok( shouldTriggerWidget, "foo called from _on" ); + }, + bar: function() { + ok( shouldTriggerWidget, "bar called from _on" ); + } + }); + element.bind( "foo bar", function( event ) { + ok( shouldTriggerOther, event.type + " called from bind" ); + }); + shouldTriggerWidget = true; + shouldTriggerOther = true; + element.trigger( "foo" ); + element.trigger( "bar" ); + shouldTriggerWidget = false; + widget._off( element ); + element.trigger( "foo" ); + element.trigger( "bar" ); +}); + test( "._hoverable()", function() { $.widget( "ui.testWidget", { _create: function() { diff --git a/ui/jquery.ui.accordion.js b/ui/jquery.ui.accordion.js index 4cfc265a3..e8a10b746 100644 --- a/ui/jquery.ui.accordion.js +++ b/ui/jquery.ui.accordion.js @@ -219,8 +219,7 @@ $.widget( "ui.accordion", { if ( key === "event" ) { if ( this.options.event ) { - this.headers.unbind( - this.options.event.split( " " ).join( ".accordion " ) + ".accordion" ); + this._off( this.headers, this.options.event ); } this._setupEvents( value ); } diff --git a/ui/jquery.ui.button.js b/ui/jquery.ui.button.js index 810191775..0c72e9c4b 100644 --- a/ui/jquery.ui.button.js +++ b/ui/jquery.ui.button.js @@ -54,8 +54,8 @@ $.widget( "ui.button", { }, _create: function() { this.element.closest( "form" ) - .unbind( "reset.button" ) - .bind( "reset.button", formResetHandler ); + .unbind( "reset" + this.eventNamespace ) + .bind( "reset" + this.eventNamespace, formResetHandler ); if ( typeof this.options.disabled !== "boolean" ) { this.options.disabled = !!this.element.prop( "disabled" ); @@ -79,7 +79,7 @@ $.widget( "ui.button", { this.buttonElement .addClass( baseClasses ) .attr( "role", "button" ) - .bind( "mouseenter.button", function() { + .bind( "mouseenter" + this.eventNamespace, function() { if ( options.disabled ) { return; } @@ -88,13 +88,13 @@ $.widget( "ui.button", { $( this ).addClass( "ui-state-active" ); } }) - .bind( "mouseleave.button", function() { + .bind( "mouseleave" + this.eventNamespace, function() { if ( options.disabled ) { return; } $( this ).removeClass( hoverClass ); }) - .bind( "click.button", function( event ) { + .bind( "click" + this.eventNamespace, function( event ) { if ( options.disabled ) { event.preventDefault(); event.stopImmediatePropagation(); @@ -102,16 +102,16 @@ $.widget( "ui.button", { }); this.element - .bind( "focus.button", function() { + .bind( "focus" + this.eventNamespace, function() { // no need to check disabled, focus won't be triggered anyway that.buttonElement.addClass( focusClass ); }) - .bind( "blur.button", function() { + .bind( "blur" + this.eventNamespace, function() { that.buttonElement.removeClass( focusClass ); }); if ( toggleButton ) { - this.element.bind( "change.button", function() { + this.element.bind( "change" + this.eventNamespace, function() { if ( clickDragged ) { return; } @@ -121,7 +121,7 @@ $.widget( "ui.button", { // prevents issue where button state changes but checkbox/radio checked state // does not in Firefox (see ticket #6970) this.buttonElement - .bind( "mousedown.button", function( event ) { + .bind( "mousedown" + this.eventNamespace, function( event ) { if ( options.disabled ) { return; } @@ -129,7 +129,7 @@ $.widget( "ui.button", { startXPos = event.pageX; startYPos = event.pageY; }) - .bind( "mouseup.button", function( event ) { + .bind( "mouseup" + this.eventNamespace, function( event ) { if ( options.disabled ) { return; } @@ -140,7 +140,7 @@ $.widget( "ui.button", { } if ( this.type === "checkbox" ) { - this.buttonElement.bind( "click.button", function() { + this.buttonElement.bind( "click" + this.eventNamespace, function() { if ( options.disabled || clickDragged ) { return false; } @@ -148,7 +148,7 @@ $.widget( "ui.button", { that.buttonElement.attr( "aria-pressed", that.element[0].checked ); }); } else if ( this.type === "radio" ) { - this.buttonElement.bind( "click.button", function() { + this.buttonElement.bind( "click" + this.eventNamespace, function() { if ( options.disabled || clickDragged ) { return false; } @@ -166,7 +166,7 @@ $.widget( "ui.button", { }); } else { this.buttonElement - .bind( "mousedown.button", function() { + .bind( "mousedown" + this.eventNamespace, function() { if ( options.disabled ) { return false; } @@ -176,13 +176,13 @@ $.widget( "ui.button", { lastActive = null; }); }) - .bind( "mouseup.button", function() { + .bind( "mouseup" + this.eventNamespace, function() { if ( options.disabled ) { return false; } $( this ).removeClass( "ui-state-active" ); }) - .bind( "keydown.button", function(event) { + .bind( "keydown" + this.eventNamespace, function(event) { if ( options.disabled ) { return false; } @@ -190,7 +190,7 @@ $.widget( "ui.button", { $( this ).addClass( "ui-state-active" ); } }) - .bind( "keyup.button", function() { + .bind( "keyup" + this.eventNamespace, function() { $( this ).removeClass( "ui-state-active" ); }); diff --git a/ui/jquery.ui.dialog.js b/ui/jquery.ui.dialog.js index 4ef8a20cb..a22eef806 100644 --- a/ui/jquery.ui.dialog.js +++ b/ui/jquery.ui.dialog.js @@ -224,7 +224,7 @@ $.widget("ui.dialog", { if ( this.overlay ) { this.overlay.destroy(); } - this.uiDialog.unbind( "keypress.ui-dialog" ); + this._off( this.uiDialog, "keypress" ); if ( this.options.hide ) { this.uiDialog.hide( this.options.hide, function() { @@ -310,7 +310,7 @@ $.widget("ui.dialog", { // prevent tabbing out of modal dialogs if ( options.modal ) { - uiDialog.bind( "keydown.ui-dialog", function( event ) { + this._on( uiDialog, { keydown: function( event ) { if ( event.keyCode !== $.ui.keyCode.TAB ) { return; } @@ -326,7 +326,7 @@ $.widget("ui.dialog", { last.focus( 1 ); return false; } - }); + }}); } // set focus to the first tabbable element in the content area or the first button diff --git a/ui/jquery.ui.menu.js b/ui/jquery.ui.menu.js index eb0be494c..616389ba2 100644 --- a/ui/jquery.ui.menu.js +++ b/ui/jquery.ui.menu.js @@ -44,7 +44,7 @@ $.widget( "ui.menu", { }) // need to catch all clicks on disabled menu // not possible through _on - .bind( "click.menu", $.proxy(function( event ) { + .bind( "click" + this.eventNamespace, $.proxy(function( event ) { if ( this.options.disabled ) { event.preventDefault(); } @@ -168,7 +168,7 @@ $.widget( "ui.menu", { this.element.find( ".ui-menu-divider" ).removeClass( "ui-menu-divider ui-widget-content" ); // unbind currentEventTarget click event handler - $( currentEventTarget ).unbind( "click.menu" ); + this._off( $( currentEventTarget ), "click" ); }, _keydown: function( event ) { diff --git a/ui/jquery.ui.tabs.js b/ui/jquery.ui.tabs.js index 21736641f..aed4ba8fb 100644 --- a/ui/jquery.ui.tabs.js +++ b/ui/jquery.ui.tabs.js @@ -58,7 +58,7 @@ $.widget( "ui.tabs", { .addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" ) .toggleClass( "ui-tabs-collapsible", options.collapsible ) // Prevent users from focusing disabled tabs via click - .delegate( ".ui-tabs-nav > li", "mousedown.tabs", function( event ) { + .delegate( ".ui-tabs-nav > li", "mousedown" + this.eventNamespace, function( event ) { if ( $( this ).is( ".ui-state-disabled" ) ) { event.preventDefault(); } @@ -69,7 +69,7 @@ $.widget( "ui.tabs", { // We don't have to worry about focusing the previously focused // element since clicking on a non-focusable element should focus // the body anyway. - .delegate( ".ui-tabs-anchor", "focus.tabs", function() { + .delegate( ".ui-tabs-anchor", "focus" + this.eventNamespace, function() { if ( $( this ).closest( "li" ).is( ".ui-state-disabled" ) ) { this.blur(); } @@ -480,7 +480,7 @@ $.widget( "ui.tabs", { }); } - this.anchors.add( this.tabs ).add( this.panels ).unbind( ".tabs" ); + this._off( this.anchors.add( this.tabs ).add( this.panels ) ); this._on( this.anchors, events ); this._on( this.tabs, { keydown: "_tabKeydown" } ); this._on( this.panels, { keydown: "_panelKeydown" } ); diff --git a/ui/jquery.ui.tooltip.js b/ui/jquery.ui.tooltip.js index 6a7aab706..c4bdbb0d7 100644 --- a/ui/jquery.ui.tooltip.js +++ b/ui/jquery.ui.tooltip.js @@ -233,7 +233,7 @@ $.widget( "ui.tooltip", { }); target.removeData( "tooltip-open" ); - target.unbind( "mouseleave.tooltip focusout.tooltip keyup.tooltip" ); + this._off( target, "mouseleave focusout keyup" ); this.closing = true; this._trigger( "close", event, { tooltip: tooltip } ); diff --git a/ui/jquery.ui.widget.js b/ui/jquery.ui.widget.js index 22049cbb8..5366beefe 100644 --- a/ui/jquery.ui.widget.js +++ b/ui/jquery.ui.widget.js @@ -387,6 +387,11 @@ $.Widget.prototype = { }); }, + _off: function( element, eventName ) { + eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace; + element.unbind( eventName ).undelegate( eventName ); + }, + _delay: function( handler, delay ) { function handlerProxy() { return ( typeof handler === "string" ? instance[ handler ] : handler ) -- 2.39.5