Как да определим щракването извън елемента?

Имам някои HTML менюта, които показвам изцяло, когато потребителят кликне върху заглавието на тези менюта. Бих искал да скрия тези елементи, когато потребителят кликне извън областта на менюто.

Възможно ли е нещо подобно с jQuery?

 $("#menuscontainer").clickOutsideThisElement(function() { // Hide the menus }); 
2160
30 сент. определени от Sergio del Amo 30 сеп. 2008-09-30 16:17 '08 в 16:17 2008-09-30 16:17
@ 78 отговора
  • 1
  • 2
  • 3

ЗАБЕЛЕЖКА. Използването на stopEventPropagation() трябва да се избягва, тъй като нарушава нормалния поток от събития в DOM. Вижте тази статия за повече информация. Опитайте да използвате този метод .

Прикачете събитие за кликване към тялото на документа, който затваря прозореца. Прикрепете отделно събитие за кликване към контейнер, който спира разпространението в тялото на документа.

 $(window).click(function() { //Hide the menus if visible }); $('#menucontainer').click(function(event){ event.stopPropagation(); }); 
1671
30 сент. отговорът е даден от Eran Galperin 30 септември 2008-09-30 16:38 '08 в 16:38 ч. 2008-09-30 16:38

Можете да слушате събитие за кликване за document и след това да се уверите, че #menucontainer не е предшественик или цел на .closest() елемента с .closest() .

Ако случаят не е такъв, елементът #menucontainer е извън #menucontainer и може да бъде скрит безопасно.

 export function hideOnClickOutside(selector) { const outsideClickListener = (event) => { $target = $(event.target); if (!$target.closest(selector).length  $(selector).is(':visible')) { $(selector).hide(); removeClickListener(); } } const removeClickListener = () => { document.removeEventListener('click', outsideClickListener) } document.addEventListener('click', outsideClickListener) } 
border=0

Редактиране - 2018-03-11

За тези, които не искат да използват jQuery. Ето го горния код в обикновен vanillaJS (ECMAScript6).

https://developer.mozilla.org/en-US/docs/Web/API/Element/closest. 

1212
12 июня '10 в 11:35 2010-06-12 11:35 Отговорът е даден Art 12 юни '10 в 11:35 2010-06-12 11:35

Как да открием щракване извън елемента?

Причината, поради която този въпрос е толкова популярен и има толкова много отговори, е, че е измамно сложен. След почти осем години и десетки отговори съм искрено изненадан да видя колко малко внимание се обръща на достъпността.

Бих искал да скрия тези елементи, когато потребителят кликне извън областта на менюто.

Това е благородна кауза и е действителен проблем. Името на въпроса е това, което повечето отговори изглежда се опитват да разрешат, съдържа нещастен червен херинг.

Съвет: тази дума е "клик"!

Всъщност не искате да обвързвате манипулаторите на кликвания.

Ако свържете манипулаторите на кликвания, за да затворите диалоговия прозорец, вече не сте успели. Причината да се провалите е, че не всички предизвикват click . Потребителите, които не използват мишката, ще могат да излязат от диалоговия прозорец (а изскачащото ви меню може да е вид диалогов прозорец), като натиснете Tab , след което няма да могат да четат съдържанието зад диалоговия прозорец без да щракват.

Нека преформулираме въпроса.

Как да затворите диалога, когато потребителят приключи с него?

Това е целта. За съжаление, сега трябва да userisfinishedwiththedialog събитие, и това свързване не е толкова просто.

И така, как можем да разберем, че потребителят е завършил използването на диалоговия прозорец?

събитие за focusout

Добро начало е да се определи дали фокусът е напуснал диалога.

Съвет: внимавайте с blur на събитията, blur не се прилага, ако събитието е свързано с фазата на изпускане!

jQuery focusout ще бъде страхотно. Ако не можете да използвате jQuery, можете да използвате blur в етапа на заснемане:

 element.addEventListener('blur', ..., true); // use capture: ^^^^ 

Освен това, за много диалогови прозорци ще трябва да позволите на контейнера да се фокусира. Добавете tabindex="-1" така че диалоговият прозорец да може да получава динамично фокусиране без прекъсване на потока на раздела.

 div { display: none; } .active { display: block; } 
Example <div id="example" tabindex="-1"> Lorem ipsum <a href="http://example.com">dolor sit amet. </div> 

Ако играете с тази демонстрация повече от минута, трябва бързо да започнете да разглеждате проблемите.

Първо, връзката в диалоговия прозорец не е налична. Опитвайки се да кликнете върху него или върху него, той ще затвори диалоговия прозорец, преди да се получи взаимодействие. Това се дължи на факта, че фокусирането на вътрешния елемент причинява събитие за focusout преди да се focusin фокусиращото събитие.

Fix е опашка на състоянието в цикъла на събитията. Това може да стане чрез setImmediate(...) или setTimeout(..., 0) за браузъри, които не поддържат setImmediate . След като е поставен в опашка, той може да бъде отменен с последващо focusin :

 $('.submenu').on({ focusout: function (e) { $(this).data('submenuTimer', setTimeout(function () { $(this).removeClass('submenu--active'); }.bind(this), 0)); }, focusin: function (e) { clearTimeout($(this).data('submenuTimer')); } }); 

 div { display: none; } .active { display: block; } 
Example <div id="example" tabindex="-1"> Lorem ipsum <a href="http://example.com">dolor sit amet. </div> 

Вторият проблем е, че диалоговият прозорец няма да се затвори, когато кликнете отново върху връзката. Това се дължи на факта, че диалогът губи фокуса, причинявайки задействането при затваряне, след което натискането на връзката води до отваряне на диалога.

Както и в предишната версия, трябва да контролирате състоянието на фокуса. Като се има предвид, че промяната на състоянието вече е поставена на опашка, въпросът е да се обработват фокус събития в интерактивен режим:

Трябва да изглежда познато.
 $('a').on({ focusout: function () { $(this.hash).data('timer', setTimeout(function () { $(this.hash).removeClass('active'); }.bind(this), 0)); }, focusin: function () { clearTimeout($(this.hash).data('timer')); } }); 

 div { display: none; } .active { display: block; } 
Example <div id="example" tabindex="-1"> Lorem ipsum <a href="http://example.com">dolor sit amet. </div> 

Клавиш Esc

Ако смятате, че всичко е направено чрез управление на състоянията на фокуса, можете да направите повече за опростяване на потребителския опит.

Това често е „приятно да имаш“, но обикновено се случва, когато имаш модален или изскачащ файл от всякакъв вид, който го затваря с клавиша Esc .

 keydown: function (e) { if (e.which === 27) { $(this).removeClass('active'); e.preventDefault(); } } 

 div { display: none; } .active { display: block; } 
Example <div id="example" tabindex="-1"> Lorem ipsum <a href="http://example.com">dolor sit amet. </div> 

Ако знаете, че в диалоговия прозорец има персонализирани елементи, не е необходимо да фокусирате директно диалоговия прозорец. Ако създадете меню, можете да фокусирате първия елемент от менюто.

 click: function (e) { $(this.hash) .toggleClass('submenu--active') .find('a:first') .focus(); e.preventDefault(); } 

 .menu { list-style: none; margin: 0; padding: 0; } .menu:after { clear: both; content: ''; display: table; } .menu__item { float: left; position: relative; } .menu__link { background-color: lightblue; color: black; display: block; padding: 0.5em 1em; text-decoration: none; } .menu__link:hover, .menu__link:focus { background-color: black; color: lightblue; } .submenu { border: 1px solid black; display: none; left: 0; list-style: none; margin: 0; padding: 0; position: absolute; top: 100%; } .submenu--active { display: block; } .submenu__item { width: 150px; } .submenu__link { background-color: lightblue; color: black; display: block; padding: 0.5em 1em; text-decoration: none; } .submenu__link:hover, .submenu__link:focus { background-color: black; color: lightblue; } 
Menu 1</a> <ul class="submenu" id="menu-1" tabindex="-1"> <li class="submenu__item"><a class="submenu__link" href="http://example.com/#1">Example 1</a></li> <li class="submenu__item"><a class="submenu__link" href="http://example.com/#2">Example 2</a></li> <li class="submenu__item"><a class="submenu__link" href="http://example.com/#3">Example 3</a></li> <li class="submenu__item"><a class="submenu__link" href="http://example.com/#4">Example 4</a></li> </ul> </li> <li class="menu__item"> <a class="menu__link" href="#menu-2">Menu 2</a> <ul class="submenu" id="menu-2" tabindex="-1"> <li class="submenu__item"><a class="submenu__link" href="http://example.com/#1">Example 1</a></li> <li class="submenu__item"><a class="submenu__link" href="http://example.com/#2">Example 2</a></li> <li class="submenu__item"><a class="submenu__link" href="http://example.com/#3">Example 3</a></li> <li class="submenu__item"><a class="submenu__link" href="http://example.com/#4">Example 4</a></li> </ul> </li> </ul> lorem ipsum <a href="http://example.com/">dolor sit amet. 

WAI-ARIA роли и друга помощ за достъпност

Надявам се, че този отговор покрива основите на наличната поддръжка на клавиатура и мишка за тази функция, но тъй като вече е доста значителен, ще избегна да обсъждам ролите и атрибутите на WAI-ARIA , но силно препоръчвам на разработчиците да се позовават на спецификацията за подробности ролите, които трябва да използват, и всички други атрибути, които са от значение.

220
12 июля '16 в 2:29 2016-07-12 02:29 отговорът е даден zzzzBov 12 юли '16 в 2:29 ч. 2016-07-12 02:29

Други решения тук не ми помогнаха, така че трябваше да използвам:

 if(!$(event.target).is('#foo')) { // hide menu } 
132
07 июля '09 в 2:10 2009-07-07 02:10 отговорът е даден на Денис на 07.07.09 в 2:10 2009-07-07 02:10

Имам приложение, което работи подобно на примера на Eran, с изключение на това, че при отварянето на менюто прикачвам събитие за натискане на тялото ...

 $('#menucontainer').click(function(event) { $('html').one('click',function() { // Hide the menus }); event.stopPropagation(); }); 

Повече информация за функцията jQuery one()

122
30 сент. Отговор, даден от Joe Lencioni 30 септември 2008-09-30 21:13 '08 в 21:13 ч. 2008-09-30 21:13
 $("#menuscontainer").click(function() { $(this).focus(); }); $("#menuscontainer").blur(function(){ $(this).hide(); }); 

Работи за мен.

36
17 нояб. Отговорът е даден от user212621 17 ноември. 2009-11-17 09:13 '09 в 9:13 2009-11-17 09:13

Сега има приставка за това: външни събития ( публикация в блога )

Следното се случва, когато манипулаторът на clickoutside (WLOG) е свързан с елемент:

  • елемент се добавя към масив, който съдържа всички елементи с манипулатори на clickoutside.
  • манипулаторът на ( имена ) е свързан с документа (ако вече не съществува)
  • при всяко кликване в документа събитието clickoutside се задейства за тези елементи в този масив, които не са равни или родителският обект на целевото кликване
  • Освен това, event.target за събитието clickoutside е настроен на елемента, върху който потребителят е натиснал (така че дори знаете, че потребителят е щракнал, а не само, че е щракнал отвън).

По този начин, не се прекратяват събития поради разпространението, а допълнителните манипулатори за кликвания могат да се използват „по-горе“ с помощта на външен манипулатор.

35
05 апр. отговорът е даден Волфрам 05 апр. 2010-04-05 13:07 '10 в 13:07 2010-04-05 13:07

След проучване намерих три работни решения (забравих връзките към страниците за справка)

Първо решение

 <script> //The good thing about this solution is it doesn't stop event propagation. var clickFlag = 0; $('body').on('click', function () { if(clickFlag == 0) { console.log('hide element here');  } else { clickFlag=0; } }); $('body').on('click','#testDiv', function (event) { clickFlag = 1; console.log('showed the element');  }); </script> 

Второ решение

 <script> $('body').on('click', function(e) { if($(e.target).closest('#testDiv').length == 0) {  } }); </script> 

Трето решение

 <script> var specifiedElement = document.getElementById('testDiv'); document.addEventListener('click', function(event) { var isClickInside = specifiedElement.contains(event.target); if (isClickInside) { console.log('You clicked inside') } else { console.log('You clicked outside') } }); </script> 
32
02 нояб. отговорът е даден Rameez Rami 02 Nov. 2015-11-02 11:33 '15 в 11:33 2015-11-02 11:33

Той работи чудесно за мен!

 $('html').click(function (e) { if (e.target.id == 'YOUR-DIV-ID') { //do something } else { //do something } }); 
29
04 июня '12 в 17:08 2012-06-04 17:08 отговорът е даден srinath 04 юни '12 в 17:08 2012-06-04 17:08

Не мисля, че наистина трябва да затворите менюто, когато потребителят го кликне; трябва да затворите менюто, когато потребителят кликне навсякъде в страницата. Ако кликнете върху меню или външно меню, трябва ли да го затворите правилно?

Не намирайки задоволителни отговори, помолих да напиша този блог предишния ден. За по-педантични, има няколко грешки, които да вземете под внимание:

  • Ако прикрепите манипулатор на събитие при натискане към елемента на тялото по време на кликване, не забравяйте да изчакате втори клик, преди да затворите менюто и да развържете събитието. В противен случай, на екрана на слушателя ще се появи събитие, което е отворило менюто, което трябва да затвори менюто.
  • Ако използвате event.stopPropogation () в събитие за кликване, никакви други елементи на страницата ви не могат да имат функцията click-where-to-close.
  • Прикрепването на манипулатор на събитие при натискане към елемент на тялото е неограничено не е ефективно решение.
  • Сравняването на целта на събитието и неговите родители с създателя на манипулатора предполага, че искате да затворите менюто, когато щракнете върху него, когато това, което наистина искате, е да го затворите, когато кликнете някъде в страницата.
  • Слушането на събития на елемента body ще направи вашия код по-крехък. Стилът е невинен, тъй като може да го разбие: body { margin-left:auto; margin-right: auto; width:960px;} body { margin-left:auto; margin-right: auto; width:960px;}
24
19 мая '11 в 0:15 2011-05-19 00:15 отговорът е даден 34m0 19 май '11 в 0:15 2011-05-19 00:15

Като друг постер се казва, че има много грешки, особено ако елементът, който показвате (в случая менюто) има интерактивни елементи. Намерих следния метод доста надежден:

 $('#menuscontainer').click(function(event) { //your code that shows the menus fully //now set up an event listener so that clicking anywhere outside will close the menu $('html').click(function(event) { //check up the tree of the click target to check whether user has clicked outside of menu if ($(event.target).parents('#menuscontainer').length==0) { // your code to hide menu //this event listener has done its job so we can unbind it. $(this).unbind(event); } }) }); 
23
22 дек. отговор, даден от benb на 22 декември 2011-12-22 14:59 '11 в 14:59 2011-12-22 14:59

Просто решение на ситуацията е:

 $(document).mouseup(function (e) { var container = $("YOUR SELECTOR"); // Give you class or ID if (!container.is(e.target)  // If the target of the click is not the desired div or section container.has(e.target).length === 0) // ... nor a descendant-child of the container { container.hide(); } }); 

Горният скрипт ще скрие div ако външността на събитие за кликване на div се задейства.

За повече информация вижте следния блог: http://www.codecanal.com/detect-click-outside-div-using-javascript/

20
15 дек. Отговорът е даден от Jitendra Damor на 15 декември. 2015-12-15 06:50 '15 в 6:50 2015-12-15 06:50

Проверете целевата точка на събитие за щракване на прозореца (тя трябва да бъде разпределена в прозореца, ако не е заснета никъде) и се уверете, че тя не е от елементите на менюто. Ако не, вие сте извън менюто си.

Или проверете позицията за кликване и вижте дали е в областта на менюто.

18
30 сент. Отговор, даден от Крис Макдоналд на 30 септември 2008-09-30 16:20 '08 в 16:20 ч. 2008-09-30 16:20

разтваряне1

Вместо да използвате event.stopPropagation (), който може да има някои странични ефекти, просто дефинирайте прост променлив флаг и добавете едно, if условие. Изпробвах го и работих добре без странични ефекти от стоппропагацията:

 var flag = "1"; $('#menucontainer').click(function(event){ flag = "0"; // flag 0 means click happened in the area where we should not do any action }); $('html').click(function() { if(flag != "0"){ // Hide the menus if visible } else { flag = "1"; } }); 

Solution2

Използване на опростено условие:

 $(document).on('click', function(event){ var container = $("#menucontainer"); if (!container.is(event.target)  // If the target of the click isn't the container... container.has(event.target).length === 0) // ... nor a descendant of the container { // Do whatever you want to do when click is outside the element } }); 
18
28 янв. Отговор Iman Sedighi Jan 28 2015-01-28 20:24 '15 в 20:24 2015-01-28 20:24

Успях с нещо подобно:

 var $menuscontainer = ...; $('#trigger').click(function() { $menuscontainer.show(); $('body').click(function(event) { var $target = $(event.target); if ($target.parents('#menuscontainer').length == 0) { $menuscontainer.hide(); } }); }); 

Логиката е следната: когато #menuscontainer показан #menuscontainer , #menuscontainer манипулатора на щракване с тяло, което крие #menuscontainer само ако целта (клик) не е дете.

15
02 дек. Отговорът е даден от Chu Yeow 02 Dec. 2010-12-02 12:53 '10 в 12:53 2010-12-02 12:53

Като опция:

 var $menu = $('#menucontainer'); $(document).on('click', function (e) { // If element is opened and click target is outside it, hide it if ($menu.is(':visible')  !$menu.is(e.target)  !$menu.has(e.target).length) { $menu.hide(); } }); 

Няма проблем да спре разпространението на събития и по-добре да поддържа няколко менюта на една и съща страница, където щракването върху второто меню при първото отваряне ще остави първото откритие в решението StopPropagation.

14
24 июля '14 в 12:41 2014-07-24 12:41 Отговорът е даден от Богдан Лизанец на 24 юли 14 в 12:41 2014-07-24 12:41

Намерих този метод в някои jQuery календарен плъгин.

 function ClickOutsideCheck(e) { var el = e.target; var popup = $('.popup:visible')[0]; if (popup==undefined) return true; while (true){ if (el == popup ) { return true; } else if (el == document) { $(".popup").hide(); return false; } else { el = $(el).parent()[0]; } } }; $(document).bind('mousedown.popup', ClickOutsideCheck); 
12
28 июля '11 в 17:06 2011-07-28 17:06 отговорът е даден от nazar kuliyev 28 юли '11 в 17:06 2011-07-28 17:06

Тук е решението за vanilla javascript за бъдещи зрители.

При кликване върху който и да е елемент в документа, ако се кликне върху идентифицирания елемент, или скритият елемент не е скрит, а скритият елемент не съдържа елемент с едно кликване, превключете елемента.

 (function () { "use strict"; var hidden = document.getElementById('hidden'); document.addEventListener('click', function (e) { if (e.target.id == 'toggle' || (hidden.style.display != 'none'  !hidden.contains(e.target))) hidden.style.display = hidden.style.display == 'none' ? 'block' : 'none'; }, false); })(); 

Ако имате няколко ключове на една и съща страница, можете да използвате нещо подобно:

  • Добавете името на класа, hidden в сгъващия елемент.
  • Когато кликнете върху документ, затворете всички скрити елементи, които не съдържат елемент, с едно кликване и не са скрити.
  • Ако щракваният елемент е превключвател, превключете посочения елемент.

10
20 мая '15 в 1:52 2015-05-20 01:52 отговорът е даден на Tiny Giant на 20 май'15 в 1:52 2015-05-20 01:52

Събитието има свойство, наречено event.path на елемента, което е "статично подреден списък на всичките му предци в дървесен ред". За да проверите дали дадено събитие е възникнало от конкретен DOM елемент или един от неговите дъщерни елементи, просто проверете пътя към този конкретен DOM елемент. Тя може да се използва и за проверка на няколко елемента на логическо OR при проверка на елемент в some функция.

 #main { display: inline-block; } 
14 апр. Отговор, даден от Дан Филип на 14 април 2017-04-14 07:27 '17 в 7:27 2017-04-14 07:27