← Animation accessor on animation and transition events

v151 · css · attribute reference

TransitionEvent.animation

A read-only, nullable attribute holding the CSSTransition that fired this transitionrun, transitionstart, transitionend, or transitioncancel event — null on events a script constructed without one. This page also covers the matching TransitionEventInit.animation dictionary member.

Syntax

// CSS Transitions Level 2 (Working Draft) — partial interface, normative IDL
partial interface TransitionEvent {
  readonly attribute CSSTransition? animation;
};

partial dictionary TransitionEventInit {
  CSSTransition? animation = null;
};

// Read it on any CSS transition event:
const anim = event.animation; // CSSTransition | null

The base TransitionEvent interface (propertyName, elapsedTime, pseudoElement, constructor) is defined in CSS Transitions Level 1; Level 2 adds animation via the partial interface above. The member is named animation (not transition) even though its type is CSSTransition — the naming choice was deliberate in csswg-drafts #9010, keeping both event interfaces symmetrical. Implementation divergence: Chromium's bindings type it as the Web Animations base interface — [RuntimeEnabled=AnimationEventAnimation] readonly attribute Animation? animation; in transition_event.idl and [RuntimeEnabled=AnimationEventAnimation] Animation? animation = null; in transition_event_init.idl. For UA-dispatched events the runtime value is still a CSSTransition (asserted by WPT); the wider static type only matters for the constructor (see the dictionary member).

Source: CSS Transitions Level 2 — Interface TransitionEvent; #Events-TransitionEvent-animation; Chromium transition_event.idl; Chromium transition_event_init.idl

Inputs

The attribute is read-only — it takes no inputs. The UA sets it when it dispatches the event (in Chromium, the transition event delegate constructs the event with the firing Animation*, in css_animations.cc). The dictionary member is the constructor input: new TransitionEvent("transitionend", { animation }). Per WebIDL interface-type conversion, the value must be an object implementing the interface (CSSTransition per spec, Animation in Chromium) or null; omitted or explicitly undefined, it takes the declared default null.

Source: CSS Transitions Level 2 — TransitionEvent and TransitionEventInit; WebIDL — interface type conversion; Chromium css_animations.cc (event construction)

Outputs

A CSSTransition object, or null. The spec's attribute description: “The CSS Transition corresponding to the transition that fired the event.” For every UA-dispatched transition event — transitionrun, transitionstart, transitionend, transitioncancel — the value is the firing transition, never null (Chromium's dispatch path always passes the animation; the level-2 dispatch algorithm fires transition events only for transitions with an owning element). For script-constructed events the value is whatever the init dictionary carried, defaulting to null. Runtime type guarantee: WPT events-008.html asserts event.animation instanceof CSSTransition on transitionrun/transitionstart/transitionend and that its transitionProperty matches the transitioned property — so the value is usable as a full CSSTransition, not just a base Animation.

Source: CSS Transitions Level 2 — animation attribute definition; CSS Transitions Level 2 — Event dispatch; WPT — events-008.html

Errors

Reading the attribute never throws: it always has a value (CSSTransition or null) for the event's lifetime. The traps are semantic, not exceptional: null does not mean “no transition was involved” — it marks a script-constructed event (check event.isTrusted when the distinction matters); and on engines without the attribute the property is simply absent — feature-detect with "animation" in event (or TransitionEvent.prototype) before branching, treating absence as “unknown”. Constructor-side failure modes are documented under the dictionary member's errors.

Source: CSS Transitions Level 2 — animation attribute definition; DOM Standard — Event.isTrusted; ChromeStatus API feature record (activation_risks polyfill)

Context

Receiver: any TransitionEvent — UA-dispatched to the transition's owning element, or script-constructed.

Exposure: [Exposed=Window] — the TransitionEvent interface (and therefore this attribute) exists in window contexts only, not workers; CSS transitions are a document concept. No secure-context, permission, or user-gesture requirement beyond the interface itself.

Availability: Chrome 151 per the milestone listing (Enabled by default); runtime feature AnimationEventAnimation, status stable at trunk (no flag). No owning element → no transition events are dispatched at all; the animation playback events from Web Animations still fire at the CSSTransition object itself — those are AnimationPlaybackEvents, a different interface without an animation attribute.

Source: CSS Transitions Level 2 — TransitionEvent IDL (Exposed=Window); CSS Transitions Level 2 — Event dispatch (owning element); Chromium runtime_enabled_features.json5; chromestatus.com/feature/6046278267043840

Lifecycle

The reference is fixed when the event is created and never changes for the event's lifetime. Across one transition's life the events all carry the same object, following the level-2 phase-change dispatch table: transitionrun first (when the transition is created, possibly still in its delay phase), then — except for paused or infinite-running transitions — the invariants transitionrun → exactly one of transitionend/transitioncancel, and every transitionend preceded by a transitionstart. The typical sequences: regular playback transitionrun, transitionstart, transitionend; interrupted transitionrun, transitionstart, transitioncancel; interrupted during delay transitionrun, transitioncancel. Seeking or reversing via the Web Animations API can produce further start/end pairs — the table accounts for every phase transition. Cancelling (event.animation.cancel(), or a style change that kills the transition) produces a transitioncancel whose animation is that same object, now with playState === "idle"; after a cancel the transition leaves getAnimations() results, yet the event keeps referencing it (Blink holds the animation as a traced member). Same-object semantics: neither spec annotates the attribute [SameObject] or [NewObject]; Blink stores the pointer and returns the identical object on every access (animation_.Get() in transition_event.cc).

