TODO: SkipNavigation

CSSのtransitionendイベントが意図しないタイミングで発生する問題とその原因

JavaScriptでtransitionendイベントを扱う際に、意図しないタイミングでイベントが発生するという問題に遭遇することがあります。今回は、その原因と解決策について詳しく解説します。

問題の概要

例えば、ある親要素に対してトランジションを設定し、transitionendイベントをリッスンしているとします。しかし、実際には期待しないタイミングでイベントが発火し、思わぬ挙動を引き起こすことがあります。

原因:子要素のtransitionendイベント

この問題の主な原因は、子要素のtransitionendイベントが親要素にも伝播していることです。イベントのバブリングにより、子要素で発生したイベントが親要素にも届いてしまうため、親要素のイベントリスナーが意図せずに反応してしまいます。

具体的な例

html
<div id="parent">
  <div class="child"></div>
</div>
css
#parent {
  width: 200px;
  height: 200px;
  background-color: blue;
  transition: background-color 1s;
}

.child {
  width: 100px;
  height: 100px;
  background-color: red;
  transition: transform 1s;
}
javascript
const parent = document.getElementById('parent');
parent.addEventListener('transitionend', () => {
  console.log('Parent transition ended');
});

このコードでは、親要素と子要素の両方にトランジションが設定されています。子要素のトランジションが終了すると、親要素のtransitionendイベントリスナーも発火してしまいます。

解決策

イベントの発生元を特定する

transitionendイベントのコールバック関数にはEventオブジェクトが渡されます。event.targetプロパティを利用して、イベントが発生した要素を特定できます。

javascript
parent.addEventListener('transitionend', (event) => {
  if (event.target === parent) {
    console.log('Parent transition ended');
  }
});

このように条件分岐を追加することで、親要素自身のトランジション終了時にのみ処理を行うことができます。

イベントの伝播を止める(場合によっては非推奨)

子要素のイベントリスナー内でevent.stopPropagation()を使用して、イベントのバブリングを止める方法もあります。ただし、この方法は他の機能に影響を及ぼす可能性があるため、注意が必要です。

javascript
const child = document.querySelector('.child');
child.addEventListener('transitionend', (event) => {
  event.stopPropagation();
});