Jak wykryć kliknięcie poza elementem?

Mam kilka menu HTML, które wyświetlam całkowicie, gdy użytkownik kliknie na głowę tych menu. Chciałbym ukryć te elementy, gdy użytkownik kliknie poza obszarem menu.

Czy coś takiego jest możliwe z jQuery?

$("#menuscontainer").clickOutsideThisElement(function() {
    // Hide the menus
});
Author: Sergio del Amo, 2008-09-30

30 answers

Uwaga: używanie {[1] } jest czymś, czego należy unikać, ponieważ przerywa normalny przepływ zdarzeń w DOM. Zobacz Ten artykuł aby uzyskać więcej informacji. Rozważ użycie zamiast tej metody .

Dołącz Zdarzenie kliknięcia do treści dokumentu, które zamyka okno. Dołącz osobne Zdarzenie kliknięcia do kontenera, które zatrzymuje propagację do treści dokumentu.

$(window).click(function() {
//Hide the menus if visible
});

$('#menucontainer').click(function(event){
    event.stopPropagation();
});
 1640
Author: Eran Galperin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-04 01:03:34

Możesz nasłuchać zdarzenia click na document, a następnie upewnić się, że #menucontainer nie jest przodkiem ani celem klikniętego elementu, używając .closest().

Jeśli tak nie jest, to kliknięty element znajduje się poza #menucontainer i można go bezpiecznie ukryć.

$(document).click(function(event) { 
    if(!$(event.target).closest('#menucontainer').length) {
        if($('#menucontainer').is(":visible")) {
            $('#menucontainer').hide();
        }
    }        
});

Edycja-2017-06-23

Możesz również posprzątać po słuchaczu zdarzeń, jeśli planujesz zamknąć menu i chcesz przestać słuchać wydarzeń. Funkcja ta oczyści tylko nowo utworzone listener, zachowując wszelkie inne słuchacze kliknięć na document. Składnia ES2015:

export function hideOnClickOutside(selector) {
  const outsideClickListener = (event) => {
    if (!$(event.target).closest(selector).length) {
      if ($(selector).is(':visible')) {
        $(selector).hide()
        removeClickListener()
      }
    }
  }

  const removeClickListener = () => {
    document.removeEventListener('click', outsideClickListener)
  }

  document.addEventListener('click', outsideClickListener)
}

Edycja-2018-03-11

Dla tych, którzy nie chcą używać jQuery. Oto powyższy kod w prostym vanillaJS (ECMAScript6).

function hideOnClickOutside(element) {
    const outsideClickListener = event => {
        if (!element.contains(event.target)) { // or use: event.target.closest(selector) === null
            if (isVisible(element)) {
                element.style.display = 'none'
                removeClickListener()
            }
        }
    }

    const removeClickListener = () => {
        document.removeEventListener('click', outsideClickListener)
    }

    document.addEventListener('click', outsideClickListener)
}

const isVisible = elem => !!elem && !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ) // source (2018-03-11): https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js 

Uwaga: Jest to oparte na komentarzu Alex, aby po prostu użyć !element.contains(event.target) zamiast części jQuery.

Ale element.closest() jest teraz dostępny również we wszystkich głównych przeglądarkach(wersja W3C różni się nieco od jQuery). Polyfills można znaleźć tutaj: https://developer.mozilla.org/en-US/docs/Web/API/Element/closest

 1169
Author: Art,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-11 07:15:22

Jak wykryć kliknięcie poza elementem?

Powodem, dla którego to pytanie jest tak popularne i ma tak wiele odpowiedzi, jest to, że jest zwodniczo złożone. Po prawie ośmiu latach i dziesiątkach odpowiedzi jestem szczerze zaskoczony, widząc, jak mało troszczymy się o dostępność.

Chciałbym ukryć te elementy, gdy użytkownik kliknie poza obszarem menu.

Jest to szlachetna przyczyna i jest to rzeczywisty problem. Na tytuł pytania-czyli to, na co większość odpowiedzi zdaje się próbować odpowiedzieć-zawiera niefortunny czerwony śledź.

Podpowiedź: to słowo "kliknij" !

Tak naprawdę nie chcesz wiązać programów obsługi kliknięć.

