Как отследить javascript событие в браузере

Обновлено: 03.07.2024

Параллелизм/Многопоточность в JavaScript работает за счёт цикла событий (event loop), который отвечает за выполнение кода, сбора и обработки событий и выполнения под-задач из очереди (queued sub-tasks). Эта модель весьма отличается от других языков программирования, таких как C и Java.

Концепция жизненного цикла

В следующей секции объясняется теоретическая модель. Современные JavaScript движки внедряют/имплементируют и существенно оптимизируют этот процесс.

Визуальное представление

Вызов любой функции создаёт контекст выполнения (Execution Context). При вызове вложенной функции создаётся новый контекст, а старый сохраняется в специальной структуре данных - стеке вызовов (Call Stack).

Когда вызывается функция g , создаётся первый контекст выполнения, содержащий аргументы функции g и локальные переменные. Когда g вызывает f , создаётся второй контекст с аргументами f и её локальными переменными. И этот контекст выполнения f помещается в стек вызовов выше первого. Когда f возвращает результат, верхний элемент из стека удаляется. Когда g возвращает результат, её контекст также удалится, и стек становится пустым.

Объекты размещаются в куче. Куча — это просто имя для обозначения большой неструктурированной области памяти.

Очередь

Среда выполнения JavaScript содержит очередь задач. Эта очередь — список задач, подлежащих обработке. Каждая задача ассоциируется с некоторой функцией, которая будет вызвана, чтобы обработать эту задачу.

Когда стек полностью освобождается, самая первая задача извлекается из очереди и обрабатывается. Обработка задачи состоит в вызове ассоциированной с ней функции с параметрами, записанными в этой задаче. Как обычно, вызов функции создаёт новый контекст выполнения и заносится в стек вызовов.

Обработка задачи заканчивается, когда стек снова становится пустым. Следующая задача извлекается из очереди и начинается её обработка.

Цикл событий

Модель событийного цикла ( event loop ) называется так потому, что отслеживает новые события в цикле:

queue.waitForMessage ожидает поступления задач, если очередь пуста.

Запуск до завершения

Каждая задача выполняется полностью, прежде чем начнёт обрабатываться следующая. Благодаря этому мы точно знаем: когда выполняется текущая функция – она не может быть приостановлена и будет целиком завершена до начала выполнения другого кода (который может изменить данные, с которыми работает текущая функция). Это отличает JavaScript от такого языка программирования как C. Поскольку в С функция, запущенная в отдельном потоке, в любой момент может быть остановлена, чтобы выполнить какой-то другой код в другом потоке.

Добавление событий в очередь

В браузерах события добавляются в очередь в любое время, если событие произошло, а так же если у него есть обработчик. В случае, если обработчика нет – событие потеряно. Так, клик по элементу, имеющему обработчик события по событию click , добавит событие в очередь, а если обработчика нет – то и событие в очередь не попадёт.

Вызов setTimeout добавит событие в очередь по прошествии времени, указанного во втором аргументе вызова. Если очередь событий на тот момент будет пуста, то событие обработается сразу же, в противном случае событию функции setTimeout придётся ожидать завершения обработки остальных событий в очереди. Именно поэтому второй аргумент setTimeout корректно считать не временем, через которое выполнится функция из первого аргумента, а минимальное время, через которое она сможет выполниться.

Нулевые задержки

Связь нескольких потоков между собой

Никогда не блокируется

Очень интересное свойство цикла событий в JavaScript, что в отличие от множества других языков, поток выполнения никогда не блокируется. Обработка I/O обычно осуществляется с помощью событий и колбэк-функций, поэтому даже когда приложение ожидает запрос от IndexedDB или ответ от XHR, оно может обрабатывать другие процессы, например пользовательский ввод.

Существуют хорошо известные исключения как alert или синхронный XHR, но считается хорошей практикой избегать их использования.

Основная ценность javascript - в его интеграции со страницей. Любой документ или DOM-элемент умеет инициировать различные события, а на событие, зная его имя, можно назначить обработчик.

  1. Назначение обработчиков
  2. Объект "событие" ( event )
  3. Что дает объект события?
  4. Порядок срабатывания событий
  5. Действие браузера по умолчанию
  6. Смысл return false из обработчика
  7. Резюме

Практически все JavaScript-приложения выполняют те или иные действия, откликаясь на различные события.

Событие - это сигнал от браузера о том, что что-то произошло. читать дальше »

В объекте события содержится подробнейшая информация о том, что и где произошло.

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

Для инициализации страницы исторически использовалось событие window.onload , которое срабатывает после полной загрузки страницы и всех объектов на ней: счетчиков, картинок и т.п.

Событие onDOMContentLoaded - гораздо лучший выбор в 99% случаев. В этой статье рассмотрен код и основные приемы для его кроссбраузерной реализации. читать дальше »

