The normal way of handling onclick in d3 is
selection.append(element)
.on("click", someFunction)
If I do it this way on 1000 svg elements, does it mean I just attached 1000 different listeners. If this is the case, is there event delegation for d3 specifically?
Using the once option We can pass an object as an argument to the addEventListener method and specify that the event is only handled once. This is achieved by passing the property once to the object. If we set once to true, the event will only be fired once.
The <g> SVG element is a container used to group other SVG elements. Transformations applied to the <g> element are performed on its child elements, and its attributes are inherited by its children. We can create a group element with D3. js by appending a g element using any selection.
The method addEventListener() works by adding a function, or an object that implements EventListener , to the list of event listeners for the specified event type on the EventTarget on which it's called.
For every event type that the browser supports, SVG supports that as an event attribute, following the same requirements as for HTML event attributes. The global event attributes are available on all SVG elements. Other event attributes are available on a case by case basis for each elements.
@AlexW answer is (partially) correct: there is no event delegation in D3, only event binding.
However, I said partially because it'd be better saying that "there is no native method for event delegation in D3", since in fact it's quite ease to implement it: the ugly alternative to do event delegation with D3 consists in using d3.event.target.
For instance, in this very simple demo, we bind this data...
var data = ["foo", "bar", "baz"];
... to the circles inside an <g>
element. Then, we bind an event listener to the group, and get the datum of each circle on click:
g.on("click", function() {
console.log(d3.select(d3.event.target).datum())
})
Here is it:
var svg = d3.select("svg");
var g = svg.append("g");
var data = ["foo", "bar", "baz"];
var circles = g.selectAll(null)
.data(data)
.enter()
.append("circle")
.attr("cy", 40)
.attr("cx", function(_, i) {
return 50 + 100 * i
})
.attr("r", 20)
.attr("fill", "teal");
g.on("click", function() {
console.log(d3.select(d3.event.target).datum())
})
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg></svg>
The nice thing about this approach is that, just as a jQuery event delegation, it works with elements created after the listener was defined. In the following demo, the red circle:
var svg = d3.select("svg");
var g = svg.append("g");
var data = ["foo", "bar", "baz"];
var circles = g.selectAll(null)
.data(data)
.enter()
.append("circle")
.attr("cy", 40)
.attr("cx", function(_, i) {
return 50 + 75 * i
})
.attr("r", 20)
.attr("fill", "teal");
g.on("click", function() {
console.log(d3.select(d3.event.target).datum())
});
g.append("circle")
.attr("cy", 40)
.attr("cx", 275)
.attr("r", 20)
.attr("fill", "firebrick")
.datum("foobar")
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg></svg>
So, despite D3 not having a native, explicit method for event delegation, the solution is quite simple and straightforward.
Yes this would add 1000 event listeners:
Adds or removes a listener to each selected element for the specified event typenames.
https://github.com/d3/d3-selection/blob/master/README.md#selection_on
If you have 1000+ elements, you may not want to use SVG, as the DOM gets easily bogged down with that many elements. It may be more efficient to use a canvas, etc.
D3 doesn't do event delegation it only does event binding. So you may want to implement the delegation using jQuery or vanilla JS, if you're still considering using SVG.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With