Jeśli wiążesz programy obsługi kliknięć, aby zamknąć okno dialogowe, już się nie powiodło. Powodem, dla którego zawiodłeś, jest to, że nie wszyscy wywołują click zdarzenia. Użytkownicy nie używający myszy będą mogli uciec z okna dialogowego (a wyskakujące menu jest prawdopodobnie typ okna dialogowego) przez naciśnięcie Tab , a następnie nie będą mogli odczytać zawartości za oknem bez późniejszego wywołania zdarzenia click.

Więc przeformułujmy pytanie.

Jak zamknąć okno dialogowe, gdy Użytkownik skończy z nim?

To jest cel. Niestety, teraz musimy powiązać Zdarzenie userisfinishedwiththedialog, a to Wiązanie nie jest takie proste.

Więc jak możemy wykryć, że użytkownik skończył używać dialog?

focusout Zdarzenie

Dobrym początkiem jest ustalenie, czy fokus opuścił okno dialogowe.

Wskazówka: uważaj na zdarzenie blur, blur nie propaguje się, jeśli zdarzenie było związane z fazą bulgotania!

JQuery ' s focusout będzie dobrze. Jeśli nie możesz użyć jQuery, możesz użyć blur podczas fazy przechwytywania:

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

Ponadto w przypadku wielu okien dialogowych musisz zezwolić kontenerowi na uzyskanie ostrości. Dodaj tabindex="-1", aby umożliwić okno dialogowe, aby otrzymywać ostrość dynamicznie bez przerywania przepływu zakładek.

$('a').on('click', function () {
  $(this.hash).toggleClass('active').focus();
});

$('div').on('focusout', function () {
  $(this).removeClass('active');
});
div {
  display: none;
}
.active {
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#example">Example</a>
<div id="example" tabindex="-1">
  Lorem ipsum <a href="http://example.com">dolor</a> sit amet.
</div>

Jeśli grasz z tym demo dłużej niż minutę powinieneś szybko zacząć widzieć problemy.

Po pierwsze, link w oknie dialogowym nie jest klikalny. Próba kliknięcia na nią lub zakładkę do niej doprowadzi do zamknięcia okna dialogowego przed rozpoczęciem interakcji. Dzieje się tak, ponieważ skupienie elementu wewnętrznego wyzwala zdarzenie focusout przed ponowne wywołanie zdarzenia focusin.

Poprawką jest kolejkowanie zmiany stanu w pętli zdarzenia. Można to zrobić za pomocą setImmediate(...) lub setTimeout(..., 0) dla przeglądarek, które nie obsługują setImmediate. Po kolejce można ją anulować przez kolejne 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'));
  }
});

$('a').on('click', function () {
  $(this.hash).toggleClass('active').focus();
});

