Bubble fire event in svg.js

61 Views Asked by At

I have this structure:

 ___________________
| g1 __________     |
|   | g2       |    | 
|   |          |    |
|   |   ____   |    |
|   |  | r1 |  |    |
|   |  |____|  |
|   |__________|    |
|___________________|

Only the object on which fire is called receives the event.

r1.fire('myevent')
r1.on('myevent',doSomething) //works
g2.on('myevent',doSomething) //not called
g1.on('myevent',doSomething) //not called

How can I trigger a fire on a child element and have all parents receive the event?

Stack Snippet:

var canvas = SVG().addTo("body");
var g1 = canvas.group();
var g2 = g1.group(); //inside group1
var r1 = g2.rect(100, 100).fill("blue"); //inside group2

g1.on("customEvent", function (e) {
    console.log("Custom event captured by group1");
});

g2.on("customEvent", function (e) {
    console.log("event fired");
    //this.parent().fire("customEvent");// this shouldn't be needed
});

// Trigger the custom event on rectangle2, manually propagate it up to both group1 and group2
r1.on("click", function () {
    this.parent().fire("customEvent");
    //this.parent().fire("customEvent");// this shouldn't be needed
});
r1.on("customEvent", function () {
    console.log("event fired");
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/svg.js/3.2.0/svg.js"></script>

2

There are 2 best solutions below

2
On BEST ANSWER

svg.js uses CustomEvents under the hood and you can pass options as third parameter to the fire method:

element.fire('eventname', someData, { bubbles: true })

This is public api but I realized its extremely hidden in the docs under: https://svgjs.dev/docs/3.1/events/#custom-events

const canvas = SVG().addTo("body");
const g1 = canvas.group();
const g2 = g1.group();
const r1 = g2.rect(100, 100).fill("blue");

g1.on("custom-event", (e) => {
    console.log(`g1 saw ${e.type}: ${e.detail.answer}`);
});
g2.on("custom-event", (e) => {
    console.log(`g2 saw ${e.type}: ${e.detail.answer}`);
});
r1.on("custom-event", (e) => {
    console.log(`r1 saw ${e.type}: ${e.detail.answer}`);
});
r1.on("click", (e) => {
    r1.fire("custom-event", {answer: 42}, {bubbles: true});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/svg.js/3.2.0/svg.js"></script>

1
On

Kicking around the svg.js documentation and experimenting a fair bit, I'm not finding a way to make its custom events bubble.

If you don't get a pure svg.js-based solution (Fuzzma has provided one now) you could use DOM events on the underlying SVG elements it creates (accessed via the .node property on the svg.js objects):

const canvas = SVG().addTo("body");
const g1 = canvas.group();
const g2 = g1.group();
const r1 = g2.rect(100, 100).fill("blue");

g1.node.addEventListener("custom-event", (e) => {
    console.log(`g1 saw ${e.type}: ${e.detail.answer}`);
});
g2.node.addEventListener("custom-event", (e) => {
    console.log(`g2 saw ${e.type}: ${e.detail.answer}`);
});
r1.node.addEventListener("custom-event", (e) => {
    console.log(`r1 saw ${e.type}: ${e.detail.answer}`);
});
r1.node.addEventListener("click", (e) => {
    // Respond to click by raising a custom event that bubbles
    const customEvent = new CustomEvent("custom-event", {
        bubbles: true,
        detail: {answer: 42} // (If you want custom detail)
    });
    r1.node.dispatchEvent(customEvent);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/svg.js/3.2.0/svg.js"></script>