How Does SMIL Handle beginEvent, endEvent, and repeatEvent in DOM?
Synchronized Multimedia Integration Language (SMIL) elements embedded
within SVG interact directly with the Document Object Model (DOM) by
dispatching specialized lifecycle events—specifically
beginEvent, endEvent, and
repeatEvent (often standardized under the
TimeEvent interface). This article explores the
architecture of SMIL event timing, how browsers expose these animation
milestones to JavaScript via standard event listeners, the payload
properties carried by each event, and how developers can utilize these
hooks to coordinate complex SVG graphics with broader web
applications.
The SMIL Lifecycle and the TimeEvent Interface
SMIL animation elements—such as <animate>,
<animateTransform>, <set>, and
<animateMotion>—rely on an internal timeline to
govern playback. As these elements transition through various states,
the SMIL timing engine emits notifications into the DOM tree.
In standard DOM implementations, SMIL lifecycle occurrences are
represented by the TimeEvent interface. This interface
inherits from the base DOM Event interface and exposes
specialized metadata about the timing state:
view: The abstract view from which the event was generated (typically the browser'swindowobject).detail: A numeric value providing additional context, most notably the repetition iteration number for repeat events.
Core SMIL Animation Events
The SMIL specification defines three primary event types corresponding to the execution phase of an animation:
1. beginEvent
The beginEvent fires at the exact moment the animation
interval begins. If an animation has a delayed start (for example,
begin="2s" or begin="click + 1s"), the event
does not dispatch when the document loads, but rather when the delay
expires and the visual attribute transformation actually starts.
const animation = document.querySelector('#pulseAnimation');
animation.addEventListener('beginEvent', (event) => {
console.log('SMIL animation started at document time:', event.timeStamp);
});2. endEvent
The endEvent is dispatched when the active duration of
the animation completes. This occurs when the duration specified by the
dur attribute runs out, when an explicit end
condition is met, or when an animation is halted programmatically. If an
animation is set to repeat indefinitely
(repeatCount="indefinite"), endEvent will not
fire unless the animation is externally terminated.
animation.addEventListener('endEvent', (event) => {
console.log('SMIL animation has completed its active duration.');
});3. repeatEvent
When an animation is configured to run multiple times using the
repeatCount or repeatDur attributes,
repeatEvent fires at the completion of each individual
cycle (excluding the final end of the animation, which triggers
endEvent). The detail property of the event
object contains an integer representing the 0-indexed count of the
iteration just completed.
animation.addEventListener('repeatEvent', (event) => {
console.log(`Animation completed cycle iteration: ${event.detail}`);
});Capturing Events with DOM Scripting
Attaching listeners to SMIL nodes follows standard DOM Level 2 and
Level 3 event paradigms. Developers can register callbacks using
addEventListener directly on the SVG animation tags.
Consider the following SVG markup:
<svg width="200" height="200" viewBox="0 0 100 100">
<circle id="targetCircle" cx="50" cy="50" r="20" fill="coral">
<animate
id="growAnimation"
attributeName="r"
from="20"
to="40"
dur="1.5s"
repeatCount="3"
begin="0s" />
</circle>
</svg>To capture and orchestrate actions across the full animation
lifecycle, JavaScript can attach handlers to the
<animate> element directly:
const animElement = document.getElementById('growAnimation');
// Triggered immediately upon playback start
animElement.addEventListener('beginEvent', () => {
document.body.classList.add('animation-active');
});
// Triggered after each 1.5s cycle (iterations 0 and 1)
animElement.addEventListener('repeatEvent', (e) => {
console.log(`Current repetition: ${e.detail + 1} of 3`);
});
// Triggered when the 3rd repetition finishes
animElement.addEventListener('endEvent', () => {
document.body.classList.remove('animation-active');
console.log('All iterations finished.');
});Event-Driven Synchronization in SMIL
Beyond JavaScript observation, SMIL's internal syntax can capture these same DOM events declaratively to chain animations without writing external script.
An animation can define its begin or end
attributes using the syntax elementID.eventName:
<rect width="50" height="50" fill="teal">
<!-- Starts when the circle animation emits its beginEvent -->
<animate
id="slideRect"
attributeName="x"
from="0"
to="100"
dur="2s"
begin="growAnimation.beginEvent" />
<!-- Fades out whenever growAnimation finishes a repeat cycle -->
<animate
attributeName="opacity"
from="1"
to="0.2"
dur="0.5s"
begin="growAnimation.repeatEvent" />
</rect>Practical Considerations and Compatibility
When integrating SMIL events into modern DOM applications, keep the following technical behaviors in mind:
- Event Bubbling: SMIL timing events generally do not
bubble up to the parent SVG container or the
documentroot. Event listeners must typically be attached directly to the specific animation element target. - Case Sensitivity: Unlike standard HTML events that
use all-lowercase names (such as
clickorload), SMIL timing events in standard DOM implementations are case-sensitive (beginEvent,endEvent,repeatEvent). - Programmatic Control: When triggering SMIL
animations via DOM methods like
element.beginElement()orelement.endElement(), the correspondingbeginEventandendEventhandlers will fire synchronously or at the designated seek time, maintaining timing state integrity across both declarative and imperative execution paths.