Event Bubbling

Demo

Key pressed:

HTML #

 1<div id="demo">
 2  Key pressed: <span data-text="$key"></span>
 3  <div id="event-bubbling-container" data-on:click="$key = evt.target.closest('button[data-id]')?.dataset.id ?? $key">
 4    <button data-id="KEY ELSE" class="gray">KEY<br/>ELSE</button>
 5    <button data-id="CM">CM</button>
 6    <button data-id="OM">OM</button>
 7    <button data-id="FETCH">FETCH</button>
 8    <button data-id="SET">SET</button>
 9    <button data-id="EXEC">EXEC</button>
10    <button data-id="TEST ALARM" class="gray">TEST<br/>ALARM</button>
11    <button data-id="3">3</button>
12    <button data-id="2">2</button>
13    <button data-id="1">1</button>
14    <button data-id="ENTER">ENTER</button>
15    <button data-id="CLEAR">CLEAR</button>
16  </div>
17</div>
18
19<style>
20  #event-bubbling-container {
21    pointer-events: none;
22
23    button {
24      user-select: none;
25
26      * {
27        pointer-events: none;
28        user-select: none;
29      }
30    }
31  }
32</style>

Explanation #

This example shows how event bubbling can be leveraged using Datastar. A data-on:click attribute on the parent container of the buttons. The listener is on the container, but evt.target can be a nested element inside a button, so we resolve the nearest matching button first with evt.target.closest('button[data-id]')?.dataset.id. This allows us to handle all button clicks with a single event listener.

Note the pointer-events: none; style on the button container to prevent container clicks, and pointer-events: none; on button contents (plus user-select: none;) so nested elements like <br> don’t become the click target.