'How can I select :last-child in d3.js?

I need to manipulate the text elements of the first and last tick of an axis to bring them more towards the center.

I am trying to select them, one at the time, with something like svg.select('.tick:last-child text') but it doesn't work. I'd then apply .transform('translate(4,0)')...

Am I doing something wrong? How can I achieve this?



Solution 1:[1]

One thing you could do is to create custom sub-selections by adding methods to d3.selection.prototype. You could create a selection.first() method that selects the first item in a selection, and a selection.last() method that selects the last item. For instance:

d3.selection.prototype.first = function() {
  return d3.select(this[0][0]);
};
d3.selection.prototype.last = function() {
  var last = this.size() - 1;
  return d3.select(this[0][last]);
};

This would let you do the following:

var tickLabels = svg.selectAll('.tick text');

tickLabels.first()
  .attr('transform','translate(4,0)');
tickLabels.last()
  .attr('transform','translate(-4,0)');

Of course, you need to make sure that you only have one axis if you do it that way. Otherwise, specify the axis in your initial selection:

var tickLabels = svg.selectAll('.axis.x .tick text');

HERE is an example.

Solution 2:[2]

Here's the cleanest method I've found:

g.selectAll(".tick:first-of-type text").remove();
g.selectAll(".tick:last-of-type text").remove();

Solution 3:[3]

As google brought me here, I also want to add a cleaner method to what Adam Grey wrote. Sometimes you just want to do it without taking a reference of selectAll .

svg.selectAll('.gridlines').filter(function(d, i,list) {
    return i === list.length - 1;
}).attr('display', 'none');

the 3rd parameter of the filter function gives you the selected List of elements.

Solution 4:[4]

They don't exist in d3 specifically, but you can use the .firstChild and .lastChild methods on a node.

You can first select all of the parents of the node, and then operate within the scope of a .each() method, like so:

d3.selectAll('.myParentElements').each(function(d,i){
  var firstChild = this.firstChild,
      lastChild = this.lastChild;

  //Do stuff with first and last child
});

Within the scope of .each(), this refers to the individual node, which is not wrapped by a d3 selection, so all of the standard methods on a node are available.

Solution 5:[5]

Using .filter() with a function also works selection.filter(filter) :

var gridlines;

gridlines = svg.selectAll('.gridlines');

gridlines.filter(function(d, i) {
  return i === gridlines.size() - 1;
}).attr('display', 'none');

Solution 6:[6]

It's for D3.js v4

d3.selection.prototype.first = function() {
    return d3.select(
        this.nodes()[0]
    );
};
d3.selection.prototype.last = function() {
    return d3.select(
        this.nodes()[this.size() - 1]
    );
};

Example:

var lines = svg.selectAll('line');
   lines.first()
      .attr('transform','translate(4,0)');
   lines.last()
      .attr('transform','translate(-4,0)');

Solution 7:[7]

Here is another, even though I used Fered's solution for a problem I met.

d3.select(d3.selectAll('*').nodes().reverse()[0])

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 jshanley
Solution 2 emilyinamillion
Solution 3 Fered
Solution 4 ckersch
Solution 5 Adam Grey
Solution 6 vampire
Solution 7 Anton vBR