selectionOne thing we haven't made a huge deal about in class is the difference between a DOM element and a d3 selection. Here is the difference. Suppose we have an HTML snippet like this:
<div id="plot">
<svg>
<circle></circle>
</svg>
</div>
Here, <svg>, <div>, <circle> are all DOM elements or, interchangably, DOM nodes. You can access and interact with these DOM elements via JavaScript's built-in functions. For example, to access the <div> node, we can use:
document.getElementById('plot') //returns the DOM node
DOM nodes have built in properties like clientWidth, and these can be accessed directly from the node:
document.getElementById('plot').clientWidth //returns a number
However, by themselves, plain DOM nodes don't come with many built-in functions. We can't, for example, set attributes and styles directly by using .style() or .attr(). The code below doesn't work:
document.getElementById('plot').style('width','400px'); //WRONG!!
BUT, by turning plain DOM nodes into a d3 selection, a wealth of functions become available. To do that, we use d3.select() or d3.selectAll():
var node = document.getElementById('plot');
d3.select(node) //this is a selection!
.style('width','400px');
If the above looked a little weird to you, that's because we are used to creating selection using CSS selectors. Both methods work:
d3.select('#plot').style('width','400px');
d3.select()/d3.selectAll()+ DOM node = d3 selection
Full list of functions available to selection are here:
Besides the ones we commonly use, like
selection.attr()selection.data()/selection.datum()selection.append()...we also need to be proficient with these
selection.filter()selection.each()selection.call()selection.node()selection.on()Please take a moment to review, and let me know if anything doesn't make sense.
selection.call(): how it works, and whyFirst of all, review the previous section on what a selection is. Then take a look at the API reference for selection.call()
It should be clear from looking at the API reference: selection.call(function[, arguments…]) that
selection.call() expects one argument, a function (we'll ignore the arguments for now)function expects one argument, which is the selection itselfNot as convoluted as it sounds--let's see how it works:
var circle = svg.append('circle') .attr('r',3) .call(colorMeRed); function colorMeRed(selection){ selection .style('fill','red') .style('stroke','white') .style('stroke-width','1px'); }
Here, the circles selection calls the colorMeRed function, passing into the function the circles selection. The end result is the same as:
var circle = svg.append('circle')
.attr('r',3)
.style('fill','red')
.style('stroke','white')
.style('stroke-width','1px');
selection.call()?While the above example is trivial, selection.call() is useful when you want to apply the same operations repeatedly to different selections. Suppose we want to change the fill, stroke and stroke-width of several different selections; using selection.call(), we can put all the tedious code in a function, and call it repeatedly.
svg.append('rect').call(colorMeRed);
svg.append('path').call(colorMeRed);
svg.selectAll('.green-circles').call(colorMeRed);
...
//no need to use .style(...).style(...).style(...) in a row
selection.each()We know that a selection can refer/contain multiple DOM elements:
var circles = d3.selectAll('circle')
.data([43,22,56,39,100]) //array of 5 elements
.enter()
.append('circle');
In the above example, the circles variable is a selection that refers to 5 DOM elements.
selection.each() lets you access individual elements in a selectionselection.each(function) lets you access each element individually. This function allows two arguments:
d is the datum bound to the current elementi is the index/order of the current elementthis refers to the current DOM element itselfcircles.each(function(d,i){ //This function will run 5 times console.log(d); //in succession, this will log 43, 22, 56, 39, 100 console.log(i); //in turn, this will log 0, 1, 2, 3, 4 d3.select(this).attr('r',d/2); //this will set the 'r' attr of each circle to be 1/2 of its datum });
thisWithin the callback function for selection.each(), this refers to the individual DOM element. Remember, DOM element is NOT the same as selection. To turn it into a selection, we still had to use d3.select() to unlock d3's full capabilities.