2

I'm using Data-Driven Documents (D3.js) library to plot a bar chart.It is working fine.Now I need to change the color of the bar chart when the user clicks it.But it is not working.Can you tell me why?

Note: When I use D3.selectAll(".interval").style("fill", "#F96845"); then it changes the color of all the bar charts.But I need to change only the color of clicked one.

for (var i = 0; i < this.data.length; i++) {
        var item = this.data[i];
        this.svg.append("rect")
            .attr("class", "interval")
            .attr("y", offset + item['start'] * max_height / 86400)
            .attr("x", horz_inset)
            .attr("height", item['length'] * max_height / 86400)
            .attr("width", width - 2 * horz_inset)
            .on("click", () => {
              D3.select(".interval").style("fill", "#F96845");//here it is not working?
        });
    }
Sampath
  • 63,341
  • 64
  • 307
  • 441

1 Answers1

2

Any seasoned D3 coder would simply say: "drop the arrow function and use this with a regular function", which is a correct advise. However, this question should link your previous one, making clear that you have a reason to use an arrow function.

Thus, your real problem is: how to get this referring to the DOM element in an arrow function?

The solution is: use a combination of the second and third arguments to get this DOM element inside an arrow function:

.on("click", (d, i, n) => {
    d3.select(n[i]).style("fill", "#F96845");
});

To read more about getting this DOM element in an arrow function, have a look at this example: d3 v4 retrieve drag DOM target from drag callback when `this` is not available

EDIT: If you want to change the style of the clicked element and revert the style of the previously clicked element, you can simply apply a style to all elements using selectAll before applying the style in the clicked one, or, alternatively, you can apply the style to all elements, including the clicked one, at once:

var circles = d3.select("svg").selectAll("foo")
  .data(d3.range(10))
  .enter()
  .append("circle")
  .style("fill", "firebrick")
  .attr("cy", 50)
  .attr("cx", d => 20 + d * 30)
  .attr("r", 10);

circles.on("click", (d, i, n) => {
  circles.style("fill", (e, j) => n[i] === n[j] ? "lime" : "firebrick")
})
<script src="https://d3js.org/d3.v4.js"></script>
<svg></svg>
Graham
  • 7,431
  • 18
  • 59
  • 84
Gerardo Furtado
  • 100,839
  • 9
  • 121
  • 171
  • @Sampath I saw your previous comment, before you deleted it. Check the edit regarding how to do what you asked. – Gerardo Furtado Apr 22 '17 at 05:59
  • Hehe... :D Thank you so much for your invaluable feedback. For that use case I just do this hack `D3.selectAll(".interval").style("fill", "#10BDD7");//remove highlight` before the `D3.select(n[i]).style("fill", "#F96845");//add highlight`. – Sampath Apr 22 '17 at 06:13