Isaac Dontje Lindell
Isaac Dontje Lindell

Reputation: 3446

Creating nested HTML structures with D3.js

I have an object that looks something like this:

data = {
  questions: [ 
               value: "Question 1", answers: [ {value:"answer1"}, {value:"answer2"} ],
               value: "Question 2", answers: [ {value:"answer1"}, {value:"answer2"} ]
             ]
}

(This is simplified - answers have a count that will be displayed using bar graphs. I think I know how to do that once I get there, however.)

The issue I'm having is dynamically (because the data changes frequently) creating the HTML structure, which should look something like this:

<div class="question">
  Question 1  
  <div class="answer">Answer 1</div>
  <div class="answer">Answer 2</div>
</div>
<div class="question">
  Question 2  
  <div class="answer">Answer 1</div>
  <div class="answer">Answer 2</div>
</div>

I can get D3 to create the question divs (the ones w/ class="question"), and put in the text for that (i.e. "Question 1", etc.) but I can't figure out how to get D3 to create a child div for the answers (i.e. the ones w/ class="answer").

Here's what I have right now:

var questions_chart = d3.select('#survey-questions')
    .selectAll("div")
    .data(data.questions);

questions_chart.transition()
    .text(function(d) { return d.value; });

questions_chart.enter().append("div")
    .text(function(d) { return d.value; })
    .attr("class", "question rounded")

questions_chart.exit().remove();

Basically, how can I nest D3 appends in such a way that I can nest divs for each answer within the div for that question?

Upvotes: 1

Views: 621

Answers (2)

Loren
Loren

Reputation: 190

This extends the answer Peter gave to add the "class" information you requested in your original question, as well as answer your follow-up question in the comment:

var divQ = d3.select("body").selectAll("div")
    .data(data.questions);

divQ
    .enter()
    .append("div") // this creates the question divs
    .attr("class","question")
    .text(function(d) { return d.value; });

divQ
    .exit()
    .remove();


var divA = divQ.selectAll("div")
    .data(function(d) { return d.answers; });

divA
    .exit()
    .remove();

divA
    .enter()
    .append("div") // this creates the nested answer divs
    .attr("class","answer")
    .text(function(d) { return d.value; });

Upvotes: 1

Peter
Peter

Reputation: 12711

As Lars indicated, you want to use nested selections for this:

var data = {
  questions: [ 
    {value: "Question 1", answers: [ {value:"answer1"}, {value:"answer2"} ]},
    {value: "Question 2", answers: [ {value:"answer1"}, {value:"answer2"} ]}
  ]
};

d3.select("body").selectAll("div")
  .data(data.questions)
  .enter().append("div") // this creates the question divs
    .text(function(d) { return d.value; })
  .selectAll("div")
  .data(function(d) { return d.answers; })
  .enter().append("div") // this creates the nested answer divs
    .text(function(d) { return d.value; });

Note: I had to fix your data a little, making data.questions an array of objects.

Upvotes: 2

Related Questions