12.10.2009 | Категория: Web-мастеру и не только | Автор: ManHunter
Сейчас трудно представить мышь без хотя бы одного колесика. Оно используется для прокрутки страницы, для изменения масштаба каких-либо элементов, листания списков и других действий. В основном полная обработка колесика выполняется только в стационарных приложениях, а на сайтах дело ограничивается штатными средствами браузеров. Мы не будем отставать от прогресса, и сегодня разберем как грамотно обрабатывать колесико мыши в современных web-приложениях. Начинаем с теории.
Обработчик события - функция на JavaScript, которая назначается некоторой паре "объект страницы" + "название события". Когда для объекта происходит назначенное событие, выполняются все обработчики, назначенные этому объекту.
Вращение колесика мышки - это тоже событие, значит его тоже можно обработать. Но тут есть особенность: в разных браузерах событие вращения колесика обозначается по-разному. Причину этого я объяснить не могу, впрочем как и многие другие танцы с бубнами для достижения полноценной кроссбраузерности. Сперва напишем функции установки и отмены события для любого элемента страницы, причем сделаем их универсальными, они нам еще не раз пригодятся в будущем. В браузере Internet Explorer обработчики добавляются функцией attachEvent, а названия событий имеют вид 'on'+событие, например, 'onclick', 'onload', 'onmousewheel' и т.д. В браузерах на движке Gecko (Mozilla, Firefox), на движке WebKit (Chrome, Safari) и Opera обработчики добавляются функцией addEventListener, а названия событий никак не меняются, то есть 'scroll', 'keypress', 'mousewheel'. И отдельно в этом списке стоит имя события от колесика мышки для браузеров на движке Gecko, оно единственное и уникальное - 'DOMMouseScroll'. Теперь попробуем все эти данные собрать в кучу.
Получилась вот такая универсальная кроссбраузерная функция установки обработчика события для любого элемента на странице:
//------------------------------------------------------------------
// Функция установки обработчика события
// Параметры вызова:
// hElem - DOM-элемент или его ID
// eventName - событие
// callback - функция, которая будет вызвана при событии
// На выходе:
// TRUE - обработчик установлен
// FALSE - элемент не найден или браузер не поддерживает события
//------------------------------------------------------------------
function hookEvent(hElem, eventName, callback) {
if (typeof(hElem) == 'string') {
// Если передан ID, то получить DOM-элемент
hElem = document.getElementById(hElem);
}
// Если такого элемента нет, то возврат с ошибкой
if (!hElem) { return false; }
if (hElem.addEventListener) {
if (eventName == 'mousewheel') {
// Событие вращения колесика для Mozilla
hElem.addEventListener('DOMMouseScroll', callback, false);
}
// Колесико для Opera, WebKit-based, а также любые другие события
// для всех браузеров кроме Internet Explorer
hElem.addEventListener(eventName, callback, false);
}
else if (hElem.attachEvent) {
// Все события для Internet Explorer
hElem.attachEvent('on' + eventName, callback);
}
else { return false; }
return true;
}
Параметр hElem - элемент страницы или его идентификатор, в этом случае функция попробует найти этот элемент самостоятельно. eventName - имя события 'click', 'mousewheel' и т.д. callback - имя функции обработчика, которая получит управление при возникновении события. Если на один элемент установлено несколько разных обработчиков одинакового события, то порядок их срабатывания зависит только от особенностей того или иного браузера, никаких стандартов, к сожалению, тут нет.
Функция снятия обработчика события по функционалу практически полностью повторяет функцию установки, за исключением того, что в ней используются функции detachEvent и removeEventListener. Каждое событие отменяется индивидуально.
//------------------------------------------------------------------
// Функция снятия обработчика события
// Параметры вызова:
// hElem - DOM-элемент или его ID
// eventName - событие
// callback - функция обработки события, которую надо отменить
//------------------------------------------------------------------
function unhookEvent(hElem, eventName, callback) {
if (typeof(hElem) == 'string') {
// Если передан ID, то получить DOM-элемент
hElem = document.getElementById(hElem);
}
// Если такого элемента нет, то возврат с ошибкой
if (!hElem) { return false; }
if (hElem.removeEventListener) {
if (eventName == 'mousewheel') {
// Событие вращения колесика для Mozilla
hElem.removeEventListener('DOMMouseScroll', callback, false);
}
// Колесико для Opera, WebKit-based, а также любые другие события
// для всех браузеров кроме Internet Explorer
hElem.removeEventListener(eventName, callback, false);
}
else if (hElem.detachEvent) {
// Все события для Internet Explorer
hElem.detachEvent('on' + eventName, callback);
}
else { return false; }
return true;
}
Параметры вызова совпадают с параметрами вызова функции установки обработчика. Теперь перейдем к самой функции обработчика. В ней надо получить два параметра: направление вращения колесика и элемент страницы, от которого пришло это событие. Во всех браузерах, кроме Internet Explorer и некоторых версий Opera, функции обработчика в качестве параметра передается объект события, в Internet Explorer и Opera событие записывается в window.event. Теперь, когда мы получили объект события, из него можно извлечь элемент и значение поворота колесика. В браузерах Internet Explorer и Opera элемент содержится в свойстве события srcElement, в остальных браузерах в свойстве target. Тут все просто. А вот со значением поворота колесика сложнее. Само значение поворота колесика в браузерах Internet Explorer и Opera элемент содержится в свойстве события wheelDelta, в остальных браузерах в свойстве detail. Но числовое значение существенно разнится практически во всех браузерах. Проведем эксперимент: установим обработчик колесика мышки, откроем страничку в разных браузерах, повернем колесико в какое-нибудь одинаковое направление и выведем на экран числовое значение поворота. Результаты эксперимента (колесико вращалось вверх):
Internet Explorer (wheelDelta) : 120
Firefox (detail) : -3
Google Chrome (wheelDelta): 120
Старые версии Chrome (wheelDelta): 12000
Safari (wheelDelta): 120
Opera (detail) : -3 / (wheelDelta): 120
Опять же для меня остается загадкой логика и несогласованность разработчиков различных браузеров. Придется учитывать эти особенности в нашей функции обработчика. И остается последний момент. Все функции событий обрабатываются последовательно, они как бы "всплывают". Это значит, что после выполнения нашего обработчика колесика мыши управление получат следующие или же обработчики по умолчанию, то есть страница или фрейм прокрутятся как обычно. Чтобы подавить подобные действия браузера по умолчанию, надо сделать так, чтобы наш обработчик события стал завершающим в цепочке. Для этого напишем вспомогательную функцию, которая будет вызываться в самом конце нашего обработчика, и будет подавлять или отменять все последующие обработчики. Входной параметр для нее - объект события. Функция универсальная, подходит для любых событий, в ней учтены особенности всех браузеров.
//------------------------------------------------------------------
// Кроссбраузерная функция подавления события
//------------------------------------------------------------------
function cancelEvent(e) {
e = e ? e : window.event;
if (e.stopPropagation) {
e.stopPropagation();
}
if (e.preventDefault) {
e.preventDefault();
}
e.cancelBubble = true;
e.cancel = true;
e.returnValue = false;
return false;
}
Теперь у нас есть все данные для написания полноценного обработчика колесика мыши. Вот примерная заготовка:
//------------------------------------------------------------------
// Пример функции обработки колесика мыши
//------------------------------------------------------------------
function MouseWheelFunction(e) {
e = e ? e : window.event;
// Получить элемент, на котором произошло событие
var wheelElem = e.target ? e.target : e.srcElement;
// Получить значение поворота колесика мыши
var wheelData = e.detail ? e.detail * -1 : e.wheelDelta / 40;
// В движке WebKit возвращается значение в 100 раз больше
if (Math.abs(wheelData)>100) { wheelData=Math.round(wheelData/100); }
// Теперь в переменной wheelElem содержится элемент, который получил
// собщение от колесика мыши, а в wheelData значение поворота колеса
...
Ваш обработчик колесика мыши
...
// Отменить штатные действия браузера при кручении колеса мыши
return cancelEvent(e);
}
Переменная wheelData содержит числовое значение поворота колесика мыши: отрицательное значение - колесико повернуто на себя, положительное значение - поворот от себя. Переменная wheelElem - элемент web-страницы, от которого пришло событие. В приложении пример скрипта обработки колесика мыши и html-страница с различными элементами, реагирующими на колесико: картинка, таблица, div и textarea.