Source: CSS Transitions Level 2 — Event dispatch (sequences and invariants); Chromium transition_event.cc; WPT — events-008.html

Examples

// Reverse the exact transition that fired the event.
element.addEventListener("transitionstart", (event) => {
  if (event.animation === null) return; // script-constructed event
  console.log("transitioning", event.animation.transitionProperty);
  event.animation.finished.then(() => element.classList.add("settled"));
});

// Speed up every transition as it starts running.
element.addEventListener("transitionrun", (event) => {
  event.animation.playbackRate = 2;
});

// transitioncancel carries the same (now idle) CSSTransition.
element.addEventListener("transitioncancel", (event) => {
  console.log(event.animation.transitionProperty, "cancelled;",
    "playState:", event.animation.playState); // "idle"
});

// Constructing one explicitly (defaults to null if omitted):
const transition = element.getAnimations().find(a => "transitionProperty" in a);
const synthetic = new TransitionEvent("transitionend", {
  propertyName: "opacity",
  elapsedTime: 0.3,
  animation: transition,
});
console.log(synthetic.animation === transition); // true

// Feature-detect before relying on the attribute:
const getFiring = (event) => ("animation" in event)
  ? event.animation
  : event.target.getAnimations().find(
      a => a.transitionProperty === event.propertyName);
Source: csswg-drafts issue #9010; CSS Transitions Level 2 — TransitionEvent; WPT — events-008.html

TransitionEventInit.animation

The dictionary member mirrors the attribute for constructor use: CSSTransition? animation = null in TransitionEventInit (Chromium: Animation? animation = null). The exact WebIDL semantics, because constructor behavior is where the traps live:

Source: CSS Transitions Level 2 — TransitionEventInit.animation; WebIDL — interface type conversion; WebIDL — dictionary semantics; Chromium transition_event_init.idl

TransitionEventInit.animation — outputs

The constructed event's animation attribute reads back the very object passed in (Blink copies the initializer's pointer into the event member, mirroring animation_(initializer->animation()) in animation_event.cc), or null when omitted. No wrapping, cloning, or re-resolution happens.

Source: Chromium transition_event.cc (constructor); CSS Transitions Level 2 — TransitionEventInit

TransitionEventInit.animation — errors

Two throwing paths. First, type conversion: a provided value that is not null/undefined and not an Animation object (Chromium) / CSSTransition object (spec contract) throws a TypeError out of the constructor. Second, retrieval: reading the member off the input object is a property access — a throwing getter or a Proxy with a throwing get trap propagates that exception before any conversion runs. With ordinary values the remaining failure modes are silent semantic ones: a misspelled member name is ignored, and a valid value merely echoes back — none of it proves anything about a UA-dispatched event.

Source: WebIDL — interface type conversion; WebIDL — dictionary semantics

TransitionEventInit.animation — security and privacy

An authored event can carry any Animation object — including one attached to a different element or document subtree — so event.animation on an untrusted event is not evidence of what fired anything; the authored-vs-native distinction is event.isTrusted (false for every script-constructed event). Handlers that blindly drive event.animation (pause/cancel/finish) should be aware a same-origin script can feed them arbitrary animations through dispatchEvent — the same caveat that already applies to all scripted events. The member adds no storage, permission, or cross-origin channel.

Source: DOM Standard — Event.isTrusted; CSS Transitions Level 2 — Privacy Considerations

Compatibility

TransitionEvent.animation — from BCD api/TransitionEvent.json (checked 2026-07-26)
Engine / runtimeSupportNotes
Chrome151Matches the milestone listing (Enabled by default); Chrome for Android, WebView (Android + iOS), Opera, Samsung Internet are BCD mirrors
EdgemirrorBCD mirrors Chrome's data (151)
Firefox152BCD version_added: "152" (Firefox for Android mirrors); ChromeStatus records “Shipped/Shipping”
Safari27BCD version_added: "27" (Safari on iOS mirrors); ChromeStatus records “Shipped/Shipping”
Deno / Node.jsUnknownNo BCD entries as of 2026-07-26 (window-exposed DOM event API)

The BCD entry links its canonical references directly (mdn_url to this attribute's MDN page, spec_url to the css-transitions-2 definition with its unusually cased anchor); the sibling AnimationEvent.animation entry in api/AnimationEvent.json carries identical rows, and the animation-side WPT evidence is animationevent-types.html. WPT: css/css-transitions/events-008.html asserts transitionrun/transitionstart/transitionend expose the attribute, that its value instanceof CSSTransition, and that transitionProperty is correct; the interface harness expectations (transitionevent-interface) were updated in the implementing CL 7914303. Not a separately tracked web-feature on webstatus.dev (animationevent query returns zero entries, checked 2026-07-26).

Source: BCD api/TransitionEvent.json; ChromeStatus API feature record; WPT — events-008.html

Security and privacy

The attribute exposes no information that was not already reachable: the same CSSTransition objects are enumerable through element.getAnimations() and document.getAnimations(), so this is an ergonomics change, not a new disclosure surface. CSS Transitions Level 2 reports no new privacy considerations and no new security considerations; the ChromeStatus entry's security and privacy reviews are both “Not applicable”. The one behavioral caveat is authored events: a script-constructed event can carry an arbitrary Animation (see the dictionary member), so treat event.animation on untrusted events as data, not provenance — event.isTrusted separates the two.

Source: CSS Transitions Level 2 — Privacy Considerations; CSS Transitions Level 2 — Security Considerations; ChromeStatus API feature record (review statuses); DOM Standard — Event.isTrusted