Программная генерация событий DOM 2 Events

image

Вступление


Здравствуйте, Хабрачеловеки.

В этой статье я хочу рассказать сообществу о такой полезной штуке, как DOM Events. Все, кто хоть как то связан с Javascript, знают, что в этом языке события и их обработка являются одним из важнейших и часто используемых свойств, но не многие знают, как эти события генерировать программно. Собственно, этому и посвящена статья: в ней рассказано, что это, зачем нужно, и как это использовать. О listener'ах речи идти не будет, хоть эта тема весьма тесто связана с рассматриваемой.



Что это?



DOM Events позволяют событийно-ориентированным языкам программирования, таким, как JavaScript, JScript, ECMAScript, VBScript and Java, регистрировать различные обработчики событий/слушатели событий (event handlers/listeners) на узлы элементов внутри дерева DOM (HTML, XHTML, XUL, SVG).

Это, к примеру, onClick, onBlur, onMouseOver и так далее. События эти запускаются по определённому действию пользователя: нажатие клавиши, клик, движение курсора за границы элемента. Т.е., при физическом воздействии на «органы управления». Мы же будем учиться запускать такие события программно, без использования устройств ввода и манипуляции.

Зачем это нужно?



Применение можно найти самое разное: just for fun, разработка ботов для bbmmorpg, скрипты автоматизации, читерства (скрипт для «Клавогонок»: вставка нужной буквы в строку ввода и запуск события onKeyUp), «вебдванольности», встречал даже мысли о написании некоего подобия юнит-теста. В общем, на сколько фантазии хватит.

Каких то толковых мануалов по этой теме интернетах найти не удалось, поэтому решил свести и увековечить свои находки на хабре.

Однако, стоит помнить следующее:

«Ручной» запуск (генерация) события не создает по умолчанию действие, связанное с этим событием. Например, программная генерирация события focus на элементе не означает, он получит фокус, вручную сгенерированное событие submit не заставит форму отправить данные (следует использовать метод submit формы), сгенерированное событие нажатия клавиши не означает, что символ появится в поле ввода, программное нажание на ссылку не активирует переход по ней, и т. д. Такие ограничения необходимы в целях безопасности и предотвращения сценариев имитации работы пользователя и взаимодействия с браузером.

Как это использовать?



В общем порядке, алгоритм един:
— получить объект узла DOM, на который будет вешаться событие;
— создать объект необходимого модуля событий;
— инициализировать объект события;
— назначить событие на необходимый узел DOM;

Для начала ознакомимся с существующими модулями:
— Events — родительский модуль. Его наследуют все модуля, о чём будет сказано ниже.
— HTMLEvents — включает в себя события элементов DOM.
— UIEvents — события клавиатуры.
— MouseEvents — события мыши.
— MutationEvents — события модификации дерева DOM.

Структура DOM 2 Events


Существует пять модулей DOM 2 events. Их имена отображают суть события:

Events
  • Охватывает все типы событий


HTMLEvents
  • abort
  • blur
  • change
  • error
  • focus
  • load
  • reset
  • resize
  • scroll
  • select
  • submit
  • unload


UIEvents
  • DOMActivate
  • DOMFocusIn
  • DOMFocusOut
  • keydown
  • keypress
  • keyup
  • также косвенно охватывает MouseEvents


MouseEvents
  • click
  • mousedown
  • mousemove
  • mouseout
  • mouseover
  • mouseup


MutationEvents
  • DOMAttrModified
  • DOMNodeInserted
  • DOMNodeRemoved
  • DOMCharacterDataModified
  • DOMNodeInsertedIntoDocument
  • DOMNodeRemovedFromDocument
  • DOMSubtreeModified


Другие события (такие как 'dblClick') не являются частью спецификации, и браузеры не требуют их поддержки. Браузеры, которые поддерживают их, либо выберут соответствующий модуль (например, ' dblClick' очень хорошо подходит для модуля MouseEvents) либо создадут новый модуль. Вы должны знать, в какой модуль браузер определит такое событие.

Реализация примера события — onClick


«Фальшивые» события создаются с использованием метода document.createEvent, который принимает имя соответствующего модуля событий, включающий необходимое событие. Далее следует подготовка события с помощью метода initEvent (название метода отличается для каждого модуля) и, наконец, запуск события на указанном элементе с помощью метода dispatchEvent. Если вы используете более конкретный тип события, а не универсальный (generic), можно предоставить дополнительную информацию о событии при его подготовке. Несколько сумбурно, но пример всё прояснит:

