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:

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: