Ars Longa, Vita Brevis

Янв 16, 2008

IE6, Sortable и whatever:hover

Рубрика: JavaScript
Метки: , , , , , , ,
Vladimir @ 6:20 дп
RSS 2.0

Как подружить whatever:hover и Scriptaculous

Работая над одним сайтом, столкнулся с одной проблемой: в IE6 при использовании whatever:hover для выпадающего меню, построенного чисто на CSS, и Scriptaculous (а именно, Sortable, причем для элемента, не имеющего никакого отношения к меню) возникало непонятное мерцание.

Страница, на которой проявляется мерцание (только IE6).
То, что "виноват" именно Scriptaculous, доказывается очень легко: в исходном тексте test.html комментируется строка, создающая Draggable:

[-]
View Code Javascript
    var user_id = 5060;
    var id = 'user_' + user_id;

    if ($(id)) {
        Sortable.create(
            $(id),
            {
            }
        );
    }

Результат можно увидеть здесь — мерцание исчезло.

Путём долгих и нудных экспериментов были найдены строки, вызывающие такое поведение:

[-]
View Code Javascript
var Draggables = {
  drags: [],
  observers: [],

  register: function(draggable) {
    if(this.drags.length == 0) {
      this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
      this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
      this.eventKeypress  = this.keyPress.bindAsEventListener(this);

      Event.observe(document, "mouseup", this.eventMouseUp);
      Event.observe(document, "mousemove", this.eventMouseMove); // <---
      Event.observe(document, "keypress", this.eventKeypress);
    }
    this.drags.push(draggable);
  },
//...
};

Таким образом, если мы уберём Event.observe(document, "mousemove", this.eventMouseMove), то мерцание исчезнет. Естественно, это не выход, так как весь код, который прямо или косвенно использует Draggables, перестанет работать.

Попытаемся решить проблему (лезть в чужой код ой как не хочется). Одно ясно: вызов Event.observe(document, "mousemove", this.eventMouseMove) придется закомментировать. Добавлять обработчик события придется при активации draggable (в функции Draggables.activate), а убирать — в Draggables.deactivate() и Draggables.endDrag(). Возможно, хватит просто endDrag(), но ничего плохого не случится, если для подстраховки изменим оба метода.

Исходный код:

[-]
View Code Javascript
  register: function(draggable) {
    if(this.drags.length == 0) {
      this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
      this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
      this.eventKeypress  = this.keyPress.bindAsEventListener(this);

      Event.observe(document, "mouseup", this.eventMouseUp);
      Event.observe(document, "mousemove", this.eventMouseMove); //<-- нужно закомментировать
      Event.observe(document, "keypress", this.eventKeypress);
    }
    this.drags.push(draggable);
  },

  activate: function(draggable) {
    if(draggable.options.delay) {
      this._timeout = setTimeout(function() {
        Draggables._timeout = null;
        window.focus(); //<-- здесь нужно повесить обработчик события
        Draggables.activeDraggable = draggable;
      }.bind(this), draggable.options.delay);
    } else {
      window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
                            //<-- здесь нужно повесить обработчик события
      this.activeDraggable = draggable;
    }
  },

  deactivate: function() {
    this.activeDraggable = null; //<-- здесь нужно убрать обработчик события
  },

  endDrag: function(event) {
    if(this._timeout) {
      clearTimeout(this._timeout);
      this._timeout = null;
    }
    if(!this.activeDraggable) return;
    this._lastPointer = null;
    this.activeDraggable.endDrag(event);
    this.activeDraggable = null; //<-- здесь нужно убрать обработчик события
  },

Окончательный результат:

[-]
View Code Javascript
  register: function(draggable) {
    if(this.drags.length == 0) {
      this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
      this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
      this.eventKeypress  = this.keyPress.bindAsEventListener(this);

      Event.observe(document, "mouseup", this.eventMouseUp);
      Event.observe(document, "keypress", this.eventKeypress);
    }
    this.drags.push(draggable);
  },

  activate: function(draggable) {
    if(draggable.options.delay) {
      this._timeout = setTimeout(function() {
        Draggables._timeout = null;
        window.focus();
        Event.observe(document, "mousemove", this.eventMouseMove);
        Draggables.activeDraggable = draggable;
      }.bind(this), draggable.options.delay);
    } else {
      window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
      if (draggable) {
          Event.observe(document, "mousemove", this.eventMouseMove);
      }
      this.activeDraggable = draggable;
    }
  },

  deactivate: function() {
    this.activeDraggable = null;
    Event.stopObserving(document, "mousemove", this.eventMouseMove);
  },

  endDrag: function(event) {
    if(this._timeout) {
      clearTimeout(this._timeout);
      this._timeout = null;
    }
    if(!this.activeDraggable) return;
    this._lastPointer = null;
    this.activeDraggable.endDrag(event);
    this.activeDraggable = null;
    Event.stopObserving(document, "mousemove", this.eventMouseMove);
  },

Всё работает!
Патч в формате unified diff.

Комментарии к статье "IE6, Sortable и whatever:hover" (1) »

  1. [Март 10, 2008 5:22 дп] Vladimir:

    Побочный эффект: элементы li теперь не перетаскиваются за bullet, только за содержимое (причем только в IE6; в остальных браузерах всё отлично работает).

    #1

RSS лента комментариев к этой записи. TrackBack URL

Оставить комментарий к записи "IE6, Sortable и whatever:hover"

Изображения должны быть включены!

XHTML: Вы можете использовать эти теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Оставляя комментарий, Вы выражаете своё согласие с Правилами комментирования.

Подписаться, не комментируя