d3.each()
does begin at index 0
. What you're seeing in your code is the expected behaviour, given what you have in your code.
The issue here is simple: there is a <body>
element in the page, of course. Your data array has 5 elements, and one of them is being bound to the <body>
.
Let's show this. Have a look at the size of the "enter" selection:
data = [0, 1, 2, 3, 4]
var foo = d3.select("body")
.data(data)
.enter();
console.log("Size of enter selection: " + foo.size())
<script src="https://d3js.org/d3.v4.js"></script>
We can also show that the first element in the array was bound to the <body>
:
data = [0, 1, 2, 3, 4]
var foo = d3.select("body")
.data(data)
.enter();
console.log("Data of body: " + d3.select("body").data())
<script src="https://d3js.org/d3.v4.js"></script>
Yet another way for showing this is using the third argument (technically speaking, parameter), which is the current group:
data = [0, 1, 2, 3, 4]
d3.select("body")
.data(data)
.enter()
.each((d, i, p) =>
// ^---- this is the third argument
console.log(p)
)
Here I cannot provide a working Stack snippet, because it crashes if we try to log a D3 selection. But the result will be this:
[undefined × 1, EnterNode, EnterNode, EnterNode, EnterNode]
That undefined
is the "update" selection (the body), and the 4 EnterNode
s are the "enter" selection. And that brings us to the explanation of why each()
behaves that way in your code.
If you have a look at the source code...
function(callback) {
for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {
if (node = group[i]) callback.call(node, node.__data__, i, group);
}
}
return this;
}
You're gonna see that it works comparing the nodes to the group, and your group contains both the "update" selection and the "enter" selection. The update selection corresponds to index 0
and the enter selection corresponds to indices 1
, 2
, 3
, and 4
.
Solution:
This is what you want, pay attention to selectAll
and null
:
data = [0, 1, 2, 3, 4]
d3.select("body")
.selectAll(null)
.data(data)
.enter()
.each((d, i) =>
console.log(i, d)
)
<script src="https://d3js.org/d3.v4.js"></script>
Thus, as you can see, selecting null
assure us that our "enter" selection always have all the elements in the data array.
Bonus: select
and selectAll
behave differently. Most people think that the only difference is that the former selects only 1 element and the latter selects all the elements. But there are more subtle differences. Have a look at this table:
Method |
select() |
selectAll() |
Selection |
selects the first element that matches the selector string |
selects all elements that match the selector string |
Grouping |
Does not affect grouping |
Affects grouping |
Data propagation |
Propagates data |
Doesn't propagate data |