Selection is the basis of D3. It is used to select elements and supports operations such as editing and binding data. For their own consolidation and sharing, I wrote this analysis of D3 anthology of the article.

To read this article, you need to have studied the basics of D3. If wrong, thanks for correcting.

parsing

Members of the structure

Let’s first look at the internal member structure of an anthology, using this level 3 element structure as an example:

<div class="level-1">
  <div class="level-2">
    <div class="level-3"></div>
  </div>
  <div class="level-2">
    <div class="level-3"></div>
    <div class="level-3"></div>
  </div>
</div>
<div class="level-1">
  <div class="level-2">
    <div class="level-3"></div>
    <div class="level-3"></div>
    <div class="level-3"></div>
  </div>
</div>
Copy the code

If we select only the first level element:

let selection = D3.selectAll('.level-1')
Copy the code

Print the result and we can see:

{
    _groups:[NodeList]
    _parents:[HTML]
}
Copy the code

Here the object is referred to as an element type, just for illustration.

_groups and _parents are arrays, and the content element of _groups is NodeList. The number of internal arrays is dependent on the number of NodeList elements in the _groups that “call” the selectAll method. These parents also appear in _parents.

NodeList is an array-like object containing two. Level-1 elements.

Calling selectAll directly with D3 here is equivalent to starting the search from the root element (HTML), requiring an HTML element in the _parents attribute.

We then select the second-level element:

let selection = D3.selectAll('.level-1').selectAll('.level-2')
Copy the code

Because D3 uses chained calls, this is equivalent to:

let level1 = D3.selectAll('.level-1')
let selection = level1.selectAll('.level')
Copy the code

There are two. Level-1 elements involved in the selection, so we can see the print result:

{
    _groups:[NodeList, NodeList],
    _parents:[HTMLDIVElement, HTMLDIVElement]
}
Copy the code

_groups contains two nodelists, each containing a. Level-2 element selected from two. Level-1 elements, grouped into two groups. No matter how deep in the hierarchy the selected child elements are, they are placed in the same NodeList. The two. Level-1 elements participating in the selection are added to the _parents property.

The first NodeList has two level-2 elements, and the second NodeList has one Level-2 element.

Then we select the third level element:

let selection = D3.selectAll('.level-1').selectAll('.level-2').selectAll('.level-3')
Copy the code

Results:

{
    _groups:[NodeList, NodeList, NodeList],
    _parents:[HTMLDIVElement, HTMLDIVElement, HTMLDIVElement]
}
Copy the code

Since there were three. Level-2 elements selected last time (regardless of grouping), there are three groups of selected elements. The first NodeList has one level-3 element, the second NodeList has two Level-3 elements, and the third NodeList has three Level-3 elements.

traverse

Selection is a Selection class object that has a number of methods that operate on elements, such as attr, classed, and style. Calling these methods will operate on all elements in any grouping.

These methods usually take a function as an argument and pass d (bound data), I (index), and g (grouping) as arguments.

Let’s take the each method as an example:

selection.each((d, i, g) = > {
  console.log(d, i, g)
})
Copy the code

Print result:

undefined 0 NodeList
undefined 0 NodeList(2)
undefined 1 NodeList(2)
undefined 0 NodeList(3)
undefined 1 NodeList(3)
undefined 2 NodeList(3)
Copy the code

The numbers in parentheses represent the number of content elements.

Because we haven’t bound the data yet, so the d’s are undefined. From the printed result, the each method is traversal group by group.

The editor

Some methods edit the selected elements and quantities (the contents of the _groups members), such as filter, merge. So what happens to elements after you edit them?

filter

The filter method is fairly straightforward. It operates on each group individually, similar to the Filter method of Array.

The original structure of the _groups member for Selection is:

[
    [element],
    [element, element],
    [element, element, element]
]
Copy the code

This is indicated by the class of the elementdivElements.

let filtered = selection.filter(':nth-child(odd)')
Copy the code

The: nth-Child (odd) selector represents the selection of elements with an odd index (starting from 1), resulting in:

[
    [element],
    [element],
    [element, element]
]
Copy the code

The number of elements in selection3 groups is 1, 2, and 3 respectively, so the filtering results are in line with expectations.

merge

The merge method is used to merge a collection with another collection.

Here we add an anthology extra:

[
    [extra, extra],
    [extra],
    []
]
Copy the code

Selection Executes the merge operation:

let merged = selection.merge(extra)
Copy the code

Results obtained:

[
    [element],
    [element, element],
    [element, element, element]
]
Copy the code

The result is no different from selection, because the merge method is not a simple “add”, it is used to fill in parts of the selection that do not exist (or null and undefined).

For example, if selection looks like this:

[
    [null],
    [undefined, element],
    [empty, element, element]
]
Copy the code

Empty represents the value of an array[0]Does not exist.

The Extra anthology goes like this:

[
    [extra, extra],
    [extra],
    []
    [extra]
]
Copy the code

Perform the merge operation:

let merged = selection.merge(extra)
Copy the code

You get the result:

[
    [extra]
    [extra, element]
    [empty, element, element]
]
Copy the code

If an element exists at the same location in extra, it will be filled into selection. If there is none in extra, the corresponding position in Selection is left empty. Also, if extra has additional groups, it will not be added to Selection.

As to why there is a void, it depends on the data binding.

