This is the fourth day of my participation in the November Gwen Challenge. Check out the details: The last Gwen Challenge 2021
See the Data Visualization column for a series of articles
reference
D3 developers have created an online development environment called Observable, which, in addition to official tutorials and samples, is also a visualization sharing community where you can find great visualizations.
- D3 website
- Learn D3
- Let’s Make a Bar Chart
- D3.js Chinese document (unofficial)
- d3-selection
This article introduces the basic flow of D3.js based on data manipulation of DOM, involving several core concepts:
- Select elements: Select DOM nodes (typically SVG elements) that need to be manipulated
- Bind data: Associate data with DOM nodes, which enables data-driven attribute style updates of DOM elements
- Add and delete elements: Use the join operation (or use the enter-update-exit operation) to add and delete DOM nodes
💡 This is the basic process for D3 to map data to page elements, mainly using the Selection module, but D3 also provides other modules for more complex data visualization.
The selector
To “drive” the DOM based on data, you first select the element you want to operate on. Javascript natively provides verbose code for methods that manipulate the DOM. D3 provides a declarative method d3.select(query) or d3.selectall (query) to select DOM nodes or sets of nodes.
The method d3.select(query) selects the first DOM element that matches the criteria, while the method selectAll(query) selects all DOM elements that match the criteria. Returns an empty selection set if no element is selected.
The input parameter received by this method is Query, which is a CSS supported selector, such as tag selector, class selector, ID selector, attribute value selector, etc.
This method returns a selection set object (or array) with a wealth of methods, such as setting style properties, changing HTML or text content, registering event listeners, adding, removing, sorting nodes, and so on, so that you can manipulate the DOM as a chain call.
// Operate a single node
d3.select('body')
.style('background-color'.'black'); // Change the background color of the element
// You can also manipulate a collection of nodes
d3.selectAll('p')
.style('color'.'white'); // Change the font color of the element
Copy the code
The second operation is equivalent to the native operation
const paragraphs = document.getElementByTagName('p');
for (let i=0; i<paragraphs.length; i++) {
const paragraph = paragraph.item(i);
paragraph.style.setProperty('color'.'white');
}
Copy the code
💡 In most cases in D3, the setting of attribute values not only supports the transfer of static constant values, but also supports the transfer function to return the values obtained by dynamic calculation. For example, the graphics module SHAPE of D3 provides related functions to calculate the value of attribute D of
// Set a random color for each paragraph
d3.selectAll('p').style('color'.function() {
return 'hsl(' + Math.random() * 360 + ', 100%, 50%) ';
}
Copy the code
Data binding
As D3 supports function calculation of dynamic values when setting the attributes of DOM elements, if chart Data is taken as a parameter, data-driven Documents can be realized
By default, DOM nodes (selection set) and data(listed in array form) are one-to-one mapped according to the index join-by-index (i.e. the first element of the data array is bound to the first node of the selection set). Then, when using the function to set the DOM node style attribute, the corresponding Data are respectively passed to the setting function as the first parameter D, and the attribute value is dynamically calculated, so as to realize the data-driven Documents.
// Set different font sizes for paragraphs based on the data
d3.selectAll('p') // Select all elements
.data([4.8.15.16.23.42]) // Bind data to a DOM element
.style('font-size'.function(d) { return d + 'px'; }); // Dynamically calculate the font size of each element based on the data
Copy the code
💡 After the DOM node is bound to data, D3 can read the bound data from the selection set again when D3 operates the DOM node (such as updating the DOM attribute style). That is, once the DOM node is bound to data, it will have state. Add an attribute named __data__ to the selection set. This eliminates the need to constantly map data.
In order to track DOM nodes and assign them to different selection sets, a key function selection. Data (data, keyFunction) is usually provided when binding data, and its return value is usually a string, such as a place name, ID, etc. Matching DOM nodes with data (instead of the default join-by-index pairing)
<div id="Ben"></div>
<div id="Tom"></div>
<div id="Jack"></div>
<div id="shouldBeDeletedNode"></div>
<script>
const dataset = [
{name: 'Ben'.number: 4},
{name: 'Tom'.number: 8},
{name: 'Jack'.number: 15},]; d3.selectAll('div')
.data(dataset, function(d) {
// The key function returns the d.name of the data as an identifier, or the id attribute of the node if no data is bound
return d ? d.name : this.id;
})
.text(function(d) { return d.number; });
</script>
Copy the code
Add or delete elements
In general, when the nodes and data are bound in D3, the nodes in the set and the elements of the data array are selected to match one by one, but the number of nodes and data elements may not match.
To solve this problem, D3 puts forward three concepts:
- If there are too many DOM nodes, the node with unbound data will enter the nameExiting selection(Nodes that are to be “removed” from the page are usually deleted in subsequent operations.)
- If there are more data elements, then there are morePlaceholder nodes(virtual node) will enter the nameIf selection(Nodes that are ready to “enter” the page are typically instantiated in subsequent operations and inserted in the appropriate place on the page)
- The DOM node that corresponds to the data can be entered in the nameUpdating selection, which is the default selection set, i.e
data()
The update () and Exit () methods return objects that are called by the Enter and exit selectorsenter()
å’Œexit()
Method to get)
💡 after binding data, D3 did not update (add or delete) page nodes immediately, but generated three selection sets, thus providing greater flexibility and customizability for data visualization. For example, for nodes no longer part of the selection set, faded dynamic effects can be set during deletion. You can set different colors for nodes in the entering selection set, highlighting them as newly added to the page
Enter the old method – update – exit
We then perform different operations on different selection sets, so we can dynamically add and delete DOM elements based on the data elements. The process is as follows (the following selection represents the selection set) :
- Remove DOM nodes on pages pertaining to the Coinage of choices
selection.exit().remove()
- Add a entering selection set of virtual nodes to the page
enter = selection.enter().append('tagName')
- Merging the entering and updating selection set (updating selection set is in the original
selection
C), soThe returned selection set corresponds to the new data one to oneenter.merge(selection)
, and then you can easily set the uniform style of the DOM nodes corresponding to the new data
const circle = d3.selectAll('circle').data(anotherDataset)
.style('fill'.'blue'); // This style setting will only apply to the Updating selection set
circle.exit().remove(); // Remove DOM nodes corresponding to the Great selection set
circle = circle.enter()
.append('circle') // Add a virtual node in the selection set to the page
.style('fill'.'green') // Set entering select set node style
.merge(circle) // Merge entering and updating selection sets
.style('stroke'.'black'); // Set the node style in the selection set after the merge
Copy the code
💡 is generally merged with the entering and updating selectset after adding nodes using the append() method, so that subsequent actions can be applied to both the Enter and update selectsets (this is the old method, See the new, more concise method below.)
New method to join
D3 added a new method join() which will automatically delete the DOM nodes in the exit selection set and automatically add the virtual nodes in the entering selection set to the page, then add the entering selection set and updating selection set and return
d3.. selectAll('circle')
.data(newDataset)
.join('circle') // Return a merge set of entering and updating selection sets
// You can then set the overall style of the DOM node with the new data bound
.attr('r', radius)
Copy the code
💡 If you wish to operate separately on the Withdraw selection set, the entering selection set, or the updating selection set, you can pass the corresponding function accordingly in the method join
d3.selectAll('circle')
.data(newData, d= > d)
.join(
  // The first function entry passed is the entering selection set
  enter= > {
   // entering selection handler
   // Finally you need to return the node instantiated by the entering selection set so that the join method will finally merge it with the updating selection set
   return enter.append('circle')},// The second function input parameter passed is the updating selection set
  update= > {
    // updating selection handler
    update.attr("fill"."blue")
 }
  exit => {
   // exiting selection handler
   exit.remove() // Remove nodes pertaining to the Great selection set from the page})// Finally join() returns the selection set of entering and updating
 // Continue with the other chain calls......
Copy the code