Understanding D3 selections

The D3 selection plays important role in our D3 visualization project. Selections allow us to select an element on the page. Once an element is selected, we can perform powerful data-driven transformation of the document object model (DOM): set attributes, styles, properties, HTML or text content, and more.

The D3 has its own selection API, as standard W3C selection have the limitation when it comes to visualization. When selecting multiple elements in W3C we have to use a loop to iterate the multiple selections and we can easily handle with the D3 section.

Selecting a single element and selecting all paragraph element
D3 have two selection function, d3.select, and d3.selectAll. The d3.select will select the first match element and d3.selectall will return an array of array of selected DOM element.

<!DOCTYPE html>
<html> ...
    <script type="text/javascript" src="node_modules/d3/build/d3.js"></script>
</head>
<body>
<p>D3 Selection</p>
<p id="target"></p> 

<script type="text/javascript">
    d3.select("p#target") 
        .text("Hello world!"); 
	
	d3.selectAll("p")
    .attr("class", "graf")
    .style("color", "red");
</script>

</body>
</html>

In above example, we are first selecting first p element with id as the target and second code we are selecting all p element in the page with D3 select. All the D3 selections support a set of standard modifier functions. The text function in above code is one of example.

 

Adding style to selected element
The style() function allows us to set the CSS style on the selected element.

var div = d3.selectAll("div")
    .style("color","white")
    .style("background","blue");

In D3 version 4 doesn’t support styles and attrs, not style and attr as before. The code below will generate an error in D4 version 4.

var div = d3.selectAll("div")
    .attrs({ "title": "Hello, world!", "name" : "greeter" })
    .styles({ "color": "red", "background" : "green" });

The function chaining is the common pattern in the Javascript. Function chaining is a technique that can be used to simplify code in scenarios that involve calling multiple functions on the same object consecutively.

var body = d3.select("body");
body.append("section")
	.attr("id", "main")
	.append("div")
		.attr("class", "box brown")
	.append("p")
		.text("Chaining all over here");

 

Adding and remove class to selected element
In D3 selection.classed function allows us to add and remove CSS classes on the selected element as follow.

	
	d3.select("p").classed("pAll", "true");
	d3.select("p").classed("pAll", function(){ false; });

We can use attr function to apply two class box and content to all div in our page.

    d3.selectAll("div") 
        .attr("class", "box content"); 

 

Setting or Modifying an Attribute
The selection.attr function allows us to set or update a given attribute on the selected element.

	d3.selectAll('rect').attr('width', 10)

 

Adding text to content of the element
The selection.text function allows us to access and set the text content of the selected element.

 d3.select("p#target") 
        .text("Hello world!");
d3.select("p").text(function(){
			return Date();
		});

 

Iterating through D3 a selection:

We can easily iterate in D3 multiple selections. The each function takes an iterator function as the parameter. The each function takes two optional parameter d and i and one more hidden parameter passed in as this reference, which points to the current DOM element object. Where i represent index number of current element object being iterated through.

<div></div>
<div></div>
<div></div>
<div></div>

<script type="text/javascript">
    d3.selectAll("div") 
            .attr("class", "black box") 
            .each(function (d, i) {
                d3.select(this).append("h1").text(i);
            });
</script>

 

Selecting subselection
Scope selection, some time we have to select all elements of a particular tag within particular section element.We can achieve subselection in the number of ways.

d3.selectAll('tbody tr:last-child').remove()

