Страницы: 1
Сразу оговорюсь, что всё нижеприведенное относится именно к 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)). Однако нам в вызываемом методе нужен именно контекст объекта, который этот метод содержит, поэтому делаем так:
- вроде бы всё в порядке, получаем алерт с содержимым foo. Теперь нам кнопку надо отписать от этого метода - т.е. жмем кнопку 2 - и ничего не происходит. Меняем код отписки с вышеприведенного на вот такой:
btn.removeEventListener("command",some.something.doSome.bind(some.something),false);
- толку ноль. Изменяем метод doSome вот таким образом:
и убираем bind(...) в подписке - всё в порядке, кнопка нормально подписывается на обработчик, и нормально от него отписывается. Видимо, дело заключается именно в наличии bind(). На всякий случай проверяю документацию:
Calling removeEventListener with arguments which do not identify any currently registered EventListener on the EventTarget has no effect.
Поскольку сигнатура removeEventListener такова:
и с первым и третьим аргументом ошибиться сложно, остается второй - передаваемая ссылка на 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);})
Отсутствует
Мда.. то ли авторы документации не совсем внятно выразились, то ли я не до конца понял вышепроцитированное вот это:
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 дает нам на выходе анонимный метод. Всем спасибо
Отсутствует
Еще можно сделать объект с методом handleEvent и передавать его в addEventListener/removeEventListener вместо функции, тогда this будет ссылаться на этот объект.
https://developer.mozilla.org/en/DOM/EventListener
Прошлое – это локомотив, который тянет за собой будущее. Бывает, что это прошлое вдобавок чужое. Ты едешь спиной вперед и видишь только то, что уже исчезло. А чтобы сойти с поезда, нужен билет. Ты держишь его в руках. Но кому ты его предъявишь?
Виктор Пелевин. Желтая стрела
Отсутствует
Infocatcher
Ага, спасибо. Я как-то сосредоточился на игре с bind, и этот вариант упустил из рассмотрения.
Отсутствует
Страницы: 1