Data binding

If we have an element like this:

<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
Copy the code

And data like this:

let data = [0.1.2]
Copy the code

To make the element display a number in the data, we perform a data binding:

let update = selection.data(data)
Copy the code

The data method returns a new selection, internally structured like this:

{
    _groups:[element, element, element],
    _enter:[%empty%, %empty%, %empty%],
    _exit:[%empty%, %empty%, %empty%]
}
Copy the code

Note: %empty% here means no elements. It is not represented as an empty array because it is actually an empty array, but the value of an arraylengthThe property is 3.

Let’s start by looking at the element in _groups, which is the one we selected earlier, and its __data__ property is injected with the corresponding data, one element for each number in order.

We can ask D3 to display the number on the element for us:

update.text(d= > d)
Copy the code

enter

If there are more data, for example:

data = [0.1.2.3]
update = update.data(data)
Copy the code

The update to:

{
    _groups:[element, element, element, %empty%]
    _enter:[%empty%, %empty%, %empty%, EnterNode],
    _exit:[%empty%, %empty%, %empty%]
}
Copy the code

You can see that the _groups array is 4 in length and the fourth element is empty. The _enter array is 4 in length and the first three elements are still empty, but the fourth element, an EnterNode, represents the new element that needs to be added and is the fourth element that _groups is missing. Because four pieces of data require four DOM elements to display, there are only three.

So we call the Enter method to select all nodes that need to be added (we use the word “node” because we haven’t created a DOM element yet) :

let enter = update.enter()
Copy the code

The _groups attribute of the Enter selection is:

[%empty%, %empty%, %empty%, EnterNode]
Copy the code

EnterNode is not a DOM element, but it also has a __data__ attribute and is bound to data.

Now we can create the div element and display the number:

enter
  .append('div')
  .classed('element'.true)
  .text(d= > d)
Copy the code

exit

If there is less data, for example:

data = [0.1]
update = update.data(data)
Copy the code

The update to:

{
    _groups:[element, element]
    _enter:[%empty%, %empty%],
    _exit:[%empty%, %empty%, element]
}
Copy the code

You can see that the _groups array is 2 in length, 1 element missing. The third element of _exit is non-empty and is an existing DOM element that represents the new element to be removed, which is the third additional element of _groups. Two pieces of data require only two DOM elements to display, now one more.

So we call the exit method to select all the elements that need to be removed:

let exit = update.exit()
Copy the code

The _groups attribute of the exit selection is:

[%empty%, %empty%, %empty%, element]
Copy the code

We then call the remove method to remove the element:

exit.remove()
Copy the code

join

Data changes require modification of elements to change the presentation, so we usually encapsulate this process into a function, such as the draw function. Sometimes when the data changes, it’s uncertain, we don’t know if it’s more or less, we have to combine the enter and exit procedures.

Example:

function draw(data) {
  let update = D3.select(document.body).selectAll('.element').data(data)

  update
    .enter()
    .append('div')
    .classed('element'.true)
    .text(d= > d)

  update.exit().remove()
}
Copy the code

Draw ([1, 2, 3]), draw([1, 2, 3]), draw([1, 2, 3]), draw([1, 2]), draw([1, 2])

But if we run draw([1, 2, 3]), draw([2, 3, 4]), we find that the element number is not shown 234, it is still 123. This is because in such a case, D3 comparisons are simply based on differences in the number of elements and data. Here, the number of data is the same, so the number of elements is sufficient, so there are no new or deleted elements.

Another problem here is that we only set the text of the element when we add it, but if the data changes, the text of the existing element is not automatically updated. So we’ll change the draw function to:

function draw(data) {
  let update = D3.select(document.body).selectAll('.element').data(data)

  update
    .enter()
    .append('div')
    .classed('element'.true)
    .text(d= > d)

  update.exit().remove()

  update.text(d= > d)
}
Copy the code

This way, the element is updated correctly. We can also rely on the merge method to merge the Enter and Update selections. From the previous discussion, we can see that the two selections are of the same length and complement each other in terms of elements. The advantage of this is that we only need to write the logic of the text method call once:

function draw(data) {
  let update = D3.select(document.body).selectAll('.element').data(data)

  let enter = update.enter().append('div').classed('element'.true)

  update.merge(enter).text(d= > d)

  update.exit().remove()
}
Copy the code

This is the most common type of update logic in D3. Because it is so common, D3 has added the join method in the new version to simplify such operations.

Use the join method to simplify the draw function:

function draw(data) {
  D3.select(document.body)
    .selectAll('.element')
    .data(data)
    .join('div')
    .classed('element'.true)
    .text(d= > d)
}
Copy the code

The join method can take an element name as an argument or three functions that pass the Enter, update, and exit selections, respectively. The above is equivalent to:

function draw(data) {
  D3.select(document.body)
    .selectAll('.element')
    .data(data)
    .join(
      enter= > enter.append('div'),
      update= > update,
      exit= > exit.remove()
    ).classed('element'.true)
    .text(d= > d)
}
Copy the code

The thing to notice is that incomingenterThe function needs to return a selection of the newly added elements (in this case, the newly created div element selection), passed inupdateThe function needs to return the updated selection while passing inexitThe function does not need to return a value. Because the first two return values will bejoinMethod merge and return for further processing.

Custom methods

