Como impedir que o evento onclick de um pai seja acionado quando uma âncora filho é clicada?

346

Atualmente, estou usando o jQuery para tornar uma div clicável e nessa div também tenho âncoras. O problema que eu encontro é que, quando clico em uma âncora, os dois eventos de clique estão disparando (para a div e a âncora). Como impedir que o evento onclick da div seja acionado quando uma âncora é clicada?

Aqui está o código quebrado:

Javascript

var url = $("#clickable a").attr("href");

$("#clickable").click(function() {
    window.location = url;
    return true;
})

HTML

<div id="clickable">
    <!-- Other content. -->
    <a href="http://foo.com">I don't want #clickable to handle this click event.</a>
</div>
Jonathon Watney
fonte

Respostas:

458

Bolha de eventos até o ponto mais alto no DOM no qual um evento de clique foi anexado. Portanto, no seu exemplo, mesmo se você não tivesse nenhum outro elemento clicável explicitamente na div, cada elemento filho da div colocaria um balão no evento click do DOM até que o manipulador de eventos click do DIV o capturasse.

Existem duas soluções para isso: verificar quem realmente originou o evento. O jQuery passa um objeto eventargs junto com o evento:

$("#clickable").click(function(e) {
    var senderElement = e.target;
    // Check if sender is the <div> element e.g.
    // if($(e.target).is("div")) {
    window.location = url;
    return true;
});

Você também pode anexar um manipulador de eventos de clique aos seus links, para que eles parem de borbulhar depois que o próprio manipulador for executado:

$("#clickable a").click(function(e) {
   // Do something
   e.stopPropagation();
});
Rex M
fonte
3
Ótima resposta. É bom saber que também existem opções.
Jonathon Watney
4
+1! Anexar um manipulador de cliques com stopPropagation é um truque muito bom, obrigado!
Thomas
2
se você tiver vários elementos nos quais deseja impedir a propagação, poderá encontrar o elemento pai e impedir que borbulhe lá em cima também. Então, em vez de interceptar todos os links a em uma div, digamos, por exemplo - basta interceptar o clique mesmo na própria div e impedir que ela suba mais e você está pronto.
Sucitivel 9/11/12
21
O uso if( e.target !== this) return;no pai é melhor do que e.stopPropagation()no filho, pois você nunca sabe se alguém anexa algum manipulador aos filhos ou se uma biblioteca deve anexar um manipulador a um filho (e você não quer mexer no código da biblioteca). É uma melhor separação de preocupações.
UTF_or_Death
3
Colocar a if( e.target !== this) return;verificação no pai significa que, se algum dos outros filhos desse pai que não possui manipuladores onClick forem clicados, nada acontecerá. Portanto, é inútil para uma situação em que você tem, por exemplo, uma div principal clicável. e.stopPropagation()funciona bem no entanto.
Derf26
100

Use o método stopPropagation , veja um exemplo:

$("#clickable a").click(function(e) {
   e.stopPropagation();
});

Como dito pelo jQuery Docs:

stopPropagation O método evita que o evento borbulhe na árvore DOM, impedindo que qualquer manipulador pai seja notificado sobre o evento.

Lembre-se de que isso não impede que outros ouvintes lidem com esse evento (por exemplo, mais de um manipulador de cliques para um botão); se esse não for o efeito desejado, você deverá usá-lo stopImmediatePropagation.

Cleiton
fonte
48

Aqui está minha solução para todos que procuram um código não-jQuery (javascript puro)

document.getElementById("clickable").addEventListener("click", function( e ){
    e = window.event || e; 
    if(this === e.target) {
        // put your code here
    }
});

Seu código não será executado se você clicar nos filhos dos pais

Sabaz
fonte
11
Isso funcionou para mim, eu precisava de uma solução não JS / jQuery. 10x!
LA
Ótima resposta. Acabei de passar o evento diretamente. Portanto, eu não usei window.event, o que o MDN desencoraja: developer.mozilla.org/en-US/docs/Web/API/Window/event . Se você pode poupar o tempo, eu apreciaria se você pudesse comentar por que usou window.event(talvez seja um problema que existisse em 2015 ou que não entendi o motivo).
RMo
15

você também pode tentar isso

$("#clickable").click(function(event) {
   var senderElementName = event.target.tagName.toLowerCase();
   if(senderElementName === 'div')
   {
       // do something here 
   } 
   else
   {
      //do something with <a> tag
   }
});
Rashad Annara
fonte
14

Se você não pretende interagir com o (s) elemento (s) interno (s) em nenhum caso, uma solução CSS pode ser útil para você.

Basta definir o (s) elemento (s) interno (s) como pointer-events: none

no seu caso:

.clickable > a {
    pointer-events: none;
}

ou direcionar todos os elementos internos em geral:

.clickable * {
    pointer-events: none;
}

Esse hack fácil me salvou muito tempo enquanto desenvolvia com o ReactJS

O suporte ao navegador pode ser encontrado aqui: http://caniuse.com/#feat=pointer-events

Hooman Askari
fonte
8

Se você tiver vários elementos na div clicável, faça o seguinte:

$('#clickable *').click(function(e){ e.stopPropagation(); });
Ivan Ivković
fonte
8

Usar return false;ou e.stopPropogation();não permitirá que mais códigos sejam executados. Parará o fluxo neste ponto em si.

Imamudin Naseem
fonte
Sim, isso é importante - me deparei com isso. Mas a resposta acima do Rex é útil - é possível obter o elemento que foi clicado e, em alguns casos, usar isso na lógica que você está tentando parar. .target.nodeName também foi útil para ter uma idéia clara do que foi atingido.
Watercayman
8

