Jak wyłączyć zdarzenia mouseout wyzwalane przez elementy potomne?

107

Opiszę szczegółowo problem:

Chcę pokazać bezwzględnie umieszczony element div po najechaniu kursorem na element. To naprawdę proste z jQuery i działa dobrze. Ale kiedy wskaźnik myszy nad jednym z elementów potomnych, wyzwala zdarzenie mouseout elementu div. Jak zapobiec wyzwalaniu przez JavaScript zdarzenia mouseout elementu zawierającego po najechaniu na element podrzędny.

Jaki jest najlepszy i najkrótszy sposób na zrobienie tego z jQuery?

Oto uproszczony przykład ilustrujący, co mam na myśli:

HTML:

<a>Hover Me</a>
<div>
  <input>Test</input>
  <select>
    <option>Option 1</option>
    <option>Option 2</option>
  </select>
</div>

Javascript / jQuery:

$('a').hover( function() { $(this).next().show() }
              function() { $(this).next().hide() } );
Sander Versluys
źródło
1
Bez wersji jQuery tego pytania
Zach Saucier

Odpowiedzi:

209

Pytanie jest trochę stare, ale wpadłem na to któregoś dnia.

Najprostszym sposobem na to w przypadku najnowszych wersji jQuery jest użycie zdarzeń mouseenteri mouseleavezamiast mouseoveri mouseout.

Możesz szybko przetestować zachowanie za pomocą:

$(".myClass").on( {
   'mouseenter':function() { console.log("enter"); },
   'mouseleave':function() { console.log("leave"); }
});
bytebrite
źródło
4
Pomogło w moim problemie związanym z javascriptem innym niż jquery. Używałem mouseenter / mouseout zamiast mouseleave z moimi odbiornikami zdarzeń!
Jo E.
1
Ze względu na wyszukiwanie te zachowania zdarzeń myszy są równoważne ROLL_OVERi ROLL_OUTw AS3.
Jeff Ward,
2
Uratował mi trochę bólu głowy. To dziwne, jak najechanie kursorem na elementy potomne wyzwala mouseoutzdarzenie rodzica , podczas gdy w rzeczywistości nadal znajduje się wewnątrz elementu nadrzędnego.
javiniar.leonard
18

Dla uproszczenia po prostu przeorganizowałbym nieco html, aby umieścić nowo wyświetlaną zawartość wewnątrz elementu, do którego jest przypisane zdarzenie mouseover:

<div id="hoverable">
  <a>Hover Me</a>
  <div style="display:none;">
    <input>Test</input>
    <select>
      <option>Option 1</option>
      <option>Option 2</option>
    </select>
  </div>
</div>

Następnie możesz zrobić coś takiego:

$('#hoverable').hover( function() { $(this).find("div").show(); },
                       function() { $(this).find("div").hide(); } );

Uwaga: nie polecam wbudowanego css, ale zostało to zrobione, aby przykład był łatwiejszy do zrozumienia.

Ryan McGeary
źródło
To rzeczywiście bardzo proste i czyste rozwiązanie. Dzięki za przypomnienie. Ale w mojej konkretnej sytuacji, która nie jest dokładnie taka, jak w pytaniu, nie jest to opcja. W każdym razie dzięki!
Sander Versluys,
Po wypróbowaniu różnych metod opisanych przez innych w SO, wróciłem do Twojej metody i sprawiłem, że zadziałała w moim przypadku. Yay! :-)
Sander Versluys
11

Tak, używajcie .mouseleavezamiast .mouseout:

$('div.sort-selector').mouseleave(function() {
    $(this).hide();
});

Lub nawet użyj go w połączeniu z live:

$('div.sort-selector').live('mouseleave', function() {
    $(this).hide();
});
Lord Nighton
źródło
8

Szukasz odpowiednika w jQuery funkcji zapobiegania propagacji zdarzeń w języku JavaScript.

Sprawdź to:

http://docs.jquery.com/Events/jQuery.Event#event.stopPropagation.28.29

Zasadniczo musisz złapać zdarzenie w podrzędnych węzłach DOM i tam zatrzymać ich propagację w drzewie DOM. Innym sposobem, chociaż naprawdę nie jest sugerowany (ponieważ może poważnie zepsuć istniejące wydarzenia na Twojej stronie), jest ustawienie przechwytywania zdarzeń na określony element na stronie, który odbierze wszystkie zdarzenia. Jest to przydatne w przypadku zachowania DnD i tym podobnych, ale zdecydowanie nie w Twoim przypadku.