By modifying the Selection stereotype, we can add custom methods to make our programming easier, such as methods that modify multiple properties at once:

D3.selection.prototype.attrs = function (attributes) {
  let keys = Object.keys(attributes)
  for (let key of keys) {
    this.attr(key, attributes[key])
  }
}

D3.selectAll('div').attrs({
  title: 'div'.style: 'color: blue'
})
Copy the code

The resources

  • d3-selection

API translation

Based on version: 6.

Collections provide powerful data-driven DOM transformation capabilities, including setting properties, styles, member properties, HTML or text content, and more. Using the Enter and Exit selections for joining data, you can add or remove elements based on the data.

The collection method usually returns the current collection or this new collection. With method chains, you can simply perform a series of operations on a given selection. For example, set the class name or the color style of the Document element:

d3.selectAll("p")
    .attr("class"."graf")
    .style("color"."red");
Copy the code

Is equal to:

var p = d3.selectAll("p");
p.attr("class"."graf");
p.style("color"."red");
Copy the code

By convention, methods that return the current selection are indented by four Spaces, while methods that return the new selection are indented by two Spaces. This is useful to indicate the context differences because they are prominent in the method chain:

d3.select("body")
  .append("svg")
    .attr("width".960)
    .attr("height".500)
  .append("g")
    .attr("transform"."Translate (20, 20)")
  .append("rect")
    .attr("width".920)
    .attr("height".460);
Copy the code

Anthologies are immutable. All selection methods that select elements or change the order return a new selection rather than modifying the current one. However, the element is mutable because the selection drives changes to the Document.

API reference

  • Selecting Elements
  • Modifying Elements
  • Joining Data
  • Handling Events
  • Control Flow
  • Local Variables
  • Namespaces

Select elements

The selection method takes a selector string like.fancy as an argument to select the element that has the Fancy class, or div as an argument to select the div tag element. The selection method takes two forms: select and selectAll, with the former selecting the first element matched and the latter selecting all elements matched in document order. The two top-level methods, D3. select and d3.selectAll, query the entire document, and the subset methods, selection.select and selectAll, limit the selection to descendants of the selected element.

# d3.selection()

Select the root element, document.documentElement. This method can be used to test whether an object is an instanceof D3.selection or an extended selection prototype. For example, add a method to check multiple checkboxes:

d3.selection.prototype.checked = function(value) {
  return arguments.length < 1
      ? this.property("checked")
      : this.property("checked",!!!!! value); };Copy the code

Then use it like this:

d3.selectAll("input[type=checkbox]").checked(true);
Copy the code

# d3.select(selector)

Selects the first matched element based on the specified selector string. If no element matches the selector string, an empty selection is returned. If multiple elements match the selector string, only the first matching element (in document order) is selected. For example, select the first anchor element:

var anchor = d3.select("a");
Copy the code

If selector is not a string, then select the given node. This is useful when there is already a reference to a node, such as this or global document.body in event listeners. For example, make the clicked paragraph red:

d3.selectAll("p").on("click", function() {
  d3.select(this).style("color", "red");
});
Copy the code

# d3.selectAll(selector)

Select all elements that match the selector string, and the selected elements are arranged in document order, from top to bottom. If no element in the document matches the selector, or if the selector is null or undefined, an empty selection is returned. For example, select all paragraphs:

var paragraph = d3.selectAll("p");
Copy the code

If the selector is not a string, then the given set of nodes is selected. This is useful when there are already references to some nodes, such as this.childNodes in event listeners or global document.links. These nodes can be pseudo-arrays, such as NodeList or arguments. For example, set the color of all links to red:

d3.selectAll(document.links).style("color"."red");
Copy the code

# selection.select(selector)

For each selected element, the descendant of the first matched selector is selected. If no descendant of the current element matches the given selector, then the corresponding index position in the returned selection is null. (If the selector is null, every element in the returned selection is null, which returns an empty selection.) If the current element has data associated with it, the data is passed to the corresponding selected element. If multiple elements match a selector, only the first element in document order is selected. For example, select the first bold element in each paragraph:

var b = d3.selectAll("p").select("b");
Copy the code

If selector is a function, it executes on the selected elements in order, passing the current data (D), the current index (I), and the current node group (Nodes) as arguments, and this referring to the current DOM element (Nodes [I]). It must return an element, or null if there is no element match. For example, select the previous sibling of each paragraph:

var previous = d3.selectAll("p").select(function() {
  return this.previousElementSibling;
});
Copy the code

# selection.selectAll(selector)

For each selected element, select each descendant element that matches the Selector string. The selected elements in the returned selection are grouped according to their ancestors in the selection. If no element matches the given selector or the selector is null, the grouping of the current index is empty. The selected element does not inherit data from the selection. Use selection.data to pass data to child elements. For example, select each bold element for each paragraph:

var b = d3.selectAll("p").selectAll("b");
Copy the code

If selector is a function, it executes on each selected element in order, passing the current data (D), current index (I), and current grouping (Nodes) as arguments, where this refers to the current DOM element (Nodes [I]). It must return the elements of an array (or pseudo-array, such as NodeList), or an empty array if there are no matching elements. For example, select the preceding and following sibling elements of each paragraph:

var sibling = d3.selectAll("p").selectAll(function() {
  return [
    this.previousElementSibling,
    this.nextElementSibling
  ];
});
Copy the code

