jQueryでは子要素のイベントが親要素にまで伝わってしまいます。そうすると子要素だけ処理したいのに親要素まで処理が行われる困ることがあります。
その解決策は次の2つです。
stopPropagation
を使うreturn false;
を使う
これらの方法を使うと子要素のイベントは親要素に伝わらなくなりますが、特に理由がなければstopPropagation
を使いましょう。return fasle;
には要素のデフォルトイベントまで止めてしまうという副作用があります。
この記事ではもう少し深掘りした内容を、私が実際に直面したケースを例にご紹介します。気になる方は続きをどうぞ。
子要素と親要素の両方にClickイベントを設定した例
そもそも子にも親にもClickイベントを適用するような状況があるのか?ということですが、今回はその状況にみごとに遭遇しました。
WordPressの多階層メニューをスマホ表示で次のような動作をさせたいと思いました。
2階層目以降は+ボタンをクリックしたら展開するという仕様です。+ボタンの部分にチェックボックスでも使えば今回の問題は起きないのですが、WordPressのカスタム機能を使ったメニューなのでソースをいじりにくいです。
フィルターを駆使してチェックボックスを挿入させることもできるのですが、余計な要素も入れず、もう少しスマートにしたい思い閃いたのが下記の方法です。
<li>
タグの中に<a>
タグと::after
疑似要素で作った+ボタンを並べます。<a>
タグをタップするとページ遷移、それ以外(実質的に疑似要素部分)をタップすると下の階層が表示という動作にすれば余計な要素を入れる必要もありません。clickイベントのターゲットは<li>
タグとしました。
JavaScriptは次のようになります。子メニューの展開自体はCSSで制御しています。
$('.menu-item-has-children').on('click', function (e) {
$(this).toggleClass('js-sub-menu-open');
});
ただ、HTMLの構造的に2階層目の<li>
要素は1階層目の<li>
要素の子要素になります。そのため、2階層目の+ボタンをクリックすると1階層目もクリックされたと判断され、2階層目→展開・1階層目→閉じるという動作になってしまいます。
ここで今回の記事のテーマでもあるクリックイベントを子要素だけで止める必要がでてきました。
return false;
を使った場合
まず、オススメではないreturn false;
を使うとどうなるかです。コードは次のようになります。
$('.menu-item-has-children').on('click', function (e) {
$(this).toggleClass('js-sub-menu-open');
return false;
});
メニューの開閉は文句なしです。ところが…<a>
タグ部分をクリックしてもページ遷移しません。
実はretrn fasle;
は要素のデフォルトのイベントも停止させてしまいます。
その影響で<li>
子要素である<a>
タグのデフォルトイベントまで停止してしまったのです。
stopPropagationだと問題が出ない
ということで、stopPropagation
を使ったソースがこちら。
$('.menu-item-has-children').on('click', function (e) {
$(this).toggleClass('js-sub-menu-open');
e.stopPropagation();
});
これだと、親要素にイベントは伝わらず、かつ<a>
タグのデフォルトイベントは発火して無事にページ遷移ができます。
これにて一件落着です。
実はこの記事を書いていて気付いたのですが、<li>
ではなく+ボタンの疑似要素をclickイベントのターゲットにすればこんなことに悩まなくても大丈夫でした(苦笑)まぁ、<li>
全体がターゲットだとグレーの部分のどこをタップしてもメニューが閉じるのでそれはそれで操作性はよいかも?
なにはともあれ、stopPropagation
の勉強になったから良しとしておきましょう。
このように子要素のイベントを親要素に伝えないようにする方法は2通りありますが、return false;
を使うとリンクでページ遷移しなかったりチェックボックスのオンオフができなくなったりといった思わぬ副作用が発生する可能性があります。無用なトラブルを避けるためにも特に理由がなければstopPropagation
を使うことをオススメします。
コメント
この記事へのコメントはありません。