使用事件侦听器发送数据

时间:2010-10-12 01:06:09

标签: javascript events

我有一个困境:我想用事件监听器发送一些数据,但也能够删除监听器。这是标准的封闭方法......

var fn = function(e){method(e,data)};
el.addEventListener('click',fn,false);
el.removeEventListener('click',fn,false);

你可以删除这个事件,就好了。但是说,该元素已从DOM中删除?然后,你将留下fn函数。删除了几千个DOM元素后,会导致内存泄漏。

我考虑过附加一个DOMNodeRemoved事件处理程序,它会删除任何遗留的函数/数据以及删除的节点。但显然,该事件不是跨浏览器兼容的。

我提出的唯一其他选择是修改元素的DOM。考虑...

el.MyEventData = function(e){method(e,data)};
el.addEventListener('click',el.MyEventData,false);
el.removeEventListener('click',el.MyEventData,false);

在这种情况下修改DOM是否可接受?当您尝试添加多个事件侦听器时,该解决方案的唯一粘性部分。假设我们创建了一个自定义函数来解析事件的添加/删除......

function makeEvent(fn,data){
    var dataFn = function(e){fn(e,data)};
    //create object to hold all added events
    el.myEvents = {};
    //make ID for this specific event
    var eventID = ranString();
    //add the event to the events object
    el.myEvents[eventID] = [fn,dataFn];
    //finally add the listener
    el.addEventListener('click',dataFn,false);
}
function destroyEvent(fn){
    //find all fn references
    for(var id in el.myEvents){
        if (el.myEvents[id][0] == fn){
            el.removeEventListener('click',el.myEvents[id][1],false);
            el.myEvents[id] = null;
        }
    }
}

它仍像以前一样修改DOM,当然也不是一个非常优雅的解决方案。有没有人知道任何替代方法,更好的传递数据的方法?

编辑:所以,我已经研究了一些jQuery的数据/事件脚本。我不完全理解代码,所以如果有人澄清,那将会有所帮助。但似乎他们使用类似的方法,通过制作某种类型的el.cache属性来保存事件数据。

6 个答案:

答案 0 :(得分:3)

考虑到您使用addEventListener这是不是问题,因为所有现代垃圾收集器都可以处理此类情况。事件侦听器的问题仅存在于IE的实现中(7 - )。

Test - 10 000 addEventListener并删除元素(请参阅Windows任务管理器)

  

当DOM对象包含引用时   到一个JavaScript对象(这样的事件   处理功能),当它   JavaScript对象包含一个引用   到那个DOM对象,然后循环   结构形成。这是   本身一个问题。在这个时候   没有其他参考   那么DOM对象和事件处理程序   垃圾收集器(自动收集)   内存资源管理器)将回收   他们两个,允许他们的空间   重新分配。 JavaScript垃圾   收集者了解周期和   并没有被他们困惑。

     

http://www.crockford.com/javascript/memory/leak.html

答案 1 :(得分:1)

你考虑过.delegate()吗?

答案 2 :(得分:1)

根据您的 jQuery问题

每个jQ对象都有data属性。 存储在元素本身内 - 这非常重要。 jQ对所有元素使用通用存储 - jQuery.cache。所以当你向元素添加任何东西时:

$('#myEl').data('someValue', 1);

jQ执行以下操作:

jQuery.cache[elementUniqId]['someValue'] = 1;

因此元素不包含其数据对象。它只有一个uniq id,允许它访问全局存储中的数据记录。 (elementUniqId是自动生成的)

jQ事件也存储在元素数据中:

$('#myEl').click(function() {  first listener });
$('#myEl').mouseenter(function() {  one more listener });
$('#myEl').click(function() {  anotheer listener });

将被存储:

jQuery.cache[elementUniqId]['events'] = {
    click: [function() { first listener }, function() { anotheer listene }],
    mouseenter: [function() { one more listener }]
};

它允许jQ存储附加到每个事件的所有侦听器的执行顺序。后来,当你删除dom元素时,使用jQuery - .remove(),jQuery循环遍历jQuery.cache[elementUniqId]['events']并从元素中删除每个侦听器,并在删除元素缓存记录之后。它允许jQ防止内存泄漏

答案 3 :(得分:0)

可能的解决方案可能会将您带到另一个方向:将该函数添加为元素的内联兄弟。

<span id='element12345'>Test</span><script 
  type='text/javascript'>function fn12345() { /* ... */ }</script>

然后,当您删除所需的所有事件侦听器时,您还可以删除正在使用的元素的“nextSibling()”。

答案 4 :(得分:0)

这样的设置怎么样? (使用IE语法,因为这是我现在可用的)

<div id="clickbox" style="width: 100px; height: 100px; border: 1px solid orange;">
    click here to test</div>
<input id="Button1" type="button" value="clear handler" />
<script>

    var data = "derp1";
    var el = document.getElementById('clickbox');
    var btn = document.getElementById('Button1');

    // methods
    var method = function (e, dat2) { alert(dat2); };
    var fn = function (e) { method(e, data) };
    var remover = null;

    // attachment
    el.attachEvent('onclick', fn, false);

    (function (id, handler) {

        // handler variable is local but points to the right function
        remover = function (e) {

            if (document.getElementById(id)) {
                // remove the original listener (handler is fn)
                document.getElementById(id).detachEvent('onclick', handler, false);
                alert('detached');
            }

            // remove last reference to the original method
            handler = null;
            alert('method nulled');

            // clean up the remover method
            e.srcElement.detachEvent('onclick', remover);
            remover = null;
        };

        btn.attachEvent('onclick', remover);

    })('clickbox', fn);

    // clear the original variable but the method still exists as an event listener
    fn = null;

    // you should be able to remove the div element and any references to it 
    // without leaving any stray bits around.

    setTimeout( function() {
                     var d = document.getElementById('clickbox');
                     if (d){ d.parentNode.removeChild(d) ; }
     } , 6000 );

    el = null;
    btn = null;

</script>

我假设您不希望在添加后立即删除侦听器,而是希望能够在以后删除它。为了处理这个问题,清理例程通过创建一个匿名函数给出了自己的范围,该函数立即以fn作为参数调用。然后,anon函数对fn变量中维护的handler有自己的引用。之后,可以清除fn,并在元素的侦听器列表和匿名函数的范围内存在对原始方法的其余引用。

在匿名函数范围内,函数remover可以访问handler变量,并可以使用它来分离监听器。 remover然后分离并清除自己,因此不应该有任何版本的fn/handler访问。

我现在没有办法验证所有这些,但我认为这是有道理的,应该在现代浏览器中保留。

答案 5 :(得分:0)

为什么不看看这个

使用jQuery将事件绑定到非DOM对象

http://www.bennadel.com/blog/1520-Binding-Events-To-Non-DOM-Objects-With-jQuery.htm