removeEventListener для анонимных функций в JavaScript

103

У меня есть объект, в котором есть методы. Эти методы помещаются в объект внутри анонимной функции. Выглядит это так:

var t = {};
window.document.addEventListener("keydown", function(e) {
    t.scroll = function(x, y) {
        window.scrollBy(x, y);
    };
    t.scrollTo = function(x, y) {
        window.scrollTo(x, y);
    };
});  

(кода намного больше, но этого достаточно, чтобы показать проблему)

Теперь я хочу в некоторых случаях останавливать прослушиватель событий. Поэтому я пытаюсь выполнить removeEventListener, но не могу понять, как это сделать. Я читал в других вопросах, что невозможно вызвать removeEventListener для анонимных функций, но так ли это в этой ситуации?

У меня есть метод, созданный внутри анонимной функции, и поэтому я подумал, что это возможно. Выглядит так:

t.disable = function() {
    window.document.removeEventListener("keydown", this, false);
}

Почему я не могу этого сделать?

Есть ли другой (хороший) способ сделать это?

Бонусная информация; это должно работать только в Safari, отсюда и отсутствие поддержки IE.

Bitkid
источник
Почему бы не сохранить эту функцию? Обработчик событий может быть не анонимной функцией.
kirilloid
2
Я понимаю, что это немного поздно, но вы также можете использовать методы Node.setUserData /Node.getUserData для хранения данных об элементе. Например, когда вам нужно установить анонимный слушатель (и иметь возможность удалить его), сначала установите userdata в анонимную функцию, (Elem.setUserData('eventListener', function(e){console.log('Event fired.');}, null);а затем выполните Elem.addEventListener ('event', Elem.getUserData ('eventListener'), false); ... и то же самое для removeEventListener. Надеюсь, ты это хорошо понимаешь.
Чейз
РЕДАКТИРОВАТЬ: Согласно предыдущему комментарию, я предполагаю, что это работает только в Firefox ... Я только что попробовал IE8 (IE9 unknown), Safari 5.1.2, Chrome (?), Opera 11 .. Никаких кубиков
Chase
Возможный дубликат JavaScript: удалить прослушиватель событий
Heretic Monkey

Ответы:

78

Я считаю, что в этом суть анонимной функции, у нее нет имени или способа ссылаться на нее.

На вашем месте я бы просто создал именованную функцию или поместил ее в переменную, чтобы у вас была ссылка на нее.

var t = {};
var handler = function(e) {
    t.scroll = function(x, y) {
        window.scrollBy(x, y);
    };
    t.scrollTo = function(x, y) {
        window.scrollTo(x, y);
    };
};
window.document.addEventListener("keydown", handler);

Затем вы можете удалить его с помощью

window.document.removeEventListener("keydown", handler);   
Адам Хит
источник
3
Спасибо за ваш ответ. Я использовал: var handler; window.document.addEventListener ("keydown", handler = function (e) {Но я не понимаю, почему "this" не ссылается на прослушиватель событий.
Разве
1
thisКлючевое слово может сбивать с толку. Хорошее место, чтобы прочитать об этом - quirksmode.org/js/this.html
Адам Хит,
Большое спасибо. Это было очень полезно.
bitkid
Я пытаюсь сделать это, чтобы заблокировать действительно постоянную рекламу на веб-сайте. Я знаю, что в этом суть анонимных функций, но это не значит, что я не хотел бы знать, как это сделать.
Wyatt8740 05
@bitkid: внутри функции-обработчика (при условии, что это не функция стрелки), thisотносится к элементу, к которому добавлен слушатель, а не к самому событию (которое будет параметром e). Следовательно this === e.currentTarget. прочтите developer.mozilla.org/en-US/docs/Web/API/EventTarget/…
chharvey 05
100

если вы находитесь внутри самой функции, вы можете использовать arguments.callee в качестве ссылки на функцию. как в:

button.addEventListener('click', function() {
      ///this will execute only once
      alert('only once!');
      this.removeEventListener('click', arguments.callee);
});

РЕДАКТИРОВАТЬ: это не сработает, если вы работаете в строгом режиме ( "use strict";)

Отто Наскарелла
источник
2
Это хорошо, поскольку сохраняет преимущества анонимных функций (не загрязняет пространство имен и т. Д.).
bompf
3
попробовал это в приложении WinJS, получил следующую ошибку: «Доступ к свойству 'callee' объекта аргументов не разрешен в строгом режиме»
Валентин Кантор
1
@ValentinKantor: Это потому, что в коде есть «строгое использование»; оператор, и вы не можете использовать вызываемый объект в строгом режиме.
OMA
19
Присвойте встроенной функции имя, и вы сможете ссылаться на нее, не прибегая к arguments.callee:button.addEventListener('click', function handler() { this.removeEventListener('click', handler); });
Гарри Лав
4
Как указано в Mozilla: «Предупреждение: 5-е издание ECMAScript (ES5) запрещает использование arguments.callee () в строгом режиме. Избегайте использования arguments.callee (), задавая имя выражению функции или используя объявление функции, где функция должен называть себя ".
чувак
50

Версия решения Отто Наскареллы , работающая в строгом режиме:

button.addEventListener('click', function handler() {
      ///this will execute only once
      alert('only once!');
      this.removeEventListener('click', handler);
});
Мелле
источник
4
Красивое решение!
Эрик Норкросс
2
Возможно, это неправильный путь, но это самый простой способ.
Vignesh
7
window.document.removeEventListener("keydown", getEventListeners(window.document.keydown[0].listener));  

Может быть несколько анонимных функций, keydown 1

Предупреждение: работает только в Chrome Dev Toolsи не может использоваться в коде : ссылка

Исаевский_Сергей
источник
2
Спасибо, вы отгадали загадку, по крайней мере, в Chrome, потому что то, что многие шутники говорили, было невозможно. Чувак, ты как ... Бэтмен!
JasonXA
20
getEventListenersпохоже, является частью инструментов Chrome Dev, поэтому на самом деле не может использоваться ни для чего, кроме отладки.
DBS,
1
Просто попробовал, подтвердил, что он доступен только в Devtools, а не когда включен в скрипт на странице.
Андрес Риофрио
5

в современных браузерах вы можете сделать следующее ...

button.addEventListener( 'click', () => {
    alert( 'only once!' );
}, { once: true } );

https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Parameters

Shunryu111
источник
Круто, пока вы не обнаружите, что ни одна версия IE и edge <16 не поддерживают эту функцию. По крайней мере, через 5 лет мы сможем использовать это, поскольку с тех пор IE (читай: должен) будет устаревшим, Edge займет его место, и он будет использовать движок webkit вместо своего собственного «EdgeHTML».
SidOfc
1
с этим полифилом для записей DOM уровня 4 все будет в порядке npmjs.com/package/dom4
shunryu111
2

Не такой уж анонимный вариант

element.funky = function() {
    console.log("Click!");
};
element.funky.type = "click";
element.funky.capt = false;
element.addEventListener(element.funky.type, element.funky, element.funky.capt);
// blah blah blah
element.removeEventListener(element.funky.type, element.funky, element.funky.capt);

С момента получения отзыва от Энди ( совершенно верно, но, как и во многих примерах, я хотел показать контекстное расширение идеи ), вот менее сложное изложение:

<script id="konami" type="text/javascript" async>
    var konami = {
        ptrn: "38,38,40,40,37,39,37,39,66,65",
        kl: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
    };
    document.body.addEventListener( "keyup", function knm ( evt ) {
        konami.kl = konami.kl.slice( -9 );
        konami.kl.push( evt.keyCode );
        if ( konami.ptrn === konami.kl.join() ) {
            evt.target.removeEventListener( "keyup", knm, false );

            /* Although at this point we wish to remove a listener
               we could easily have had multiple "keyup" listeners
               each triggering different functions, so we MUST
               say which function we no longer wish to trigger
               rather than which listener we wish to remove.

               Normal scoping will apply to where we can mention this function
               and thus, where we can remove the listener set to trigger it. */

            document.body.classList.add( "konami" );
        }
    }, false );
    document.body.removeChild( document.getElementById( "konami" ) );
</script>

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

Между прочим : удаление элемента сценария сразу после установки слушателя - это симпатичный трюк для сокрытия кода, который, как хотелось бы, не был совершенно очевидным для посторонних глаз ( испортило бы сюрприз ;-)

Итак, метод ( проще ):

element.addEventListener( action, function name () {
    doSomething();
    element.removeEventListener( action, name, capture );
}, capture );
Фред Гандт
источник
2
Это слишком сложно.
Бен Синклер,
@Andy Я вроде как согласен, но пытался показать, что просто невозможно удалить анонимную функцию. На нее нужно каким-то образом ссылаться (даже вызываемый (это плохо, M'Kay) ссылается на функцию) и, таким образом, предоставил пример только одного (другого) способа ссылки на функцию - и того, как она построена из частей, которые также могут быть сохранены для дальнейшего использования (важная часть). Очевидно, что по-настоящему анонимная функция в какой-то мере создается «на лету», и поэтому необходимо знать, какое действие / тип события и какой захват был использован позже. В любом случае, вот способ получше :-)
Фред Гандт
У меня отлично сработало. Я не видел другого способа передать аргументы функции, поскольку он не мог быть анонимным.
nicodemus13
2

Это не идеально, поскольку удаляет все, но может сработать для ваших нужд:

z = document.querySelector('video');
z.parentNode.replaceChild(z.cloneNode(1), z);

При клонировании узла копируются все его атрибуты и их значения, включая внутренних (встроенных) слушателей. Он не копирует прослушиватели событий, добавленные с помощью addEventListener ().

Node.cloneNode ()

Сообщество
источник
Это просто гениально,
Ахмад Альфи
1

JavaScript : метод addEventListener регистрирует указанный слушатель в EventTarget (Element | document | Window), который он вызывает.

EventTarget. addEventListener ( тип_события , функция- обработчик , всплывание | захват );

События мыши, клавиатуры Пример теста в WebConsole:

var keyboard = function(e) {
    console.log('Key_Down Code : ' + e.keyCode);
};
var mouseSimple = function(e) {
    var element = e.srcElement || e.target;
    var tagName = element.tagName || element.relatedTarget;
    console.log('Mouse Over TagName : ' + tagName);    
};
var  mouseComplex = function(e) {
    console.log('Mouse Click Code : ' + e.button);
} 

window.document.addEventListener('keydown',   keyboard,      false);
window.document.addEventListener('mouseover', mouseSimple,   false);
window.document.addEventListener('click',     mouseComplex,  false);

Метод removeEventListener удаляет прослушиватель событий, ранее зарегистрированный с помощью EventTarget.addEventListener ().

window.document.removeEventListener('keydown',   keyboard,     false);
window.document.removeEventListener('mouseover', mouseSimple,  false);
window.document.removeEventListener('click',     mouseComplex, false);

могу ли я использовать

Яш
источник
1

Чтобы дать более современный подход к этому:

//one-time fire
element.addEventListener('mousedown', {
  handleEvent: function (evt) {
    element.removeEventListener(evt.type, this, false);
  }
}, false);
Джонатас Уокер
источник
2
Объяснение было бы неплохим.
Поул Бак
1

Я наткнулся на ту же проблему, и это было лучшее решение, которое я мог получить:

/*Adding the event listener (the 'mousemove' event, in this specific case)*/
element.onmousemove = function(event) {
    /*do your stuff*/
};
/*Removing the event listener*/
element.onmousemove = null;

Имейте в виду, что я тестировал это только для windowэлемента и для 'mousemove'события, поэтому при таком подходе могут возникнуть некоторые проблемы.

Гарк Гарсия
источник
0

Возможно, не лучшее решение с точки зрения того, о чем вы спрашиваете. Я до сих пор не определил эффективный метод удаления анонимной функции, объявленной встроенной при вызове прослушивателя событий.

Я лично использую переменную для хранения <target>и объявления функции вне вызова прослушивателя событий, например:

const target = document.querySelector('<identifier>');

function myFunc(event) { function code; }

target.addEventListener('click', myFunc);

Затем, чтобы удалить слушателя:

target.removeEventListener('click', myFunc);

Это не самая лучшая рекомендация, которую вы получите, но для удаления анонимных функций единственное решение, которое я нашел полезным, - это удалить, а затем заменить элемент HTML. Я уверен, что должен быть лучший ванильный JS-метод, но я его еще не видел.

Шей Коннолли
источник
0

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

Скрипт (извиняюсь за некреативные названия методов):

window.Listener = {
    _Active: [],
    remove: function(attached, on, callback, capture){
        for(var i = 0; i < this._Active.length; i++){
            var current = this._Active[i];
            if(current[0] === attached && current[1] === on && current[2] === callback){
                attached.removeEventListener(on, callback, (capture || false));
                return this._Active.splice(i, 1);
            }
        }
    }, removeAtIndex(i){
        if(this._Active[i]){
            var remove = this._Active[i];
            var attached = remove[0], on = remove[1], callback = remove[2];
            attached.removeEventListener(on, callback, false);
            return this._Active.splice(i, 1);
        }
    }, purge: function(){
        for(var i = 0; i < this._Active.length; i++){
            var current = this._Active[i];
            current[0].removeEventListener(current[1], current[2]);
            this._Active.splice(i, 1);
        }
    }, declare: function(attached, on, callback, capture){
        attached.addEventListener(on, callback, (capture || false));
        if(this._Active.push([attached, on, callback])){
            return this._Active.length - 1;
        }
    }
};

И вы можете использовать это так:

// declare a new onclick listener attached to the document
var clickListener = Listener.declare(document, "click" function(e){
    // on click, remove the listener and log the clicked element
    console.log(e.target);
    Listener.removeAtIndex(clickListener);
});

// completely remove all active listeners 
// (at least, ones declared via the Listener object)
Listener.purge();

// works exactly like removeEventListener
Listener.remove(element, on, callback);
ГРОВЕР.
источник
-4
window.document.onkeydown = function(){};
Дун
источник
Почему не = undefined? Настоящий задира.
Бен Синклер
2
Это не приведет к удалению обработчика, который был зарегистрирован с addEventListener.
Luc125