Unlike selection. Select, selection. SelectAll does not affect grouping; each selected element is grouped according to its ancestors in the original selection. Grouping is important in data joins.

# selection.filter(filter)

Filter selection, returns a new selection containing only elements with a filter of true. A filter can be a selector string or a function. If filter were a function, it would execute on each element in order, passing as arguments the current data (D), the current index (I), and the current grouping (Nodes), where this refers to the current DOM element (Nodes [I]).

For example, filter even rows from a table selection:

var even = d3.selectAll("tr").filter(":nth-child(even)");
Copy the code

This is approximately equivalent to using d3.selectAll directly, but the index may be different:

var even = d3.selectAll("tr:nth-child(even)");
Copy the code

Similarly, we can use the function:

var even = d3.selectAll("tr").filter(function(d, i) { return i & 1; });
Copy the code

Or you can use selection. Select:

var even = d3.selectAll("tr").select(function(d, i) { return i & 1 ? this : null; });
Copy the code

Note that the index of the nTH-Child pseudo-class starts at 1, not 0. Therefore, the filter function above does not mean exactly the same as :nth-child. They depend on the selection index rather than the number of leading siblings in the DOM.

The returned filter selection retains the ancestors of the original selection, but like array.filter, it does not retain the indexes of the removed elements. If necessary, you can use selection. Select to preserve the index.

# selection.merge(other)

Returns an anthology that combines the current anthology with the given anthology. The returned selection has the same number of groupings and ancestor elements as the current selection. Any null space in the selection is filled with the corresponding existing element in the given selection. (If there are additional grouping or ancestor elements in other, they are ignored.)

This method is commonly used to merge enter and UPDATE selections after data joins. After modifying the Enter and update elements, you can combine the two selections to avoid writing two copies of code to perform the same operation. Such as:

var circle = svg.selectAll("circle").data(data) // UPDATE
    .style("fill"."blue");

circle.exit().remove(); // EXIT

circle = circle.enter().append("circle") // ENTER
    .style("fill"."green")
  .merge(circle) // ENTER + UPDATE
    .style("stroke"."black");
Copy the code

If you want to fully understand this code, look up the selection.data method, which is often referred to as update mode.

This method is not used to combine arbitrary selections, but if the current selection and a given selection have non-empty elements in the same index, the elements of the current selection are returned and the elements of the other selection are ignored.

# d3.matcher(selector)

For a given selector, return a function that returns true if the element to which this refers matches the given selector. This method is typically used inside selection.filter. Like this:

var div = selection.filter("div");
Copy the code

Is equivalent to:

var div = selection.filter(d3.matcher("div"));
Copy the code

Although D3 is not a compatibility layer, due to the recent standardization of Element.matches, this implementation also supports only browser prefix implementations.

# d3.selector(selector)

For a given selector, return a function that returns the first child of the element to which this matches the given selector. This method is usually used as an internal method for selection. Select. Such as:

var div = selection.select("div");
Copy the code

Is equivalent to:

var div = selection.select(d3.selector("div"));
Copy the code

# d3.selectorAll(selector)

For a given selector, return a function that returns all elements to which this points to the element matching the given selector. This method is usually used as an internal method for selection. SelectAll. Such as:

var div = selection.selectAll("div");
Copy the code

Is equivalent to:

var div = selection.selectAll(d3.selectorAll("div"));
Copy the code

# d3.window(node)

Returns the window object that owns the given node. If node is a node, return the owner’s default view of document; If Node is a document, return its default view; Otherwise return itself.

# d3.style(node,name)

Returns the style property value for the given name of the given node. If node has an inline attribute with the given name name, return this attribute value; Otherwise returns the calculated property value. See also selection. Style.

Modify the element

After the element is selected, the selection transformation method is used to affect the document content. For example, set the name attribute and color style of the anchor element:

d3.select("a")
    .attr("name"."fred")
    .style("color"."red");
Copy the code

# selection.attr(name[, value])

If value is given, set the name attribute of the selected element to value and return the selection. If value is a constant, all elements are assigned the same attribute value; If value is a function, the function executes on all elements in order, passing the current data (D), the current index (I), the current grouping (Nodes), and this referring to the current DOM element (Nodes [I]). The return value of the function is used to set the attributes of each element. A value of null removes the given attribute.

If no value is given, returns the attribute value of the given name name for the first non-empty element in the selection. This is usually useful if you know that the selection contains only one element.

A given name can have a namespace, such as xlink:href to specify the xlink namespace to which the href attribute belongs. See namespaces for details to see all supported namespaces. Additional namespaces can be added through registration.

# selection.classed(names[, value])

If value is given, sets or clears the given CSS class name of the selected element, returning the current selection. This method works by setting the class property or modifying the classList property. A given name can be a space-separated string. For example, to give the selected elements foo and bar class names:

selection.classed("foo bar".true);
Copy the code

If value is true, all elements are given the class name; Otherwise, the class name is cleared. If value is a function, it executes on each selected element in order, passing in the current data (D), the current index (I), and the current grouping (Nodes), whose this pointer points to the current DOM element (Nodes [I]). The return value of the function is then used to set or clear the class name of each element. For example, randomly assign half of the selected elements to foo:

selection.classed("foo".function() { return Math.random() > 0.5; });
Copy the code

