В чем разница между event.stopPropagation и event.preventDefault?

835

Кажется, они делают одно и то же ...
Один современный и один старый? Или они поддерживаются разными браузерами?

Когда я обрабатываю события самостоятельно (без фреймворка), я всегда проверяю оба и выполняю оба, если они есть. (Я тоже return false, но у меня такое ощущение, что не работает с событиями, связанными с node.addEventListener).

Так почему оба? Должен ли я продолжать проверять оба? Или на самом деле есть разница?

(Я знаю, много вопросов, но все они в некотором роде =))

Rudie
источник

Ответы:

1016

stopPropagation останавливает событие от всплытия цепочки событий.

preventDefault предотвращает действие по умолчанию, которое браузер выполняет с этим событием.

Примеры

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>

При stopPropagationэтом вызывается только buttonобработчик divщелчка, а обработчик щелчка никогда не срабатывает.

Где, как будто вы используете preventDefault, останавливается только действие браузера по умолчанию, но обработчик щелчков div все еще срабатывает.

Ниже приведены некоторые документы по свойствам и методам событий DOM из MDN:

Для IE9 и FF вы можете просто использовать protectDefault & stopPropagation.

Для поддержки IE8 и ниже заменить stopPropagationна cancelBubbleи заменить preventDefaultнаreturnValue

Raynos
источник
Так они очень разные? Есть ли в IE два разных метода событий? (Могут ли они быть одинаковыми ??) Странно, что фреймворки выполняют обе event.stopфункции ... Также странно, у меня никогда не было проблем с этим. Я часто использую пузырьки. Спасибо за пример!
Руди
@Rudie прокомментировал поддержку браузера.
Рэйнос
7
Стоит отметить (в случае, если вы не смотрите на документы MSDN), что cancelBubbleи returnValueоба свойства (так должны быть установлены cancelBubble = true;), и что preventDefaultи stopPropagationметоды (так следует называть preventDefault();)
freefaller
1
@Raynos, только одно примечание: event.stopPropagation () не только останавливает всплытие события в цепочке событий, но и останавливает распространение события на этапе захвата ( developer.mozilla.org/en-US/docs/Web/ API / Event / stopPropagation )
Юйцы
1
Было бы полезно узнать, каково действие по умолчанию для нажатия кнопки, чтобы я мог подумать, почему stopDefault()он не работает так же, как stopPropagation(). Если действие по умолчанию не вызывает onclickметод, что это?
d512
249

терминология

С quirksmode.org :

Захват событий

Когда вы используете захват событий

               | |
--------------- | | -----------------
| element1 | | |
| ----------- | | ----------- |
| | element2 \ / | |
| ------------------------- |
| Событие ЗАХВАТ |
-----------------------------------

обработчик события element1 срабатывает первым, обработчик события element2 срабатывает последним.

Событие кипит

Когда вы используете всплывающее событие

               / \
--------------- | | -----------------
| element1 | | |
| ----------- | | ----------- |
| | element2 | | | |
| ------------------------- |
| Событие BUBBLING |
-----------------------------------

обработчик события element2 срабатывает первым, обработчик события element1 срабатывает последним.

Любое событие, происходящее в модели событий W3C, сначала фиксируется до достижения целевого элемента, а затем снова всплывает .

                 | | / \
----------------- | | - | | -----------------
| element1 | | | | |
| ------------- | | - | | ----------- |
| | element2 \ / | | | |
| -------------------------------- |
| Модель событий W3C |
------------------------------------------

Интерфейс

С w3.org , для захвата событий :

Если захват EventListenerхочет предотвратить дальнейшую обработку события, он может вызвать stopPropagationметод Eventинтерфейса. Это предотвратит дальнейшую отправку события, хотя дополнительное EventListenersзарегистрированное на том же уровне иерархии все еще получит событие. После stopPropagation вызова метода события дальнейшие вызовы этого метода не имеют никакого дополнительного эффекта. Если никаких дополнительных захватчиков не существует и stopPropagationони не были вызваны, событие вызывает соответствующую EventListenersцель на самой цели.

Для всплытия событий :

Любой обработчик события может решить предотвратить дальнейшее распространение события, вызвав stopPropagationметод Eventинтерфейса. Если какой-либо EventListenerвызов этого метода, все дополнительные EventListenersна текущем EventTargetбудет вызван, но пузырьки прекратятся на этом уровне. stopPropagationТребуется только один вызов для предотвращения дальнейшего пузырения.

Для отмены мероприятия :

