Jonathan Petitcolas
Jonathan Petitcolas

Reputation: 4574

Retrieving selection's data

I got some trouble understanding the way .data works in D3.js, even by reading and re-reading the documentation. Let's imagine the following code:

const chart = selection => {
  const svg = selection.selectAll('svg').data(d => d);

  console.log(svg.data());
}

d3.select('#root').data([
  [{
      name: 'Foo',
      values: [1, 2, 3]
    },
    {
      name: 'Bar',
      values: [4, 5, 6]
    },
  ]
]).call(chart);
<script src="https://d3js.org/d3.v5.min.js"></script>
<div id="root">
</div>

I would expect to get the array with Foo and Bar objects to be logged in this code. Instead, I get an empty array.

According to documentation:

The data is specified for each group in the selection. If the selection has multiple groups (such as d3.selectAll followed by selection.selectAll), then data should typically be specified as a function. This function will be evaluated for each group in order, being passed the group’s parent datum

I probably misunderstand something here. Can an expert enlighten me? :)

Related JSFiddle here: https://jsfiddle.net/tmq4h8w2/

Upvotes: 4

Views: 60

Answers (2)

Gerardo Furtado
Gerardo Furtado

Reputation: 102174

What you have here...

const svg = selection.selectAll('svg').data(d => d);

... is what we call an "update" selection in a D3 code. What it does is:

  1. Select all <svg> elements;
  2. Binds the data to them;

Since you don't have any <svg> element here, your update selection is empty. Check the console in the demo below, which uses your code exactly as it is:

const chart = selection => {
  const svg = selection.selectAll('svg').data(d => d);

  console.log("The size of the update selection is: " + svg.size());
}

d3.select('#root').data([
  [{
      name: 'Foo',
      values: [1, 2, 3]
    },
    {
      name: 'Bar',
      values: [4, 5, 6]
    },
  ]
]).call(chart);
<script src="https://d3js.org/d3.v5.min.js"></script>
<div id="root">
</div>

That's why, for appending elements, we don't need to select anything:

selection.selectAll(null)//see the 'null' here
    .data(data)
    .enter()
    .append("svg");

On the other hand, if you already had <svg> elements in your HTML, your "update" selection wouldn't be empty: it would bind the data to the selected elements and you could clearly log the bound data. The demo below just adds two SVG elements in the HTML, your code is the same:

const chart = selection => {
  const svg = selection.selectAll('svg').data(d => d);
  console.log(svg.data());
}

d3.select('#root').data([
  [{
      name: 'Foo',
      values: [1, 2, 3]
    },
    {
      name: 'Bar',
      values: [4, 5, 6]
    },
  ]
]).call(chart);
<script src="https://d3js.org/d3.v5.min.js"></script>
<div id="root">
  <svg></svg>
  <svg></svg>
</div>

Upvotes: 4

Ryan Morton
Ryan Morton

Reputation: 2695

The issue is that your selection doesn't bind that data to svg on its own. You need to enter the data join (and append the object that uses the data) so that it properly comes into being:

const chart = selection => {
    const svg = selection.selectAll('svg').data(d => d)
     .enter()
        .append('svg');

    console.log(svg.data());
}

d3.select('#root').data([[
    { name: 'Foo', values: [1, 2, 3] },
    { name: 'Bar', values: [4, 5, 6] },
]]).call(chart);

Fiddle here: https://jsfiddle.net/dfzc0ub7/

After you call it into being, you should be able to select and update without the need for enter().

Upvotes: 2

Related Questions