Браузерные события, часть 4 из 5. Действие браузера по умолчанию
27.04.2022
Теги: DOM • Frontend • HTML • JavaScript • Web-разработка • Событие • Теория
Многие события автоматически влекут за собой действие браузера. Например, клик по ссылке инициирует переход на новый адрес, а нажатие кнопки «отправить» в форме — отправку данных на сервер. Если мы обрабатываем событие в JavaScript, то зачастую такое действие браузера нам не нужно.
Отмена действия браузера
Есть два способа отменить действие браузера:
- Основной способ — это воспользоваться стандартным методом
preventDefault()
объекта событияevent
- Если обработчик назначен через
on{event}
(не черезaddEventListener
) — вернутьfalse
из обработчика
В следующем примере при клике по ссылке переход не произойдёт:
Нажмите <a href="/" onclick="return false">здесь</a> или <a href="/" onclick="event.preventDefault()">здесь</a>
Пример меню для сайта
Рассмотрим простое меню для сайта, например:
<ul id="menu"> <li><a href="/html">HTML</a></li> <li><a href="/javascript">JavaScript</a></li> <li><a href="/css">CSS</a></li> </ul>
В html-разметке все элементы меню являются не кнопками, а ссылками. Но нам необходимо обрабатывать клики в JavaScript, а стандартное действие браузера (переход по ссылке) — отменить.
menu.onclick = function(event) { if (event.target.nodeName !== 'A') return; let href = event.target.getAttribute('href'); alert(href); // здесь обработка события клика return false; // отменить действие браузера }
Если мы уберём return false
, то после выполнения обработчика события браузер выполнит «действие по умолчанию» — переход по адресу из href
. А это нам здесь не нужно, мы обрабатываем клик сами.
Кстати, использование здесь делегирования событий делает наше меню очень гибким. Мы можем добавить вложенные списки и стилизовать их с помощью CSS — обработчик не потребует изменений.
mousedown
для поля <input>
приводит к фокусировке на нём и запускает событие focus
. Если мы отменим событие mousedown, то фокусирования не произойдёт.
Опция «passive» для обработчика
Необязательная опция passive:true
для addEventListener
сигнализирует браузеру, что обработчик не собирается выполнять preventDefault()
. Почему это может быть полезно?
Есть некоторые события, как touchmove
на мобильных устройствах (когда пользователь перемещает палец по экрану), которое по умолчанию начинает прокрутку, но мы можем отменить это действие, используя preventDefault()
в обработчике.
Поэтому, когда браузер обнаружит такое событие, он должен для начала запустить все обработчики и после, если preventDefault()
не вызывается нигде, он может начать прокрутку. Это может вызвать ненужные задержки в пользовательском интерфейсе.
Опция passive:true
сообщает браузеру, что обработчик не собирается отменять прокрутку. Тогда браузер начинает её немедленно, обеспечивая максимально плавный интерфейс, параллельно обрабатывая событие.
Для некоторых браузеров (Firefox, Chrome) опция passive
по умолчанию включена в true
для таких событий, как touchstart
и touchmove
.
Свойство event.defaultPrevented
Свойство event.defaultPrevented
установлено в true
, если действие по умолчанию было предотвращено, и false
— если нет. Давайте посмотрим практический пример, когда использование defaultPrevented
может быть полезно.
По умолчанию браузер при событии contextmenu
(клик правой кнопкой мыши) показывает контекстное меню со стандартными опциями. Но мы хотим вместо стандартного меню показывать свое — причем для кнопок и документа будут разные меню.
<p>Правый клик здесь вызывает контекстное меню документа</p> <button id="elem">Правый клик здесь вызывает контекстное меню кнопки</button> <script> elem.oncontextmenu = function(event) { event.preventDefault(); alert('Контекстное меню кнопки'); }; document.oncontextmenu = function(event) { event.preventDefault(); alert('Контекстное меню документа'); }; </script>
Сейчас проблема заключается в том, что когда мы кликаем по элементу elem
, то мы получаем два меню — контекстное меню для кнопки и (событие всплывает вверх) контекстное меню для документа. Давайте это исправим.
<p>Правый клик здесь вызывает контекстное меню документа</p> <button id="elem">Правый клик здесь вызывает контекстное меню кнопки</button> <script> elem.oncontextmenu = function(event) { event.preventDefault(); event.stopPropagation(); alert('Контекстное меню кнопки'); }; document.oncontextmenu = function(event) { event.preventDefault(); alert('Контекстное меню документа'); }; </script>
Теперь контекстное меню для кнопки работает как задумано. Но цена слишком высока. Мы навсегда запретили доступ к информации о правых кликах для любого внешнего кода, включая счётчики, которые могли бы собирать статистику, и т.п. Это слегка неразумно.
Альтернативным решением было бы проверить в обработчике document
, было ли отменено действие по умолчанию? Если да, тогда событие было обработано, и нам не нужно на него реагировать.
<p>Правый клик здесь вызывает контекстное меню документа</p> <button id="elem">Правый клик здесь вызывает контекстное меню кнопки</button> <script> elem.oncontextmenu = function(event) { event.preventDefault(); alert('Контекстное меню кнопки'); }; document.oncontextmenu = function(event) { if (event.defaultPrevented) return; // событие уже обработано event.preventDefault(); alert('Контекстное меню документа'); }; </script>
- Браузерные события, часть 5 из 5. Генерация пользовательских событий
- Браузерные события, часть 3 из 5. Делегирование
- Браузерные события, часть 2 из 5. Погружение и всплытие
- Браузерные события, часть 1 из 5. Основные сведения
- DOM, часть 4 из 4. Атрибуты и свойства
- DOM, часть 3 из 3. Поиск элементов
- DOM, часть 2 из 3. Изменение документа
Поиск: DOM • Frontend • HTML • JavaScript • Web-разработка • Событие • Теория • Event