Part Three: Selections

In this post I’ll be discussing D3 selections. A selection allows you to:

  • grab hold of HTML and SVG elements in a webpage and
  • do something with them (such as remove them, change their style etc.)

They’re one of the cornerstones of D3 and are used anytime HTML or SVG needs to be added, modified or removed.

Making a selection

To make a selection there are two functions:

Each function takes a CSS3 selector string as input:

    d3.select('body');
    d3.select('#my-chart');
    d3.selectAll('#chart .bars');

d3.select() selects the first matching element whilst the d3.selectAll() is ‘greedy’ in that it’ll select everything that matches.

Manipulation

Once we’ve made a selection we can manipulate the elements using functions such as:

Function Description Example
.style() Add/modify CSS style declaration d3.selectAll(‘circle’).style(‘fill’, ‘blue’);
.attr() Add/modify element attributes d3.selectAll(‘circle’).attr(‘r’, ‘100’);
.classed() Add/remove a CSS class d3.select(‘.tooltip’).classed(‘visible’, true);
.text() Set an element’s text content d3.select(‘.tooltip’).text(‘Dave’);
.html() Set an element’s HTML content d3.select(‘.tooltip’).html(‘<h1>Dave<\h1>’);


For example, suppose we have an SVG element with five <circle> elements in it:

we can modify their colour by selecting them and setting their fill style:

    d3.selectAll('circle').style('fill', 'steelblue');


Similarly, suppose we now have some <div> elements

    Item 1
    Item 2
    Item 3

we can change their style using:

    d3.selectAll('div')
      .style('background-color', 'darkblue')
      .style('width', '100px')
      .style('margin', '2px')
      .style('color', 'white')
      .style('text-align', 'center');

This results in:

    Item 1
    Item 2
    Item 3


.each() and .call()

There’s a couple of functions .each() and .call() which are useful not just when manipulating elements but when working with reusable components.

.each() invokes a callback function on each element in the selection:

    function setColor() {
      d3.select(this).style('fill', 'steelblue');
    }
    d3.selectAll('circle').each(setColor);

The above code will iterate through each <circle> element and call setColor. Within the callback function, the ‘this’ keyword is the iterated element. Note that we can also pass an element directly into d3.select().

.call() invokes a callback function on the selection itself:

    function setSelectionFill(selection) {
      selection.style('fill', 'steelblue');
    }
    d3.selectAll('circle').call(setSelectionFill):

The selection itself is passed into the callback function so that the callback can operate on the selection.

Both .each() and .call() allow us to write reusable chunks of D3 code and we’ll look at this in more detail in another post.

All selections are nested…

If you’ve ever peeked under the hood at a selection you may have noticed that a selection is not a flat array, but an array of arrays. The reason for this is that D3 selections are nested.

We can see this most clearly when we chain two selections together as in the following example where we have <g> elements containing <rect> elements:

    <g>
      <rect></rect>
      <rect></rect>
      <rect></rect>
    </g>
    <g>
      <rect></rect>
      <rect></rect>
      <rect></rect>
    </g>

We can chain together two selections:

    d3.selectAll('g').selectAll('rect');

resulting in an array of arrays:

    [
      [ <rect>, <rect>, <rect> ],
      [ <rect>, <rect>, <rect> ]
    ]

Each <rect> element is grouped according to its parent element in the selection. (The parent elements are the elements of the preceding selection in the selection chain.)

Even if we make a flat selection such as

    d3.selectAll('rect')

D3 will still return an array of arrays:

    [ [ <rect>, <rect>, <rect>, <rect>, <rect>, <rect> ] ]

because it implicitly creates a parent selection.

Most of the time you don’t need to worry about nested selections but it’ll make a few D3 concepts clearer such as how .append() works and how to create nested elements. I’ll cover these more advanced topics in a future post.

Stay in touch

I’ll be publishing more D3 must knows over the coming months and covering subjects such as scale functions, data joins, enter/exit and lots more. If you’d like to be the first to read the next articles then please add your name to my mailing list and you’ll be the first to receive articles as they’re published.