Когда возникает событие мыши, свойства clientX и clientY объекта события хра_ нят координаты указателя мыши. Эти координаты являются координатами в ок_ не, т. е. измеряются относительно верхнего левого угла клиентской области окна броузера и не учитывают прокрутку документа. Зачастую возникает необходи_ мость преобразовать эти значения в координаты документа, например, чтобы ото_ бразить всплывающую подсказку рядом с указателем мыши, а для определения координат всплывающего окна необходимо иметь координаты указателя в доку_ менте. Пример 17.3 продолжает пример 16.4. В примере 16.4 просто выводилось окно с всплывающей подсказкой в заданных координатах документа. Данный пример расширяет возможности прошлого примера за счет добавления метода Tooltip.schedule(), который отображает всплывающую подсказку с учетом коор_ динат, полученных от объекта события мыши. Поскольку событие мыши по_ ставляет оконные координаты, метод schedule() преобразует их в координаты до_ кумента с помощью методов модуля Geometry, приводившегося в примере 14.2.
Пример 17.3. Позиционирование всплывающих подсказок по событиям мыши
// Следующие значения используются методом schedule(), определенным далее.
// Они используются как константы, но доступны для записи, поэтому вы можете
// переопределить эти значения, предлагаемые по умолчанию.
Tooltip.X_OFFSET = 25; // пикселов вправо от указателя мыши
Tooltip.Y_OFFSET = 15; // пикселов вниз от указателя мыши
Tooltip.DELAY = 500; // миллисекунд после события mouseover
/**
* Данный метод планирует появление всплывающей подсказки над указанным
* элементом через Tooltip.DELAY миллисекунд от момента события.
* Аргумент “e” должен быть объектом события mouseover. Данный метод извлекает
* координаты мыши из объекта события, преобразует их из оконных координат
* в координаты документа и добавляет вышеуказанные смещения.
436 Глава 17. События и обработка событий
* Определяет текст подсказки, обращаясь к атрибуту "tooltip" заданного
* элемента. Данный метод автоматически регистрирует обработчик события
* onmouseout и отменяет его регистрацию. Этот обработчик выполняет скрытие
* подсказки или отменяет ее запланированное появление.
// Получить текст для отображения. Если текст отсутствует _ ничего не делать. var text = target.getAttribute("tooltip");
if (!text) return;
// Объект события хранит оконные координаты указателя мыши.
// Поэтому они преобразуются в координаты документа с помощью модуля Geometry. var x = e.clientX + Geometry.getHorizontalScroll();
var y = e.clientY + Geometry.getVerticalScroll();
// Добавить смещения, чтобы подсказка появилась правее и ниже указателя мыши. x += Tooltip.X_OFFSET;
y += Tooltip.Y_OFFSET;
// Запланировать появление подсказки.
var self = this; // Это необходимо для вложенных функций
var timer = window.setTimeout(function() { self.show(text, x, y); }, Tooltip.DELAY);
// Зарегистрировать обработчик onmouseout, чтобы скрыть подсказку
// или отменить появление запланированной подсказки.
if (target.addEventListener) target.addEventListener("mouseout", mouseout, false);
else if (target.attachEvent) target.attachEvent("onmouseout", mouseout); else target.onmouseout = mouseout;
// Реализация слушателя события приводится далее function mouseout() {
self.hide(); // Скрыть подсказку, если она уже на экране, window.clearTimeout(timer); // отменить все запланированные подсказки // и удалить себя, т.к. обработчик запускается единожды
if (target.removeEventListener) target.removeEventListener("mouseout", mouseout, false);
else if (target.detachEvent) target.detachEvent("onmouseout",mouseout);
else target.onmouseout = null;
}
}
// Определить единственный глобальный объект Tooltip для общего пользования Tooltip.tooltip = new Tooltip();
/*
* Следующая статическая версия метода schedule() использует
Итак, мы обсудили вопросы распространения событий, регистрации обработчи_ ков и различных интерфейсов объектов событий для моделей DOM Level 2 и IE, и можем, наконец, на практическом примере показать, как это все работает. В примере 19.4 представлена JavaScript_функция drag(), которая, будучи вызва_ на из обработчика события mousedown, позволяет пользователю перетащить эле_ мент документа. Функция drag() может работать как в модели DOM, так и в мо_ дели IE.
Функция drag() принимает два аргумента. Первый – перетаскиваемый элемент. Это может быть элемент, в котором произошло событие mousedown, или содержа_ щий его элемент (например, можно разрешить пользователю, перетаскивая за_ головок окна, переместить все окно). Однако в обоих случаях он должен ссы_ латься на элемент документа, абсолютно позиционируемый с помощью CSS_ат_ рибута position. Второй аргумент – это объект события, связанный с вызывае_ мым событием mousedown.
Функция drag() записывает позицию, в которой произошло событие mousedown,
и затем регистрирует обработчики для событий mousemove и mouseup, следующих за событием mousedown. Обработчик события mousemove отвечает за перемещение элемента документа, а обработчик события mouseup – за отмену регистрации себя
и обработчика mousemove. Важно отметить, что обработчики mousemove и mouseup за_ регистрированы как перехватывающие, т. к. пользователь может двигать мышь быстрее, чем элемент документа будет успевать за ней следовать, и некоторые из этих событий могут происходить вне исходного элемента документа. Кроме то_ го, обратите внимание, что функции moveHandler() и upHandler(), регистрируемые для обработки этих событий, определены как вложенные внутри функции drag()
и поэтому могут использовать ее аргументы и локальные переменные, что зна_ чительно упрощает их реализацию.
Пример 17.4. Перетаскивание элементов документа
/**
* Drag.js: перетаскивание абсолютно позиционируемых HTML_элементов.
*
* Данный модуль определяет единственную функцию drag(),
* которая предназначена для вызова из обработчика события onmousedown.
* Последующие события mousemove будут вызывать перемещение заданного элемента.
// Зарегистрировать обработчики событий mousemove и mouseup,
// которые последуют вслед за событием mousedown.
if (document.addEventListener) { // Модель событий DOM уровня 2 // Зарегистрировать перехватывающие обработчики событий document.addEventListener("mousemove", moveHandler, true); document.addEventListener("mouseup", upHandler, true);
}
else if (document.attachEvent) { // Модель событий IE 5+
// В модели обработки событий IE перехват событий производится
else if (document.detachEvent) { // Модель событий IE 5+ elementToDrag.detachEvent("onlosecapture", upHandler); elementToDrag.detachEvent("onmouseup", upHandler); elementToDrag.detachEvent("onmousemove", moveHandler); elementToDrag.releaseCapture();
}
else { // Модель событий IE 4
// Восстановить первоначальные обработчики, если они были document.onmouseup = olduphandler;
document.onmousemove = oldmovehandler;
}
// И прервать дальнейшее распространение события.
if (e.stopPropagation) e.stopPropagation( ); // DOM уровня 2 else e.cancelBubble = true; // IE
}
}
Следующий фрагмент демонстрирует, как можно использовать функцию drag() в HTML_документе (это упрощенная версия примера 16.3, куда была добавлена возможность перетаскивания элементов).
<script src="Drag.js"></script> <!__ Подключить сценарий Drag.js __> <!__ Определить перетаскиваемый элемент __>
onmousedown="drag(this.parentNode, event);"> Перетащи меня <!__ Содержимое "заголовка" __> </div>
<!__ Содержимое перетаскиваемого элемента __>
440 Глава 17. События и обработка событий
<p>Это тест. Тестирование, тестирование и еще раз тестирование.
<p>Это тест.<p>Тест.
</div>
Ключевым здесь является атрибут onmousedown во вложенном элементе <div>. Не_ смотря на то что функция drag() использует модели обработки событий DOM и IE, регистрация ее для удобства производится с применением модели Level 0.
Вот другой простой пример использования функции drag(). В нем определяется изображение, которое можно перетащить, если при этом будет удерживаться клавиша Shift: