Evitar onmouseout ao pairar o elemento filho da div absoluta pai SEM jQuery

169

Estou tendo problemas com a onmouseoutfunção em uma div positonada absoluta. Quando o mouse atinge um elemento filho na div, o evento mouseout é acionado, mas não quero que ele seja acionado até que o mouse esteja fora do pai, div absoluto.

Como impedir que o mouseoutevento seja disparado quando ele atinge um elemento filho sem jquery.

Eu sei que isso tem algo a ver com a agitação do evento, mas não estou tendo sorte em descobrir como resolver isso.

Encontrei um post semelhante aqui: Como desativar os eventos de mouseout acionados por elementos filho?

No entanto, essa solução usa jQuery.

John
fonte
Acabei resolvendo o problema usando um tempo limite e limpá-lo em foco dos elementos filhos
John
Oi, eu olhei para a sua demo, mas quando eu passo os elementos no painel inferior direito, nada acontece?
John
1
Encontre esta resposta se você estiver procurando impedir o mouseout no evento pai ao passar o mouse sobre um elemento filho: javascript mouseover / mouseout .
user823527
1
Confira a resposta do
craigmichaelmartin 28/10

Respostas:

94
function onMouseOut(event) {
        //this is the original element the event handler was assigned to
        var e = event.toElement || event.relatedTarget;
        if (e.parentNode == this || e == this) {
           return;
        }
    alert('MouseOut');
    // handle mouse event here!
}



document.getElementById('parent').addEventListener('mouseout',onMouseOut,true);

Fiz uma rápida demonstração do JsFiddle, com todo o CSS e HTML necessários, confira ...

Link EDIT FIXED para suporte entre navegadores http://jsfiddle.net/RH3tA/9/

Observe que isso só verifica o pai imediato, se o div pai tiver aninhado filhos, você precisará percorrer os elementos pais procurando o "Elemento original"

Exemplo EDIT para filhos aninhados

EDIT Corrigido para esperançosamente cross-browser

function makeMouseOutFn(elem){
    var list = traverseChildren(elem);
    return function onMouseOut(event) {
        var e = event.toElement || event.relatedTarget;
        if (!!~list.indexOf(e)) {
            return;
        }
        alert('MouseOut');
        // handle mouse event here!
    };
}

//using closure to cache all child elements
var parent = document.getElementById("parent");
parent.addEventListener('mouseout',makeMouseOutFn(parent),true);

//quick and dirty DFS children traversal, 
function traverseChildren(elem){
    var children = [];
    var q = [];
    q.push(elem);
    while (q.length > 0) {
      var elem = q.pop();
      children.push(elem);
      pushAll(elem.children);
    }
    function pushAll(elemArray){
      for(var i=0; i < elemArray.length; i++) {
        q.push(elemArray[i]);
      }
    }
    return children;
}

E um novo link atualizado do JSFiddle , EDIT

Amjad Masad
fonte
1
Você deve adicionar um evento de mouseout ao violino para que possa ser testado. Um alerta ou algo deve fazer.
John John
Desculpe, parece que você tem alerta ('MouseOut'), mas não funciona para mim? Eu ter atingido prazo sem opção biblioteca JS
John
Testado em safari e chrome, mas deve funcionar em tudo! qual é o seu navegador?
Amjad Masad
2
E se você tem netos? ou bisnetos? ou seja, você pode ter uma solução recursiva em vez de apenas imidiar crianças?
precisa saber é o seguinte
20
Isso não funciona. mouseleaveé a resposta correta
Code Whisperer
126

Use onmouseleave.

Ou, no jQuery, use mouseleave()

É exatamente o que você está procurando. Exemplo:

<div class="outer" onmouseleave="yourFunction()">
    <div class="inner">
    </div>
</div>

ou, em jQuery:

$(".outer").mouseleave(function(){
    //your code here
});

um exemplo está aqui .

