将窗口大小调整事件静默绑定到jQuery插件而不保留对目标元素的引用的最佳方法

时间:2012-10-17 15:18:54

标签: javascript jquery jquery-plugins

我正在寻找最佳实践建议。

我正在编写一个小的jQuery插件来管理元素的水平滚动。

我需要该插件所针对的所有dom元素来更新窗口大小。

事实是,我的网站是一个完整的ajax'app',所以当我删除DOM元素时,我需要它们,所以内存不会泄漏。

但是我找不到一种方法来绑定resize事件而不保留对DOM节点的引用。

编辑:

实际上我需要调整大小处理程序来在'调用'时获取插件目标元素,因为我不想在内存中保留对这些元素的任何引用,因为我可能会调用.html('' )他们的父母 ......

我没有粘贴所有代码,只是一个空shell。我已经有一个取消绑定处理程序的destroy方法。但是我正在动态地生成,删除和追加html节点,并且插件所针对的元素将以静默方式删除。

Kevin B表示我可以覆盖jQuery .remove方法来处理处理程序,但是必须加载jQuery UI才能工作。我不想要那个..

这是我尝试过的(尝试评论过):

(function($) {
    // SOLUTION 2 (see below too)
    // Not good either coz elements are not removed until resize is triggered
    /*
    var hScrolls = $([]);
    $(window).bind('resize.hScroll',function(){
        if(!hScrolls.length) return;
        hScrolls.each(function(){
            if($(this).data('hScroll')) $(this).hScroll('updateDimensions');
            else hScrolls = hScrolls.not($(this));
        });
    });
    */
    // END SOLUTION 2

    // SOLUTION 3 (not implemented but I think I'm on the right path)
    $(window).bind('resize.hScroll',function(){
        // need to get hScroll'ed elements via selector...
        $('[data-hScroll]').hScroll('updateDimensions');
        // I don't know how....
    });
    // END SOLUTION 3
    var methods = {
        init : function(options) {
            var settings = $.extend( {
                defaults: true
            }, options);

            return this.each(function() {
                var $this = $(this),
                    data = $this.data('hScroll');
                if (!data) {
                    $this.data('hScroll', {
                        target: $this
                    });
                    // SOLUTION 1
                    // This is not good: it keeps a reference to $this when I remove it...
                    /*
                    $(window).bind('resize.hScroll', function(){
                        $this.hScroll('updateDimensions');
                    });
                    */
                    // END SOLUTION 1
                    $this.hScroll('updateDimensions');

                    // SOLUTION 2 (see above too)

                    hScrolls = hScrolls.add(this);

                }
            });
        },
        updateDimensions: function(){
            var hScroll = this.data('hScroll');
            // do stuff with hScroll.target
        }
    }

    $.fn.hScroll = function(method) {
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if ( typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
        } else {
            $.error('Method ' + method + ' does not exist on jQuery.hScroll');
        }
    };
})(jQuery);​

提前全部谢谢!

3 个答案:

答案 0 :(得分:2)

每次执行删除或替换元素的操作时,jQuery都会调用cleanData(是的,即使您使用parent.html(""))。您可以通过扩展它并使其触发目标元素上的事件来利用它。

// This is taken from https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.widget.js 10/17/2012
if (!$.widget) { // prevent duplicating if jQuery ui widget is already included
    var _cleanData = $.cleanData;
    $.cleanData = function( elems ) {
        for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
            try {
                $( elem ).triggerHandler( "remove" );
                // http://bugs.jquery.com/ticket/8235
            } catch( e ) {}
        }
        _cleanData( elems );
    };
}

现在,您可以在设置插件时绑定到remove事件并让它运行您的destroy方法。

$(elem).bind("remove",methods.destroy)

答案 1 :(得分:1)

您可以使用班级名称并转发调整大小事件:

$.fn.hScroll = function(method) {
    this
      .addClass('hScroll')
      .data('method', arguments)
};

var methods['alert_text'] = function(config){
  alert( config + " " + $(this).text() );
}

$(window).bind('resize.hScroll',function(){
  $(".hScroll").each(function(){
     var method_config = $(this).data('method');
     var method = method_config.shift();
     // Forward the resize event with all resize event arguments:
     methods[method].apply(this, method_config);
   })
})

// Register a resize event for all a.test elements:
$("a.test").hScroll('alert_text', "hey");
// Would alert "hey you" for <a class="test">you</a> on every resize

更新

如果您更改dom并想要保留选择器,您可以试试这个:

var elements = [];
 $.fn.hScroll = function(method) {
     elements.push({'selector' : this.selector, 'arguments' : arguments });
 };

var methods['alert_text'] = function(config){
  alert( config + " " + $(this).text() );
}

$(window).bind('resize.hScroll',function(){
  $.each(elements,function(i, element){
    $(element.selector).each(function(){
       var method_config = element.arguments;
       var method = method_config.shift();
       // Forward the resize event with all resize event arguments:
       methods[method].apply(this, method_config);
     })
   })
})

// Register a resize event for all a.test elements:
$("a.test").hScroll('alert_text', "hey");
$(document.body).html("<a class='test'>you</a>");
// Would alert "hey you" for every window resize

答案 2 :(得分:0)

您应该在扩展程序中绑定滚动事件。此外,您还需要在扩展中添加“destroy”方法。在从DOM中删除元素之前,您需要调用此方法。在detroy方法中,您将要取消绑定resize事件。

使这项工作的一个重要事项是您可以引用绑定到resize事件的每个处理程序方法。或者,您可以在删除元素时取消绑定所有调整大小事件,然后将滚动事件重新绑定到需要它的其余元素。