Here in above code, we are removing the last row in the table.

  1. Child Combinator: The child combinator offers a more strict way to achieve a parent-child relationship between two elements. We can define child with character  > separating between parent and child selector as follow parent > child
    <section class="section1">
        <div><p>Section one here</p></div>
    </section>
    <section id="section2">
    </section>
    <section><p>sub selection</section>
    
    <script type="text/javascript">
        d3.select(".section1 > div") 
                .style("color", "red");
    </script>
  2. Descendant combinator: The descendant combinator provides the loose parent-child relationship between two selection. The relationship can be a parent to child, or grandchild or greater grandchild. Example
    d3.select("#section p).style("color", "blue");
  3. The D3 nested subselection: We can also select parent element along with it sub-element.
    d3.select("#section2") 
            .style("font-size", "2em") 
            .select("div") 
            .attr("class", "blue box");

Selecting and manipulating table elements

The D3 allows us to easily select table element. We can select all table row and all column at once. We can apply D3 function and style easily to selected element. In the example below, we are applying D3 to table column and row.

The D3 selection has node() function that returns an array containing the selected element node.

<body>
<table class="table">
    <thead><tr>
        <th>S No</th>
        <th>Name</th>
        <th>Credit</th>
    </tr>
    </thead>
    <tbody>
    <tr>
        <td></td>
        <td>Arun</td>
        <td>300</td>
    </tr>
    <tr>
        <td></td>
        <td>Mickey</td>
        <td>5450</td>
    </tr>
    <tr>
        <td></td>
        <td>Zack</td>
        <td>8900</td>
    </tr>
    </tbody>
</table>

<script type="text/javascript">
    var trSelection = d3.selectAll("tr");

//Select the first row in table as table header
    var headerElement = trSelection.nodes()[0]; 
	console.log("node 0" + headerElement);
	
    console.log("headerElement is an instance of DOM Element: " 
        + (headerElement instanceof Element));

    d3.select(headerElement).attr("class", "table-header"); 
    console.log("d3.select(headerElement) is an instanceof of d3.selection: " 
        + (d3.select(headerElement) instanceof d3.selection));
	
	// selection on column
	 d3.selectAll("td").filter(":first-child")
						.style("color", "yellow")
						.text(function (d,i){ return ++i; });
	d3.selectAll('table').selectAll('td:nth-child(2)')
						.style("color", "blue");
	
	d3.selectAll('table').selectAll('td:last-child')
						.style("color", "red");
	
	//selection on row
    var rows = trSelection.nodes();
    d3.select(rows[1]).attr("class", "table-row-odd");
    d3.select(rows[2]).attr("class", "table-row-even");
    d3.select(rows[3]).attr("class", "table-row-odd"); 
</script>

</body>
</html>

 

In the second example, we are applying some custom function on table element in D3.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">

    <title>Functional Javascript</title>
    <link rel="stylesheet" type="text/css" href="css/style.css"/>
    <script type="text/javascript" src="node_modules/d3/build/d3.js"></script>
	 <style type="text/css">
<style>
      h1 {
        font: 16px sans-serif;
        font-weight: bold;
        text-decoration: underline;
      }
      
      div {
        height: 17px;
      }
      div.container {
        float: left;
        width: 50%;
      }
      div.negative {
        float: right;
        background-color: brown;
      }
      div.positive {
        background-color: steelblue;
      }
      
      table {
        font: 14px sans-serif;
        vertical-align: middle;
        text-align: left;
        border-collapse: collapse;
      }
      
      td,th {
        padding-top: 2px;
        padding-bottom: 2px;
      }
      
      tr.even {
        background-color: #fff3f3; 
      }
      
      td.chart {
        background-color: white; 
      }
     
      th {
        padding-left: 10px;
      }
      
      th.total {
        text-align: right;
      }
      
      td.data {
        padding-left: 10px;

      }
      td.value {
        text-align: right;
      }
      
    </style>
    </style>
</head>

 <body>
  <h1>Language know</h1>
    <script>
      var chartWidth = "100px",
          percent = d3.format(".2%");

      var data = [
        ["Javascript", 0.0050],
        ["C", 0.80],
        ["C++", -0.70],
        ["Java", 0.4],
        ["C#", 0.47]
      ];
      
      var total = d3.sum(data, function(d, i) { return d[1]; });
      
      // Sort the data in descending order
      data.sort(function(a, b) {return d3.descending(a[1], b[1])});
      
      // Setup the scale for the values for display, use abs max as max value
      var x = d3.scaleLinear()
          .domain([0, d3.max(data, function(d) { return Math.abs(d[1]); })])
          .range(["0%", "100%"]);
	
      var table = d3.select("body").append("table");
      
      // Create a table with rows and bind a data row to each table row
      var tr = table.selectAll("tr.data")
          .data(data)
          .enter()
          .append("tr")
          .attr("class", "datarow");
      
      // Set the class to even columns
      d3.selectAll(".datarow").filter(":nth-child(even)").attr("class", "datarow even")
      
      // Add the name to first column 
      tr.append("td").attr("class", "data name")
          .text(function(d) { return d[0] });
          
      // Add class name for second column
      tr.append("td").attr("class", "data value")
          .text(function(d) { return percent(d[1]) })

      // Create a column at the beginning of the table for the chart
      var chart = tr.append("td").attr("class", "chart").attr("width", chartWidth);
      
      // Create the div structure of the chart
      chart.append("div").attr("class", "container").append("div").attr("class", "negative");
      chart.append("div").attr("class", "container").append("div").attr("class", "positive");

      // Creates the negative div bar
      tr.select("div.negative")
        .style("width", "0%")
        .transition()
        .duration(500)
          .style("width", function(d) { return d[1] > 0 ? "0%" : x(Math.abs(d[1]));});

      // Creates the positive div bar
      tr.select("div.positive")
        .style("width", "0%")
        .transition()
        .duration(500)
          .style("width", function(d) { return d[1] > 0 ? x(d[1]) : "0%"; });
    </script>
  </body>
</html>