PHPWannaBePro
fonte
3
onMouseLeavefoi o que acabei usando também, pois não borbulha.
Alecz
Me salvou muito tempo. thnx
npo
mouseleavenão pode ser cancelável.
grecdev
@grecdev, diga mais!
Ben Wheeler
97

Para uma solução CSS pura e simples, que funciona em alguns casos ( IE11 ou mais recente ), é possível remover as crianças pointer-eventsconfigurando-as paranone

.parent * {
     pointer-events: none;
}
Zach Saucier
fonte
4
Brilhante! Corrigido totalmente o meu problema!
precisa saber é o seguinte
4
Esta deve ser a primeira ou pelo menos a segunda resposta, eu estava pronto para lidar com o incômodo dos ouvintes de eventos e isso também resolveu totalmente o meu problema. Tão simples !
Mickael V.
9
Isso impede que o mouseout seja acionado, mas para mim também impede que o filho real seja clicado como uma seleção no menu, que é o ponto do menu.
Johnbakers
@hellofunk É por isso que você aplicaria o evento click ao pai. O código exato necessário varia dependendo da implementação, esta resposta apenas sugere a utilização do pointer-eventsimóvel
Zach Saucier
1
isso só funciona se você não tem outros elementos do controlador (editar, apagar) dentro da área, porque esta solução bloqueia-los também ..
Zoltán Süle
28

Aqui está uma solução mais elegante com base no que veio abaixo. é responsável pelo evento borbulhando em mais de um nível de crianças. Ele também é responsável por problemas entre navegadores.

function onMouseOut(this, event) {
//this is the original element the event handler was assigned to
   var e = event.toElement || event.relatedTarget;

//check for all children levels (checking from bottom up)
while(e && e.parentNode && e.parentNode != window) {
    if (e.parentNode == this||  e == this) {
        if(e.preventDefault) e.preventDefault();
        return false;
    }
    e = e.parentNode;
}

//Do something u need here
}

document.getElementById('parent').addEventListener('mouseout',onMouseOut,true);
Sam-Elie
fonte
Sam Elie, isso parece não estar funcionando no IE, Safari ou Opera. Você tem uma versão entre navegadores desse script? Funciona como um encanto no Firefox e Chrome. Obrigado.
1
Quando colo a função acima no meu script, o Dreamweaver e o FF lançam um erro na primeira linha, "function onMouseOut (this, event) {". Parece que você não tem permissão para colocar "this" como parâmetro. Eu acho que meio que funciona quando deixo de fora "isso" dos parâmetros de entrada, mas não tenho certeza, porque "não sei o que não sei".
TARKUS 21/07
1
Também tive o mesmo problema que apareceu no Chrome na mesma linha, excluindo thisda linha de definição de função que @GregoryLewis mencionou corrigiu o problema. Também para obter mais suporte entre navegadores, tente o seguinte: if (window.addEventListener) {document.getElementById ('pai'). AddEventListener ('mouseout', onMouseOut, true); } else if (window.attachEvent) {document.getElementById ('pai'). attachEvent ("onmouseout", onMouseOut); }
DWils 29/01
Muito bonito! Melhor resposta se for necessário suporte para navegadores mais antigos.
BurninLeo
13

Graças a Amjad Masad que me inspirou.

Eu tenho a seguinte solução que parece funcionar no IE9, FF e Chrome e o código é bastante curto (sem o fechamento complexo e as coisas transversais da criança):

    DIV.onmouseout=function(e){
        // check and loop relatedTarget.parentNode
        // ignore event triggered mouse overing any child element or leaving itself
        var obj=e.relatedTarget;
        while(obj!=null){
            if(obj==this){
                return;
            }
            obj=obj.parentNode;
        }
        // now perform the actual action you want to do only when mouse is leaving the DIV
    }
Lawrence Mok
fonte
13

em vez de onmouseout, use onmouseleave.

Você não nos mostrou seu código específico; portanto, não posso mostrar em seu exemplo específico como fazê-lo.

Mas é muito simples: basta substituir onmouseout por onmouseleave.

Isso é tudo :) Então, simples :)

Se não tiver certeza de como fazê-lo, consulte a explicação em:

https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_onmousemove_leave_out

Paz de bolo :) Divirta-se :)

kavi
fonte
Este um grande grito é, vale a pena conferir se você tem uma situação semelhante
Chris
12

Se você estiver usando o jQuery, também poderá usar a função "mouseleave", que lida com tudo isso para você.

$('#thetargetdiv').mouseenter(do_something);
$('#thetargetdiv').mouseleave(do_something_else);

do_something será acionado quando o mouse entrar no objeto de entretenimento ou em qualquer um de seus filhos, do_something_else somente será acionado quando o mouse sair do objeto de entretenimento e de qualquer um de seus filhos.

Jamie Brown
fonte
8

Acho que o Quirksmode tem todas as respostas que você precisa (comportamento de borbulhamento de navegadores diferentes e eventos do mouseenter / mouseleave ), mas acho que a conclusão mais comum dessa confusão de borbulhamento de eventos é o uso de uma estrutura como JQuery ou Mootools (que possui o mouseenter e eventos do mouse , que são exatamente o que você intuiu que aconteceria).

Veja como eles fazem isso, se você quiser, faça você mesmo
ou você pode criar sua versão personalizada do "Mootools" com apenas a parte do evento (e suas dependências).

Ruben
fonte
7

Experimentar mouseleave()

Exemplo:

<div id="parent" mouseleave="function">
   <div id="child">

   </div>
</div>

;)

Jean-Philippe
fonte
5

Eu encontrei uma solução muito simples,

basta usar o evento onmouseleave = "myfunc ()" do que o evento onmousel = "myfunc ()"

No meu código funcionou !!

Exemplo:

<html>
<head>
<script type="text/javascript">
   function myFunc(){
      document.getElementById('hide_div').style.display = 'none';
   }
   function ShowFunc(){
      document.getElementById('hide_div').style.display = 'block';
   }
</script>
</head>
<body>
<div onmouseleave="myFunc()" style='border:double;width:50%;height:50%;position:absolute;top:25%;left:25%;'>
   Hover mouse here
   <div id='child_div' style='border:solid;width:25%;height:25%;position:absolute;top:10%;left:10%;'>
      CHILD <br/> It doesn't fires if you hover mouse over this child_div
   </div>
</div>
<div id="hide_div" >TEXT</div>
<a href='#' onclick="ShowFunc()">Show "TEXT"</a>
</body>
</html>

Mesmo exemplo com a função mouseout:

<html>
<head>
<script type="text/javascript">
   function myFunc(){
      document.getElementById('hide_div').style.display = 'none';
   }
   function ShowFunc(){
      document.getElementById('hide_div').style.display = 'block';
   }
</script>
</head>
<body>
<div onmouseout="myFunc()" style='border:double;width:50%;height:50%;position:absolute;top:25%;left:25%;'>
   Hover mouse here
   <div id='child_div' style='border:solid;width:25%;height:25%;position:absolute;top:10%;left:10%;'>
      CHILD <br/> It fires if you hover mouse over this child_div
   </div>
</div>
<div id="hide_div">TEXT</div>
<a href='#' onclick="ShowFunc()">Show "TEXT"</a>
</body>
</html>

Espero que ajude :)

Arminius
fonte
2

Existem duas maneiras de lidar com isso.

1) Verifique o resultado event.target no seu retorno de chamada para ver se ele corresponde ao seu div principal

var g_ParentDiv;

function OnMouseOut(event) {
    if (event.target != g_ParentDiv) {
        return;
    }
    // handle mouse event here!
};


window.onload = function() {
    g_ParentDiv = document.getElementById("parentdiv");
    g_ParentDiv.onmouseout = OnMouseOut;
};

<div id="parentdiv">
    <img src="childimage.jpg" id="childimg" />
</div>

2) Ou use a captura de eventos e chame event.stopPropagation na função de retorno de chamada

var g_ParentDiv;

