How to select HTML and SVG elements using D3 selections. This article shows how to select, insert, remove and modify elements, how to add event handlers, how to apply a function to selections and how to filter and sort selections.
D3 selections let you choose some HTML or SVG elements and change their style and/or attributes.
For example, if your index.html
file contains 5 SVG circle elements:
<svg width="760" height="140">
<g transform="translate(70, 70)">
<circle/>
<circle cx="120" />
<circle cx="240" />
<circle cx="360" />
<circle cx="480" />
</g>
</svg>
you can use d3.selectAll
to select the circles then .style
to change their fill and .attr
to change their radius:
d3.selectAll('circle')
.style('fill', 'orange')
.attr('r', function() {
return 10 + Math.random() * 40;
});
D3 selections also let you perform data joins (see the Data Joins chapter).
Making selections
D3 has two functions to make selections d3.select
and d3.selectAll
.
d3.select
selects the first matching element whilst d3.selectAll
selects all matching elements.
Both functions take a string as its only argument. The string specifies which elements to select and is in the form of a CSS selector string (e.g. div.item
, #my-chart
or g:first-child
).
For example to select all elements with class item
use d3.selectAll('.item')
.
(If you’re not familiar with CSS selectors take a look at the CSS section in my free Fundamentals of HTML, SVG, CSS and JavaScript for Data Visualization course.)
Modifying elements
Once you've made a selection you can modify the elements in it using the following functions:
Name | Behaviour | Example |
---|---|---|
.style | Update the style | d3.selectAll('circle').style('fill', 'red') |
.attr | Update an attribute | d3.selectAll('rect').attr('width', 10) |
.classed | Add/remove a class attribute | d3.select('.item').classed('selected', true) |
.property | Update an element's property | d3.selectAll('.checkbox').property('checked', false) |
.text | Update the text content | d3.select('div.title').text('My new book') |
.html | Change the html content | d3.select('.legend').html('<div class="block"></div><div>0 - 10</div>') |
Whether .select
or .selectAll
is used, all elements in the selection will be modified.
Note that the second argument of .classed
is a boolean. I often forget to include true
or false
and it raises an non-obvious error message!
Here's an example of all of these functions in use:
Updating selections with functions
In addition to passing constant values to .style
, .attr
, .classed
, .property
, .text
and .html
you can pass in a function. For example:
d3.selectAll('circle')
.attr('cx', function(d, i) {
return i * 100;
});
The function accepts two arguments which are usually named d
and i
. The first argument d
is the joined data (or 'datum') and will be covered in the data joins chapter. i
is the index of the element within the selection.
If you want to update elements in a selection according to their position within the selection, you can use the i
argument. For example to position some rect
elements horizontally you can use:
d3.selectAll('rect')
.attr('x', function(d, i) {
return i * 40;
});
In the majority of cases when functions are passed in, anonymous functions are used. However you can also use named functions. For example:
function positionRects(d, i) {
return i * 40;
}
d3.selectAll('rect')
.attr('x', positionRects);
Event handling
You can add event handlers to selected elements using the .on
method.
This method has two arguments:
-
the first is a string specifying the event type
-
the second is a function (a 'callback function') that's called when the event is triggered. This callback function has two arguments that are usually named
e
andd
.e
is the DOM event object andd
is the joined data (which will be covered in the data joins chapter).
Up to and including D3 version 5, the callback function was passed the datum d
and index i
. This is a breaking change.
The most common events include (see MDN event reference for more details):
Event name | Description |
---|---|
click | Element has been clicked |
mouseenter | Mouse pointer has moved onto the element |
mouseover | Mouse pointer has moved onto the element or its children |
mouseleave | Mouse pointer has moved off the element |
mouseout | Mouse pointer has moved off the element or its children |
mousemove | Mouse pointer has moved over the element |
In the event callback function the this
variable is bound to the DOM element that triggered the event. This allows us to do things such as:
d3.selectAll('circle')
.on('click', function(e, d) {
d3.select(this)
.style('fill', 'orange');
});
Note that this
is a DOM element and not a D3 selection so if you wish to modify it using D3 you must first select it using d3.select(this)
.
Inserting and removing elements
Elements can be added to a selection's elements using D3's .append
and .insert
methods. Elements can be removed using .remove
.
.append
appends an element to each element in a selection. If the elements already have children, the new element will become the last child. The first argument specifies the type of element.
As an example let's start with 3 g
elements, each containing a circle
:
<g class="item" transform="translate(0, 0)">
<circle r="40" />
</g>
<g class="item" transform="translate(120, 0)">
<circle r="40" />
</g>
<g class="item" transform="translate(240, 0)">
<circle r="40" />
</g>
You can append a text
element to each using:
d3.selectAll('g.item')
.append('text')
.text('A');
resulting in a text
element being added to each g.item
:
<g class="item" transform="translate(0, 0)">
<circle r="40" />
<text>A</text>
</g>
<g class="item" transform="translate(120, 0)">
<circle r="40" />
<text>A</text>
</g>
<g class="item" transform="translate(240, 0)">
<circle r="40" />
<text>A</text>
</g>
.insert
is similar to .append
but it allows us to specify a second argument which specifies (as a CSS selector) which element to insert the new element before.
In this example, .insert
is used and the second argument is 'circle'
:
d3.selectAll('g.item')
.insert('text', 'circle')
.text('A');
This results in the text element being inserted before the circle:
<g class="item" transform="translate(0, 0)">
<text>A</text>
<circle r="40" />
</g>
<g class="item" transform="translate(120, 0)">
<text>A</text>
<circle r="40" />
</g>
<g class="item" transform="translate(240, 0)">
<text>A</text>
<circle r="40" />
</g>
.remove
removes all the elements in a selection from the page. For example, given some circles, you can remove them using:
d3.selectAll('circle')
.remove();
Chaining
The return value of most selection methods is the selection itself. This means that selection methods such as .style
, .attr
and .on
can be chained. For example:
d3.selectAll('circle')
.style('fill', '#333')
.attr('r', 20)
.on('click', function(d, i) {
d3.select(this)
.style('fill', 'orange');
});
.each()
The .each
method lets you call a function for each element of a selection.
The callback function has two arguments usually named d
and i
. The first argument d
is the joined data (or 'datum') and will be covered in the data joins chapter. i
is the index of the element within the selection. The this
keyword refers to the current HTML or SVG element in the selection.
Here's an example where .each
is used to call a function for each of the selection's elements. The function computes whether the index is odd or even and modifies the circle accordingly:
d3.selectAll('circle')
.each(function(d, i) {
var odd = i % 2 === 1;
d3.select(this)
.style('fill', odd ? 'orange' : '#ddd')
.attr('r', odd ? 40 : 20);
});
Note that this
refers to the current HTML or SVG element (or the i
th element in the selection). If you wish to modify it using D3 you can select it using d3.select(this)
.
.call()
The .call
method allows a function to be called into which the selection itself is passed as the first argument.
.call
is useful where you want a reusable function that operates on a selection.
For example, colorAll
takes a selection and sets the fill of the selection's elements to orange:
function colorAll(selection) {
selection
.style('fill', 'orange');
}
d3.selectAll('circle')
.call(colorAll);
Filtering and sorting selections
You can filter a selection using D3's .filter
method. The first argument is a function which returns true
if the element should be included. The filtered selection is returned by the filter
method so you can continue chaining selection methods.
In this example you filter through even-numbered elements and colour them orange:
d3.selectAll('circle')
.filter(function(d, i) {
return i % 2 === 0;
})
.style('fill', 'orange');
Sorting only really makes sense if data has been joined to the selection, so please read up on data joins first.
You can sort elements in a selection by calling .sort
and passing in a comparator function. The comparator function has two arguments, usually a
and b
, which represent the datums on the two elements being compared. If the comparator function returns a negative number, a
will be placed before b
and if positive, a
will be placed after b
.
Thus if you have the following data joined to a selection:
[
{
"name": "Andy",
"score": 37
},
{
"name": "Beth",
"score": 39
},
{
"name": "Craig",
"score": 31
},
{
"name": "Diane",
"score": 35
},
{
"name": "Evelyn",
"score": 38
}
]
you can sort by score
using:
d3.selectAll('.person')
.sort(function(a, b) {
return b.score - a.score;
});