jQuery: Закрытие элемента по клику за пределами его области (вне элемента)

Когда бывает нужно скрыть (закрыть) элемент по клику за его пределами? Первое, что мне приходит в голову, это всплывающие (popup) окна и выпадающие (dropdown) меню. Понятное дело, что применение может быть гораздо шире.

Согласитесь, что намного удобнее закрыть всплывающее окно, кликнув за его пределами, чем тащить указатель мыши к крестику и кликать по нему. А в некоторых ситуациях крестик и вовсе использовать не удастся (выпадающие кастомные селекты или меню).

И да, как только люди с этим не шаманят! Решений в интернете полно, но оптимальными их не назовёшь. Самое часто встречающееся из них, это когда скрипт проверяет, находится ли указатель мыши над элементом или нет.

Ну вот зачем усложнять код?

Хотя на самом деле ответ действительно очевиден — разработчики не знают, что можно сделать правильнее и проще, и делают так, как могут. Давайте разберем алгоритм:

  1. У нас открыто всплывающее окно, меню или что-то ещё.
  2. Нам нужно, чтобы оно закрывалось не только на крестик (если таковой вообще имеется), но и по клику где-нибудь за его границами.
  3. Значит нам нужно событие «Когда произошёл клик по странице».
  4. В событие нужно добавить два условия «Если клик был не по нашему элементу» и «Если клик был не по дочерним элементам нашего элемента».
  5. Если оба условия выполняются, скрываем элемент.

Проще простого, код jQuery будет таким:

jQuery(function($){
	$(document).mouseup(function (e){ // событие клика по веб-документу
		var div = $("#popup"); // тут указываем ID элемента
		if (!div.is(e.target) // если клик был не по нашему блоку
		    && div.has(e.target).length === 0) { // и не по его дочерним элементам
			div.hide(); // скрываем его
		}
	});
});
Всплывающее окно на jQuery

Для данного примера можно ещё добавить событие onclick, чтобы окно также закрывалось при клике на крестик.

Миша

В последние годы я долго не знал, что мне делать с сайтом misha.blog, ведь он практически не приносит никакого профита, но недавно я осознал, что моя миссия – способствовать распространению WordPress. Ведь WordPress – это лучший движок для разработки сайтов – как для тех, кто готов использовать заложенную структуру этой CMS, так и для тех, кто предпочитает headless решения.

Сам же я впервые познакомился с WordPress в 2009 году. Организатор WordCamp. Преподаватель в школах Epic Skills и LoftSchool.

Если вам нужна помощь с вашим сайтом или может даже разработка с нуля на WordPress / WooCommerce — пишите. Я и моя команда сделаем вам всё на лучшем уровне.

Комментарии — 18

Комментирование этого поста более не доступно.
  • Илья 2 января 2015 #

    Вот вопрос - а надо ли проверять дочерние элементы, если событие всё равно поднимается по дереву DOM вверх? Я как-то делал без проверки, и работало.

    • Миша 3 января 2015 #

      Если у вас работает без проверки, значит не обязательно их проверять.

  • Алексей 14 мая 2015 #

    Коротко и ясно. Спасибо!

  • Алексей 15 сентября 2015 #

    // тут указываем ID элемента
    тут не только id можно указывать, но и класс элемента.

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

  • Stepan 10 октября 2015 #

    Спасибо большое за решение! Полдня потратил почти, ничего вразумительного не нашёл)) А тут ваша статья!

  • j4d 10 октября 2015 #

    Спасибо! Отличное решение.

  • Maxim 19 октября 2015 #

    Супер! Спасибо большое все работает! Подскажите стоит писать проверку если popup закрыт? чтобы скрипт срабатывал не каждый раз когда я нажимаю не важно где!

    • Миша 20 октября 2015 #

      Как хотите, но не обязательно)

  • Александр 9 ноября 2015 #

    Есть такой вопрос. Сам попап больших размеров высоту, я сделал так, что при открытие попапа скрол у боди исчезает, появляется попап с маской, у которой как раз скрол и появляется, но проблема в том, что при событие клика работает и на сам скрол, т.е при зажатие ползунка и перетягивании его и отпускании закрывается весь попап. Как избежать подобной ситуации? Спасибо.

    • Миша 9 ноября 2015 #

      Предыдущий ответ на ваш комментарий выглядел как спам..

      Значит у попапа должен быть скролл?
      Я рекомендую сам попап запихнуть в ещё один блок div и именно на этот div, который без скролла и повесить событие.

      • Александр 9 ноября 2015 #

        Я не совсем корректно выразился. Так и есть, попап лежит в диве, который при своем появлении имеет посишн фиксед, скрипт в этот момент убирает скрол у всего боди. В этом диве и лежит попап, который посишн релатив, и добавлен скрол авто диву, тем самым появляется скрол с боку страницы, только в случае, если поап больше высоты экрана. Вопрос заключается в следующем, как избежать срабатывания события клика по самому скролу и данного дива? Точнее выходит такЖ появляется попап, он больше высоты монитора, я могу его прокрутить колесом мышки либо же зажать позунок и тянуть вверх(вниз), но после отпускания ползунка сработает срипт на закрытие всего попапа. Скрол находится у родительского элемента попапа. Спасибо.

        • Миша 10 ноября 2015 #

          Нужно глянуть на сайт, можете скинуть ссылку?

          • Александр 13 ноября 2015 #

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

  • Евгений Кириллов 30 июня 2016 #

    на iPhone не сработало 🙁

    Сделал так:
    label - фоновый фиксированный блок 100% высоты и ширины с затемнением 🙂
    input type="checkbox" - скрыт, через состояние выбран/не выбран влияем на отображение последующих блоков через css

    input[id="fon-inp"]:checked ~ .fon {
       display: none;
    }
    • Миша 30 июня 2016 #

      А можно ссылку, гляну у себя 🙂

      • Евгений Кириллов 30 июня 2016 #

        Ссылку на "не сработало" или ссылку на вариант через "label"?

        • Миша 30 июня 2016 #

          На неработающий вариант (если конечно он где-то остался).

          • Евгений Кириллов 30 июня 2016 #

            Уже убрал, но вечером попробую воссоздать.