Прекратите наблюдать события с JS Prototype, не работающим с .bind (это) - PullRequest
0 голосов
/ 02 апреля 2010

Я работаю над классом Javascript, основанным на библиотеке прототипов. Этот класс должен наблюдать событие для выполнения операции перетаскивания (текущие элементы управления перетаскиванием не подходят для этой ситуации), но у меня возникают проблемы с тем, чтобы он перестал наблюдать за событиями.

Вот пример, который вызывает эту проблему:

var TestClass = Class.create({
    initialize: function(element) {
        this.element = element;
        Event.observe(element, 'mousedown', function() {
            Event.observe(window, 'mousemove', this.updateDrag.bind(this));
            Event.observe(window, 'mouseup', this.stopDrag.bind(this));
        });
    },
    updateDrag: function(event) {
        var x = Event.pointerX(event);
        var y = Event.pointerY(event);
        this.element.style.top = y + 'px';
        this.element.style.left = x + 'px';
    },
    stopDrag: function(event) {
        console.log("stopping drag");
        Event.stopObserving(window, 'mousemove', this.updateDrag.bind(this));
        Event.stopObserving(window, 'mouseup', this.stopDrag.bind(this));
    }
});

Без .bind (this) this.element не определен, но с ним события не перестают наблюдаться (хотя вывод консоли все же происходит).

1 Ответ

4 голосов
/ 02 апреля 2010

bind возвращает новую ссылку на функцию каждый раз, когда вы вызываете ее (это ее работа :-)), а stopObserving только отцепит обработчик, если ссылка на функцию соответствует ===.

Чтобы исправить это, запомните обработчик событий, который вы связали как свойство, а затем используйте это свойство с stopObserving. Или, если вы отвечаете за этот элемент, вы можете отсоединить все обработчики для событий mousemove и mouseup, просто отключив третий параметр. (См. Связанные документы для получения дополнительной информации об отключении параметров до stopObserving).

Так что либо:

initialize: function(element) {
    this.element = element;
    this.boundUpdateDrag = this.updateDrag.bind(this);
    this.boundStopDrag = this.stopDrag.bind(this);
    Event.observe(element, 'mousedown', function() {
        // Off-topic, but see note at end of answer, unrelated bug here
        Event.observe(window, 'mousemove', this.boundUpdateDrag);
        Event.observe(window, 'mouseup', this.boundStopDrag);
    });
},
stopDrag: function(event) {
    console.log("stopping drag");
    Event.stopObserving(window, 'mousemove', this.boundUpdateDrag);
    Event.stopObserving(window, 'mouseup', this.boundStopDrag);
}

или просто

stopDrag: function(event) {
    console.log("stopping drag");
    Event.stopObserving(window, 'mousemove');
    Event.stopObserving(window, 'mouseup');
}

Но обратите внимание, что последний удаляет все обработчики для тех событий на этом элементе (ну, те, которые подключены через Prototype).


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

Так что либо используйте bind снова:

initialize: function(element) {
    this.element = element;
    this.boundUpdateDrag = this.updateDrag.bind(this);
    this.boundStopDrag = this.stopDrag.bind(this);
    Event.observe(element, 'mousedown', (function() {
        Event.observe(window, 'mousemove', this.boundUpdateDrag);
        Event.observe(window, 'mouseup', this.boundStopDrag);
    }).bind(this));
},

Или используйте тот факт, что вы в любом случае определяете замыкание:

initialize: function(element) {
    var self;

    self = this; // Remember 'this' on a variable that will be in scope for the closure
    this.element = element;
    this.boundUpdateDrag = this.updateDrag.bind(this);
    this.boundStopDrag = this.stopDrag.bind(this);
    Event.observe(element, 'mousedown', function() {
        // Note we're using 'self' rather than 'this'
        Event.observe(window, 'mousemove', self.boundUpdateDrag);
        Event.observe(window, 'mouseup', self.boundStopDrag);
    });
},
...