Отмены осуществляются путем вызова Event«S preventDefault метода. Если один или несколько EventListenersвызовов preventDefaultво время любой фазы потока событий, действие по умолчанию будет отменено.

Примеры

В следующих примерах щелчок по гиперссылке в веб-браузере запускает поток события (слушатели события выполняются) и действие по умолчанию для цели события (открывается новая вкладка).

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

Пример 1 : это приводит к выводу

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

Пример 2 : добавление stopPropagation()в функцию

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

результаты в выводе

DIV event capture

Слушатель события предотвратил дальнейшее распространение события вниз и вверх. Однако это не помешало действию по умолчанию (открытие новой вкладки).

Пример 3 : добавление stopPropagation()в функцию

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

или функция

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

результаты в выводе

DIV event capture
A event capture
A event bubbling

Это связано с тем, что оба прослушивателя событий зарегистрированы на одной и той же цели. Слушатели события предотвратили дальнейшее распространение события вверх. Однако они не помешали действию по умолчанию (открытие новой вкладки).

Пример 4 : добавление preventDefault()к любой функции, например

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

предотвращает открытие новой вкладки.

Ashkan
источник
23
Спасибо за более глубокое различие между захватом и всплытием, тогда как другой ответ касается только проблем jQuery.
Флорибон
@floribon говорят, что вы не собираетесь обидеть Райноса за его ответ.
Махи
3
@ Махи что? Я просто говорю факты, принятый ответ использует jQuery, который не подразумевается OP, поэтому для меня это не фактический ответ. Теперь это просто технический факт, ничего личного и никого не обидеть.
floribon
2
Ваш ответ очень систематический и хорошо демонстрирует, что происходит. Изначально я не хотел беспокоить вас и сам вносить некоторые изменения в ваш ответ, потому что я думаю, что его можно немного улучшить для ясности. Этот ответ объясняет модель события для начинающих, и поэтому я думаю, что каждая помощь, которую мы можем оказать новичкам, имеет большое значение. Мое предложение по редактированию было отклонено общими аргументами, с которыми я не согласен. Если вы, @Ashkan, чувствуете, что эти небольшие изменения могут помочь улучшить ответ, пожалуйста, включите их.
Мукахо
68

вернуть ложь;


return false; делает 3 отдельные вещи, когда вы называете это:

  1. event.preventDefault() - Останавливает поведение браузера по умолчанию.
  2. event.stopPropagation() - Он предотвращает распространение события (или «всплеск») DOM.
  3. Останавливает выполнение обратного вызова и немедленно возвращается при вызове.

Обратите внимание, что это поведение отличается от обычных (не jQuery) обработчиков событий, в которых, в частности, return falseне останавливается всплывание события.

preventDefault ();


preventDefault(); делает одно: останавливает поведение браузера по умолчанию.

Когда их использовать?


Мы знаем, что они делают, но когда их использовать? Просто это зависит от того, чего вы хотите достичь. Используйте, preventDefault();если вы хотите «просто» предотвратить поведение браузера по умолчанию. Используйте return false; когда вы хотите предотвратить поведение браузера по умолчанию и предотвратить распространение DOM события. В большинстве ситуаций, где вы бы использовали return false; что вы действительно хотите preventDefault().

Примеры:


Давайте попробуем разобраться с примерами:

Мы увидим чистый пример JAVASCRIPT

Пример 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>

Запустив приведенный выше код, вы увидите гиперссылку «Нажмите здесь, чтобы перейти на stackoverflow.com». Теперь, если вы сначала нажмете на эту ссылку, вы получите предупреждение JavaScript. Ссылка нажата. Далее вы получите уведомление о нажатии JavaScript, которое будет нажато, и сразу же вы будете перенаправлены на stackoverflow.com.

Пример 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>

Запустите приведенный выше код , вы увидите гиперссылку «Нажмите здесь , чтобы посетить stackoverflow.com» Теперь , если нажать на эту ссылку , сначала вы получите уведомление яваскрипта Link Кликов Далее вы получите уведомление Javascript Див Clicked Далее вы увидите гиперссылку " Нажмите здесь, чтобы посетить stackoverflow.com », замененный текстом« Событие клика предотвращено », и вы не будете перенаправлены на stackoverflow.com. Это происходит из-за метода event.preventDefault (), который мы использовали для предотвращения срабатывания щелчка по умолчанию.

Пример 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>

