Кроссбраузерная работа с событиями. RoolsEvent
Привет, привет, привет!
Недавно мне понадобилась кроссбраузерная работа с JavaScript-событиями DOM-объектов.
Мне нужен был только этот функционал без ничего лишнего. Различные библиотеки не подходили и я взялся за реализацию своего варианта
Сначала прошёлся по гуглу и взял за основу 2 заметки:
- Кроссбраузерная работа с событиями
Автор: Дмитрий Ищенко
Адрес: http://www.jstoolbox.com/2008/01/25/krossbrauzernaya-rabota-s-sobytiyami/ - Flexible Javascript Events
Автор John Resig
Адрес: http://ejohn.org/blog/flexible-javascript-events/
Прежде чем продолжить дальше, я бы порекомендовал вам прочесть эти 2 статьи.
Далее в течении вчерашнего дня я написал скрипт под названием RoolsEvent, код которого хочу сразу же привести:
* RoolsEvent - JavaScript-инструмент для полноценной кроссбраузерной работы с событиями
* Версия 0.0.1
*
* Copyright (c) 2010 Александр Кузнецов aka Regent(http://vl.vg/)
* Идея и описание - 2010 Александр Кузнецов aka Regent - http://vl.vg/14.01.2010/cross-events/
* Прототип кода:
* Дмитрий Ищенко - http://www.jstoolbox.com/2008/01/25/krossbrauzernaya-rabota-s-sobytiyami/
* John Resig - http://ejohn.org/blog/flexible-javascript-events/
* Дата создания: 14.01.2010
* Лицензия:
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://docs.jquery.com/License
*/
(
function(){
var
// Быстрая ссылка на window
w = this,
// Быстрая ссылка на document
d = w.document,
// Временные переменные
tmp, i, length,
// События DOM-объектов
objectEvents = [ 'blur', 'change', 'click', 'dblclick', 'error', 'focus', 'keydown', 'keypress', 'keyup', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'select', 'submit' ],
// События window
windowEvents = [ 'load', 'unload', 'scroll', 'resize' ],
// Создаём временные span для тестов
span = d.createElement( 'span' ),
// Internet Explorer
IE = !!span.attachEvent,
// DOM уровень 1
DOM1 = !IE && !span.addEventListener,
// DOM2 совместимый браузер
standartBrowser = !IE && !DOM1,
// Функция добавления события для DOM2-совместимого браузера
addEvent = standartBrowser ? function( obj, type, fn ){
obj.addEventListener( type, fn, false );
// Функция добавления события для Internet Explorer
} : IE ? function( obj, type, fn ){
obj.attachEvent( 'on' + type, fn );
// Функция добавления события для браузера с DOM level 1
} : function( obj, type, fn ){
obj[ 'on' + type ] = !obj[ 'on' + type ] ? fn : ( fn.prevfn = obj[ 'on' + type ] ) && function(){
fn.prevfn();
fn();
};
},
// Функция удаления события для DOM2-совместимого браузера
removeEvent = standartBrowser ? function( obj, type, fn ){
obj.removeEventListener( type, fn, false );
// Функция удаления события для Internet Explorer
} : IE ? function( obj, type, fn ){
obj.detachEvent( 'on' + type, fn );
// Функция удаления события для браузера с DOM level 1
} : function( obj, type, fn ){
obj[ 'on' + type ] = null;
},
// Главная управляющая функция event(Менеджер событий)
event = function( obj, type, fn, remove ){
// Если DOM-объект не передан или не указан тип события - конец функции
if( !obj || !type )
return false;
// Если передан только DOM-объект и тип события - инициализация события
else if( arguments.length == 2 && obj[ type ] ){
try{
obj[ type ]();
}
catch( e ){};
}
// Если не указано удаление события - производим установку события
else if( remove != true ){
// Функция-посредник для
var browserFn = function( e ){
e = e || w.event;
// Исправления объекта event в Internet Explorer
if( IE ){
e.charCode = e.type == 'keypress' ? e.keyCode : 0;
e.eventPhase = 2;
e.isChar = e.charCode > 0;
e.pageX = e.clientX + document.body.scrollLeft;
e.pageY = e.clientY + document.body.scrollTop;
e.type == 'mouseout' ? e.relatedTarget = e.toElement : e.type == 'mouseover' ? e.relatedTarget = e.fromElement : '';
e.preventDefault = function(){
this.returnValue = false;
};
e.stopPropagation = function(){
this.cancelBubble = true;
};
e.target = e.srcElement;
e.time = ( new Date() ).getTime();
};
// Инициализация переданной функции с установкой внутри неё this - ссылки на DOM-объект и передачей event-объекта
// Если функция возвращает false, то вызывает событие event-объекта preventDefault
if( fn.call( obj, e ) == false )
return e.preventDefault() && false;
};
// Сохранение всех данных в массив данных по событиям
event.steak[ event.steak.length ] = {
'obj' : obj,
'type' : type,
'fn' : fn,
'browserFn' : browserFn,
'timestamp' : ( new Date() ).getTime()
};
// Добавление события
addEvent(
obj,
type,
browserFn
);
}
// Удаления события, если четвёртый переданный в функцию параметр равен true
else if( remove == true )
// Поиск и сравнение функции события
for( var i = 0, length = event.steak.length; i < length; i++ )
if( event.steak[ i ].fn == fn )
removeEvent( obj, type, event.steak[ i ].browserFn );
return true;
};
// Массив данных по событиям
event.steak = [];
// Назначение window событий load, unload, scroll, resize
for( var i = 0, length = windowEvents.length; i < length; i++ )
event[ windowEvents[ i ] ] = (
function( type ){
return function( fn ){
return !fn ? w[ 'on' + type ]() : !w[ 'on' + type ] ? w[ 'on' + type ] = fn
: ( fn.prevfn = w[ 'on' + type ] ) &&
(
w[ 'on' + type ] = function(){
fn.prevfn();
fn();
}
);
};
}
)( windowEvents[ i ] );
// Назначение быстрых ссылок на события(Пример event.click( obj, fn );)
for( var i = 0, length = objectEvents.length; i < length; i++ )
event[ objectEvents[ i ] ] = (
function( type ){
return function( obj, fn, remove ){
event( obj, type, fn, remove );
};
}
)( objectEvents[ i ] );
// Функция загрузки DOM-дерева
event.ready = function( fn ){
!event.ready.fns && ( event.ready.fns = [] );
fn && event.ready.fns.push( fn );
if( event.ready.is == true ){
event.ready.interval && clearInterval( event.ready.interval );
for( var i = 0, length = event.ready.fns.length; i < length; i++ )
event.ready.fns[ i ].call( w );
}
else if( !event.ready.interval )
event.ready.interval = setInterval(
function(){
if( document && document.getElementsByTagName && document.getElementById && document.body )
( event.ready.is = true ) && event.ready();
},
50
);
return event.ready.is;
};
// Удаление всех обработчиков событий при уходе со страницы для избежания утечек памяти
event.unload(
function(){
for( var i = 0, length = event.steak.length; i < length; i++ )
removeEvent( event.steak[ i ].obj, event.steak[ i ].type, event.steak[ i ].browserFn );
}
);
// Expose RoolsEvent to the global object
w.Event = event;
}
)();
В коде старался делать пояснения. Но пояснения на мой взгляд не сильно идеальны
Итак, сейчас я расскажу вам об основных моментах ![]()
Минимизированная версия скрипта весит 3,02 КБ(без копирайтов 2.27 КБ).
Внутри скрипта собирается объект event, в последствии зеркалом которого становится window.Event.
Немного об API. В RoolsEvent есть одна единственная главная функция Event(является глобальным объектом. Т. е. window.Event == Event). В неё передаётся DOM-объект, тип назначаемого события(click, focus, etc…) и функция, которая должна выполнятся по наступлению события. На событие одного DOM-объекта можно повесить бесконечное количество функций. Четвёртый параметр, передаваемый в функцию Event – удаление данной функции из события объекта.
Сразу приведу пример:
var fn = function(){ alert( 'Работает!' ); return false; };
Event( obj, 'click', fn );
Кликните для обзора результата – пример!
Ура, мы назначили обработчик события динамически! Что равносильно этому:
Кстати! Если функция, переданная в обработчик события возвращает false, то отменяются стандартные действия, которые должны были быть вызваны при данном событии(например переход по ссылки при клике).
Теперь же я покажу пример удаления функции из обработчика события + мы повесим на DOM-объект две функции при наступлении события!
var obj1_fn = function(){ alert( 'Работает!' ); return false; };
var obj1_fn1 = function(){
if( obj1_fn != null ){
Event( obj1, 'click', obj1_fn, true/*true означает удаление*/ );
// obj1_fn = null - не обязательно, просто для примера
obj1_fn = null;
alert( 'Успешно удалили первую функцию!' );
};
alert( 'Работает вторая функция!' );
return false;
};
Event( obj1, 'click', obj1_fn );
Event( obj1, 'click', obj1_fn1 );
Кликните для обзора результата – пример!
При первом клике срабатывает первая функция и вторая функция.
Вторая функция удаляет с объекта первую функцию. И при следующих кликах на ссылку будет работать только вторая функция!
Именованные функции. Можно также назначать/удалять функции на обработчики события посредством именованных функций в объекте Event. Сразу же пример для события click:
Event.click( object, myFunction )
// Удаление
Event.click( object, myFunction, true )
На данный момент поддерживаются события: blur, change, click, dblclick, error, focus, keydown, keypress, keyup, mousedown, mousemove, mouseout, mouseover, mouseup, select, submit.
События window. Event имеет функции для назначения событий глобальному объекту window. Поддерживаемые события:
- load – вызывается при полной загрузке страницы
- unload – вызывается при удалении страницы из окна
- scroll – вызывается при скроле страницы
- resize – вызывается при изменении размеров страницы
Например назначаем событие при уходе со страницы:
function(){
alert( 'Event говорит вам пока!)' );
}
);
Event.ready. Событие Event.load вызывается при полной загрузки страницы, включая картинки и всё остальное. Обычно это долгий процесс. Когда нам, как разработчику, это совершенно не нужно, а в частности нужно событие, которое запускалось бы тогда, когда DOM-структура была бы вся загружена. Это событие наступает намного раньше Event.load.
И у нас есть решение этой проблемы! Прототип следующего кода вы можете увидеть по ссылке на статью “Проверка загрузки DOM” и там же прочитать ещё теории на эту тему.
Вот изящный исходный код моей функции Event.ready:
!event.ready.fns && ( event.ready.fns = [] );
fn && event.ready.fns.push( fn );
if( event.ready.is == true ){
event.ready.interval && clearInterval( event.ready.interval );
for( var i = 0, length = event.ready.fns.length; i < length; i++ )
event.ready.fns[ i ].call( w );
}
else if( !event.ready.interval )
event.ready.interval = setInterval(
function(){
if( document && document.getElementsByTagName && document.getElementById && document.body )
( event.ready.is = true ) && event.ready();
},
50
);
return event.ready.is;
};
Кстати!. При уходе со страницы все обработчики событий удаляются автоматически, чтобы не вызывать всевозможных утечек памяти!
Я бы с удовольствием рассказал Вам о внутреннем устройство RoolsEvent, но чтец теории JavaScript из меня не очень ![]()
По этому поводу оставляю вам исходный код RoolsEvent на самостоятельное изучение!)
Я был бы рад, если вы указали на какую-нибудь ошибку или подсказали какое-нибудь нововведение! На данный момент я чувствую, что в скрипте чего то не хватает.
Итак, если Вам нужна кроссбраузерная работа с событиями размером в 3.02 КБ, вперёд!
Либо скачать новый нормальный браузер
Комментарии(8)
Для минимального функционала без селекторов -- отлично.
Код очень изящный, но разобраться не просто ) Буду разбираться )
Продолжайте писать, первые статьи просто отличные!
Вопрос, а где наловчились такому JS'у? Книги, блоги, ковыряния в «сердцах» jQuery и еже сними?
P.S. галочка об уведомлениях с бордером рядом с кнопкой добавления -- как то не очень удобно, так и хочется по ней кликнуть что бы отправить?
Может убрать бордер?
P.P.S. Если пытаться отправить комментарий не зарегистрировавшись / авторизовавшись -- выдает ошибку что это бот ((( Это явно бага -- и мыло не указано, даже не написать никак )
На счёт jQuery, буквально вчера писал ссылки здесь: эммм... форум тот не работает
На счёт добавления комментария - форма использует JavaScript. Если JS в браузере не работает, либо ошибка какая-то произошла, форма не отправит комментарий.
P. S. Скоро пересмотрим дизайн
JS точно работает, иначе не мог бы запускать код примеров кнопочкой ;) Кстати очень классно придумано, ты первый блогер у которого такое заметил -- очень удобно и наглядно.
Пробовал добавлять из лисы несколько раз, и страницу обновлял, и заново открывал, и регистрировался / авторизовался (авторизация прошла, и появились ссылки на выход и профиль) и все равно ничего.
Из Оперы добавилось с первого раза.
Даже сейчас добавляю из оперы, т.к. из лисы не хочет.
На счёт выполнения примеров - там всего навсего 10 строчек кода
На сколько я знаю, такое есть на данный момент на javascript.ru и больше пока нигде не видел
Кстати, всё никак не могу решить, оповещать ли тогда, когда код примера при выполнении создаёт ошибку... Пока же такое не сделал.
Ещё хотел сделать просмотр примеров HTML-кода в всплывающем окне, но руки никак не дойдут
Скорее всего сообщение не отправляется, т. к. в твоём браузере мой JS код генерирует какую-то ошибку. Сейчас убрал по моему мнению аварийный участок в JS-коде. Попробуй ещё раз отправить комментарий пожалуйста из тех браузеров.
Кстати, вот ссылка, как обещал, на вчерашнюю тему изучения jQuery:
http://forum.sape.ru/showthread.php?t=45304
Там в конце страницы я привёл ссылки на интересные сайты
Пробую из лисы, авторизованным )
Я выкинул из головы тот не несущий пользы метод