jquery.makeCollapsible: fix jQuery memory leak
authorMatmaRex <matma.rex@gmail.com>
Sun, 16 Jun 2013 13:10:28 +0000 (15:10 +0200)
committerMatmaRex <matma.rex@gmail.com>
Sun, 16 Jun 2013 13:10:28 +0000 (15:10 +0200)
jQuery saves event data in jQuery.cache and magically clears it when
relevant elements are removed from DOM. However, if an element is
created, has event handlers attached and is never added to the DOM,
it never gets a chance to clear the data, resulting in a memory leak.

Only build the default toggle link when needed to avoid this.

Bug: 49626
Change-Id: I0c92e5c28a66c6a6469e107593dc9b6d3baa8a10

resources/jquery/jquery.makeCollapsible.js

index 2d46bde..2823174 100644 (file)
                }
 
                return this.each( function () {
-                       var $collapsible, collapseText, expandText, $toggle, clickHandler, $defaultToggleLink,
+                       var $collapsible, collapseText, expandText, $toggle, clickHandler, buildDefaultToggleLink,
                                premadeToggleHandler, $toggleLink, $firstItem, collapsibleId, $customTogglers, firstval;
 
                        // Ensure class "mw-collapsible" is present in case .makeCollapsible()
                                opts = $.extend( defaultOpts, options, opts );
                                togglingHandler( $( this ), $collapsible, e, opts );
                        };
-                       $defaultToggleLink =
-                               $( '<a href="#"></a>' )
+                       // Default toggle link. Only build it when needed to avoid jQuery memory leaks (event data).
+                       buildDefaultToggleLink = function () {
+                               return $( '<a href="#"></a>' )
                                        .text( collapseText )
                                        .wrap( '<span class="mw-collapsible-toggle"></span>' )
                                                .parent()
                                                .prepend( '&nbsp;[' )
                                                .append( ']&nbsp;' )
                                                .on( 'click.mw-collapsible', clickHandler );
+                       };
 
                        // Default handler for clicking on premade toggles
                        premadeToggleHandler = function ( e, opts ) {
 
                                        // If theres no toggle link, add it to the last cell
                                        if ( !$toggle.length ) {
-                                               $toggleLink = $defaultToggleLink.prependTo( $firstItem.eq( -1 ) );
+                                               $toggleLink = buildDefaultToggleLink().prependTo( $firstItem.eq( -1 ) );
                                        } else {
                                                clickHandler = premadeToggleHandler;
                                                $toggleLink = $toggle.on( 'click.mw-collapsible', clickHandler );
                                                if ( firstval === undefined || !firstval || firstval === '-1' || firstval === -1 ) {
                                                        $firstItem.attr( 'value', '1' );
                                                }
-                                               $toggleLink = $defaultToggleLink;
+                                               $toggleLink = buildDefaultToggleLink();
                                                $toggleLink.wrap( '<li class="mw-collapsible-toggle-li"></li>' ).parent().prependTo( $collapsible );
                                        } else {
                                                clickHandler = premadeToggleHandler;
 
                                        // If theres no toggle link, add it
                                        if ( !$toggle.length ) {
-                                               $toggleLink = $defaultToggleLink.prependTo( $collapsible );
+                                               $toggleLink = buildDefaultToggleLink().prependTo( $collapsible );
                                        } else {
                                                clickHandler = premadeToggleHandler;
                                                $toggleLink = $toggle.on( 'click.mw-collapsible', clickHandler );