В статье приводится сравнение способов добавления обработчиков через on(click) , addEventListener и attachEvent .

Рассмотрены особенности, плюсы и минусы разных способов.

Считается, что вы знакомы с основными свойствами объекта события и порядком их обработки. читать дальше »

В этой статье мы создадим мини-библиотеку, которая будет кросс-браузерно работать с событиями.

Ее задача - навешивать/убирать обработчики, а также делать одинаковой работу с ними для разных браузеров.

Основные требования: простота, грамотность и компактный код. читать дальше »

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

Если какой-нибудь обработчик содержит ошибку, то, генерируя исключение, он ломает цикл вызова остальных обработчиков текущего элемента/события.

Получается, что обработчики зависят друг от друга. Есть различные способы, как этого избежать. читать дальше »

  1. Основные сведения
  2. Пакеты событий
  3. Очередь событий
  4. Вложенные события
  5. Последовательность действий при рендеринге (отображении страницы)
  6. Отложенное выполнение
  7. Не-пользовательские события
  8. Alert-ы
  9. Загрузка страницы
  10. Выполнение блоков JavaScript
  11. Использование document.write()
  12. Построение DOM
  13. Прогрессивный рендеринг
  14. Долго выполняющиеся скрипты
  15. Состояния гонки
  16. Советы по управлению очерёдностью событий

