Прежде, чем начать, сразу скажу, что в этом уроке вы найдёте две реализации обработки клика вне блока – на чистом JavaScript и на jQuery.
Кроме того, если вы хотить прокачать свои знания в JavaScript, jQuery и даже React.js, то хочу порекомендовать вам свой видеокурс!
Когда бывает нужно скрыть (закрыть) элемент по клику за его пределами? Первое, что мне приходит в голову, это всплывающие (popup) окна и выпадающие (dropdown) меню. Понятное дело, что применение может быть гораздо шире.
Согласитесь, что намного удобнее осуществлять закрытие модального окна по клику вне окна, чем тащить указатель мыши к крестику и кликать по нему. А в некоторых ситуациях крестик и вовсе использовать не удастся (выпадающие кастомные селекты или меню).
И да, как только люди с этим не шаманят! Решений в интернете полно, но оптимальными их не назовёшь. Самое часто встречающееся из них, это когда скрипт проверяет, находится ли указатель мыши над элементом или нет.
Ну вот зачем усложнять код?
Хотя на самом деле ответ действительно очевиден — разработчики не знают, что можно сделать правильнее и проще, и делают так, как могут. Давайте разберем алгоритм:
const div = document.querySelector( '#popup'); document.addEventListener( 'click', (e) => { const withinBoundaries = e.composedPath().includes(div); if ( ! withinBoundaries ) { div.style.display = 'none'; // скрываем элемент т к клик был за его пределами } })
И, так как мы всё-же говорили о закрытии модального окна, то я не могу в качестве бонуса не предоставить вам код его закрытия при нажатии на клавишу Escape на клавиатуре.
document.addEventListener('keydown', function(e) { if( e.keyCode == 27 ){ // код клавиши Escape, но можно использовать e.key div.style.display = 'none'; } });
jQuery(function($){ $(document).mouseup( function(e){ // событие клика по веб-документу var div = $( "#popup" ); // тут указываем ID элемента if ( !div.is(e.target) // если клик был не по нашему блоку && div.has(e.target).length === 0 ) { // и не по его дочерним элементам div.hide(); // скрываем его } }); });
Для разнообразия я использовал событие mouseup
, т.е. когда кнопка мыши была нажата и уже отпущена. Для данного примера можно ещё добавить событие onclick
на крестике, чтобы окно также закрывалось при клике на крестик.
И также в качестве бонуса – закрытие модального окна при нажатии клавиши Escape на клавиатуре:
$(document).on('keyup', function(e) { if ( e.key == "Escape" ) { $( "#popup" ).hide(); } });
Чтобы оставить комментарий, пожалуйста, зарегистрируйтесь или войдите.
Вот вопрос - а надо ли проверять дочерние элементы, если событие всё равно поднимается по дереву DOM вверх? Я как-то делал без проверки, и работало.
Если у вас работает без проверки, значит не обязательно их проверять.
Коротко и ясно. Спасибо!
// тут указываем ID элемента
тут не только id можно указывать, но и класс элемента.
Спасибо за статью, время сберегла! Очень толково и сразу все заработало.
Спасибо большое за решение! Полдня потратил почти, ничего вразумительного не нашёл)) А тут ваша статья!
Спасибо! Отличное решение.
Супер! Спасибо большое все работает! Подскажите стоит писать проверку если popup закрыт? чтобы скрипт срабатывал не каждый раз когда я нажимаю не важно где!
Как хотите, но не обязательно)
Есть такой вопрос. Сам попап больших размеров высоту, я сделал так, что при открытие попапа скрол у боди исчезает, появляется попап с маской, у которой как раз скрол и появляется, но проблема в том, что при событие клика работает и на сам скрол, т.е при зажатие ползунка и перетягивании его и отпускании закрывается весь попап. Как избежать подобной ситуации? Спасибо.
Предыдущий ответ на ваш комментарий выглядел как спам..
Значит у попапа должен быть скролл?
Я рекомендую сам попап запихнуть в ещё один блок div и именно на этот div, который без скролла и повесить событие.
Я не совсем корректно выразился. Так и есть, попап лежит в диве, который при своем появлении имеет посишн фиксед, скрипт в этот момент убирает скрол у всего боди. В этом диве и лежит попап, который посишн релатив, и добавлен скрол авто диву, тем самым появляется скрол с боку страницы, только в случае, если поап больше высоты экрана. Вопрос заключается в следующем, как избежать срабатывания события клика по самому скролу и данного дива? Точнее выходит такЖ появляется попап, он больше высоты монитора, я могу его прокрутить колесом мышки либо же зажать позунок и тянуть вверх(вниз), но после отпускания ползунка сработает срипт на закрытие всего попапа. Скрол находится у родительского элемента попапа. Спасибо.
Нужно глянуть на сайт, можете скинуть ссылку?
Спасибо, разобрался, поставил обработчик на родительский блок. Проблема решена.
на iPhone не сработало :(
Сделал так:
label - фоновый фиксированный блок 100% высоты и ширины с затемнением :)
input type="checkbox" - скрыт, через состояние выбран/не выбран влияем на отображение последующих блоков через css
А можно ссылку, гляну у себя :)
Ссылку на "не сработало" или ссылку на вариант через "label"?
На неработающий вариант (если конечно он где-то остался).
Уже убрал, но вечером попробую воссоздать.
Зашибись решение. Наверное, оно прекрасно работает на странице, где есть один элемент, и больше ничего нет :)
А как быть, когда у меня на странице пять выпадающих списков и ещё двадцать других элементов со своими обработчиками onClick?
Рад, что оно вам понравилось :)
А так ещё существуют CSS-классы...
только в примере указано событие mouseup а не click
по этому на тачскринах и не работает
Спасибо, друг. Не первый раз твои советы помогли. Успехов тебе и здоровья!
Спасибо!
Если закрывать вне поля, то поле (модальное окно в моём случае) больше не открывается. С чем может быть связано?
Возможно, что ваше модальное окно отображается другим способом, а не при помощи display:none / display:block;