If no value is given, return true if and only if the first non-empty element has a given class name. This is useful if you know there is only one element in the anthology.

# selection.style(name[, value[, priority]])

If given value, sets the style attribute of the selected element’s given name, name, to value and returns the current selection. If value is a constant, then all elements are set to the given style attribute value; Otherwise, if value is a function, it executes on each element in order, passing in the current data (D), the current index (I), and the current grouping (Nodes), whose this pointer points to the current DOM element (Nodes [I]). The return value of the function is used to set the style attribute value for each element. A null value removes the style attribute. The optional attribute priority can be set to null or the string important (no exclamation mark is required).

If no value is given, returns the value of the given style attribute for the first non-empty element in the selection. If an inline value exists for the element, the value is returned, not the calculated value. Getting the current style value is useful when the current selection has only one element.

Warning: Unlike many SVG attributes, CSS styles generally have units associated with them. For example, 3px is a valid line width attribute value, but 3 is not. Some browsers implicitly add px values, but not all: IE, for example, throws an “invalid parameter” error.

# selection.property(name[, value])

Some HTML elements have attributes that cannot be retrieved using attribute or style, such as the field text value of the form element and the Checked Boolean value of the checkbox. You can use this method to get or set these properties.

If value is given, set the name attribute of the selected element to value and return the selection. If value is a constant, all elements are assigned the same attribute value; If value is a function, the function executes on all elements in order, passing the current data (D), the current index (I), the current grouping (Nodes), and this referring to the current DOM element (Nodes [I]). The return value of the function is used to set the attributes of each element. A value of null removes the given attribute.

If no value is given, returns the attribute value of the given name name for the first non-empty element in the selection. This is usually useful if you know that the selection contains only one element.

# selection.text([value])

If value is given, set the content of the selected element to value and return the selection. If value is a constant, all elements are assigned the same content value; If value is a function, the function executes on all elements in order, passing the current data (D), the current index (I), the current grouping (Nodes), and this referring to the current DOM element (Nodes [I]). The return value of the function is used to set the contents of each element. A value of null removes content.

Returns the contents of the first non-empty element in the selection if no value is given. This is usually useful if you know that the selection contains only one element.

# selection.html([value])

If value is given, set the innerHTML of the selected element to value and return the selection. If value is a constant, all elements are given the same innerHTML; If value is a function, the function executes on all elements in order, passing the current data (D), the current index (I), the current grouping (Nodes), and this referring to the current DOM element (Nodes [I]). The return value of the function is used to set the contents of each element. A value of null removes innerHTML.

If no value is given, return the innerHTML of the first non-empty element in the selection. This is usually useful if you know that the selection contains only one element.

You should use selection. Append or Selection. Insert to create data-driven content, which is used when you need a little HTML, such as rich text styles. Colleague selection. HTML supports only HTML elements. SVG elements and other non-HTML elements do not support the innerHTML attribute, so they cannot use selection.html. Consider using XMLSerializer to convert a DOM subtree to text. See also innersvG.polyfill, which provides a way to make an SVG element support the innerHTML attribute.

# selection.append(type)

If the given type is a string, add a child element of that type (tag name) to the end of each element in the selection. If the selection is an Enter selection, it is inserted before the next sibling element of the Update selection. This approach to the Enter selection allows you to insert elements into the DOM based on newly bound data. Note, however, that if the elements being updated change the order, you still need to call selection.order.

If value is a function, the function executes on all elements in order, passing the current data (D), the current index (I), the current grouping (Nodes), and this referring to the current DOM element (Nodes [I]). This function should return an element to add to the DOM. (The method usually creates a new element, but may also return an existing element.) For example, insert a DIV element into each paragraph:

d3.selectAll("p").append("div");
Copy the code

This is equivalent to:

d3.selectAll("p").append(function() {
  return document.createElement("div");
});
Copy the code

It is also equivalent to:

d3.selectAll("p").select(function() {
  return this.appendChild(document.createElement("div"));
});
Copy the code

In both cases, this method returns a new selection containing newly added elements. Each new element inherits the current element’s data, as with selection.select.

A given name can have a namespace prefix, such as SVG :text used to declare the text attribute in an SVG namespace. See also Namespaces to see all supported namespaces, or you can register additional namespaces. If no namespace is specified, it is inherited from the parent element. Alternatively, if name is a known prefix, the corresponding namespace is used. (For example, SVG implicitly means SVG: SVG)

# selection.insert(type[, before])

If the given type is a string, insert a new element of that type (tag name) before each selected element that matches the given before selector. For example, if the before selector is: fitst-Child, the element is inserted before the first element. If no before is given, it defaults to null. (To insert elements in the same order as the binding data, use selection.append.)

Both type and before can be functions that execute on all elements in order, passing the current data (D), the current index (I), the current grouping (Nodes), and this referring to the current DOM element (Nodes [I]). The type function should return an element to be inserted. The before function should return the child element that should be used as an insert reference, and the new element will be inserted before that element. For example, insert a DIV element into each paragraph:

d3.selectAll("p").insert("div");
Copy the code

This is equivalent to:

d3.selectAll("p").insert(function() {
  return document.createElement("div");
});
Copy the code

It is also equivalent to:

d3.selectAll("p").select(function() {
  return this.insertBefore(document.createElement("div"), null);
});
Copy the code

