0

I have been struggling with an issue which perhaps is really simple. The wider context is a force directed graph in which I want to generate a series of filtering buttons dynamically whose categories come from a JSON file from an external API. I want to assign each category as an argument in the call of the button, however I have not been able to do this via JS. The code that relates to that part is:

//Create TA nodes buttons dynamically
for(var ta_name in ta_nodes) {
  d3.select("#controls").append("button").on("click", filterNetwork(ta_nodes[ta_name].ta_name)).html(ta_nodes[ta_name].ta_name);
}

function filterNetwork(thematic_area_filtered) {
    simulation.stop();
    [...]
}

Any ideas on how I could generate the button while passing ta_nodes[ta_name].ta_name to the function?

Thanks!

Gerardo Furtado
  • 100,839
  • 9
  • 121
  • 171
drozas
  • 11
  • 2

2 Answers2

0

I did a workaround to solve this as follows:

    // Adding TA buttons from nodes via a text 
var ta_discursive_nodes_buttons = d3.select("#controls")
    .append("div")
    .attr("class", "ta_discursive_nodes_buttons")
    .selectAll("div")
    .data(ta_discursive_nodes)
    .enter()
    .append("div")
    .text(function(d){
        return d.ta_name;
      });
// Adding event handler
 ta_discursive_nodes_buttons.on("click", function(d) {
     //alert(d.ta_name);
    filterRelationshipsByThematicArea(d.ta_name);
 });    

Probably not the most elegant solution, but it works! I hope it can be useful for other people :-)

drozas
  • 11
  • 2
0

Your code (in your question, not in your answer) has some problems:

  • Do not pass the value of the function as the listener. When you do this:

    .on("click", foo())
    

    You are passing the returned value of foo, not calling foo on click, which would be:

    .on("click", foo)
    

    Or, in the case that foo has arguments:

    .on("click", function(){
        foo(bar)
    }) 
    
  • You cannot chain your function and html() the way you did.

  • This is the most important issue: do not use loops to append elements in a D3 code. Things will break, as you just saw. Besides that, creating functions inside loops is complicated.

That being said, use an "enter" selection, setting the listeners. Here is a basic example:

var data = ["foo", "bar", "baz"];

var body = d3.select("body");

var buttons = body.selectAll(null)
  .data(data)
  .enter()
  .append("button")
  .text(function(d, i) {
    return "Button " + (i + 1)
  })
  .on("click", function(d) {
    displayValue(d)
  });

function displayValue(value) {
  alert("This button has the datum '" + value + "'")
}
button {
  margin: 2px;
  padding: 6px;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
Gerardo Furtado
  • 100,839
  • 9
  • 121
  • 171