На этот раз, если вы нажмете на ссылку, функция executeParent () не будет вызвана, и вы не получите уведомление о нажатии JavaScript . Это связано с тем, что мы предотвратили распространение на родительский div с помощью метода event.stopPropagation (). Далее вы увидите гиперссылку «Нажмите здесь, чтобы посетить stackoverflow.com», замененную текстом «Событие Click будет выполнено», и вы сразу же будете перенаправлены на stackoverflow.com. Это потому, что мы не предотвратили срабатывание щелчка по умолчанию на этот раз с помощью метода event.preventDefault ().

Пример 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>

Если вы нажмете на ссылку, функция executeParent () не будет вызвана, и вы не получите предупреждение javascript. Это связано с тем, что мы предотвратили распространение на родительский div с помощью метода event.stopPropagation (). Далее вы увидите гиперссылку «Нажмите здесь, чтобы посетить stackoverflow.com», замененную текстом «Событие клика запрещено», и вы не будете перенаправлены на stackoverflow.com. Это потому, что мы предотвратили срабатывание щелчка по умолчанию на этот раз с помощью метода event.preventDefault ().

Пример 5:

Для возврата false у меня есть три примера, и все, кажется, делают одно и то же (просто возвращают false), но на самом деле результаты совершенно разные. Вот что на самом деле происходит в каждом из вышеперечисленных.

случаи:

  1. Возврат false из встроенного обработчика событий предотвращает переход браузера по адресу ссылки, но не останавливает распространение события через DOM.
  2. Возвращение false из обработчика событий jQuery не позволяет браузеру перейти на адрес ссылки и останавливает распространение события через DOM.
  3. Возвращение false из обычного обработчика событий DOM абсолютно ничего не делает.

Увидим все три примера.

  1. Встроенный возврат false.

<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. Возвращает false из обработчика событий 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. Возвращает false из обычного обработчика событий DOM.

<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>

Надеюсь, эти примеры понятны. Попробуйте выполнить все эти примеры в html-файле, чтобы увидеть, как они работают.

Кевал Бхатт
источник
1
Хорошие примеры, но без jQuery было бы еще лучше, потому что jQuery подделывает объект события, так что это технически отличается.
Руди
1
@Rudie Я обновил свой ответ и удалил из него jquery, а также добавил пример для возврата false.
Кевал Бхатт
17

Это цитата отсюда

event.preventDefault

Метод protectDefault предотвращает выполнение событием своих функций по умолчанию. Например, вы использовали бы protectDefault для элемента A, чтобы перестать щелкать по этому элементу, чтобы он покинул текущую страницу:

//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!'); 
};

В то время как функциональные возможности элемента по умолчанию заложены, событие продолжает всплывать в DOM.

event.stopPropagation

Второй метод, stopPropagation, позволяет использовать функциональность события по умолчанию, но предотвращает распространение события:

//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 эффективно не дает родительским элементам знать о данном событии своего дочернего элемента.

В то время как простой метод остановки позволяет нам быстро обрабатывать события, важно подумать о том, что именно вы хотите, чтобы произошло с пузырьками. Могу поспорить, что все, что действительно нужно разработчику, это предотвращение дефолта в 90% случаев! Неправильная «остановка» события может привести к многочисленным неприятностям; Ваши плагины могут не работать, а сторонние плагины могут быть заблокированы. Или еще хуже - ваш код нарушает другие функции сайта.

VladL
источник
2

Event.preventDefault - останавливает поведение браузера по умолчанию. Теперь перейдем к поведению браузера по умолчанию. Предположим, у вас есть тег привязки, у него есть атрибут href, и этот тег привязки вложен в тег div, который получил событие click. Поведение по умолчанию тега привязки происходит при нажатии на тег привязки, по которому он должен перемещаться, но в случае event.preventDefault он останавливает навигацию. Но это никогда не останавливает пузыри события или эскалации события, т.е.

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

Результат будет

"элемент был нажат"

"контейнер был нажат"

Теперь event.StopPropation останавливает всплывание события или эскалацию события. Теперь с приведенным выше примером

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

Результат будет

"элемент был нажат"

Для получения дополнительной информации обратитесь по этой ссылке https://codeplanet.io/preventdefault-vs-stoppropagation-vs-stopimmediatepropagation/

Найас Субраманян
источник
1

event.preventDefault(); Останавливает действие элемента по умолчанию.

event.stopPropagation(); Предотвращает всплытие события в DOM-дереве, предотвращая уведомление любых родительских обработчиков о событии.

Например, если есть ссылка с методом щелчка, прикрепленным внутри DIVили к FORMкоторой также прикреплен метод щелчка, это предотвратит запуск метода DIVили FORMщелчка.

Майк Кларк
источник
-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>

Маниш Сингх
источник