$('div').on({
  focusout: function () {
    $(this).data('timer', setTimeout(function () {
      $(this).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this).data('timer'));
  }
});
div {
  display: none;
}
.active {
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#example">Example</a>
<div id="example" tabindex="-1">
  Lorem ipsum <a href="http://example.com">dolor</a> sit amet.
</div>

Drugi problem polega na tym, że okno dialogowe nie zostanie zamknięte po ponownym naciśnięciu łącza. Dzieje się tak, ponieważ okno dialogowe traci ostrość, uruchamiając zachowanie zamknięcia, po czym kliknięcie łącza uruchamia okno dialogowe do ponownego otwarcia.

Podobnie jak w poprzednim numerze, stan skupienia musi być zarządzany. Ponieważ zmiana stanu została już ustawiona w kolejce, jest to tylko kwestia obsługi zdarzeń focus na wyzwalaczach dialogowych: [37]} to powinno wyglądać znajomo

$('a').on({
  focusout: function () {
    $(this.hash).data('timer', setTimeout(function () {
      $(this.hash).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this.hash).data('timer'));  
  }
});

$('a').on('click', function () {
  $(this.hash).toggleClass('active').focus();
});

$('div').on({
  focusout: function () {
    $(this).data('timer', setTimeout(function () {
      $(this).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this).data('timer'));
  }
});

$('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;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#example">Example</a>
<div id="example" tabindex="-1">
  Lorem ipsum <a href="http://example.com">dolor</a> sit amet.
</div>

Esc klucz

Jeśli myślałeś, że skończyłeś z obsługą Stanów skupienia, możesz zrobić więcej, aby uprościć obsługę.

To często jest to funkcja "przyjemna", ale często zdarza się, że gdy masz modalne lub wyskakujące okienko dowolnego rodzaju, klawisz Esc zamknie ją.

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

$('a').on('click', function () {
  $(this.hash).toggleClass('active').focus();
});

$('div').on({
  focusout: function () {
    $(this).data('timer', setTimeout(function () {
      $(this).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this).data('timer'));
  },
  keydown: function (e) {
    if (e.which === 27) {
      $(this).removeClass('active');
      e.preventDefault();
    }
  }
});

$('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;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#example">Example</a>
<div id="example" tabindex="-1">
  Lorem ipsum <a href="http://example.com">dolor</a> sit amet.
</div>

Jeśli wiesz, że w oknie dialogowym znajdują się elementy, które można ustawić ostrość, nie musisz ustawiać ostrości bezpośrednio. Jeśli tworzysz menu, możesz skupić się na pierwszej pozycji menu.

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

$('.menu__link').on({
  click: function (e) {
    $(this.hash)
      .toggleClass('submenu--active')
      .find('a:first')
      .focus();
    e.preventDefault();
  },
  focusout: function () {
    $(this.hash).data('submenuTimer', setTimeout(function () {
      $(this.hash).removeClass('submenu--active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this.hash).data('submenuTimer'));  
  }
});

$('.submenu').on({
  focusout: function () {
    $(this).data('submenuTimer', setTimeout(function () {
      $(this).removeClass('submenu--active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this).data('submenuTimer'));
  },
  keydown: function (e) {
    if (e.which === 27) {
      $(this).removeClass('submenu--active');
      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;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="menu">
  <li class="menu__item">
    <a class="menu__link" href="#menu-1">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</a> sit amet.

WAI-ARIA role i inne Wsparcie Dostępności

Ta odpowiedź, miejmy nadzieję, obejmuje podstawy dostępnej obsługi klawiatury i myszy dla tej funkcji, ale ponieważ jest już dość spora, uniknę dyskusji na temat ról i atrybutów WAI-ARIA , jednak ja {46]} wysoce {47]} zalecam, aby implementatorzy odwoływali się do specyfikacji, aby uzyskać szczegóły na temat ról, których powinni używać i wszelkich innych odpowiednich atrybutów.

 204
Author: zzzzBov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-29 16:10:01

Inne rozwiązania tutaj nie działały dla mnie, więc musiałem użyć:

if(!$(event.target).is('#foo'))
{
    // hide menu
}
 129
Author: Dennis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-06 23:10:07

Mam aplikację, która działa podobnie do przykładu Eran, z tym, że dołączam Zdarzenie click do ciała, gdy otwieram menu... Trochę tak:

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

  event.stopPropagation();
});

Więcej informacji o funkcji jQuery one()

 120
Author: Joe Lencioni,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-26 14:57:12
$("#menuscontainer").click(function() {
    $(this).focus();
});
$("#menuscontainer").blur(function(){
    $(this).hide();
});
Mi pasuje.
 34
Author: user212621,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-17 06:13:44

Teraz jest do tego plugin: zdarzenia zewnętrzne (blog post )

Następująca sytuacja ma miejsce, gdy clickoutside handler (WLOG) jest powiązany z elementem:

  • element jest dodawany do tablicy, która zawiera wszystkie elementy zclickoutside handlerami
  • a ( namespace) Kliknij handler jest powiązany z dokumentem (jeśli go nie ma)
  • na dowolnym Kliknij w dokumencie, Zdarzenie clickoutside jest wyzwalane dla tych elementów w tablicy, które nie są równe lub rodzicem click-events target
  • dodatkowo wydarzenie.cel zdarzenia clickoutside jest ustawiony na element, na który użytkownik kliknął (więc wiesz nawet, co użytkownik kliknął, a nie tylko, że kliknął na zewnątrz)

Więc żadne zdarzenia nie są zatrzymywane przed propagacją i dodatkoweclick mogą być użyte "powyżej" elementu z zewnętrznym-handlerem.

 33
Author: Wolfram,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-13 07:46:56

To zadziałało dla mnie doskonale!!

$('html').click(function (e) {
    if (e.target.id == 'YOUR-DIV-ID') {
        //do something
    } else {
        //do something
    }
});
 28
Author: srinath,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-04 14:30:47

Po badaniach znalazłem trzy działające rozwiązania (zapomniałem linków do strony)

Pierwsze rozwiązanie

<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');
            /* Hide element here */
        }
        else {
            clickFlag=0;
        }
    });
    $('body').on('click','#testDiv', function (event) {
        clickFlag = 1;
        console.log('showed the element');
        /* Show the element */
    });
</script>

Drugie rozwiązanie

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

Trzecie rozwiązanie

<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>
 28
Author: Rameez Rami,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-30 16:55:18

Nie wydaje mi się, aby to, czego naprawdę potrzebujesz, to zamknięcie menu, gdy użytkownik kliknie na zewnątrz; to, czego potrzebujesz, to zamknięcie menu, gdy użytkownik kliknie w dowolnym miejscu na stronie. Jeśli klikniesz na menu, lub poza menu powinno się zamknąć prawo?

Brak satysfakcjonujących odpowiedzi powyżej skłonił mnie do napisania tego posta na blogu na drugi dzień. Dla bardziej pedantycznych, istnieje wiele gotchas do zwrócenia uwagi:

  1. jeśli dołączysz obsługę zdarzenia click do ciała element w momencie kliknięcia pamiętaj, aby poczekać na drugie kliknięcie przed zamknięciem menu i odłączeniem zdarzenia. W przeciwnym razie Zdarzenie kliknięcia, które otworzyło menu, będzie bańką aż do słuchacza, który musi zamknąć menu.
  2. Jeśli używasz event.stopPropogation() w przypadku zdarzenia click, żadne inne elementy na twojej stronie nie mogą mieć funkcji click-anywhere-to-close.
  3. dołączenie obsługi zdarzenia click do elementu body w nieskończoność nie jest rozwiązaniem wykonującym
  4. porównanie celu zdarzenia, a rodzice twórcy programu obsługi zakładają, że to, co chcesz, to zamknięcie menu po kliknięciu go, kiedy to, co naprawdę chcesz, to zamknięcie go po kliknięciu w dowolnym miejscu na stronie.
  5. słuchanie zdarzeń na elemencie ciała sprawi, że Twój kod będzie bardziej kruchy. Stylizacja tak niewinna jak to by ją złamało: body { margin-left:auto; margin-right: auto; width:960px;}
 23
Author: 34m0,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-19 07:00:42

Jak powiedział inny plakat, istnieje wiele gotchas, zwłaszcza jeśli wyświetlany element (w tym przypadku menu) ma elementy interaktywne. Znalazłem następującą metodę, która jest dość solidna:

$('#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);
        }

    })
});
 22
Author: benb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-22 11:59:02

Sprawdź okno Kliknij obiekt docelowy zdarzenia (powinien się rozprzestrzeniać do okna, o ile nie jest przechwycony nigdzie indziej) i upewnij się, że nie jest to żaden z elementów menu. Jeśli nie, to jesteś poza menu.

Lub sprawdź pozycję kliknięcia i sprawdź, czy znajduje się ono w obszarze menu.

 18
Author: Chris MacDonald,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-30 13:20:30

Proste rozwiązanie tej sytuacji to:

$(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();
    }
});

Powyższy skrypt ukryje div, jeśli poza div zostanie wywołane zdarzenie click.

Możesz zobaczyć poniższy blog, aby uzyskać więcej informacji : http://www.codecanal.com/detect-click-outside-div-using-javascript/

 18
Author: Jitendra Damor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-10 08:32:39

Solution1

Zamiast używania eventu.stopPropagation (), która może mieć pewne afekty poboczne, wystarczy zdefiniować prostą zmienną flag i dodać jeden warunek if. Testowałem to i działało poprawnie bez żadnych skutków ubocznych stopropagacji:

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

Z prostym if warunkiem:

$(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
    }
});
 16
Author: Iman Sedighi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-30 16:51:41

Jako wariant:

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();
    }
});