Проблемы с очерёдностью исполнения являются источником некоторых наиболее коварных багов в JavaScript-приложениях. Ошибки, которые не проявлялись во время разработки, внезапно начинают приводить к проблемам, когда приложение используется конечным пользователем на старом компьютере или с медленным доступом в интернет. читать дальше »

  1. Виды кликов-событий
  2. Возможность отключения реакции браузера
  3. Последовательность генерации
    1. Левый одиночный клик
    2. Правый одиночный клик
    3. Средний клик
    4. Двойной клик
    1. Порядок и частота
    2. Текстовые элементы

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

    К разным кнопкам браузеры привязывают свои собственные "действия по-умолчанию", например - Firefox при клике на среднюю кнопку мыши открывает новую вкладку.
    В зависимости от браузера, для каких-то кнопок действия по-умолчанию можно отменять, а для каких-то - нельзя. читать дальше »

    События online и offline, которые описаны в стандарте HTML 5 предназначены для определения соединения с интернет.

    Это - лишь одно из множества усовершенствований для оффлайн-работы, которые предлагаются HTML5, и в настоящий момент эти события поддерживаются браузерами Firefox 3.5 и Internet Explorer 8.

    Чтобы продемонстрировать, как они работают, я снял небольшое видео. читать дальше »

    Все события JavaScript можно разделить на следующие категории: события мыши (Mouse Events), события клавиатуры (Keyboard Events), события объектов и фреймов (Frame/Object Events), события формы и элементов управления (Form Events), события перетаскивания (Drag Events), события анимации (Animation Events), события буфера обмена (Clipboard Events), события мультимедиа (Media Events), события трансформации (Transition Events), события, посылаемые сервером (Server-Sent Events), события касания (Touch Events), события печати (Print Events), разные события (Misc Events).

    JavaScript - Виды событий

    Внимание: все события в JavaScript пишутся строчными (маленькими) буквами.

    События мыши

    • mousedown – при нажатии кнопки мыши;
    • mouseup – при отпускании кнопки мыши;
    • click – при клике (порядок возникновения событий при click : mousedown -> mouseup -> click );
    • dblclick – при двойном клике (порядок возникновения событий при dblclick : mousedown -> mouseup -> click -> mousedown -> mouseup -> click -> dblclick );
    • mousemove – при перемещении курсора мыши;
    • mouseover – при вхождении курсора мыши в область, принадлежащей целевому элементу и других элементов, вложенных в него;
    • mouseenter – при вхождении указателя мыши в целевой элемент (в отличие от mouseover происходит только один раз при вхождении курсора в целевой элемент; при дальнейшем движении курсора и его вхождении в другие элементы (находящихся в целевом) – оно больше не возникает);
    • mouseout – при уходе курсора с целевого элемента и других элементов, вложенных в него;
    • mouseleave – при покидании границ целевого элемента (в отличие от mouseout не возникает при покидании курсора элементов вложенных в целевой);
    • contextmenu – при открытии контекстного меню.

    События при CSS переходе

    • transitionrun – возникает при создании CSS перехода (т.е. когда он добавляется к набору запущенных переходов, но не обязательно он начался);
    • transitionstart – происходит, когда CSS переход начинает выполняться;
    • transitioncancel – возникает, если CSS переход был отменен;
    • transitionend – происходит при завершении CSS перехода.

    События при CSS анимации

    • animationstart – происходит, когда CSS анимация начинается;
    • animationiteration – возникает, когда заканчивается одна итерация CSS анимации и начинается другая;
    • animationend – происходит при окончании CSS анимации.

    События клавиатуры

    Порядок возникновения событий: keydown -> keypress -> keyup .

    • keydown - событие происходит, когда нажата клавиша на клавиатуре над элементом, но ещё не отпущена.
    • keyup - событие происходит, когда нажатая клавиша на клавиатуре над элементом перешла в состояние отпущено.
    • keypress - событие происходит, когда пользователь нажал клавишу на клавиатуре над элементом.

    События объектов и фреймов

    События формы и элементов управления

    События перетаскивания

    События, связанные с перетаскиваемым объектом (draggable target, исходный объект):

    • dragstart – событие происходит, когда пользователь начал перетаскивать элемент;
    • drag – событие происходит, когда пользователь перетаскивает элемент;
    • dragend – событие происходит, когда пользователь закончил перетаскивания элемента, т.е. когда отпустил курсор мыши.

    События, связанные с объектом (drop target), который принимает перетаскиваемый объект (draggable target):

    • dragenter – событие происходит, когда перетаскиваемый объект (draggable target) вошёл в область элемента (drop target), который может принять перетаскиваемый объект (draggable target).
    • ragleave – событие происходит, когда перетаскиваемый объект (draggable target) покидает пределы элемента (drop target), который может его принять.
    • dragover - событие происходит, когда перетаскиваемый объект (draggable target) перемещается в области элемента (drop target), который может его принять.
    • drop - событие происходит, когда пользователь отпускает перетаскиваемый объект (draggable target) в область элемента (drop target), который может его принять.

    События буфера обмена

    • сopy - событие происходит, когда пользователь копирует содержимое элемента. Событие copy также происходит, когда пользователь копирует элемент (например, изображения, созданные с помощью элемента img ). Событие copy используется в основном для элементов input с type="text" .
    • сut - событие происходит, когда пользователь вырезает содержимое элемента.
    • paste - событие происходит, когда пользователь вставляет некоторое содержимое в элемент.

    События печати

    • afterprint - событие происходит, когда страница начинает печататься (т.е. после нажатия кнопки "Печать" в диалоговом окне) или если диалоговое окно "Печать" было закрыто.
    • beforeprint - событие возникает перед печатью страницы, т.е. до открытия диалогового окна "Печать".

    События, посылаемые сервером

    События мультимедиа

    В процессе загрузки аудио/видео события происходят в следующем порядке: loadstart -> durationchange -> loadedmetadata -> loadeddata -> progress -> canplay -> canplaythrough .

    Сегодня, в переводе десятого материала из серии, посвящённой особенностям работы механизмов JavaScript, мы расскажем о том, как отслеживать изменения в DOM с помощью API MutationObserver.

    Клиентские части веб-приложений становятся всё сложнее, требуют всё больше системных ресурсов. Происходит это по разным причинам, в частности из-за того, что таким приложениям нужны продвинутые интерфейсы, благодаря которым раскрываются их возможности, и из-за того, что им приходится выполнять сложные вычисления на стороне клиента.


    Всё это ведёт к усложнению задачи контроля состояния интерфейсов приложений в процессе их жизненного цикла. Эта задача становится ещё масштабнее в том случае, если речь идёт о разработке чего-то вроде фреймворка или даже обычной библиотеки, когда, например, нужно реагировать на то, что происходит со страницей и выполнять какие-то действия, зависящие от DOM.

    Обзор

    MutationObserver — это Web API, предоставляемое современными браузерами и предназначенное для обнаружения изменений в DOM. С помощью этого API можно наблюдать за добавлением или удалением узлов DOM, за изменением атрибутов элементов, или, например, за изменением текстов текстовых узлов. Зачем это нужно?

    Есть немало ситуаций, в которых API MutationObserver может оказаться очень кстати. Например:

    • Вам нужно уведомить пользователя веб-приложения о том, что на странице, с которой он работает, произошли какие-то изменения.
    • Вы работаете над новым интересным JS-фреймворком, который динамически загружает JavaScript-модули, основываясь на изменениях DOM.
    • Возможно, вы работаете над WYSIWYG-редактором и пытаетесь реализовать функционал отмены и повтора действий. Воспользовавшись API MutationObserver , вы будете, в любой момент, знать о том, какие изменения произошли на странице, а это означает, что вы легко сможете их отменять.


    Текстовый редактор, работающий в браузере

    Выше приведены лишь несколько ситуаций, в которых возможности MutationObserver могут оказаться полезными. На самом деле их гораздо больше.

    Как пользоваться MutationObserver

    Использовать MutationObserver в веб-приложениях довольно просто. Нужно создать экземпляр MutationObserver , передав соответствующему конструктору функцию, которая будет вызываться каждый раз, когда в DOM будут происходить изменения. Первый аргумент функции — это коллекция всех произошедших мутаций в виде единого пакета. Для каждой мутации предоставляется информация о её типе и об изменениях, которые она представляет.


    У созданного объекта есть три метода:

    • Метод observe запускает процесс отслеживания изменений DOM. Он принимает два аргумента — узел DOM, за которым нужно наблюдать, и объект с параметрами.
    • Метод disconnect останавливает наблюдение за изменениями.
    • Метод takeRecords возвращает текущую очередь экземпляра MutationObserver , после чего очищает её.


    Теперь предположим, что в DOM имеется простейший элемент div :


    Используя jQuery, можно удалить атрибут class из этого элемента:


    Благодаря тому, что мы начали наблюдение за изменениями, предварительно вызвав mutationObserver.observe(. ) , и тому, что функция, реагирующая на поступление нового пакета изменений, выводит полученные данные в консоль, мы увидим в консоли содержимое соответствующего объекта MutationRecord:


    Объект MutationRecord

    Тут можно видеть мутации, причиной которых стало удаление атрибута class .

    И, наконец, для того, чтобы прекратить наблюдение за DOM после того, как работа завершена, можно сделать следующее:

    Поддержка MutationObserver в различных браузерах

    API MutationObserver пользуется широкой поддержкой в браузерах:


    Поддержка MutationObserver

    Альтернативы MutationObserver

    Стоит отметить, что механизм наблюдениями за изменениями DOM, который предлагает MutationObserver , не всегда был доступен разработчикам. Чем они пользовались до появления MutationObserver ? Вот несколько вариантов:

    • Опрос (polling).
    • Механизм MutationEvents .
    • CSS-анимация.

    ▍Опрос

    Самый простой и незамысловатый способ отслеживания изменений DOM — опрос. Используя метод setInterval можно запланировать периодическое выполнение функции, которая проверяет DOM на предмет изменений. Естественно, использование этого метода значительно снижает производительность веб-приложений.

    ▍MutationEvents

    API MutationEvents было представлено в 2000 году. Несмотря на то, что это API позволяет решать возлагаемые на него задачи, события мутации вызываются после каждого изменения DOM, что, опять же, приводит к проблемам с производительностью. Теперь API MutationEvents признано устаревшим и вскоре современные браузеры перестанут его поддерживать.


    Поддержка MutationEvents

    ▍CSS-анимация

    На самом деле, альтернатива MutationObserver в виде CSS-анимаций может показаться несколько странной. Причём тут анимация? В целом, идея тут заключается в создании анимации, которая будет вызвана после того, как элемент будет добавлен в DOM. В момент запуска анимации будет вызвано событие animationstart . Если назначить обработчик для этого события, можно узнать точное время добавления нового элемента в DOM. Время выполнения анимации при этом должно быть настолько маленьким, чтобы она была практически незаметна для пользователя.

    Для того чтобы воспользоваться этим методом, сначала нужен родительский элемент, за добавлением в который новых узлов мы хотим наблюдать:


    Для организации наблюдения за добавлением в него узлов нужно настроить последовательность ключевых кадров CSS-анимации, которые запустятся при добавлении узла:


    После создания ключевых кадров анимация должна быть применена к элементам, за которыми нужно наблюдать. Обратите внимание на длительность анимации. Она очень мала, благодаря чему анимация оказывается практически незаметной.


    Тут мы добавляем анимацию ко всем узлам-потомкам элемента container-element . Когда анимация заканчивается, вызывается соответствующее событие.

    Теперь нужна JS-функция, которая будет играть роль обработчика событий. Внутри функции, в первую очередь, необходимо выполнить проверку event.animationName для того, чтобы убедиться, что это — именно та анимация, которая нас интересует.


    Теперь добавим обработчик события к родительскому элементу. В разных браузерах это делается по-разному:


    Вот как обстоит дело с поддержкой CSS-анимации в различных браузерах.


    Поддержка CSS-анимации в различных браузерах

    Итоги

    Мы рассмотрели API MutationObserver и альтернативные способы наблюдения за изменениями DOM. Надо отметить, что MutationObserver имеет множество преимуществ перед этими альтернативами. В целом, можно говорить о том, что это API способно сообщать о любых изменениях, которые могут возникать в DOM, о том, что оно хорошо оптимизировано, давая информацию об изменениях, собранную в пакеты. Кроме того, API MutationObserver пользуется поддержкой всех основных современных браузеров, существуют и полифиллы для него, основанные на MutationEvents .

    Автор материала отмечает, что MutationObserver занимает центральное место в библиотеке SessionStack, которая направлена на организацию сбора данных о том, что происходит с веб-страницами.

    Предыдущие части цикла статей:

    Читайте также: