Qual é a diferença entre event.stopPropagation e event.preventDefault?

834

Eles parecem estar fazendo a mesma coisa ...
Um moderno e um velho? Ou eles são suportados por diferentes navegadores?

Quando eu manejo eventos sozinho (sem estrutura), sempre verifico os dois e os executo se presentes. (Eu também return false, mas tenho a sensação de que não funciona com eventos associados node.addEventListener).

Então, por que ambos? Devo continuar verificando os dois? Ou existe realmente uma diferença?

(Eu sei, muitas perguntas, mas são todas iguais =))

Rudie
fonte

Respostas:

1015

stopPropagation impede que o evento borbulhe na cadeia de eventos.

preventDefault impede a ação padrão que o navegador faz nesse evento.

Exemplos

preventDefault

$("#but").click(function (event) {
  event.preventDefault()
})
$("#foo").click(function () {
  alert("parent click event fired!")
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="foo">
  <button id="but">button</button>
</div>

stopPropagation

$("#but").click(function (event) {
  event.stopPropagation()
})
$("#foo").click(function () {
  alert("parent click event fired!")
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="foo">
  <button id="but">button</button>
</div>

Com stopPropagation, apenas o buttonmanipulador de cliques do é chamado enquanto o divmanipulador de cliques do nunca é acionado.

Onde você usa preventDefault, apenas a ação padrão do navegador é interrompida, mas o manipulador de cliques da div ainda é acionado.

Abaixo estão alguns documentos sobre as propriedades e métodos do evento DOM do MDN:

Para o IE9 e o FF, você pode usar preventDefault e stopPropagation.

Para suportar o IE8 e inferior, substitua stopPropagationpor cancelBubblee substitua preventDefaultporreturnValue

Raynos
fonte
Então eles são muito diferentes? O IE também tem dois métodos de eventos diferentes? (Eles podem ser os mesmos ??) É estranho que os frameworks façam as duas coisas em suas event.stopfunções ... Também estranho, nunca tive problemas com isso. Eu uso muito borbulhando. Obrigado pelo exemplo!
Rudie
@Rudie comentou sobre o suporte ao navegador.
Raynos
7
Pena de notar (no caso de você não olhar para a documentação do MSDN) que cancelBubblee returnValuesão as duas propriedades (por isso deve ser definido cancelBubble = true;), e que preventDefaulte stopPropagationsão métodos (por isso deve ser chamado preventDefault();)
freefaller
1
@Raynos, apenas uma observação: event.stopPropagation () não apenas impede que o evento borbulhe na cadeia de eventos, mas também impede a propagação de eventos na fase de captura ( developer.mozilla.org/en-US/docs/Web/ API / Event / stopPropagation )
Yuci
1
Seria útil saber qual é a ação padrão para um clique no botão, para que eu possa raciocinar sobre por stopDefault()que não funciona da mesma maneira que stopPropagation(). Se a ação padrão não está chamando o onclickmétodo, o que é?
d512 21/06/19
249

Terminologia

Do quirksmode.org :

Captura de eventos

Quando você usa a captura de eventos

               | |
--------------- | | -----------------
| element1 | | |
| ----------- | ----------- |
| | element2 \ / | |
| ------------------------- |
| Evento CAPTURANDO |
-----------------------------------

o manipulador de eventos do elemento1 é acionado primeiro, o manipulador de eventos do elemento2 é acionado por último.

Evento borbulhante

Quando você usa bolhas de eventos

               / \
--------------- | | -----------------
| element1 | | |
| ----------- | ----------- |
| | element2 | | | |
| ------------------------- |
| Evento BUBBLING |
-----------------------------------

o manipulador de eventos do elemento2 é acionado primeiro, o manipulador de eventos do elemento1 é acionado por último.

Qualquer evento ocorrendo no modelo de evento W3C é capturado primeiro até atingir o elemento de destino e depois borbulha novamente .

                 | | / \
----------------- | | - | | -----------------
| element1 | | | | |
| ------------- | | - | | ----------- |
| | element2 \ / | | | |
| -------------------------------- |
| Modelo de evento W3C |
------------------------------------------

Interface

Do w3.org , para captura de eventos :

Se a captura EventListenerdesejar impedir o processamento adicional do evento, ele poderá chamar o stopPropagationmétodo da Eventinterface. Isso impedirá o envio adicional do evento, embora outros EventListenersregistros registrados no mesmo nível de hierarquia ainda recebam o evento. Depois que o stopPropagation método de um evento é chamado, outras chamadas para esse método não têm efeito adicional. Se nenhum capturador adicional existir e stopPropagationnão tiver sido chamado, o evento acionará o apropriado EventListenersno próprio destino.

Para o evento borbulhante :

Qualquer manipulador de eventos pode optar por impedir a propagação de eventos adicionais chamando o stopPropagationmétodo da Eventinterface. Se alguém EventListenerchamar esse método, todos os adicionais EventListenersna corrente EventTargetserão acionados, mas a bolha cessará nesse nível. stopPropagationÉ necessária apenas uma chamada para evitar mais bolhas.

Para cancelamento de evento :

Cancelamento é feito chamando o Event's preventDefault método. Se uma ou mais EventListenerschamadas preventDefaultdurante qualquer fase do fluxo de eventos, a ação padrão será cancelada.

Exemplos

Nos exemplos a seguir, um clique no hiperlink no navegador da Web dispara o fluxo do evento (os ouvintes de eventos são executados) e a ação padrão do destino do evento (uma nova guia é aberta).

HTML:

<div id="a">
  <a id="b" href="http://www.google.com/" target="_blank">Google</a>
</div>
<p id="c"></p>

JavaScript:

var el = document.getElementById("c");

function capturingOnClick1(ev) {
    el.innerHTML += "DIV event capture<br>";
}

function capturingOnClick2(ev) {
    el.innerHTML += "A event capture<br>";
}

function bubblingOnClick1(ev) {
    el.innerHTML += "DIV event bubbling<br>";
}

function bubblingOnClick2(ev) {
    el.innerHTML += "A event bubbling<br>";
}

// The 3rd parameter useCapture makes the event listener capturing (false by default)
document.getElementById("a").addEventListener("click", capturingOnClick1, true);
document.getElementById("b").addEventListener("click", capturingOnClick2, true);
document.getElementById("a").addEventListener("click", bubblingOnClick1, false);
document.getElementById("b").addEventListener("click", bubblingOnClick2, false);

Exemplo 1 : resulta na saída

DIV event capture
A event capture
A event bubbling
DIV event bubbling

Exemplo 2 : adicionando stopPropagation()à função

function capturingOnClick1(ev) {
    el.innerHTML += "DIV event capture<br>";
    ev.stopPropagation();
}

resulta na saída

DIV event capture

O ouvinte de eventos impediu a propagação descendente e ascendente do evento. No entanto, isso não impediu a ação padrão (uma nova guia é aberta).

Exemplo 3 : adicionando stopPropagation()à função

function capturingOnClick2(ev) {
    el.innerHTML += "A event capture<br>";
    ev.stopPropagation();
}

ou a função

function bubblingOnClick2(ev) {
    el.innerHTML += "A event bubbling<br>";
    ev.stopPropagation();
}

resulta na saída

DIV event capture
A event capture
A event bubbling

Isso ocorre porque os dois ouvintes de eventos são registrados no mesmo destino de evento. Os ouvintes do evento impediram a propagação para cima do evento. No entanto, eles não impediram a ação padrão (uma nova guia é aberta).

Exemplo 4 : adicionando preventDefault()a qualquer função, por exemplo

function capturingOnClick1(ev) {
    el.innerHTML += "DIV event capture<br>";
    ev.preventDefault();
}

impede que uma nova guia seja aberta.

Ashkan
fonte
23
Obrigado pela distinção mais profunda entre capturar e borbulhar, enquanto a outra resposta trata apenas das preocupações do jQuery.
Floribon
@flibibon diz que você não tem intenção de ofender os raynos por sua resposta.
Mahi
3
@Mahi what? Estou apenas dizendo fatos, a resposta aceita está usando jQuery, que não é assumido pelo OP, então para mim não é a resposta real. Agora isso é apenas um fato técnico, nada pessoal e ninguém para se ofender.
Floribon
2
Sua resposta é muito sistemática e demonstra bem o que está acontecendo. Inicialmente, eu não queria incomodá-lo e fazer algumas alterações na sua resposta, porque acho que poderia ser melhorado um pouco para maior clareza. Esta resposta explica o modelo de evento para iniciantes e, como tal, acho que toda a ajuda que podemos dar aos iniciantes é importante. Minha sugestão de edição foi recusada por argumentos genéricos, dos quais discordo. Se você, @Ashkan, acha que essas pequenas alterações podem ajudar a melhorar a resposta, incorpore-as.
Mucaho 15/04
68

retorna falso;


return false; faz 3 coisas separadas quando você chama:

  1. event.preventDefault() - Para o comportamento padrão do navegador.
  2. event.stopPropagation() - Impede que o evento propague (ou "borbulhe") o DOM.
  3. Pára a execução de retorno de chamada e retorna imediatamente quando chamado.

Observe que esse comportamento difere dos manipuladores de eventos normais (não jQuery), nos quais, notavelmente, return falsenão impede que o evento borbulhe.

preventDefault ();


preventDefault(); faz uma coisa: interrompe o comportamento padrão do navegador.

Quando usá-los?


Sabemos o que eles fazem, mas quando usá-los? Simplesmente depende do que você deseja realizar. Use preventDefault();se você quiser "apenas" impedir o comportamento padrão do navegador. Use return false; quando você deseja impedir o comportamento padrão do navegador e impedir que o evento propague o DOM. Na maioria das situações em que você usaria return false; o que você realmente quer é preventDefault().

Exemplos:


Vamos tentar entender com exemplos:

Veremos um exemplo puro de JAVASCRIPT

Exemplo 1:

<div onclick='executeParent()'>
  <a href='https://stackoverflow.com' onclick='executeChild()'>Click here to visit stackoverflow.com</a>
</div>
<script>
  function executeChild() {
    alert('Link Clicked');
  }

  function executeParent() {
    alert('div Clicked');
  }
</script>

Execute o código acima e você verá o hiperlink 'Clique aqui para visitar o stackoverflow.com' agora, se você clicar nesse link primeiro, receberá o alerta javascript Link Clicado Em seguida, você receberá o alerta javascript div Clicou e imediatamente será redirecionado para stackoverflow.com.

Exemplo 2:

<div onclick='executeParent()'>
  <a href='https://stackoverflow.com' onclick='executeChild()'>Click here to visit stackoverflow.com</a>
</div>
<script>
  function executeChild() {
    event.preventDefault();
    event.currentTarget.innerHTML = 'Click event prevented'
    alert('Link Clicked');
  }

  function executeParent() {
    alert('div Clicked');
  }
</script>

Execute o código acima e você verá o hiperlink 'Clique aqui para visitar o stackoverflow.com' agora, se você clicar nesse link primeiro, receberá o alerta javascript Link Clicado Em seguida, você receberá o alerta javascript div Clicado Em seguida, você verá o hiperlink ' Clique aqui para visitar o stackoverflow.com 'substituído pelo texto' Evento de clique impedido 'e você não será redirecionado para o stackoverflow.com. Isso se deve ao método event.preventDefault () que usamos para impedir que a ação de clique padrão seja acionada.

Exemplo 3:

<div onclick='executeParent()'>
  <a href='https://stackoverflow.com' onclick='executeChild()'>Click here to visit stackoverflow.com</a>
</div>
<script>
  function executeChild() {
    event.stopPropagation();
    event.currentTarget.innerHTML = 'Click event prevented'
    alert('Link Clicked');
  }

  function executeParent() {
    alert('div Clicked');
  }
</script>

Desta vez, se você clicar em Vincular, a função executeParent () não será chamada e você não receberá o alerta javascript div. Clicado dessa vez. Isso ocorre porque impedimos a propagação para a div pai usando o método event.stopPropagation (). Em seguida, você verá o hiperlink 'Clique aqui para visitar o stackoverflow.com' substituído pelo texto 'O evento Click será executado' e imediatamente você será redirecionado para o stackoverflow.com. Isso ocorre porque não impedimos que a ação de clique padrão seja acionada dessa vez usando o método event.preventDefault ().

Exemplo 4:

<div onclick='executeParent()'>
  <a href='https://stackoverflow.com' onclick='executeChild()'>Click here to visit stackoverflow.com</a>
</div>
<script>
  function executeChild() {
    event.preventDefault();
    event.stopPropagation();
    event.currentTarget.innerHTML = 'Click event prevented'
    alert('Link Clicked');
  }

  function executeParent() {
    alert('Div Clicked');
  }
</script>

Se você clicar no link, a função executeParent () não será chamada e você não receberá o alerta javascript. Isso ocorre porque impedimos a propagação para a div pai usando o método event.stopPropagation (). Em seguida, você verá o hiperlink 'Clique aqui para visitar o stackoverflow.com' substituído pelo texto 'Evento de clique impedido' e você não será redirecionado para o stackoverflow.com. Isso ocorre porque impedimos que a ação de clique padrão seja acionada dessa vez usando o método event.preventDefault ().

Exemplo 5:

Para return false, tenho três exemplos e todos parecem estar fazendo exatamente a mesma coisa (retornando false), mas, na realidade, os resultados são bem diferentes. Aqui está o que realmente acontece em cada uma das opções acima.

casos:

  1. Retornar false de um manipulador de eventos embutido impede que o navegador navegue para o endereço do link, mas não impede que o evento seja propagado pelo DOM.
  2. Retornar false de um manipulador de eventos jQuery impede o navegador de navegar para o endereço do link e impede a propagação do evento pelo DOM.
  3. Retornar false de um manipulador de eventos DOM normal não faz absolutamente nada.

Verá todos os três exemplos.

  1. Retorno em linha falso.

<div onclick='executeParent()'>
  <a href='https://stackoverflow.com' onclick='return false'>Click here to visit stackoverflow.com</a>
</div>
<script>
  var link = document.querySelector('a');

  link.addEventListener('click', function() {
    event.currentTarget.innerHTML = 'Click event prevented using inline html'
    alert('Link Clicked');
  });


  function executeParent() {
    alert('Div Clicked');
  }
</script>

  1. Retornando false de um manipulador de eventos jQuery.

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
  <a href='https://stackoverflow.com'>Click here to visit stackoverflow.com</a>
</div>
<script>
  $('a').click(function(event) {
    alert('Link Clicked');
    $('a').text('Click event prevented using return FALSE');
    $('a').contents().unwrap();
    return false;
  });
  $('div').click(function(event) {
    alert('Div clicked');
  });
</script>

  1. Retornando false de um manipulador de eventos DOM regular.

<div onclick='executeParent()'>
  <a href='https://stackoverflow.com' onclick='executeChild()'>Click here to visit stackoverflow.com</a>
</div>
<script>
  function executeChild() {
    event.currentTarget.innerHTML = 'Click event prevented'
    alert('Link Clicked');
    return false
  }

  function executeParent() {
    alert('Div Clicked');
  }
</script>

Espero que esses exemplos sejam claros. Tente executar todos esses exemplos em um arquivo html para ver como eles funcionam.

Keval Bhatt
fonte
1
Bons exemplos, mas teria sido ainda melhor sem o jQuery, porque o jQuery falsifica o objeto do evento, portanto é tecnicamente diferente.
Rudie 27/05
1
@ Rudie Atualizei minha resposta e removi o jquery, além de adicionar um exemplo para retornar false.
Keval Bhatt
17

Esta é a citação daqui

Event.preventDefault

O método preventDefault impede que um evento execute sua funcionalidade padrão. Por exemplo, você usaria preventDefault em um elemento A para parar de clicar nesse elemento para sair da página atual:

//clicking the link will *not* allow the user to leave the page 
myChildElement.onclick = function(e) { 
    e.preventDefault(); 
    console.log('brick me!'); 
};

//clicking the parent node will run the following console statement because event propagation occurs
logo.parentNode.onclick = function(e) { 
    console.log('you bricked my child!'); 
};

Enquanto a funcionalidade padrão do elemento estiver emparedada, o evento continuará realçando o DOM.

Event.stopPropagation

O segundo método, stopPropagation, permite que a funcionalidade padrão do evento ocorra, mas impede a propagação do evento:

//clicking the element will allow the default action to occur but propagation will be stopped...
myChildElement.onclick = function(e) { 
    e.stopPropagation();
    console.log('prop stop! no bubbles!'); 
};

//since propagation was stopped by the child element's onClick, this message will never be seen!
myChildElement.parentNode.onclick = function(e) { 
    console.log('you will never see this message!'); 
};

stopPropagation efetivamente impede que elementos pai conheçam um determinado evento em seu filho.

Embora um método simples de parada nos permita manipular eventos rapidamente, é importante pensar no que exatamente você quer que aconteça com a bolha. Aposto que tudo o que um desenvolvedor realmente quer é preventDefault 90% do tempo! Incorretamente "parar" um evento pode causar inúmeros problemas na linha; seus plug-ins podem não funcionar e seus plug-ins de terceiros podem estar bloqueados. Ou pior: seu código quebra outras funcionalidades em um site.

VladL
fonte
2

Event.preventDefault - interrompe o comportamento padrão do navegador. Agora vem o que é o comportamento padrão do navegador. Suponha que você tenha uma marca de âncora e ela possua um atributo href e essa marca de âncora esteja aninhada dentro de uma marca div que possui um evento de clique. O comportamento padrão da marca âncora é quando você clica na marca âncora em que deve navegar, mas o que event.preventDefault faz é interromper a navegação nesse caso. Mas isso nunca interrompe o borbulhar do evento ou a escalada do evento, ou seja,

<div class="container">
 <a href="#" class="element">Click Me!</a>
</div>

$('.container').on('click', function(e) {
 console.log('container was clicked');
});

$('.element').on('click', function(e) {
  e.preventDefault(); // Now link won't go anywhere
  console.log('element was clicked');
});

O resultado será

"elemento foi clicado"

"o contêiner foi clicado"

Agora event.StopPropation para de borbulhar de evento ou de escalação de evento. Agora com o exemplo acima

$('.container').on('click', function(e) {
  console.log('container was clicked');
});

$('.element').on('click', function(e) {
  e.preventDefault(); // Now link won't go anywhere
  e.stopPropagation(); // Now the event won't bubble up
 console.log('element was clicked');
});

O resultado será

"elemento foi clicado"

Para obter mais informações, consulte este link https://codeplanet.io/preventdefault-vs-stoppropagation-vs-stopimmediatepropagation/

Nayas Subramanian
fonte
1

event.preventDefault(); Interrompe a ação padrão de um elemento.

event.stopPropagation(); Impede que o evento borbulhe na árvore DOM, impedindo que qualquer manipulador pai seja notificado sobre o evento.

Por exemplo, se houver um link com um método click anexado dentro de DIVou FORMque também tenha um método click anexado, isso impedirá que o método DIVou FORMclick seja acionado.

Mike Clark
fonte
-3

$("#but").click(function(event){
console.log("hello");
  event.preventDefault();
 });


$("#foo").click(function(){
 alert("parent click event fired !");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="foo">
  <button id="but">button</button>
</div>

Manish Singh
fonte