Nie ma problemu z zatrzymaniem propagacji zdarzeń i lepiej obsługuje wiele menu na tej samej stronie, gdzie kliknięcie drugiego menu, gdy pierwsze jest otwarte, pozostawi pierwsze otwarte w rozwiązaniu stopropagacji.

 14
Author: Bohdan Lyzanets,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-30 16:43:21

Odniosłem sukces z czymś takim:

var $menuscontainer = ...;

$('#trigger').click(function() {
  $menuscontainer.show();

  $('body').click(function(event) {
    var $target = $(event.target);

    if ($target.parents('#menuscontainer').length == 0) {
      $menuscontainer.hide();
    }
  });
});

Logika jest następująca: gdy #menuscontainer jest pokazana, powiązaj funkcję obsługi kliknięć z ciałem, które ukrywa #menuscontainer tylko wtedy, gdy cel (kliknięcia) nie jest jego potomkiem.

 13
Author: Chu Yeow,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-03 07:42:56

Znalazłem tę metodę w jakiejś wtyczce kalendarza 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
Author: nazar kuliyev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-30 16:15:45

Jeśli piszesz skrypty dla IE i FF 3.* i chcesz tylko wiedzieć, czy kliknięcie nastąpiło w określonym obszarze pola, Możesz również użyć czegoś takiego jak:

