From 89af4c292eaa5fc1c83437ca71085264dcbef34a Mon Sep 17 00:00:00 2001 From: Alexander Schmitz Date: Mon, 12 Sep 2016 10:26:24 -0400 Subject: [PATCH] Widget: Untrack classes elements when they are removed from the DOM Fixes #15043 Closes gh-1744 --- tests/unit/widget/classes.js | 42 +++++++++++++++++++++++++++++++----- ui/widget.js | 13 +++++++++++ 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/tests/unit/widget/classes.js b/tests/unit/widget/classes.js index d355c3a36..28f61f272 100644 --- a/tests/unit/widget/classes.js +++ b/tests/unit/widget/classes.js @@ -15,6 +15,8 @@ QUnit.module( "widget factory classes", { }, _create: function() { this.span = $( "" ) + .add( "" ) + .add( "" ) .appendTo( this.element ); this.element.wrap( "
" ); @@ -68,9 +70,9 @@ function elementHasClasses( widget, method, assert ) { "_" + method + "Class works with ( null, extra" + toggle + " )" ); assert.hasClasses( widget.parent(), "ui-classes-widget ui-theme-widget", "_" + method + "Class works with ( element, null, extra" + toggle + " )" ); - assert.hasClasses( widget.find( "span" ), "ui-classes-span ui-core-span", + assert.hasClasses( widget.find( "span" )[ 0 ], "ui-classes-span ui-core-span", "_" + method + "Class works with ( element, keys, extra" + toggle + " )" ); - assert.hasClasses( widget.find( "span" ), "ui-core-span-null", + assert.hasClasses( widget.find( "span" )[ 0 ], "ui-core-span-null", "_" + method + "Class works with ( element, keys, null" + toggle + " )" ); } function elementLacksClasses( widget, method, assert ) { @@ -84,9 +86,9 @@ function elementLacksClasses( widget, method, assert ) { "_" + method + "Class works with ( null, extra" + toggle + " )" ); assert.lacksClasses( widget.parent(), "ui-classes-widget ui-theme-widget", "_" + method + "Class works with ( element, null, extra" + toggle + " )" ); - assert.lacksClasses( widget.find( "span" ), "ui-classes-span ui-core-span", + assert.lacksClasses( widget.find( "span" )[ 0 ], "ui-classes-span ui-core-span", "_" + method + "Class works with ( element, keys, extra" + toggle + " )" ); - assert.lacksClasses( widget.find( "span" ), "ui-core-span-null", + assert.lacksClasses( widget.find( "span" )[ 0 ], "ui-core-span-null", "_" + method + "Class works with ( element, keys, null" + toggle + " )" ); } @@ -113,7 +115,7 @@ QUnit.test( ".option() - classes setter", function( assert ) { "Setting to empty value leaves structure class" ); assert.lacksClasses( testWidget.element, "ui-theme-element-2", "Setting empty value removes previous value classes" ); - assert.hasClasses( testWidget.span, "ui-classes-span custom-theme-span", + assert.hasClasses( testWidget.span[ 0 ], "ui-classes-span custom-theme-span", "Adding a class to an empty value works as expected" ); assert.hasClasses( testWidget.wrapper, "ui-classes-widget custom-theme-widget", "Appending a class to the current value works as expected" ); @@ -144,4 +146,34 @@ QUnit.test( "._add/_remove/_toggleClass()", function( assert ) { elementLacksClasses( widget, "remove", assert ); } ); +QUnit.test( "Classes elements are untracked as they are removed from the DOM", function( assert ) { + assert.expect( 9 ); + + var widget = $( "#widget" ).classesWidget(); + var instance = widget.classesWidget( "instance" ); + + assert.equal( instance.classesElementLookup[ "ui-classes-span" ].length, 3, + "Widget is tracking 3 ui-classes-span elements" ); + assert.equal( instance.classesElementLookup[ "ui-core-span-null" ].length, 3, + "Widget is tracking 3 ui-core-span-null elements" ); + assert.equal( instance.classesElementLookup[ "ui-core-span" ].length, 3, + "Widget is tracking 3 ui-core-span elements" ); + + widget.find( "span" ).eq( 0 ).remove(); + assert.equal( instance.classesElementLookup[ "ui-classes-span" ].length, 2, + "After removing 1 span from dom 2 ui-classes-span elements are tracked" ); + assert.equal( instance.classesElementLookup[ "ui-core-span-null" ].length, 2, + "After removing 1 span from dom 2 ui-core-span-null elements are tracked" ); + assert.equal( instance.classesElementLookup[ "ui-core-span" ].length, 2, + "After removing 1 span from dom 2 ui-core-span elements are tracked" ); + + widget.find( "span" ).remove(); + assert.equal( instance.classesElementLookup[ "ui-classes-span" ].length, 0, + "No ui-classes-span elements are tracked after removing all spans" ); + assert.equal( instance.classesElementLookup[ "ui-core-span-null" ].length, 0, + "No ui-core-span-null elements are tracked after removing all spans" ); + assert.equal( instance.classesElementLookup[ "ui-core-span" ].length, 0, + "No ui-core-span elements are tracked after removing all spans" ); +} ); + } ); diff --git a/ui/widget.js b/ui/widget.js index 06a3ce88e..a3922675f 100644 --- a/ui/widget.js +++ b/ui/widget.js @@ -514,6 +514,10 @@ $.Widget.prototype = { } } + this._on( options.element, { + "remove": "_untrackClassesElement" + } ); + if ( options.keys ) { processClassString( options.keys.match( /\S+/g ) || [], true ); } @@ -524,6 +528,15 @@ $.Widget.prototype = { return full.join( " " ); }, + _untrackClassesElement: function( event ) { + var that = this; + $.each( that.classesElementLookup, function( key, value ) { + if ( $.inArray( event.target, value ) !== -1 ) { + that.classesElementLookup[ key ] = $( value.not( event.target ).get() ); + } + } ); + }, + _removeClass: function( element, keys, extra ) { return this._toggleClass( element, keys, extra, false ); },