Чтобы создать событие 'click' на элементе, ID которого равен 'element_id', необходимо:


var element = document.getElementById('element_id'); // Получаем объект необходимого элемента
var o = document.createEvent('MouseEvents');  // Создаём объект события, выбран модуль событий мыши
o.initMouseEvent( 'click', true, true, window, 1, 12, 345, 7, 220, false, false, true, false, 0, null ); // Инициализируем объект события
element.dispatchEvent(o);  // Запускаем событие на элементе


Так как список параметров методов инициализации событий и их описания слишком велик, то во избежание захламления топика, даю ссылку, перейдя по которой можно детальнее ознакомиться с ними. тыц. Так же, ниже имена методов инициализации события оформлены как ссылки на страницы с описаниями параметров.

Примеры инициализации события для всех модулей:


HTMLEvents и generic Events
initEvent( 'type', bubbles, cancelable )

UIEvents
initUIEvent( 'type', bubbles, cancelable, windowObject, detail )

MouseEvents
initMouseEvent( 'type', bubbles, cancelable, windowObject, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget )

MutationEvents
initMutationEvent( 'type', bubbles, cancelable, relatedNode, prevValue, newValue, attrName, attrChange )

Обратите внимание, хотя prevValue, NewValue, и attrName могут принимать значение null, attrChange всегда должен быть определён, даже если событие не DOMAttrModified (значение 1, 2 или 3)

MouseEvents является подклассом UIEvents, в свою очередь, последний является подклассом модуля Events, так что если вы создаете объект MouseEvents, он будет также наследовать initUIEvent и initEvent методы UIEvents и интерфейсов Events. Так что вы можете выбрать метод инициализации события, который необходимо использовать при его подготовке.(nit*Event)

Запуск события с использованием generic-модуля событий (Демонстрация наследования)


Пример ниже демонстрирует запуск события, в котором используется универсальный метод initEvent, и оставляются неопределенными свойства в их значениях по умолчанию (однако, даже при использовании общих initEvent или initUIEvent, вы все равно должны использовать точное имя модуля, а не имя generic-модуля, или он не будет работать в Mozilla/Firefox, Konqueror, Safari):


var element = document.getElementById('element_id');
var o = document.createEvent('MouseEvents');
o.initEvent('click', true, true); // Используется унаследованный метод модуля Events, а не аналогичный модуля MouseEvents, объект которого был создан
element.dispatchEvent(o);


Клавиатурные события


Клавиатурные события немного сложнее, потому что Mozilla / Firefox не реализует стандарт корректно, и не позволит вам создать ключевые события с использованием общих (generic) UIEvents (он может просто зависнуть или «упасть» с критической ошибкой). Вместо этого он предоставляет внутренний модуль «KeyEvents, и метод инициализации „initKeyEvent“. Метод initKeyEvent принимает следующие параметры:'type', bubbles, cancelable, windowObject, ctrlKey, altKey, shiftKey, metaKey, keyCode, charCode. Вы можете использовать конструкцию if(window.KeyEvent) для обнаружения поддержки модуля KeyEvents в браузере. Браузеры, которые используют UIEvents, не имеют специфической функции инициализации для ключевых событий, поэтому некоторые свойства должны быть добавлены вручную:


var element = document.getElementById('element_id');

if( window.KeyEvent ) // Для FF
 {
  var o = document.createEvent('KeyEvents');
  o.initKeyEvent( 'keyup', true, true, window, false, false, false, false, 13, 0 );
} 
else // Для остальных браузеров
 {
  var o = document.createEvent('UIEvents');
  o.initUIEvent( 'keyup', true, true, window, 1 );
  o.keyCode = 13; // Указываем дополнительный параметр, так как initUIEvent его не принимает
}

element.dispatchEvent(evObj);


События в Internet Explorer


Инициализация событий в Internet Explorer существенно отличается от остальных браузеров. IE имеет метод для ручного запуска события, который называется fireEvent, и доступен с IE 5.5 +. В своей простейшей форме, это похоже на версию DOM, но пузырьки (bubbles) и отменяемые свойства (cancelable properties) недоступны. Метод fireEvent ожидает передачи одного или двух параметров. Первым должно быть название события (например, 'OnChange'), и необязательный второй параметр должен быть объект события, передаваемый обработчику.

Хотя объект window может обнаружить несколько событий в IE (например, load, unload, scroll, resize — все это должно, в соответствии со спецификацией, быть обнаружено объектом document, а не window), это не значит, что fireEvent возможно использовать с объектом window.


var element = document.getElementById('element_id');

