Get one element from d3js selection, by index

In d3 v4 and above, you can use Selection.nodes(). Assuming i is the index number you want:

d3.select(someSelection.nodes()[i])

It's a natural one-liner, and it's arguably more readable: you're obviously just getting the node at i in the order, as a D3 selection.


It looks like it'd be more efficient than the alternatives, which involve looping through the entire selection with .each(). So, you might think this is O(1), while the other options are O(n).

Unfortunately, Selection.nodes() itself includes an each loop, so it's also O(n) (not that it's likely to matter in real life unless you call this thousands of times on selections with thousands of nodes):

var nodes = new Array(this.size()), i = -1;
this.each(function() { nodes[++i] = this; });
return nodes;

However, this way you can separate the looping from the getting, which could be useful if efficiency is a major concern.

For example, if you want to loop through each() in selection A and get the item in the same position from selection B, and you want to avoid loops-within-loops because those selections can be huge and you call this many times, you could structure it like this, which would be O(2n) instead of O(n^2):

var selectionBArray = selectionB.nodes()

selectionA.each(function(d, i) {
  var iFromSelectionA = this
  var iFromSelectionB = d3.select(selectionBArray[i])
})

...or if you're using arrow functions to preserve this context:

var selectionBArray = selectionB.nodes()

selectionA.each((d, i, nodes) => {
  var iFromSelectionA = d3.select(nodes[i])
  var iFromSelectionB = d3.select(selectionBArray[i])
})

You could even (ab)use Selection._groups, but I wouldn't recommend using a private property like that since it'll break if a D3 update renamed the _groups property, like this update did.


Use the preset function i variable, which references the index of the array object.

var data = [[0,0,2],[0,23,5],[2,12,5]];
circleSet = svg.selectAll()
     .data(data)
     .enter()
     .append('circle')
     .attr('fill',function(d,i){i === 1 ? return 'red' : return 'black' };

Find more on array structure references in d3.js at this tutorial

You can also encode each element you append by utilizing the count of the i index when assigning a class.

var data = [[0,0,2],[0,23,5],[2,12,5]];
    circleSet = svg.selectAll()
         .data(data)
         .enter()
         .append('circle')
         .attr("class",function(d,i){ return "yourclass item" + i })

var theSecondElement = d3.select(".item1")

Last, you could use the .each method and a conditional to target a specific element

circleSet = svg.selectAll()
         .data(data)
         .enter()
         .append('circle')
         .each(function (d, i) {
            if (i === 1) {
              var that = this;
              (function textAdd() {
                 d3.select(that).append('h1').text(i); 
              )();   
            }
          });

The most natural way to manipulate just one element is using the filter function:

var data = [[0,0,2],[0,23,5],[2,12,5]];
var circleSet = svg.selectAll()
         .data(data)
         .enter()
         .append('circle');
var filteredCircleSet = circleSet
         .filter(function (d, i) { return i === 1;})
         // put all your operations on the second element, e.g.
         .append('h1').text('foo');    

Note that depending on what you do with the other elements you might use one of the two variants of this approach:

  • variant a): use the filter in the data function (to reduce the data and the appended elements)

  • variant b): use the filter to exclude instead of to include in order to remove the other elements at the end

See also Filter data in d3 to draw either circle or square

One other way to do it is to use the selection.each method: https://github.com/mbostock/d3/wiki/Selections#wiki-each By using an if statement with the corresponding index you can create a block for one element. E.g.

var data = [[0,0,2],[0,23,5],[2,12,5]];
var circleSet = svg.selectAll()
         .data(data)
         .enter()
         .append('circle')
         .each(function (d, i) {
            if (i === 1) {
              // put all your operations on the second element, e.g.
              d3.select(this).append('h1').text(i);    
            }
          });

Tags:

D3.Js