In both cases, this method returns a new selection containing newly added elements. Each new element inherits the current element’s data, as with selection.select.

A given name can have a namespace prefix, such as SVG :text used to declare the text attribute in an SVG namespace. See also Namespaces to see all supported namespaces, or you can register additional namespaces. If no namespace is specified, it is inherited from the parent element. Alternatively, if name is a known prefix, the corresponding namespace is used. (For example, SVG implicitly means SVG: SVG)

# selection.remove()

Remove the selected element from the Document and return the selection (deleted element) that is now separated from the DOM. This is not currently an API for adding deleted elements back into the Document. However, you can pass a function to selection.append or Selection. insert to add elements again.

# selection.clone([deep])

After copying the selected element and immediately inserting it into the ontology, the newly added clone is returned. If deep is true, the children of the selected element are also copied. Otherwise, only selected elements are copied. Is equivalent to:

selection.select(function() {
  return this.parentNode.insertBefore(this.cloneNode(deep), this.nextSibling);
});
Copy the code

# selection.order(compare)

Insert the elements back into the document so that the document order and selection are the same for each group. If the data is already sorted, this method is equivalent to calling selection.sort, but faster.

# selection.raise()

Reinsert the selected elements in order as the last child of their parent element. Is equivalent to:

selection.each(function() {
  this.parentNode.appendChild(this);
});
Copy the code

# selection.lower()

Reinsert the selected elements in order as the first child of their parent element. Is equivalent to:

selection.each(function() {
 this.parentNode.insertBefore(this.this.parentNode.firstChild);
});
Copy the code

# d3.create(name)

For a given element name, returns a selection of only one element that contains the element with the given name and which is separated from the current Document.

# d3.creator(name)

For a given element name, return a function that creates the element, assuming this refers to the parent element. This method is used inside selection. Append and Selection. Insert to create new elements. Such as:

selection.append("div");
Copy the code

Is equivalent to:

selection.append(d3.creator("div"));
Copy the code

See also Namespaces for details on supported namespace prefixes.

Link data

# selection.data([data[, key]])

Joins the given data array with the selected element, and returns a new selection representing the updated selection: the data has been successfully bound to the element. The Enter and Exit selections are also returned, which can be used to add and remove elements corresponding to the new data. The given data can be any worth array (such as numbers or objects), or a function that returns an array for each grouping. When data is assigned to an element, it is stored in the __data__ attribute, making the data “stick” to the element and available on re-selection.

Data is assigned to each grouping in the anthology. If the selection has more than one grouping (such as selection. SelectAll after a call to D3.selectAll), data is usually assigned a function. This function is executed for each group in order, passing the data of the group’s parent (D, or undefined), the group index (I), and the selected group’s parent (Nodes), where this refers to the group’s parent. For example, to create an HTML table for a matrix:

var matrix = [
  [11975.5871.8916.2868],
  [ 1951.10048.2060.6171],
  [ 8010.16145.8090.8045],
  [ 1013.990.940.6907]].var tr = d3.select("body")
  .append("table")
  .selectAll("tr")
  .data(matrix)
  .enter().append("tr");

var td = tr.selectAll("td")
  .data(function(d) { return d; })
  .enter().append("td")
    .text(function(d) { return d; });
Copy the code

In this example, the data function is an identity function that returns the corresponding row in the data matrix for each row in the table.

If the key is not given, the first data in data is assigned to the first selected element, the second data is assigned to the second selected element, and so on. You can give a key function to control which data is assigned to which element. It evaluates a string identifier to identify the data and elements at once, instead of the default way to join data. The key function executes on each selected element in order, passing the current data (D), the current index (I), and the current grouping (Nodes), where this refers to the current DOM element (Nodes [I]). The string returned represents the key value of the element. The key function then does the same for each data in data, passing in the current data (d), the current index (I), and the new data for the group, where this points to the parent DOM element of the group. The string returned is the key value of the data. Data for a given key is assigned to the element that matches the key. If multiple elements have the same key, the extra elements are put into the exit selection; If multiple data have the same key, the excess data is put into the Enter selection.

For example, there are documents like this

<div id="Ford"></div>
<div id="Jarrah"></div>
<div id="Kwon"></div>
<div id="Locke"></div>
<div id="Reyes"></div>
<div id="Shephard"></div>
Copy the code

You can join data with keys like this:

var data = [
  {name: "Locke".number: 4},
  {name: "Reyes".number: 8},
  {name: "Ford".number: 15},
  {name: "Jarrah".number: 16},
  {name: "Shephard".number: 31},
  {name: "Kwon".number: 34}]; d3.selectAll("div")
  .data(data, function(d) { return d ? d.name : this.id; })
    .text(function(d) { return d.number; });
Copy the code

This example of the key function uses the data D (if present), otherwise step back and use the element’s ID attribute. Because these elements have not been previously bound to data, data D is null when the key function is executed on the selected element; D is non-null when executed on a new element.

The returned Update and Enter selections are sorted by data, while the exit selection preserves the order before the join. If the key function is given, the order of the elements in the selection may be different from that in the document, and you may need to call selection. Order or Selection. Sort.