Escrever se alguém precisar (trabalhou para mim):

event.stopImmediatePropagation()

A partir desta solução.

Bahadir Tasdemir
fonte
4

Alternativa em linha:

<div>
    <!-- Other content. -->
    <a onclick='event.stopPropagation();' href="http://foo.com">I don't want #clickable to handle this click event.</a>
</div>
Matt Wyeth
fonte
3

Aqui está um exemplo usando Angular 2+

Por exemplo, se você deseja fechar um componente modal, se o usuário clicar fora dele:

// Close the modal if the document is clicked.

@HostListener('document:click', ['$event'])
public onDocumentClick(event: MouseEvent): void {
  this.closeModal();
}

// Don't close the modal if the modal itself is clicked.

@HostListener('click', ['$event'])
public onClick(event: MouseEvent): void {
  event.stopPropagation();
}
Steve Brush
fonte
1

Você precisa impedir que o evento alcance (borbulhando para) o pai (a div). Veja a parte sobre bolhas aqui e informações da API específicas do jQuery aqui .

Matt Ball
fonte
Legal. Obrigado pela informação sobre a subida do evento.
Jonathon Watney
1

var inner = document.querySelector("#inner");
var outer = document.querySelector("#outer");
inner.addEventListener('click',innerFunction);
outer.addEventListener('click',outerFunction);

function innerFunction(event){
  event.stopPropagation();
  console.log("Inner Functiuon");
}

function outerFunction(event){
  console.log("Outer Functiuon");
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>Pramod Kharade-Event with Outer and Inner Progration</title>
</head>
<body>
<div id="outer" style="width:100px;height:100px;background-color:green;">
  <div id="inner" style="width:35px;height:35px;background-color:yellow;"></div>
</div>
</body>
</html>

Pramod Kharade
fonte
0

Para especificar algum subelemento como não clicável, escreva a hierarquia css como no exemplo abaixo.

Neste exemplo, paro a propagação para qualquer elemento (*) dentro de td dentro de tr dentro de uma tabela com a classe ".subtable"

$(document).ready(function()
{    
   $(".subtable tr td *").click(function (event)
   {
       event.stopPropagation();
   });

});
user3202819
fonte
0

ignoreParent () é uma solução JavaScript pura.

Funciona como uma camada intermediária que compara as coordenadas do clique do mouse com as coordenadas do (s) elemento (s) filho (s). Duas etapas simples de implementação:

1. Coloque o código ignoreParent () na sua página.

2. Em vez do original onclick dos pais = "parentEvent ();" , Escreva:

onclick="ignoreParent(['parentEvent()', 'child-ID']);"

Você pode passar IDs de qualquer número de elementos filho para a função e excluir outros.

Se você clicou em um dos elementos filho, o evento pai não é acionado. Se você clicou no pai, mas não em nenhum dos elementos filhos [fornecidos como argumentos], o evento pai é disparado.

código ignoreParent () no Github

Fom
fonte
0

Se estiver no contexto embutido, em HTML, tente o seguinte:

onclick="functionCall();event.stopPropagation();
Gleno
fonte
-1

Caso alguém tenha esse problema usando o React, é assim que eu o resolvi.

scss:

#loginBackdrop {
position: absolute;
width: 100% !important;
height: 100% !important;
top:0px;
left:0px;
z-index: 9; }

#loginFrame {
width: $iFrameWidth;
height: $iFrameHeight;
background-color: $mainColor;
position: fixed;
z-index: 10;
top: 50%;
left: 50%;
margin-top: calc(-1 * #{$iFrameHeight} / 2);
margin-left: calc(-1 * #{$iFrameWidth} / 2);
border: solid 1px grey;
border-radius: 20px;
box-shadow: 0px 0px 90px #545454; }

Renderização do componente ():

render() {
    ...
    return (
        <div id='loginBackdrop' onClick={this.props.closeLogin}>
            <div id='loginFrame' onClick={(e)=>{e.preventDefault();e.stopPropagation()}}>
             ... [modal content] ...
            </div>
        </div>
    )
}

Ao adicionar uma função onClick ao modal filho (div de conteúdo), os eventos de clique do mouse são impedidos de alcançar a função 'closeLogin' do elemento pai.

Isso fez o truque para mim e eu pude criar um efeito modal com 2 divs simples.

Claudio
fonte
-3

adicione da aseguinte maneira:

<a href="http://foo.com" onclick="return false;">....</a>

ou return false;do manipulador de cliques para algo #clickablecomo:

  $("#clickable").click(function() {
        var url = $("#clickable a").attr("href");
        window.location = url;
        return false;
   });
TheVillageIdiot
fonte
-3

Todas as soluções são complicadas e de jscript. Aqui está a versão mais simples:

var IsChildWindow=false;

function ParentClick()
{
    if(IsChildWindow==true)
    {
        IsChildWindow==false;
        return;
    }
    //do ur work here   
}


function ChildClick()
{
    IsChildWindow=true;
    //Do ur work here    
}
ratnesh
fonte
3
Eu acho que você cometeu um erro na linha "IsChildWindow == false;" - não deveria ser "IsChildWindow = false;"?
Andre Schweighofer
-5
<a onclick="return false;" href="http://foo.com">I want to ignore my parent's onclick event.</a>
andres descalzo
fonte
5
Isso também impede que o link navegue.
Rex M
Sim, ou não é necessário? Ou precisa ser nevegar?
andres descalzo