Reputation: 73
I'm using D3 to print multiple rect out. Now I'hope the rect could allow user click on it and the active some function.
For example, there are 3 rectangles, "Tom"
, "Mary"
and "Ben"
. When user click on different rect, it will pass the current value. Like when I click on "Tom"
it will pass "Tom"
to call the function.
However, I found that after finish print out the rect, no matter I click on which rect, they both return the least value of the dataset.
In my example, even I click on "Tom"
or "Mary"
, both return "Ben"
.
for (var i = 0; i < ward_set.length; i++) {
var ward_id = ward_set[i];
legend.append("rect")
.attr("x", legend_x + 180 + 100 * n)
.attr("y", legend_y)
.attr("width", 18)
.attr("height", 18)
.attr("fill", colors[count])
.attr("class", "legend" + ward_set[i])
.on("click", function() {
console.log(ward_id);
});
}
Upvotes: 1
Views: 38
Reputation: 102188
Your question perfectly illustrates a very important principle, a rule of thumb if you like, when writing a D3 code: never use a loop to append elements. Use the enter/update/exit pattern instead.
What happens when you use a loop (be it for
, while
, forEach
etc...) is that not only there is no data binding, but also you experience this strange outcome you described (getting always the last value), which is explained here: JavaScript closure inside loops – simple practical example
Therefore, the solution using a D3 idiomatic enter selection would be:
const data = ["Tom", "Mary", "Ben"];
const svg = d3.select("svg");
const rects = svg.selectAll(null)
.data(data)
.enter()
.append("rect")
//etc...
Then, in the click
listener, you get the first argument, which is the datum:
.on("click", function(d) {
console.log(d)
})
Here is a demo:
const data = ["Tom", "Mary", "Ben"];
const svg = d3.select("svg");
const rects = svg.selectAll(null)
.data(data)
.enter()
.append("rect")
.attr("width", 50)
.attr("height", 150)
.attr("x", (_, i) => i * 60)
.on("click", function(d) {
console.log(d)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg></svg>
Upvotes: 1