イベントの伝搬をキャンセルする

HTML ページ内でイベントが発生した場合、親要素へとイベントが伝搬していきますが、途中でこれ以上の伝搬を行わないようにイベントの伝搬を停止することができます。また同じターゲットの同じイベントに対して複数のイベントリスナーを登録できますが、途中で他のまだ呼び出されていないイベントリスナーを呼び出さないように停止することもできます。ここではイベントの伝搬をキャンセルする方法について解説します。

※ イベントがどのように他の要素に伝搬していくのかについては「イベントの伝搬(キャプチャリングとバブリング)」をご参照ください。

(Last modified: )

イベントの伝搬を止める

Event オブジェクトの stopPropagation メソッドを使用すると、イベントの伝搬を止めることができます。書式は次のとおりです。

event.stopPropagation()

例えばイベントが発生し呼び出されたイベントリスナーの中でこのメソッドが実行されると、これ以降のイベントの伝搬(キャプチャリングフェーズ、バブリングフェーズのどちらの場合でも)が行われなくなります。

イベントの伝搬を止める(1)

実際には次のように記述します。イベントが呼び出されるときに引数として渡されてくる Event オブジェクトに対して stopPropagation メソッドを実行します。

let target = document.getElementById('xxx');

target.addEventListener('click', function(event){
    console.log('Hello');
    event.stopPropagation();
});
サンプルコード

次のサンプルを見てください。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>サンプル</title>
<style type="text/css">
div {
  background-color:#E5E5E5;
  border:5px solid #8f8f8f;
}
#outer{
  width:300px;
}
#inner{
  margin:20px;
  padding:20px;
  text-align:center;
}
</style>
</head>
<body>

<p>ボタンをクリックしてください。</p>

<div id="outer">
  <div id="inner">
    <input type="button" value="buttonA" id="btnA">
    <input type="button" value="buttonB" id="btnB">
  </div>
</div>

<script>
    let outer = document.getElementById('outer');
    let inner = document.getElementById('inner');
    let btna = document.getElementById('btnA');
    let btnb = document.getElementById('btnB');

    outer.addEventListener('click', function(){
        console.log('outer');
    });

    inner.addEventListener('click', function(event){
        console.log('inner');
        event.stopPropagation();
    });

    btna.addEventListener('click', function(){
        console.log('buttonA');
    });

    btnb.addEventListener('click', function(event){
        console.log('buttonB');
        event.stopPropagation();
    });
</script>

</body>
</html>

外側の div 要素の中に内側の div 要素があり、さらに内側に input 要素が 2 つあります。それぞれの要素には click イベントに対するイベントリスナーが登録されています。今回は内側の div 要素のイベントリスナーと、右側の input 要素のイベントリスナーでそれぞれ stopPropagation メソッドを実行しています。

最初に左側の input 要素をクリックしてみます。 click イベントは親要素へと伝搬されていきますが、内側の div 要素で呼び出されたイベントリスナー内で stopPropagation メソッドを実行しているので click イベントの伝搬は止まりそれ以降は行われません。

イベントの伝搬を止める(2)

次に右側の input 要素をクリックしてみます。右側の input 要素で呼び出されたイベントリスナー内で stopPropagation メソッドを実行しているので click イベントの伝搬はそれ以降は行われませせん。

イベントの伝搬を止める(3)

同じイベントに対して登録された他のイベントリスナーの呼び出しを止める

Event オブジェクトの stopImmediatePropagation メソッドを使用すると、同じイベントに対して登録されたまだ呼び出されていない他のイベントリスナーの呼び出しを止めることができます。書式は次のとおりです。

event.stopImmediatePropagation()

addEventListener メソッドを使ってイベントリスナーを登録する場合、同じターゲットの同じイベントに対して複数のイベントリスナーを登録することができます。イベントが発生すると登録された順番にイベントリスナーが呼び出されます。

イベントが発生し呼び出されたイベントリスナーの中で stopImmediatePropagation メソッドを実行すると、同じイベントに対して登録されていてまだ呼び出されていないイベントリスナーが呼び出されなくなります。また stopPropagation メソッドを実行したときと同じように親要素へのイベント伝搬も行われなくなります。

実際には次のように記述します。イベントが呼び出されるときに引数として渡されてくる Event オブジェクトに対して stopImmediatePropagation メソッドを実行します。

let target = document.getElementById('xxx');

target.addEventListener('click', function(event){
    console.log('Hello');
    event.stopImmediatePropagation();
});

target.addEventListener('click', function(event){
    console.log('Bye');
});
サンプルコード

次のサンプルを見てください。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>サンプル</title>
<style type="text/css">
div {
  background-color:#E5E5E5;
  border:5px solid #8f8f8f;
}
#outer{
  width:300px;
}
#inner{
  margin:20px;
  padding:20px;
  text-align:center;
}
</style>
</head>
<body>

<p>ボタンをクリックしてください。</p>

<div id="outer">
  <div id="inner">
    <input type="button" value="buttonA" id="btnA">
    <input type="button" value="buttonB" id="btnB">
  </div>
</div>

<script>
    let outer = document.getElementById('outer');
    let btna = document.getElementById('btnA');
    let btnb = document.getElementById('btnB');

    outer.addEventListener('click', function(){
        console.log('outer');
    });

    btna.addEventListener('click', function(event){
        console.log('ActionA1');
        event.stopPropagation();
    });

    btna.addEventListener('click', function(){
        console.log('ActionA2');
    });

    btnb.addEventListener('click', function(event){
        console.log('ActionB1');
        event.stopImmediatePropagation();
    });

    btnb.addEventListener('click', function(){
        console.log('ActionB2');
    });
</script>

</body>
</html>

外側の div 要素の中に input 要素が 2 つあります。 input 要素にはそれぞれ 2 つのイベントリスナーが登録されており、一つ目の input 要素の最初のイベントリスナーの中では stopPropagation メソッドを実行しています。そして二つ目の input 要素の最初のイベントリスナーの中では stopImmediatePropagation メソッドを呼びだしています。

最初に左側の input 要素をクリックしてみます。呼び出された最初のイベントリスナーの中で stopPropagation メソッドを実行しているので、親要素へのイベントは伝達しません。ただ同じ要素の同じイベントに対するイベントリスナーは実行されるため、二つ目のイベントリスナーも呼び出されています。

同じイベントに対して登録された他のイベントリスナーの呼び出しを止める(1)

次に左側の input 要素をクリックしてみます。呼び出された最初のイベントリスナーの中で stopImmediatePropagation メソッドを実行しているので親要素へのイベント伝達も停止し、同じ要素の同じイベントに対するイベントリスナーの中で、まだ実行されているイベントリスナーも呼び出されなくなります。

同じイベントに対して登録された他のイベントリスナーの呼び出しを止める(2)

-- --

イベントの伝搬をキャンセルする方法について解説しました。

( Written by Tatsuo Ikura )

プロフィール画像

著者 / TATSUO IKURA

これから IT 関連の知識を学ばれる方を対象に、色々な言語でのプログラミング方法や関連する技術、開発環境構築などに関する解説サイトを運営しています。