this.outsideElementClick = function(objEvent, objElement){   
var objCurrentElement = objEvent.target || objEvent.srcElement;
var blnInsideX = false;
var blnInsideY = false;

if (objCurrentElement.getBoundingClientRect().left >= objElement.getBoundingClientRect().left && objCurrentElement.getBoundingClientRect().right <= objElement.getBoundingClientRect().right)
    blnInsideX = true;

if (objCurrentElement.getBoundingClientRect().top >= objElement.getBoundingClientRect().top && objCurrentElement.getBoundingClientRect().bottom <= objElement.getBoundingClientRect().bottom)
    blnInsideY = true;

if (blnInsideX && blnInsideY)
    return false;
else
    return true;}
 8
Author: Erik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-14 14:08:21

Zamiast używać przerwania przepływu, zdarzenia rozmycia/ostrości lub innych skomplikowanych technik, po prostu dopasuj przepływ zdarzeń do pokrewieństwa elementu:

$(document).on("click.menu-outside", function(event){
    // Test if target and it's parent aren't #menuscontainer
    // That means the click event occur on other branch of document tree
    if(!$(event.target).parents().andSelf().is("#menuscontainer")){
        // Click outisde #menuscontainer
        // Hide the menus (but test if menus aren't already hidden)
    }
});

Aby usunąć kliknij poza detektorem zdarzeń, po prostu:

$(document).off("click.menu-outside");
 8
Author: mems,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-05 15:05:20

Oto rozwiązanie vanilla JavaScript dla przyszłych widzów.

Po kliknięciu dowolnego elementu w dokumencie, jeśli identyfikator klikniętego elementu jest przełączony lub ukryty element nie jest ukryty i ukryty element nie zawiera klikniętego elementu, włącz ten element.

(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);
})();

(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);
})();
<a href="javascript:void(0)" id="toggle">Toggle Hidden Div</a>
<div id="hidden" style="display: none;">This content is normally hidden. click anywhere other than this content to make me disappear</div>

Jeśli masz zamiar mieć wiele przełączników na tej samej stronie, możesz użyć czegoś takiego:

  1. Dodaj nazwę klasy hidden do zwijanego pozycji.
  2. po kliknięciu dokumentu Zamknij wszystkie ukryte elementy, które nie zawierają klikniętego elementu i nie są ukryte
  3. jeśli kliknięty element jest przełącznikiem, Przełącz określony element.

(function () {
    "use strict";
    var hiddenItems = document.getElementsByClassName('hidden'), hidden;
    document.addEventListener('click', function (e) {
        for (var i = 0; hidden = hiddenItems[i]; i++) {
            if (!hidden.contains(e.target) && hidden.style.display != 'none')
                hidden.style.display = 'none';
        }
        if (e.target.getAttribute('data-toggle')) {
            var toggle = document.querySelector(e.target.getAttribute('data-toggle'));
            toggle.style.display = toggle.style.display == 'none' ? 'block' : 'none';
        }
    }, false);
})();
<a href="javascript:void(0)" data-toggle="#hidden1">Toggle Hidden Div</a>
<div class="hidden" id="hidden1" style="display: none;" data-hidden="true">This content is normally hidden</div>
<a href="javascript:void(0)" data-toggle="#hidden2">Toggle Hidden Div</a>
<div class="hidden" id="hidden2" style="display: none;" data-hidden="true">This content is normally hidden</div>
<a href="javascript:void(0)" data-toggle="#hidden3">Toggle Hidden Div</a>
<div class="hidden" id="hidden3" style="display: none;" data-hidden="true">This content is normally hidden</div>
 8
Author: Tiny Giant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-30 16:52:55

Zdarzenie ma właściwość o nazwie event.path of the element which is a "static ordered list of all its ancestors in tree order" . Aby sprawdzić, czy zdarzenie pochodzi z określonego elementu DOM lub jednego z jego potomków, wystarczy sprawdzić ścieżkę dla tego konkretnego elementu DOM. Może być również używany do sprawdzania wielu elementów poprzez logiczne ORsprawdzenie elementu w funkcji some.

$("body").click(function() {
  target = document.getElementById("main");
  flag = event.path.some(function(el, i, arr) {
    return (el == target)
  })
  if (flag) {
    console.log("Inside")
  } else {
    console.log("Outside")
  }
});
#main {
  display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="main">
  <ul>
    <li>Test-Main</li>
    <li>Test-Main</li>
    <li>Test-Main</li>
    <li>Test-Main</li>
    <li>Test-Main</li>
  </ul>
</div>
<div id="main2">
  Outside Main
</div>

Więc w Twoim przypadku powinno być

$("body").click(function() {
  target = $("#menuscontainer")[0];
  flag = event.path.some(function(el, i, arr) {
    return (el == target)
  });
  if (!flag) {
    // Hide the menus
  }
});
 7
Author: Dan Philip,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-01 15:03:59

Hook a click Event listener na dokumencie. Wewnątrz detektora zdarzeń można przyjrzeć się obiektowi zdarzenia , w szczególności zdarzeniu .cel aby zobaczyć, który element został kliknięty:

$(document).click(function(e){
    if ($(e.target).closest("#menuscontainer").length == 0) {
        // .closest can help you determine if the element 
        // or one of its ancestors is #menuscontainer
        console.log("hide");
    }
});
 6
Author: Salman A,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-03 09:21:57

Użycie:

var go = false;
$(document).click(function(){
    if(go){
        $('#divID').hide();
        go = false;
    }
})

$("#divID").mouseover(function(){
    go = false;
});

$("#divID").mouseout(function (){
    go = true;
});

$("btnID").click( function(){
    if($("#divID:visible").length==1)
        $("#divID").hide(); // Toggle
    $("#divID").show();
});
 6
Author: webenformasyon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-22 13:00:15
$(document).click(function() {
    $(".overlay-window").hide();
});
$(".overlay-window").click(function() {
    return false;
});

Jeśli klikniesz na dokument, Ukryj dany element, chyba że klikniesz na ten sam element.

 5
Author: Rowan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-20 18:44:18

Głosuj na najpopularniejszą odpowiedź, ale dodaj

&& (e.target != $('html').get(0)) // ignore the scrollbar

Więc kliknięcie na pasku przewijania nie powoduje [ukrycia] elementu docelowego.

 5
Author: bbe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-25 21:33:38

Dla łatwiejszego użycia i bardziej wyrazistego kodu, stworzyłem do tego wtyczkę jQuery:

$('div.my-element').clickOut(function(target) { 
    //do something here... 
});

Uwaga: target jest elementem, który użytkownik faktycznie kliknął. Ale wywołanie zwrotne jest nadal wykonywane w kontekście oryginalnego elementu, więc możesz użyć tego , Jak można się spodziewać w wywołaniu zwrotnym jQuery.

