3 Modify, Add, Remove
Read IDVW2, Chapter 6: Drawing with Data. Skip pp. 89-96 as we will not be drawing bar charts with the div
approach.
3.1 Selections
3.1.1 Select by tag
The ability to select elements on a page is key to being able to manipulate them. d3.select()
will select the first match; d3.selectAll()
will select all matches.
.select("svg").select("circle"); d3
selects the first circle in the order in which circles appear in the <svg>
grouping. If there were more than one circle we could select them all with:
.select("svg").selectAll("circle"); d3
We can select HTML elements by tag in the same way:
.select("body").select("h1");
d3.select("body").selectAll("h1"); d3
3.1.2 Select by class
Classes are selected by adding a “.” before the class name:
.select("svg").selectAll("circle.apple") d3
This provides one method of selecting a certain collection of elements of the same type.
3.1.3 Select by ID
IDs differ from classes in that they are unique identifiers. IDs are selected by adding a “#” before the ID:
.select("svg").select("circle#henry"); d3
3.1.4 Store selections
It is often helpful to store selections for later use. Here we store the svg selection in mysvg
:
const mysvg = d3.select("svg");
The JavaScript community is moving toward using
let
andconst
instead ofvar
; we, however, will stick withvar
to be consistent with IDVW2. Of course you’re welcome to useconst
andlet
instead, and if so, may find these articles helpful: Let It Be - How to declare JavaScript variables and ES2015 const is not about immutability.
Store circle selection in a variable:
const svg = d3.select("svg");
const circ = svg.selectAll("circle");
3.2 Modify existing elements
Try out the code in this section with a downloaded copy of five_green_circles.html opened in Chrome and the Console visible.
3.2.1 Modify attributes
link to get or set attribute API
.select("circle").attr("r"); // see radius
d3
.select("circle").attr("r", "10"); // set radius to 10 d3
3.2.2 Modify styles
.select("h1").style("color");
d3
.select("h1").style("color", "blue"); d3
It is often difficult to remember whether to use
.attr()
or.style()
In general, properties such as position on the SVG, class, and ID are attributes, while decorative properties such as color, font, font size, etc. are styles. However, in some cases, you can use either. For example, the following both make the circle blue:
.select("circle").attr("fill", "blue");
d3
.select("circle").style("fill", "blue"); d3
The first will add a fill="blue"
attribute to the <circle>
tag, while the latter will add style="fill: blue;"
. All is well and good until you find yourself with both in the same tag, in which case the style
property will take precedence. The bottom line: don’t mix the two options because it can cause problems.
To further complicate matters,
.style()
is just shorthand for.attr("style", "...")
so the following are in fact equivalent:
.select("circle").style("fill", "blue");
d3
.select("circle").attr("style", "fill: blue;"); d3
In other words, style is an attribute!
3.2.3 Modify text
This section is interactive: You can hover over code as directed to observe effects.
The interactivity is enabled by D3 scripts that are included in the .Rmd
source file of this page. If you’re interested you can view these scripts by either clicking on the eye icon above or opening Developer Tools in Chrome. In either case, look for the code between the <script>
</script>
tags.
HTML text
<p id="typo" class="fancy">Manhatten</p>
Manhatten
Hover to execute this code (and fix the typo):
.select("#typo").text("Manhattan"); d3
SVG text
<svg width="500" height="100">
<rect width="500" height="100" fill="#326EA4"></rect>
<text id="svgtypo" x="50" y="70" fill="white" font-weight="bold" font-size="40px">
</text>
Web scrapping is fun.</svg>
Hover on this SVG to execute the code below it (and fix the typo):
.select("#svgtypo").text("Web scraping is fun."); d3
The SVG
<text>
tag can be tricky. It differs from HTML text tags (<p>, <h1>, <h2>,
etc.) in that it hasx
andy
attributes that allow you to position text on an SVG canvas. Unlike HTML, the fill attribute controls the color of the text. Compare:
.select("p").style("color", "red"); // HTML
d3
.select("text").attr("fill", "red"); // SVG d3
3.2.4 Move SVG text
<svg width="600" height="100">
<rect width="600" height="100" fill="#326EA4"></rect>
<text id="moveleft" x="200" y="70" fill="white" font-weight="bold" font-size="40px">
</text>
I want to move left.</svg>
Hover on this SVG to execute the code below it:
.select("#moveleft").attr("x", "20").text("Thanks, now I'm happy!"); d3
3.3 Add elements
3.3.1 HTML
Continue trying out code with five_green_circles.html open in Chrome.
Or download the file and open it.
The following adds a <p>
tag but doesn’t change how the page looks, since there’s no text associated with it.
.select("body").append("p"); d3
To add text, use .text()
:
.select("body").append("p").text("This is a complete sentence."); d3
To debug adding an element, go to the Elements tab to see what was added and where. If an element is in the wrong place in the HTML tree, it will not be visible.
3.3.2 SVG
Likewise, here we add a <circle>
to the <svg>
, but we can’t see it since it has no attributes.
.select("svg").append("circle"); d3
Adding attributes will create visible circles:
.select("svg").append("rect").attr("x", "0").attr("y", "0")
d3.attr("width", "500").attr("height", "400").attr("fill", "lightblue");
.select("svg").append("circle").attr("cx", "200")
d3.attr("cy", "100").attr("r", "25").attr("fill", "orange");
.select("svg").append("circle").attr("cx", "300")
d3.attr("cy", "150").attr("r", "25").attr("fill", "red");
We can use a saved selection to assist in creating a new element:
(IDVW2, pp. 97-98)
= d3.select("svg");
mysvg
.append("circle").attr("cx", "250").attr("cy", "250").attr("r", "50")
mysvg.attr("fill", "red");
3.4 Remove elements
These methods will remove matching elements in order, starting with the first find in the document.
3.5 Exercise : green circles
Return to five_green_circles.html, open Developer Tools, and do the following in the Console with D3:
Select the circle with ID “henry” and make it blue.
Select all circles of “apple” class make them red.
Select the first circle and add an orange border (use attribute “stroke”), and stroke width (“stroke-width”) of 5.
Select all circles of “apple” class and move them to the middle of the svg.
3.6 Exercise : blue circles
Return to six_blue_circles.html, open Developer Tools, and execute Steps 1-4 one at a time in the Console. After Step 4, refresh the page to go back to Step 1 if so desired. (You do not need to create a loop as in the visual.)
This exercise is provided as a challenge. It’s fine to skip this exercise and move on to the next section.
|
3.7 Bind data… finally!
(IDVW2, pp. 98-108)
To follow along with the code in this section, download and open six_blue_circles.html.
Bind data:
.select("svg").selectAll("circle").data([90, 230, 140, 75, 180, 25]); d3
Check data binding:
.select("svg").selectAll("circle").data(); d3
Set x-coordinate of each circle to data value using arrow function:
.select("svg").selectAll("circle").attr("cx", d => d); d3
Set x-coordinate of each circle to data value with a JavaScript function:
.select("svg").selectAll("circle").attr("cx", function(d) {return d;}); d3
We’ll bind a new set of data to the circles, this time storing the dataset in a variable:
const dataset = [50, 80, 110, 140, 170, 200];
We’ll also store a selection of all circles before binding the data:
const circ = d3.select("svg").selectAll("circle");
And now, the data bind:
.data(dataset); circ
Nothing appears to have happened; the circles remain the same and there is no evidence of any changes looking at the circles in the DOM (see Elements tab).
We can check that the data are indeed bound with:
.data(); // now we see data circ
Modify elements w/ stored selections, bound data:
.attr("cx", function(d) {return d;});
circ
.attr("cx", function(d) {return d/2;});
circ
.attr("cx", function(d) {return d/4;}).attr("r", "10"); circ
Same as above, using arrow functions:
.attr("cx", d => d);
circ
.attr("cx", d => d/2);
circ
.attr("cx", d => d/4).attr("r", "10"); circ
Note that if we bind a new set of data to the DOM elements, the original set will be overwritten:
const newdata = [145, 29, 53, 196, 200, 12];
.data(newdata);
circ
.transition()
circ.duration(2000)
.attr("cx", d => 2*d);
3.8 Exercise : data bind
Return to six_blue_circles.html, open Developer Tools, and practice binding data to the circles and modifying the circles based on the data as in the examples above.