javascript - Selecting null: what is the reason of using selectAll(null) in D3.js? -


i've seen d3 codes pattern appending elements:

var circles = svg.selectall(null)     .data(data)     .enter()     .append("circle"); 

i don't snippet. why selecting null?

the way understand d3, if 1 appending circles, should be:

var circles = svg.selectall("circle")     .data(data)     .enter()     .append("circle"); 

the same way, if 1 appending html paragraphs should be:

var circles = svg.selectall("p")     .data(data)     .enter()     .append("p"); 

the same goes classes: if 1 appending elements class foo, should selectall(".foo").

however, selectall(null) does work! elements appended.

so, what's meaning of null? missing here?


note: self-answered question, trying provide "canonical" q&a on subject has been touched on many previous questions , not explained api. of answer below example wrote in removed docs.

tl;dr

the objective of using selectall(null) guarantee "enter" selection always have all elements present in data array.


the "enter" selection

to answer question, have briefly explain "enter" selection in d3.js. know, 1 of main features of d3 ability of binding data dom elements.

in d3.js, when 1 binds data dom elements, 3 situations possible:

  1. the number of elements , number of data points same;
  2. there more elements data points;
  3. there more data points elements;

in situation #3, data points without corresponding dom element belong "enter" selection.

thus, in d3.js, "enter" selections selections that, after joining elements data, contains data don't match dom element. if use append function in "enter" selection, d3 create new elements, binding data us.

this venn diagram explaining possible situations regarding number of data points/number of dom elements:

enter image description here

binding data existing dom elements

let's break proposed snippet appending circles.

this...

var circles = svg.selectall("circle")     .data(data) 

... binds data selection containing circles. in d3 lingo, that's "update" selection.

then, this...

.enter() .append("circle"); 

... represents "enter" selection, creating circle each data point doesn't match selected element.

sure, when there no element (or given class) in selection, using element (or class) in selectall method work intended. so, in snippet, if there no <circle> element in svg selection, selectall("circle") can used append circle each data point in data array.

here simple example. there no <p> in <body>, , our "enter" selection contain elements in data array:

var body = d3.select("body");  var data = ["red", "blue", "green"];  var p = body.selectall("p")    .data(data)    .enter()    .append("p")    .text(d=> "i " + d + " paragraph!")    .style("color", string)
<script src="https://d3js.org/d3.v4.min.js"></script>

but happens if already have paragraph in page? let's have look:

var body = d3.select("body");  var data = ["red", "blue", "green"];  var p = body.selectall("p")    .data(data)    .enter()    .append("p")    .text(d=> "i " + d + " paragraph!")    .style("color", string)
<script src="https://d3js.org/d3.v4.min.js"></script>  <p>look ma, i'm paragraph!</p>

the result clear: red paragraph disappeared! it?

the first data element, "red", bound existing paragraph. then, 2 paragraphs created (our "enter" selection), blue 1 , green one.

that happened because, when used selectall("p"), selected, well, <p> elements! , there 1 <p> element in page.

selecting null

however, if use selectall(null), nothing selected! doesn't matter there paragraph in page, our "enter" selection always have elements in data array.

let's see working:

var body = d3.select("body");  var data = ["red", "blue", "green"];  var p = body.selectall(null)    .data(data)    .enter()    .append("p")    .text(d=> "i " + d + " paragraph!")    .style("color", string)
<script src="https://d3js.org/d3.v4.min.js"></script>  <p>look ma, i'm paragraph!</p>

and that's purpose of selecting null: guarantee there no match between selected elements , data array.

selecting null , performance

since not selecting anything, selectall(null) far fastest way append new elements: don't have traverse dom searching anything.

here comparison, using jsperf:

https://jsperf.com/d3-selecting-null/1

in simple scenario, selectall(null) substantially faster. in real page, full of dom elements, difference may bigger.

when not use selectall(null)

as explained, selectall(null) won't match existing dom element. it's nice pattern fast code append elements in data array.

however, if plan update elements, is, if plan have "update" (and "exit") selection, do not use selectall(null). in case, select element (or class) plan update.

so, if want update circles according changing data array, this:

//this "update" selection var circles = svg.selectall("circle")     .data(data);  //this "enter" selection circles.enter()     .append("circle")     .attr("foo", ...  //this "exit" selection circles.exit().remove();  //updating elements circles.attr("foo", ... 

in case, if use selectall(null), circles appended selection, piling up, , no circle removed or updated.


Comments

Popular posts from this blog

resizing Telegram inline keyboard -

command line - How can a Python program background itself? -

php - "cURL error 28: Resolving timed out" on Wordpress on Azure App Service on Linux -