イベントのanimationプロパティで発火元のCSSAnimationとCSSTransitionを取り出す
AnimationEventとTransitionEventにanimationプロパティが追加され、animationstartなどのイベントから発火元のCSSAnimationやCSSTransitionを受け取れるようになりました。Animation accessorsとしてBaseline 2026に加わったこの機能について、getAnimations()との違いや疑似要素の扱い、使うときの注意を整理します。
- Chrome151
- Edge151
- Firefox152
- Safari27
はじめに
CSSのアニメーションやトランジションの節目は、animationstartやtransitionendなどのイベントで受け取れます。ただし、イベントから分かるのはanimationNameやpropertyNameといった名前とelapsedTimeだけでした。
アニメーションを途中で止めたり速度を変えたりするには、Animationオブジェクトが必要です。これまではgetAnimations()で要素のアニメーションを一覧にして、名前の一致するものを探していました。
element.addEventListener('animationstart', (event) => {
const animation = element
.getAnimations()
.find((a) => a.animationName === event.animationName);
});Baseline 2026では、この探し方をしなくても済むようになりました。AnimationEventとTransitionEventにanimationプロパティが追加され、イベントを発火させたAnimationをそのまま受け取れます。この2つのプロパティはBaselineでAnimation accessorsとまとめて呼ばれており、Chrome 151、Firefox 152、Safari 27が対応しています。
書き方
使い方はevent.animationを読むだけです。animationstartなどで受け取るAnimationEventのanimationにはCSSAnimationが、transitionrunなどで受け取るTransitionEventのanimationにはCSSTransitionが入っています。どちらもAnimationのサブクラスなので、playbackRateやpause()、finishedをそのまま使えます。
panel.addEventListener('transitionrun', (event) => {
const transition = event.animation;
if (transition.transitionProperty !== 'opacity') return;
// 閉じるときのフェードだけ3倍速で終わらせる
if (panel.classList.contains('is-closing')) {
transition.playbackRate = 3;
}
});transitionrunで受け取ったCSSTransitionは、同じトランジションのtransitionstartやtransitionendで受け取るものと同じオブジェクトです。1本のアニメーションのanimationstartとanimationiteration、animationendでも同様です。animationiterationで周回を数え、決めた回数でpause()を呼ぶといった使い方ができます。event.animation.finishedをawaitすれば、終わるまで待つことも可能です。
let iterations = 0;
badge.addEventListener('animationiteration', (event) => {
iterations += 1;
// 2周したところで止める
if (iterations === 2) event.animation.pause();
});getAnimations()で探す方法との違い
getAnimations()から名前で探す方法には、うまくいかない場面が2つありました。
1つ目は、同じ名前のアニメーションを2本指定したときです。animation: fade 0.1s, fade 0.2sのように書くとanimationstartは2回発火しますが、event.animationNameは2回ともfadeなので、どちらのアニメーションのイベントかを名前では区別できません。この問題は、CSSWGでこのプロパティを決めたときの動機の1つでもあります。event.animationには別々のCSSAnimationが入り、event.animation.effect.getTiming().durationを読むと100と200が返ります。
2つ目は、animationendで受け取るときです。animation-fill-modeがnoneだと、animationendの中でgetAnimations()を呼んでも、いま終わったアニメーションは見つかりません。getAnimations()が返すのは仕様でrelevantと定義されたアニメーション、つまり効果がこれから始まるか進行中か、まだ残っているものだけです。noneの場合、終わった時点でこの条件から外れます。event.animationなら、終わった後でもplayStateがfinishedのCSSAnimationとして読めます。
同じ@keyframesを長さ違いで2本指定した要素のイベントを記録し、animationNameでは同じに見える2本をevent.animationのdurationで見分けられる様子を確認できます。animationendでは、終わったアニメーションがgetAnimations()から外れていることも分かります。
イベントのログ
再生を押すと、イベントごとにevent.animationから読んだ値が並びます
要素に当てているCSSです。animationNameはどちらもbeatですが、event.animationのdurationで2本を区別できます。
@keyframes beat {
50% {
opacity: 0.2;
}
}
.animation-accessors-target {
animation:
beat 0.5s 3,
beat 0.8s 3;
}ログのanimationNameはすべてbeatですが、event.animationから読んだdurationで2本を区別できます。animationendの行ではgetAnimations().includes(animation)がfalseになり、終わったアニメーションが一覧から外れたと分かります。
疑似要素のアニメーション
::beforeなどの疑似要素に指定したアニメーションのイベントは、元の要素で発火します。event.targetは元の要素を指し、event.pseudoElementが::beforeになります。
element.getAnimations()で疑似要素のアニメーションまで探すには、getAnimations({ subtree: true })のようにオプションが要ります。既定では要素自身のアニメーションしか返さないためです。event.animationなら探す必要はなく、event.animation.effectのpseudoElementが::before、targetが元の要素になっています。
使うときの注意
使うときに気をつけたい点が2つあります。
1つ目は、event.animationがnullのことがある点です。new AnimationEvent('animationend')のように自前で作ったイベントは、初期化辞書でanimationを渡さない限りnullです。dispatchEvent()で流したイベントも同じリスナーで受けるなら、nullの場合に備えておきます。
// 初期化辞書で渡さなければ event.animation は null
const event = new AnimationEvent('animationend', { animationName: 'fade' });2つ目は、event.animationからアニメーションを操作すると、CSS側の指定が効かなくなる点です。たとえばanimation-play-stateは、CSSアニメーションをrunningで再生し、pausedで止めるプロパティです。:hoverのときだけpausedにして、ホバー中は止めるといった使い方をします。ところがevent.animationでpause()やplay()を一度呼ぶと、そのアニメーションは以後animation-play-stateの変更に従わなくなります。CSS Animations Level 2が、APIでの操作をCSSより優先すると定めているためです。effect.updateTiming()で指定した項目も同じで、対応するanimation-*プロパティの変更は反映されません。CSS側で制御し続けたいアニメーションでは、event.animationは読むだけにとどめます。
おわりに
Animation accessorsによって、アニメーションのイベントから発火元のAnimationをそのまま受け取れるようになりました。getAnimations()から名前で探す必要も、同じ名前の2本を区別する工夫も要りません。Chrome 151、Firefox 152、Safari 27が対応し、主要ブラウザで使えます。