function OnMouseOut(event) {

    event.stopPropagation(); // don't let the event recurse into children

    // handle mouse event here!
};


window.onload = function() {
    g_ParentDiv = document.getElementById("parentdiv");
    g_ParentDiv.addEventListener("mouseout", OnMouseOut, true); // pass true to enable event capturing so parent gets event callback before children
};

<div id="parentdiv">
    <img src="childimage.jpg" id="childimg" />
</div>
selbie
fonte
Como posso fazer isso funcionar para onmouseout em vez de onmouseover?
John
Opa Foi mal. Acabei de alterar todas as referências "mouseover" para "mouseout" no código acima. Isso deve bastar para você.
Selbie
Para o primeiro método, o event.target sempre corresponde ao elemento pai quando o mouse passa sobre o elemento filho, por isso não funciona
John
O mesmo ocorre com o segundo método. Quando o mouse entra um elemento filho ainda dispara o evento
John
1

Eu faço isso funcionar como um encanto com isso:

function HideLayer(theEvent){
 var MyDiv=document.getElementById('MyDiv');
 if(MyDiv==(!theEvent?window.event:theEvent.target)){
  MyDiv.style.display='none';
 }
}

Ah, e MyDivtag é assim:

<div id="MyDiv" onmouseout="JavaScript: HideLayer(event);">
 <!-- Here whatever divs, inputs, links, images, anything you want... -->
<div>

Dessa forma, quando onmouseout vai para um filho, neto, etc ... o style.display='none'não é executado; mas quando onmouseout sai do MyDiv, ele é executado.

Portanto, não há necessidade de interromper a propagação, usar temporizadores, etc ...

Obrigado por exemplos, eu poderia fazer esse código a partir deles.

Espero que isso ajude alguém.

Também pode ser melhorado assim:

function HideLayer(theLayer,theEvent){
 if(theLayer==(!theEvent?window.event:theEvent.target)){
  theLayer.style.display='none';
 }
}

E as tags DIVs assim:

<div onmouseout="JavaScript: HideLayer(this,event);">
 <!-- Here whatever divs, inputs, links, images, anything you want... -->
<div>

Então, mais geral, não apenas para uma div e não há necessidade de adicionar id="..."em cada camada.

z666zz666z
fonte
1

Se você tiver acesso ao elemento ao qual o evento está anexado dentro do mouseoutmétodo, poderá contains()ver se o event.relatedTargetelemento é filho ou não.

Como event.relatedTargeté o elemento para o qual o mouse foi transferido, se não for um elemento filho, você passou o mouse pelo elemento.

div.onmouseout = function (event) {
    if (!div.contains(event.relatedTarget)) {
        // moused out of div
    }
}
astronauta
fonte
1

Nos angulares 5, 6 e 7

<div (mouseout)="onMouseOut($event)"
     (mouseenter)="onMouseEnter($event)"></div>

Em seguida

import {Component,Renderer2} from '@angular/core';
...
@Component({
 selector: 'app-test',
 templateUrl: './test.component.html',
 styleUrls: ['./test.component.scss']
})
export class TestComponent implements OnInit, OnDestroy {
...
 public targetElement: HTMLElement;

 constructor(private _renderer: Renderer2) {
 }

 ngOnInit(): void {
 }

 ngOnDestroy(): void {
  //Maybe reset the targetElement
 }

 public onMouseEnter(event): void {
  this.targetElement = event.target || event.srcElement;
  console.log('Mouse Enter', this.targetElement);
 }

 public onMouseOut(event): void {
  const elementRelated = event.toElement || event.relatedTarget;
  if (this.targetElement.contains(elementRelated)) {
    return;
  }
  console.log('Mouse Out');
 }
}
Alan Torrico
fonte
0

Verifico o deslocamento do elemento original para obter as coordenadas da página nos limites do elemento e, em seguida, asseguro que a ação de mouseout seja acionada apenas quando o mouseout estiver fora desses limites. Sujo, mas funciona.

$(el).live('mouseout', function(event){
    while(checkPosition(this, event)){
        console.log("mouseovering including children")
    }
    console.log("moused out of the whole")
})

var checkPosition = function(el, event){
    var position = $(el).offset()
    var height = $(el).height()
    var width = $(el).width()
    if (event.pageY > position.top 
|| event.pageY < (position.top + height) 
|| event.pageX > position.left 
|| event.pageX < (position.left + width)){
    return true
}
}
saranicole
fonte
0
var elem = $('#some-id');
elem.mouseover(function () {
   // Some code here
}).mouseout(function (event) {
   var e = event.toElement || event.relatedTarget;
   if (elem.has(e).length > 0) return;

   // Some code here
});
Michael Vaganov
fonte
Por favor, dê uma explicação com sua resposta.
Sebass van Boxel
0

Se você adicionou (ou possui) uma classe ou ID CSS ao elemento pai, poderá fazer algo assim:

<div id="parent">
  <div>
  </div>
</div>

JavaScript:
document.getElementById("parent").onmouseout = function(e) {
  e = e ? e : window.event //For IE
  if(e.target.id == "parent") {
    //Do your stuff
  }
}

Portanto, as coisas só são executadas quando o evento está na div pai.

Karthik Palaniappan
fonte
0

Eu só queria compartilhar algo com você.
Eu tenho algum tempo duro com ng-mouseentere ng-mouseleaveeventos.

O estudo de caso:

Criei um menu de navegação flutuante que é alternado quando o cursor está sobre um ícone.
Este menu estava no topo de cada página.

  • Para lidar com mostrar / ocultar no menu, alterno uma classe.
    ng-class="{down: vm.isHover}"
  • Para alternar vm.isHover , eu uso os eventos do mouse ng.
    ng-mouseenter="vm.isHover = true"
    ng-mouseleave="vm.isHover = false"

Por enquanto, estava tudo bem e funcionou como esperado.
A solução é limpa e simples.

O problema de entrada:

Em uma visão específica, eu tenho uma lista de elementos.
Eu adicionei um painel de ação quando o cursor está sobre um elemento da lista.
Eu usei o mesmo código acima para lidar com o comportamento.

O problema:

Eu descobri que quando meu cursor está no menu de navegação flutuante e também na parte superior de um elemento, há um conflito entre eles.
O painel de ação apareceu e a navegação flutuante foi ocultada.

O fato é que, mesmo que o cursor esteja sobre o menu de navegação flutuante, o elemento da lista ng-mouseenter é acionado.
Não faz sentido para mim, porque eu esperaria uma interrupção automática dos eventos de propagação do mouse.
Devo dizer que fiquei desapontado e passo algum tempo para descobrir esse problema.

Primeiras reflexões:

Eu tentei usar estes:

  • $event.stopPropagation()
  • $event.stopImmediatePropagation()

Combinei muitos eventos de ponteiro ng (movimento, movimentação, ...), mas nenhum me ajudou.

Solução CSS:

Encontrei a solução com uma propriedade css simples que uso cada vez mais:

pointer-events: none;

Basicamente, eu uso assim (nos elementos da minha lista):

ng-style="{'pointer-events': vm.isHover ? 'none' : ''}"

Com este complicado, os eventos do ng-mouse não serão mais acionados e meu menu de navegação flutuante não se fechará mais quando o cursor estiver sobre ele e sobre um elemento da lista.

Ir além:

Como você pode esperar, esta solução funciona, mas eu não gosto.
Nós não controlamos nossos eventos e isso é ruim.
Além disso, você deve ter acesso ao vm.isHoverescopo para isso e pode não ser possível ou possível, mas sujo de uma maneira ou de outra.
Eu poderia mexer se alguém quiser olhar.

No entanto, não tenho outra solução ...
É uma longa história e não posso lhe dar uma batata, por favor, perdoe-me, meu amigo.
Enfim, pointer-events: noneé vida, então lembre-se.

C0ZEN
fonte