Полезная информация

Будьте в курсе последних изменений в мире Mozilla, следя за нашим микроблогом в Twitter.

№122-02-2011 14:53:07

hydrolizer
Участник
 
Группа: Extensions
Зарегистрирован: 22-07-2009
Сообщений: 1945
UA: Firefox 4.0

Firefox 4. Механизм работы removeEvenListener и bind()

Сразу оговорюсь, что всё нижеприведенное относится именно к Fx4, т.к. использует имплементацию bind() стандарта JavaScript 1.8.5 (в более ранних версиях Fx bind реализовывался "вручную"; как всё ниженаписанное могло бы выглядеть при такой реализации - не проверял). Все примеры максимально упрощены для приведения здесь, но в реальной ситуации картина абсолютно та же самая.
Итак, есть такая вот простенькая разметка на 3 кнопки:

Выделить код

Код:

<vbox>
    <button id="btn1" label="1" oncommand="doSubscribeUnsubscribe(true)"/>
    <button id="btn2" label="2" oncommand="doSubscribeUnsubscribe(false)"/>
    <button id="btn3" label="3"/>
</vbox>

и код к ней:

Выделить код

Код:

Components.utils.import("resource://gre/modules/Services.jsm");
function doSubscribeUnsubscribe(flag)
{
    try
    {
        var btn=document.getElementById("btn3");
        if (flag)
            btn.addEventListener("command",some.something.doSome,false);
        else
            btn.removeEventListener("command",some.something.doSome,false);
        Services.console.logStringMessage("doSubscribeUnsubscribe: done");
    }
    catch(err)
    {
        Services.console.logStringMessage("doSubscribeUnsubscribe: "+err);
    }
};

var some={};
some.something=
{
    thing: "foo",
    doSome: function()
    {
        alert(this.thing);
    }
}

Разумеется, в данном случае при нажатии на btn3 мы получим алерт с содержимым undefined - контекст исполнения принадлежит кнопке (в этом можно легко убедиться, сделав, например, alert(this.thing+": "+this)). Однако нам в вызываемом методе нужен именно контекст объекта, который этот метод содержит, поэтому делаем так:

Выделить код

Код:

btn.addEventListener("command",some.something.doSome.bind(some.something),false);

- вроде бы всё в порядке, получаем алерт с содержимым foo. Теперь нам кнопку надо отписать от этого метода - т.е. жмем кнопку 2 - и ничего не происходит. Меняем код отписки с вышеприведенного на вот такой:

Выделить код

Код:

btn.removeEventListener("command",some.something.doSome.bind(some.something),false);

- толку ноль. Изменяем метод doSome вот таким образом:

Выделить код

Код:

doSome: function()
    {
        alert(some.something.thing);
    }

и убираем bind(...) в подписке - всё в порядке, кнопка нормально подписывается на обработчик, и нормально от него отписывается. Видимо, дело заключается именно в наличии bind(). На всякий случай проверяю документацию:

Calling removeEventListener with arguments which do not identify any currently registered EventListener on the EventTarget has no effect.

Поскольку сигнатура removeEventListener такова:

Выделить код

Код:

element.removeEventListener(type, listener, useCapture)

и с первым и третьим аргументом ошибиться сложно, остается второй - передаваемая ссылка на listener. Опять же, из документации:

Note, however, that you'll need to keep a reference to the listener around so you can later remove it.

- именно так и поступаю, ссылка есть - листенер не удаляется. Единственное предположение заключается в следующем: если в метод, в котором навешивается/снимается подписка, вставить:

Выделить код

Код:

Services.console.logStringMessage("with bind:\r\n"+some.something.doSome.bind(some.something).toSource());
Services.console.logStringMessage("without bind:\r\n"+some.something.doSome.toSource());

то получим соответственно


with bind:
function () {[native code]}
------------------------------------------------------
without bind:
(function () {alert(this.thing);})

- т.е. в случае с bind() скриптовой движок "не видит" реализации метода листенера - может быть, поэтому он не может по ссылке идентифицировать листенер? Это только моё предположение, и если кто-то разъяснит, как указанную ситуаций обойти - буду премного благодарен, т.к. в реальном коде очень много чего завязано на контекст выполнения методов обработчиков, и переписывать их все на статический контекст было бы не особенно интересно.

Отсутствует

 

№222-02-2011 17:24:04

hydrolizer
Участник
 
Группа: Extensions
Зарегистрирован: 22-07-2009
Сообщений: 1945
UA: Firefox 4.0

Re: Firefox 4. Механизм работы removeEvenListener и bind()

Мда.. то ли авторы документации не совсем внятно выразились, то ли я не до конца понял вышепроцитированное вот это:

Note, however, that you'll need to keep a reference to the listener around so you can later remove it.

В общем, проблема решается так:

Выделить код

Код:

Components.utils.import("resource://gre/modules/Services.jsm");
function doSubscribeUnsubscribe(flag)
{
    try
    {
        var btn=document.getElementById("btn3");
        if (flag)
            btn.addEventListener("command",fWrapper,false);
        else
            btn.removeEventListener("command",fWrapper,false);
        Services.console.logStringMessage("doSubscribeUnsubscribe: done");
    }
    catch(err)
    {
        Services.console.logStringMessage("doSubscribeUnsubscribe: "+err);
    }
};
var some={};
some.something=
{
    thing: "foo",
    doSome: function()
    {
        alert(this.thing);
    }
}
var fWrapper=some.something.doSome.bind(some.something);

- т.е. надо держать ссылку не на просто метод метод подписки, а ссылку на метод, уже привязанный к нужному контексту - биндинг контекста непосредственно в вызове addEventListener дает нам на выходе анонимный метод. Всем спасибо :)

Отсутствует

 

№322-02-2011 18:44:34

Infocatcher
Not found
 
Группа: Extensions
Зарегистрирован: 24-05-2007
Сообщений: 4338
UA: Firefox 3.6

Re: Firefox 4. Механизм работы removeEvenListener и bind()

Еще можно сделать объект с методом handleEvent и передавать его в addEventListener/removeEventListener вместо функции, тогда this будет ссылаться на этот объект.
https://developer.mozilla.org/en/DOM/EventListener


Прошлое – это локомотив, который тянет за собой будущее. Бывает, что это прошлое вдобавок чужое. Ты едешь спиной вперед и видишь только то, что уже исчезло. А чтобы сойти с поезда, нужен билет. Ты держишь его в руках. Но кому ты его предъявишь?
Виктор Пелевин. Желтая стрела

Отсутствует

 

№423-02-2011 03:22:07

hydrolizer
Участник
 
Группа: Extensions
Зарегистрирован: 22-07-2009
Сообщений: 1945
UA: Firefox 4.0

Re: Firefox 4. Механизм работы removeEvenListener и bind()

Infocatcher
Ага, спасибо. Я как-то сосредоточился на игре с bind, и этот вариант упустил из рассмотрения.

Отсутствует

 

Board footer

Powered by PunBB
Modified by Mozilla Russia
Copyright © 2004–2020 Mozilla Russia GitHub mark
Язык отображения форума: [Русский] [English]