Transforming the DOM
Introduction
This chapter is about transforming (querying and modifying) the Document Object Model (DOM) using D3. The methods involved are grouped in the D3 module d3-selection.
Selecting and modifying elements in D3 works in a similar way to jQuery. The methods return the selection itself or return a new selection, resulting in a typical chain of methods (see example below).
The selection can be modified by passing constant values or by passing a function, evaluated for each selected element.
The function is being passed two arguments: the joined current data (d
) and the index (i
) of the current element within the selection.
Joined data will be covered later.
d3.selectAll('circle')
.style('fill', 'tan')
.attr('r', 40)
.attr('cy', 100)
.attr('cx', function(d, i) {
return (i * 100) + 40;
});
Selecting elements
Selection methods d3.select
and d3.selectAll
use
CSS selectors,
alike querySelector()
and querySelectorAll()
in plain JS.
<div id="container">
<div>
Lorem ipsum <span>dolor</span> sit amet,
consectetur adipiscing elit, <span>sed</span> do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</div>
<p>
Ut <span>enim</span> ad minim veniam,
quis <span>nostrud</span> exercitation ullamco laboris
nisi <span>ut</span> aliquip ex ea commodo consequat.
</p>
<p>
Duis aute irure dolor in <span>reprehenderit</span> in
voluptate velit esse cillum dolore eu fugiat nulla pariatur.
</p>
</div>
<script>
d3.selectAll('#container p') // Selects all elements that matches the specified CSS selector.
.style('color', 'orange');
d3.select('#container span') // Selects the first element that matches the specified CSS selector.
.style('color', 'magenta');
d3.selectAll("#container p").select("span") // Selects the first <span> in every <p>
.style('text-decoration', 'overline');
</script>
Result:
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Filtering selections
A selection filter returns a new selection containing only the elements for which the specified filter is true. The filter can be either a CSS selector string or a function. Filter a selection to contain only the even-indexed elements:
<span>D3</span> <span>is</span> <span>really</span> <span>incredibly</span> <span>awesome</span>.
<script>
d3.selectAll('span')
.filter(function(d, i) {
return i % 2 === 0;
})
.style('color', 'orange');
</script>
Result:
Modifying elements
Elements (or more precipice: DOM nodes) in a selection can be modified by chaining a number of modification methods.
<style>
.coloredRed { fill: red; }
</style>
<svg width="100%" height="100%" viewBox="0 0 390 100">
<g><circle /></g>
<g><circle /></g>
<g><circle /></g>
<g><circle /></g>
</svg>
<script>
d3.selectAll('circle')
.attr("r", 40)
.classed('coloredRed', true);
d3.selectAll('g')
.attr('transform', function(d, i) {
return `translate(${(i * 100) + 40}, 50)`;
})
.append("text")
.text(function(d, i) {
return `${i+1}`;
})
.style('fill', 'tan');
d3.selectAll('g')
.insert("circle", ":first-child") // (will be explained below)
.attr("r", 40)
.attr("cx", 10)
.style('fill', 'blue');
</script>
Result:
The methods modify all elements in the selection. Most methods return the selection itself,
but append
and insert
return a new selection, containing only the appended or inserted elements.
Method append
inserts a new element of the specified type as the last child of each selected element.
Method insert
inserts a new element of the specified type,
before the first element matching the specified element (second argument, via a CSS selector) for each selected element.
Another example:
<div></div>
<button onClick="removeCheckbox();">Remove checkbox</button>
<script>
// build: <input type="number" value="5" />
d3.select('div')
.append("input")
.attr("type", "number")
.property('value', 5);
// build: <label>My checkbox <input type="checkbox" checked /></label>
d3.select('div')
.append("label")
.text("My checkbox ")
.append("input")
.attr("type", "checkbox")
.property('checked', true);
function removeCheckbox() {
d3.selectAll('div label')
.remove();
};
</script>
Result:
The example above uses selection.remove
to remove the selected elements from the document
and selection.property
to set "special attributes" that are not addressable using .attr
,
such as a form field’s text or number value (the user is supposed to set or change the value) and boolean attributes that are set to true
when present, like a checkbox’s checked boolean.
Cloning selections
Method
clone
inserts a copy of each selected element immediately after the selected element and returns a selection of the newly added clones.
<style>
#container div { width:100px; height:10px; margin-bottom:4px; }
</style>
<div id="container">
<div style="background:red;"></div>
<div style="background:green;"></div>
<div style="background:blue;"></div>
</div>
<script>
d3.selectAll("#container div")
.clone(true) // *) see note below
.style("width", "120px");
</script>
Result:
*): If the argument of clone()
is truthy, it will be a deep clone, i.e., the descendant elements of the selected elements will be cloned as well.
Method raise()
removes and then re-inserts each selected element (the cloned elements in the example below), in selection order, as the last child of its parent
(the container div
in the example below).
<style>
#container div { width:100px; height:10px; margin-bottom:4px; }
</style>
<div id="container">
<div style="background:red;"></div>
<div style="background:green;"></div>
<div style="background:blue;"></div>
</div>
<script>
d3.selectAll("#container div")
.clone(true)
.style("width", "120px")
.raise();
</script>
Result: