Браузерные события, часть 4 из 5. Действие браузера по умолчанию

27.04.2022

Теги: DOMFrontendHTMLJavaScriptWeb-разработкаСобытиеТеория

Многие события автоматически влекут за собой действие браузера. Например, клик по ссылке инициирует переход на новый адрес, а нажатие кнопки «отправить» в форме — отправку данных на сервер. Если мы обрабатываем событие в 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>

Поиск: DOM • Frontend • HTML • JavaScript • Web-разработка • Событие • Теория • Event

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.