if(document.createEvent) // Для реализации кроссбраузерности
 {
  var o = document.createEvent('MouseEvents');
  o.initEvent('click', true, false );
  element.dispatchEvent(o);
} 
else if( document.createEventObject ) // Для IE
 {
   o.fireEvent('click');
}


Если вы хотите указать дополнительную информацию о событии, необходимо использовать метод createEventObject для репликации CreateEvent и init*Event методов DOM. Метод createEventObject обычно возвращает пустой объект события, и вам нужно будет определить параметры самостоятельно.



var element = document.getElementById('element_id');

if(document.createEvent)  // Пример реализации в нормальных браузерах
{
  var o = document.createEvent('MouseEvents');
  o.initMouseEvent( 'click', true, true, window, 1, 12, 345, 7, 220, false, false, true, false, 0, null );
  element.dispatchEvent(evObj);
}

else if(document.createEventObject) // Реализация события в IE
{
  var o = document.createEventObject();
  o.detail = 0;
  o.screenX = 12;
  o.screenY = 345;
  o.clientX = 7;
  o.clientY = 220;
  o.ctrlKey = false;
  o.altKey = false;
  o.shiftKey = true;
  o.metaKey = false;
  o.button = 0;
  o.relatedTarget = null;
  element.fireEvent('click', o);
}


Заключение


Я весьма кратко осветил тему, однако, этого достаточно для понимания её сути. Генерация событий позволяет сделать „за ширмой“ много интересных вещей, а учитывая количество доступных событий, эта тема становится ещё интересней.

В статье приведены лишь несколько примеров и типов событий, однако, совершенно несложно описать любые другие из списка в начале. Из-за большого их количества, про все рассказать не получится, однако имена говорят сами за себя, так что любознательный %username% без труда нагуглит описание нужного события, список принимаемых им параметров и производимое действие.

Удачных Вам жизненных событий без рандомной их генерации :)

Что почитать?