While data joins can be handy for creating (or appending) a set of elements from data, more often they are used to create, destroy, and update elements as needed, keeping the DOM consistent with the new data. Data joins can do this efficiently by performing the necessary operations (add, update, or remove) on the elements in each state. Also, transitions between states can be simply animated. Here is an example of a half-updated pattern:

var circle = svg.selectAll("circle") / / 1
  .data(data) / / 2
    .style("fill"."blue"); / / 3

circle.exit().remove(); / / 4

circle = circle.enter().append("circle") / / 5 and 9
    .style("fill"."green") / / 6
  .merge(circle) / / 7
    .style("stroke"."black"); / / 8
Copy the code

It can be broken down into several steps:

  1. Select the existing Circle element (a child of the SVG selection).
  2. The circle element joins the data and returns the Update selection.
  3. Update the circle Settings in the selection with blue padding.
  4. Any existing circle elements (exit selection) that do not match the new data are removed.
  5. For any new data that does not match an existing circle element (enter selection), add a new circle element.
  6. These newly added circle elements are filled in green.
  7. Create a new anthology representing the union of newly added and updated Circle elements.
  8. These newly added and updated circle elements have a black border.
  9. These circle elements are stored in the variable circle.

As described in the previous paragraph, the matching logic is determined by the key function passed to selection.data. Since the above example does not use the key function, elements and data are joined by indexes.

If no data is given, the method returns an array of data for the selected element.

This method cannot be used to clear bound data. Use selection. Datum instead.

# selection.enter()

Returns the Enter selection, which contains elements that have no corresponding DOM element to place space for data. (The Enter selection is empty except for the one returned by selection. Data.)

The Enter selection is typically used for “nonexistent” elements that have not yet been created based on new data. For example, to create a DIV element from a set of numbers:

var div = d3.select("body")
  .selectAll("div")
  .data([4.8.15.16.23.42])
  .enter().append("div")
    .text(function(d) { return d; });
Copy the code

If the body is initially empty, the above code creates six new DIV elements, adds them to the body in sequence, and assigns the text content to the concatenated (converted to string) numbers:

<div>4</div>
<div>8</div>
<div>15</div>
<div>16</div>
<div>23</div>
<div>42</div>
Copy the code

Conceptually, the placeholder element of the Enter selection is a pointer to the parent element (in this case document.body). The Enter selection is generally used only temporarily when adding elements, and is often merged with the Update selection after being added. In this way, changes to elements can be applied to both the Enter and Update selections.

# selection.exit()

Return exit selection: an element that has no new data in an existing selection. (The exit selection is empty except for the selection returned by selection. Data.)

The exit selection is typically used to remove “redundant” elements associated with older data. For example, to update an element previously created by a set of numbers:

div = div.data([1.2.4.8.16.32].function(d) { return d; });
Copy the code

Because the key function is given (as an identification function) and the new data contains only [4, 8, 16] numbers that match elements in the document, the Update selection contains only three elements. To preserve those elements, you can use the Enter selection to add new elements for [1, 2, 32] :

div.enter().append("div").text(function(d) { return d; });
Copy the code

Similarly, remove the existing [15, 23, 42] elements:

div.exit().remove();
Copy the code

The document now looks like this:

<div>1</div>
<div>2</div>
<div>4</div>
<div>8</div>
<div>16</div>
<div>32</div>
Copy the code

The order of DOM elements is the same as the order of data, because the order of old data is the same as the order of new data. If the order of the new data is different, use selection.order to rearrange the elements in the DOM.

# selection.datum([value])

Sets or gets the binding data for the selected element. Unlike selection. Data, this method does not evaluate joins and does not affect indexes or enter and exit selections.

If given value, sets the binding data for all selected elements to the given data. If value is a constant, all elements are assigned the same data. Otherwise, if value is a function, it executes on each element in the selection in order, passing in the current data (D), the current index (I), and the current grouping (Nodes), where this refers to the current DOM element (Nodes [I]). This function is usually used to set the new data of an element. Passing a null value will delete the bound data.

Returns binding data for the first non-empty element if no value is given. This is useful when the anthology is known to contain only one element.

This method is useful for retrieving HTML5 custom data attributes. For example, suppose we have the following elements:

<ul id="list">
  <li data-username="shawnbot">Shawn Allen</li>
  <li data-username="mbostock">Mike Bostock</li>
</ul>
Copy the code

You can expose a custom dataset attribute by setting the element’s data to the built-in dataset attribute:

selection.datum(function() { return this.dataset; })
Copy the code

Handle events

For interaction, collections allow listening and firing of events.

# selection.on(typenames[, listener[, capture]])

Adds or removes listeners for each element with the given event name. Typenames must be strings of event types, such as Click, mouseover, or Submit, which can use any browser-supported event type. The type name can be followed by one. And a name that allows multiple callback functions to register with the same type of event, such as click.foo and click.bar. If multiple type names are given, separate them with Spaces, such as input change or click.foo click.bar.

When a selected element fires an event, the given listener executes on the element, passing in the current data (D), current index (I), and current grouping (Nodes) as parameters, and this pointing to the current DOM element (Nodes [I]). The data is always up to date, but the index is an attribute of the selection and is fixed at listener assignment time. To update the index, you need to reassign the listener. To get event objects in listeners, use d3.evemt.

When an event listener was previously registered with the same event name for the selection element, the old listener is removed before the new listener is added. To remove a listener, pass null to the listener. To remove all listeners on a given name, pass null to the listener and pass.foo to the typename, where foo is the name. Remove all listeners without names, pass. To typename.

