How to create a reusable component in D3 with an API that manages each instance of the component?

Mike has an excellent article on writing reusable components in D3. This article describes how to configure components and how to apply a component to a selection.

The template allows you to reuse a single component object with several choices, attaching it to the data; eg.

var chart = myChart(); d3.select("div.chart") .data(data) .call(chart); 

My component implementation is as follows:

 function myChart() { function my(selection) { selection.each(function(d, i) { // generate chart here // `d` is the data, `i` is the index, `this` is the element var state = false; var circle = d3.select(this).append("circle") .attr("r", "10") .style("fill", "#000") .on("click", toggleState); function toggleState() { // this function updates the current instance trapped by this closure (state = !state) ? circle.style("fill", "#fff") : circle.style("fill", "#000"); } }); } my.toggleState(i) { // How do I access the `i`th instance of the component here? } return my; } 

What I would like to achieve is to allow the caller to manipulate an instance of this component based on its index. For example, if the div.chart selector above returns a selection that has two elements, I would like to call chart.toggleState(1) and update the 2nd div in the list.

Just because I am not confusing anyone why I am trying to do this, the caller needs to synchronize the two types of components together. Imagine that I have a component that is represented by a circle and another component that is represented by a rectangle. These two components must be independent and not tied to each other. I need to create 4 circles and 4 rectangles, and when I click the rectangle I would like to update the corresponding circle based on the index. I already understood how to raise events (d3.dispatch) from the component and provide the current index as a parameter in the event, but I did not understand how to call a specific instance of the component based on its index.

+7
source share
2 answers

I think the easiest way is to give each component the same class. Then, when you call chart.toggleState(1) , you can do d3.selectAll('.component').each(function(d, i) { if (i == index) doWhatever; });

0
source

The data for a particular selection is in selection [0]. You can select the ith element and set the attribute this way:

 var i = 3 d3.select(mySelection[0][i]).attr("fill","#fff") 

If you know i, you can write toggleState like this:

 function toggleState(i) { var toggle_me = d3.select(circles[0][i]) var state = !(toggle_me.attr("fill") == "#fff") toggle_me.attr("fill", (state ? "#fff": "#000")) } 

If you did not know i, you can write toggleState this way:

 function toggleState() { var toggle_me = d3.select(this) var i = circles[0].findIndex(function(d) { return d == toggle_me[0][0] }) if (i !== -1) { var state = !(toggle_me.attr("fill") == "#fff") toggle_me.attr("fill", (state ? "#fff": "#000")) // Do additional functions with i here //var triange = d3.select(triangles[0][i]) //... } } 

Working example: https://jsfiddle.net/fd7fyeoq/

For the same answer in a different context see: d3js: How to select the nth element of a group?

0
source

All Articles