Статья, которая послужила основой этого топика
Описание структуры модулей событий: таблицы, сравнения.
О событиях на Mozilla MDN
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 26
  • –1
    небольшие очепятки «жизненных» а не «жизнненых» и «безопасности» а не «безопастности», «Вобщем» пишется раздельно, не в обиду, просто исправь
    • +2
      Спасибо, исправил.
      • +2
        странно, за что коммент заминусовали :( кармы и так нет, а теперь и еще понизили, вот так помогай людям :(((
        • +1
          Обычно подобные вещи пишут в личку автору, а не в комментариях к топику. Но то такое.
          На LM можно почитать раздел «За что на хабре обычно минусуют», многим комментаторам будет полезно.
    • +2
      Эх, а ведь еще есть DOM 3 Events, от мохнатых годов, который сильно облегчает как создание, так и детектирования событий( он например позволяет узнать что у вас есть DOMMouseWheel)
      Да только нигде его нету, этого третьего :(
      • +1
        Знаю про DOM 3, но читал о нём только в документах W3C. На сайте Мозиллы есть табличка, кратко описывающая DOM 2 и DOM 3, и списки их событий.
        Если найду материал — обязательно напишу.
        • 0
          МС заявляет о поддержке некоторых частей DOM 3 Events. Но честно говоря не щупал и не изучал этого вопроса.
        • +1
          Уже за диаграмму в начале можно плюс статье поставить.
          • +2
            Последнее время топики из песочницы радуют информативностью и качеством.
            Спасибо за статью, было полезно.
            • +2
              может кому пригодится, пара вещей из опыта:
              не получится симулировать события: вставки, копирования, выделения (ctrl+v, ctrl+c, ctrl+a)

              Есть обертка, которую используют для тестирования в jquery.ui — github.com/eduardolundgren/jquery-simulate
              • +3
                Вы немного ошиблись. Все события IE начинаются с «on» прим бинда element.attachEvent("onclick", function);
                с кастомными событиями аналогично. Тут будет ошибка element.fireEvent('click', o);
                Работающий пример:
                <button onmouseover="this.fireEvent('onclick');" onclick="alert('click')">Hover me</button>

                Ещё из недостатков ИЕ: события сгенерированные вручную не вызывают defaultAction, т.е. если вы кликаете по анкору, то перехода по ссылке не будет, если кликаете по чекбоксу, то его состояние не изменися. Но ИЕ в этом плане не одинок ФФ тоже не вызывает defaultAction. Вебкиты и Опера вызывают.
                • +2
                  «Ручной» запуск (генерация) события не создает по умолчанию действие
                  Это больше не верно чем верно. defaultAction как я писал выше не вызывается в ФФ и ИЕ во всех остальных вызывается. И я считаю, что все браузеры должны вызывать defaultAction по кастомному эвенту (в таком поведении больше пользы чем вреда).

                  Пример
                  <script>
                  var simulateClick = function (ctrl, shift, isMiddle) {
                      var evt = document.createEvent('MouseEvents');
                      evt.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, 
                                           ctrl, false, shift, false, 
                                           isMiddle ? 1 : 0, null );
                      document.getElementById('link').dispatchEvent(evt);
                  }
                  </script>
                  <p><a href="http://ya.ru/" id="link">Link to ya.ru</a></p>
                  
                  <button onclick="simulateClick(true, true, false);">Simulate Ctrl+Shift+Click</button>
                  <button onclick="simulateClick(true, false, false);">Simulate Ctrl+Click</button>
                  <button onclick="simulateClick(false, false, false);">Simulate Click</button>
                  <button onclick="simulateClick(false, false, true);">Simulate Middle mouse button click</button>

                  Посмотреть живьём jsfiddle.net/azproduction/LRc7Z/
                  • +1
                    Это то что касается эмуляции click. Если же Вы будете эмулировать события клавиатуры, то ситуация такая: FF вызывает default action, а вот webkit, Opera, IE — нет. Для webkit это бага, которой прилично времени и ее до сих пор не исправили.
                    Было обидно так как делали экранную клавиатуру, решение webkit-only. Пришлось костылять.

                    С другой стороны, не всегда нужно эмулировать событие с нуля (createEvent -> initEvent). Бывают ситуации, когда событие одного элемента нужно передать другому. Например, если какой-то блок перекрывает скролируемый блок, то событие скрола (крутим колесико мышки) над перекрывающим блоком не будет приводить к прокрутке скролируемого блока (что логично, но вызывает дискомфорт у пользователя). В такой ситуации можно ловить mouse wheel событие у перекрывающего блока и передавать через dispatchEvent скролируемому. Тут нас опять ждет «сюрприз»: FF и IE9 выбрасывают исключение, Opera не поддерживает такого события (или плохо пробовали), зато в webkit все работает на ура.

                    DOM events вещь мощная, и можно много интересных вещей придумать/сделать — но вот текущая реализация стандарта в браузерах оставляет желать лучшего, у каждого свои проблемы.
                  • 0
                    Ой, я прамо испугался вначале, когда «Дом 2» в названии увидел.
                    • 0
                      А вот мне больше хотелось чтобы после DOM 2 было написано не Events, а Hell on Earth.
                  • 0
                    >>«Ручной» запуск (генерация) события не создает по умолчанию действие, связанное с этим событием.
                    Насколько я знаю, почти все события можно сгенерировать програмно, кроме onChange на input type=«file».
                    • 0
                      Как насчёт разворачивания браузера на полный экран, как по F11? мне бы очень пригодилась такая функциональность, нигде найти не могу, как сделать без принуждения пользователя нажимать F11.
                      • 0
                        F11 к DOM-у никакого отношения не имеет.
                        вы ведь не хотите чтоб у разработчика была возможность сэмулировать нажатие Enter при запросе на скачку/открытие файла…
                        • 0
                          однако, preventDefault() на открытие полноэкранного режима имеет эффект :)
                        • 0
                          Это невозможно habrahabr.ru/qa/4468/#answer_19542
                        • 0
                          Хоть это и не относится к теме DOM Events, но всё же добавлю: большинство браузеров имеют параметры, которые позволяют запустить их в полноэкранном режиме. Например, для IE это "-k":

                          iexplore.exe -k

                          либо, если необходимо открыть сразу с необходмой страницей (в том числе, локальным файлом)

                          iexplore.exe -k "http://www.google.com"

                          Одним из проектов было создание анкеты для использования её на устройствах с тач-скринами, вот там и пригодилась такая возможность: при загрузке ОС Опера стартовала сразу в полноэкранном режиме с открытой страничкой анкеты.
                      • 0
                        Тут находится последняя версия Document Object Model Level 3 Events Specification. По ссылке откроется пункт 5.5.1, в котором приведена сводная таблица событий. Любознательные могут сравнить документ с предыдущей версией от 8 сентября 2009.

                        Любопытны несколько новых событий модуля MouseEvents — mouseenter и mouseleave, что может стать полезным для всякого рода RIA, и новый модуль WheelEvent с пока единственным событием wheel.

                        Помимо прочего, добавлены CompositionEvent и FocusEvent.
                        • 0
                          Dom 2 postroi svou lubov

                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.