Отправка данных с помощью прослушивателя событий - PullRequest
5 голосов
/ 12 октября 2010

У меня затруднительное положение: я хочу отправить некоторые данные с помощью прослушивателя событий, но также могу удалить прослушиватель. Вот стандартный подход к закрытию ...

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 ]

3 голосов
/ 18 октября 2010

Учитывая, что вы используете addEventListener, это не проблема , поскольку все современные сборщики мусора могут позаботиться о таких ситуациях. Проблема с прослушивателями событий существует только в реализации IE (7 -).

Тест - 10 000 addEventListener и удаление элемента (см. Диспетчер задач Windows)

Когда объект DOM содержит ссылку к объекту JavaScript (такое событие функция обработки), и когда это JavaScript-объект содержит ссылку к этому объекту DOM, то циклический структура сформирована. Это не в сама проблема . В такое время как нет никаких других ссылок на Объект DOM и обработчик событий, затем сборщик мусора (автоматический менеджер ресурсов памяти) исправит они оба, позволяя их пространство быть перераспределены. JavaScript мусор коллекционер понимает о циклах и не смущает их.

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

1 голос
/ 20 октября 2010

По вашему jQuery вопросу :

Каждый объект jQ имеет свойство data. Он не хранится внутри самого элемента - это очень важно. jQ использует общее хранилище для всех элементов - jQuery.cache. Поэтому, когда вы добавляете что-либо к элементу, как это:

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

jQ выполняет следующие действия:

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

Таким образом, элемент не содержит своего объекта данных. Он имеет только уникальный идентификатор, который позволяет ему получить доступ к записи данных в глобальном хранилище. (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 предотвращать утечки памяти

1 голос
/ 18 октября 2010

Рассматривали ли вы .delegate ()?

0 голосов
/ 20 октября 2010

почему бы не взглянуть на это

Связывание событий с объектами, не относящимися к DOM, с помощью jQuery

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

0 голосов
/ 15 октября 2010

как насчет такой настройки? (используя синтаксис 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.

У меня нет никакого способа проверить все это прямо сейчас, но я думаю, что это имеет смысл и должно поддерживаться в современных браузерах.

0 голосов
/ 15 октября 2010

Возможное решение, которое может привести вас в другом направлении: добавьте функцию в качестве встроенного элемента в элемент.

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

Затем, когда вы удаляете всех прослушивателей событий, которые вы хотите, вы также можете удалить "nextSibling ()" элемента, с которым вы работаете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...