An optional capture flag bit can be given with the same meaning as in the W3C: “After initial capture, all events of a given type are sent first to the registered event listener and then to the event target node below them. Events that bubble up the tree do not trigger events set to use captured event listeners.

Returns the current listener for the given name event of the first non-empty selected element if no listener is given. If multiple event names are given, the first matching listener is returned.

# selection.dispatch(type[, parameters])

Sends a custom event of the given type to each selected element in order. An optional parameter mapping can be set as an additional attribute of the event. It can contain the following fields:

  • bubbles– If true, events are sent from the leaf node to the root node.
  • cancelable– If true, event.preventDefault is in effect.
  • detail– Custom data related to events.

If parameters is a function, it executes on each selected element in order, passing in the current data (D), current index (I), and current grouping (Nodes), and this pointing to the current DOM element (Nodes [I]). It must return the parameter mapping of the current element.

# d3.event

Current event. Set when the event listener executes and reset when the listener finishes. You can use it to get standard event fields, such as Event.timestamp, and methods, such as event.preventDefault. You can also use the native Event. pageX and Event. pageY, which facilitate the conversion of the event position to the local event position relative to the container object, and use d3.mouse, D3.touch, or D3.touches to receive event objects.

If you use Balbel, Webpack, or another ES6 to ES5 wrapper, be aware that the value of d3.event may change during an event. The introduction of d3. events must be dynamically bound, so packers may need to be set up to be brought in from D3’s ES6 module rather than from the generated UMD. Not all packers observe jsNext: Main. Also watch out for collisions with the global window.event.

# d3.customEvent(event, lisener[, that[, arguments]])

Call the given listener, use the given that as this context and pass in the given arguments (if any). When called, d3. Event is set to the given event. When the listener returns (or throws an error), d3.event returns to its original value. In addition, set event.sourceEvent to the preceding value of d3.event so that custom events retain references to the original native event. Returns the value returned by the listener.

# d3.mouse(container)

Returns the x and y coordinates of the current event relative to the given container. Containers can be HTML or SVG container elements, such as G elements or SVG elements. The coordinates are returned as an array of two numeric elements [x, y].

# d3.touch(container[, touches], identifier)

Returns the X and Y coordinates of the touches with the given identity associated with the current event relative to the given container. The container can be an HTML or SVG container element, such as a G element or an SVG element. The coordinates are returned as an array of two numeric elements [x, y]. If none of the touches has the given identifier, null is returned. This is useful for ignoring touch events when only a few touches move. If no Touches are given, it defaults to the changedTouches property of the current event.

# d3.clientPoint(container, event)

Returns the x and y coordinates of the given event relative to the given container. (The event could be a touch.) Containers can be HTML or SVG container elements, such as G elements or SVG elements. The coordinates are returned as an array of two numeric elements [x, y].

The control flow

In more advanced applications, anthologies provide a way to customize control flow.

# selection.each(function)

The given function is called for each selected element in order, passing in the current data (D), current index (I), and current grouping (Nodes), with this referring to the current DOM element (Nodes [I]). This method can be used to call arbitrary code for each selected element, and is useful when retrieving data for both parent and child elements, such as:

parent.each(function(p, j) {
  d3.select(this)
    .selectAll(".child")
      .text(function(d, i) { return "child " + d.name + " of " + p.name; });
});
Copy the code

# selection.call(function[, arguments…] )

Call the given function once, passing in the selection and optional arguments. Return this selection. This is equivalent to calling function manually, but providing a method chain. For example, to set multiple styles in a reusable function:

function name(selection, first, last) {
  selection
      .attr("first-name", first)
      .attr("last-name", last);
}
Copy the code

Now you can do this:

d3.selectAll("div").call(name, "John"."Snow");
Copy the code

This is roughly equivalent to:

name(d3.selectAll("div"), "John"."Snow");
Copy the code

The only difference is that selection. Call always returns the selection but does not call the function name.

# selection.empty()

Returns true if the selection has no elements.

# selection.nodes()

Returns all elements of a selection as an array.

# selection.node()

Returns the first non-empty element in the selection. If the selection is empty, null is returned.

# selection.size()

Returns the total number of elements in the selection.

A local variable

D3 allows you to define local variables independent of data. For example, when rendering small but multiple event sequence data, you may need the same X-axis scale but different Y-axis scales to compare the relative performance of each matrix. D3 local variables are limited to DOM elements: once set, the value is stored in the given element and retrieved only from the given element or the closest ancestor element that defines the variable.

# d3.local()

Declare a new local variable. Such as:

var foo = d3.local();
Copy the code

Like var, each local variable is a unique reference symbol, but unlike VAR, the value of each local variable is confined to a DOM element.

# local.set(node, value)

Set the local variable at the given node to value and return the given value. This method is usually called with selection.each:

selection.each(function(d) { foo.set(this, d.value); });
Copy the code

If you want to set only one variable, consider using selection. Property:

selection.property(foo, function(d) { return d.value; });
Copy the code

# local.get(node)

Returns the value of a local variable at a given node. If the node does not define the local variable, returns the value of the nearest ancestor element that defines the local variable. If the local variable is not defined in the ancestor element, return undefined.