Plugin:

$.fn.clickOut = function (parent, fn) {
    var context = this;
    fn = (typeof parent === 'function') ? parent : fn;
    parent = (parent instanceof jQuery) ? parent : $(document);

    context.each(function () {
        var that = this;
        parent.on('click', function (e) {
            var clicked = $(e.target);
            if (!clicked.is(that) && !clicked.parents().is(that)) {
                if (typeof fn === 'function') {
                    fn.call(that, clicked);
                }
            }
        });

    });
    return context;
};

Domyślnie detektor zdarzeń click jest umieszczony w dokumencie. Jeśli jednak chcesz ograniczyć zakres detektora zdarzeń, możesz przejść w jQuery obiekt reprezentujący element poziomu nadrzędnego, który będzie nadrzędnym nadrzędnym, przy którym będą odsłuchiwane kliknięcia. Zapobiega to niepotrzebnym detektorom zdarzeń na poziomie dokumentu. Oczywiście, nie będzie działać, chyba że dostarczony element rodzic jest rodzicem elementu początkowego.

Użyj tak:

$('div.my-element').clickOut($('div.my-parent'), function(target) { 
    //do something here...
});
 5
Author: Matt Goodwin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-15 21:27:58

Oto moje rozwiązanie tego problemu:

$(document).ready(function() {
  $('#user-toggle').click(function(e) {
    $('#user-nav').toggle();
    e.stopPropagation();
  });

  $('body').click(function() {
    $('#user-nav').hide(); 
  });

  $('#user-nav').click(function(e){
    e.stopPropagation();
  });
});
 4
Author: Spencer Fry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-21 16:24:38

Skończyłem robiąc coś takiego:

$(document).on('click', 'body, #msg_count_results .close',function() {
    $(document).find('#msg_count_results').remove();
});
$(document).on('click','#msg_count_results',function(e) {
    e.preventDefault();
    return false;
});

Mam przycisk zamykania w nowym kontenerze, aby ułatwić użytkownikom korzystanie z interfejsu użytkownika. Musiałem użyć return false, żeby nie przejść. Oczywiście, posiadanie tam a HREF, który zabierze cię gdzieś, byłoby miłe, lub mógłbyś zamiast tego zadzwonić do jakiegoś Ajaxu. Tak czy siak, u mnie Działa ok. Właśnie tego chciałem.

 4
Author: Webmaster G,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-19 01:40:52

Zaimplementowaliśmy rozwiązanie, częściowo oparte na komentarzu użytkownika powyżej, które działa dla nas doskonale. Używamy go, aby ukryć pole wyszukiwania / Wyniki po kliknięciu poza tymi elementami, z wyłączeniem elementu, który pierwotnie.

// HIDE SEARCH BOX IF CLICKING OUTSIDE
$(document).click(function(event){ 
    // IF NOT CLICKING THE SEARCH BOX OR ITS CONTENTS OR SEARCH ICON 
    if ($("#search-holder").is(":visible") && !$(event.target).is("#search-holder *, #search")) {
        $("#search-holder").fadeOut('fast');
        $("#search").removeClass('active');
    }
});

Sprawdza, czy pole wyszukiwania jest już widoczne jako pierwsze, a w naszym przypadku również usuwa aktywną klasę na przycisku Ukryj / pokaż wyszukiwanie.

 4
Author: Scott Richardson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-08 01:05:46

Funkcja:

$(function() {
    $.fn.click_inout = function(clickin_handler, clickout_handler) {
        var item = this;
        var is_me = false;
        item.click(function(event) {
            clickin_handler(event);
            is_me = true;
        });
        $(document).click(function(event) {
            if (is_me) {
                is_me = false;
            } else {
                clickout_handler(event);
            }
        });
        return this;
    }
});

Użycie:

this.input = $('<input>')
    .click_inout(
        function(event) { me.ShowTree(event); },
        function() { me.Hide(); }
    )
    .appendTo(this.node);

I funkcje są bardzo proste:

ShowTree: function(event) {
    this.data_span.show();
}
Hide: function() {
    this.data_span.hide();
}
 4
Author: Neco,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-30 16:07:48