Yuval Adam
źródło
to działa tak, jak powinno, nie wiem, dlaczego nie mogłeś go uruchomić ... tylko informacja, zmienił się link do dokumentów, nowy link to docs.jquery.com/Events/jQuery.Event#event.stopPropagation. 28.29
zappan
3

Po prostu sprawdzam, czy współrzędne myszy są poza elementem w zdarzeniu mouseout.

Działa, ale jest dużo kodu na tak prostą rzecz :(

function mouseOut(e)
{
    var pos = GetMousePositionInElement(e, element);
    if (pos.x < 0 || pos.x >= element.size.X || pos.y < 0 || pos.y >= element.size.Y)
    {
        RealMouseOut();
    }
    else
    {
         //Hit a child-element
    }
}

Kod skrócony dla czytelności, nie będzie działać po wyjęciu z pudełka.


źródło
1

Zgadzam się z Ryanem.

Twój problem polega na tym, że „następny” element div nie jest „dzieckiem” elementu A. Nie ma możliwości, aby HTML lub jQuery wiedział, że element „a” jest powiązany z podrzędnym elementem div w DOM. Zawijanie ich i umieszczenie kursora na opakowaniu oznacza, że ​​są one powiązane.

Pamiętaj jednak, że jego kod nie jest zgodny z najlepszymi praktykami. Nie ustawiaj stylu ukrytego bezpośrednio w elementach; jeśli użytkownik ma CSS, ale nie obsługuje javascript, element zostanie ukryty i nie będzie można go wyświetlić. Lepszą praktyką jest umieszczenie tej deklaracji w zdarzeniu document.ready.

user37731
źródło
Całkowicie zgadzam się z umieszczeniem ukrycia w wydarzeniu document.ready. Wbudowany css miał po prostu przekazać pełny przykład roboczy w najprostszy możliwy sposób. Zmienię swoją odpowiedź, aby było jasne.
Ryan McGeary
1

Rozwiązałem ten problem, dodając pointer-events: none;css moje elementy podrzędne

Antoine
źródło
To naprawdę bardzo pomogło przy naprawianiu jakiegoś starego kodu, budowaniu własnych popoverów z wydarzeniami mouseover i mouseleave
CM
0

Sposób, w jaki zwykle to obsługiwałem, polega na opóźnieniu około 1/2 sekundy między przesunięciem myszy z elementu HoverMe. Przesuwając kursor myszy do elementu najechanego kursorem, chciałbyś ustawić jakąś zmienną, która sygnalizuje, że najeżdżasz na element, a następnie w zasadzie zatrzymać ukrywanie części, na której jest włączona, jeśli ta zmienna jest ustawiona. Następnie musisz dodać podobną funkcję ukrywania OnMouseOut z elementu najechanego kursorem, aby znikał po usunięciu myszy. Przepraszam, że nie mogę podać żadnego kodu ani czegoś bardziej konkretnego, ale mam nadzieję, że to wskaże ci właściwy kierunek.

Kibbee
źródło
Tak, idd właśnie wdrożyłem takie rozwiązanie. To naprawdę powszechny problem. Jestem naprawdę ciekawy, jakie są inne rozwiązania ... Dzięki za odpowiedź!
Sander Versluys,
0

To stare pytanie, ale nigdy nie jest przestarzałe. Poprawną odpowiedzią powinna być ta udzielona przez bytebrite .

Chciałbym tylko zwrócić uwagę na różnicę między mouseover / mouseout a mouseenter / mouseleave. Możesz przeczytać świetne i pomocne wyjaśnienie tutaj (przejdź na sam dół strony, aby zobaczyć działające demo). Kiedy używasz mouseout, zdarzenie zatrzymuje się, gdy mysz przechodzi do innego elementu, nawet jeśli jest to element podrzędny. Z drugiej strony, gdy używasz mouseleave, zdarzenie nie jest wyzwalane, gdy wskaźnik myszy przesuwa się nad elementem podrzędnym, i jest to zachowanie, które OP chciałby osiągnąć.

Erenor Paz
źródło