Bсходя из настроек по умолчанию, браузеры могут реагировать на разные события.
Например, когда пользователь нажимает кнопку «Отправить» (Submit) на форме, форма по умолчанию отправляется по указанному URL-адресу.
При нажатии на дочерний элемент событие щелчка также возникнет и в его родительском элементе, так как он является главным объектом.
В некоторых случаях вам может потребоваться переопределить эти стандартные настройки. Из этой статьи вы узнаете, что такое методы event.preventDefault() и event.stopPropagation(), и как их использовать для того, чтобы отменить некоторые действия браузера по умолчанию.
event.preventDefault()
Этот метод позволяет предотвратить выполнение действий по умолчанию, которые браузеры выполняют при возникновении событий.
Ниже представлены примеры того, как веб-страницы действуют по умолчанию, и способы переопределения этих действий с помощью event.preventDefault().
Как переопределить отправку формы по умолчанию
Когда пользователь отправляет форму (нажимает кнопку отправки), срабатывает действие формы по умолчанию, то есть она отправляет данные, указанные в форме, на URL-адрес, где эти данные будут обрабатываться.
У элементов формы есть атрибуты action и method, которые определяют URL-адрес, куда будут отправлены данные, и тип запроса (get, post и т.д.) соответственно.
Если эти атрибуты не указаны, то URL-адресом по умолчанию, на который будет отправляться форма, является текущий URL, а типом запроса по умолчанию - get.
Например, вот этот код:
<form>
<input name="email" />
<input name="password" />
<input type="submit" />
</form>
генерирует вот такую страницу:
При отправке формы с введенными именем (dillion) и паролем (password) вы можете увидеть запрос get, отправленный по адресу 127.0.0.1:5500/index.html, например:
Это действие – пример того, как браузеры обрабатывают формы по умолчанию.
Однако, прежде чем отправлять запрос, вам может потребовать выполнить какие-нибудь действия с данными. Особенно это касается современного подхода к работе с формами.
Возможно, перед оправкой запроса на URL-адрес вы захотите выполнить какую-нибудь проверку или обработку данных, настройку заголовков и т.д.
В таком случае вам необходимо не допустить выполнение действия по умолчанию. Вы можете сделать это вот так:
<form id='form'>
...
</form>
const form = document.getElementById('form')
form.addEventListener('submit', (event) => {
event.preventDefault()
// process data and submit a request manually { // самостоятельная обработка данных и отправка запроса }
})
В результате, процесс отправки формы теперь полностью контролируется вами.
Как переопределить действие по умолчанию при нажатии на ссылку
Когда вы нажимаете на ссылку (тег привязки a с атрибутом href), то по умолчанию переходите по нажатой ссылке.
Но что, если вы хотите перехватить это действие и, возможно, сделать что-нибудь перед переходом на другую страницу? Например, проверить наличие у пользователя доступа к этой странице. И вот как это можно сделать:
<a id="link" href="https://google.com">Google</a>
const link = document.getElementById("link")
link.addEventListener("click", event => {
event.preventDefault()
// do something and navigate { // выполняете какие-то действия и переходите по ссылке }
})
Вы можете протестировать это код. Когда вы нажмете на ссылку «Google», вы не перейдете по ней, так как это действие по умолчанию запрещено. Теперь вам придется самостоятельно управлять этим процессом.
event.stopPropagation()
Propagation (англ. распространение) – это процесс распространения чего-либо, в данном случае событий. Метод stopPropagation используется для предотвращения распространения событий при возникновении события на элементе.
В JavaScript, когда вы запускаете событие на элементе, оно всплывает вверх по дереву к родителям и прародителям этого элемента. По сути, элемент, на котором произошло событие, находится «внутри» родительского объекта, поэтому он также получает события.
Чтобы лучше объяснить, приведу пример.
Нажатие на дочерний элемент
Предположим, что у вас есть вот такие элементы:
<div>
<button>Click me</button>
</div>
Когда вы нажимаете на кнопку (button), вы также нажимаете и на контейнер div, так как кнопка находится в контейнере. Эта логика говорит о том, что событие щелчка распространяется от кнопки к контейнеру, и это событие продолжает распространяться по всем предкам до тех пор, пока не достигнет корня.
Чтобы убедиться в этом, я объясню, как это работает, на примере следующего кода:
<div id="div">
<button id="button">Click me</button>
</div>
const div = document.getElementById('div')
const button = document.getElementById('button')
button.addEventListener('click', () => {
console.log('button clicked')
})
div.addEventListener('click', () => {
console.log('div container clicked')
})
Когда вы попытаетесь запустить его в своем браузере и нажмете на кнопку, то получите следующее:
Контейнер div также получил событие щелчка, поэтому также вызывается функция обратного вызова щелчка.
Распространение событий – это поведение событий и элементов по умолчанию, но иногда некоторое поведение может оказаться нежелательными. Вот один из многих примеров.
Вот так выглядит всплывающее окно для нового сообщения Gmail:
Наверху у вас есть три кнопки действий. Одна кнопка сворачивает всплывающее окно, вторая – переключает это окно в полноэкранный режим, а третья – закрывает его.
Однако верхняя панель с надписью «New message» также имеет обработчик события щелчка, поэтому при нажатии на нее всплывающее окно тоже свернется:
Здесь вам следует избегать лишь одной вещи. Вероятнее всего, вы не захотите, чтобы при нажатии любой кнопки событие щелчка распространялось и на верхнюю панель, а также выполнялось действие для этого события. Я имею в виду, что вы наверняка не захотите, чтобы при нажатии кнопки закрытия также сворачивалась верхняя панель.
В таких случаях распространение событий необходимо останавливать.
Допустим, что всплывающее окно построено следующим образом:
<div id='top-bar'>
<!-- The Message Element -->
<!-- The Buttons -->
</div>
const topBar = document.getElementById('top-bar')
const closeButton = document.getElementById('close-btn')
topBar.addEventListener('click', () => {
// minimize or maximize popup { // свернуть или развернуть всплывающее окно }
})
closeButton.addEventListener('click', () => {
// close popup { // закрыть всплывающее окно }
})
И теперь, чтобы предотвратить распространение события щелчка на верхнюю панель, вам понадобиться добавить метод stopPropagation к прослушивателю кнопки. Для этого вам необходимо его обновить:
closeButton.addEventListener('click', (event) => {
event.stopPropagation()
// close popup { // закрыть всплывающее окно }
})
При этом верхняя панель все также будет получать событие щелчка, но только при непосредственном нажатии на нее.
Заключение
Разница между event.preventDefault() и event.stopPropagation() заключается в том, что первый метод предотвращает действия браузера по умолчанию, а второй блокирует поведение событий по умолчанию, то есть их распространению вверх по дереву.
Эти действия и такое поведение не являются ошибочными, и вам не нужно беспокоиться о них, когда вы пишите код. Но есть ситуации, когда вам может потребоваться переопределить их, как в примерах выше.