d3 - kylecoberly/knowledge GitHub Wiki

Notes

  • D3 just does the math and gives you a declarative DOM API. You have to use something like HTML or SVG to actually draw the visualization
  • Much like jQuery, you start d3 chains with a selection. The methods chain together, each step returning a modified version of the selection
  • You can set attributes and style properties with constant values, or with computed properties by passing in functions
  • You can save selections part way through a chain to fork them or make things more clear
  • Binding data has 3 phases
    • Enter, which is when there is new elements being created because there are more data points than there are elements

    • Update, which is done to elements that already exist

    • Exit, which is when there are more elements than data points

  • You can make linear, ordinal and other scales to transform a data point to whatever scale it's being presented at. You set the domain (data input) and range (chart output) of the scale, and then use the resulting function to scale data to your chart size.
  • You can animate transitions with transition(), delay(), and duration()
  • You set the style of nodes with .style(), and the attributes of nodes with .attr()
  • All SVGs are absolutely positioned from the top left

Flow

  1. Start by selecting DOM elements. You will likely be selecting DOM elements that don't exist yet.
  2. Then bind data to your selected elements. It will attempt to make a 1:1 correspondance with every DOM element that's been selected.
  3. A data binding returns a selector with all of the elements it was able to match to data points, plus it allows the .enter() and .exit() methods
  • When there are more data points than matching elements, the .enter() method allows you to create them by giving you placeholder DOM nodes.
  • When there are more matching elements than data points, the .exit() method allows you to work with the excess DOM nodes.

Selections

  • Use .select() or .selectAll()
  • Subsequent selections happen inside previous ones
  • Can filter a selection with .filter()
  • Merge multiple collections with old.merge(new)
  • Can create a new element that's not already in the DOM with .create()

Data Binding

  • Bind data to a selection with .data()
  • Match data items to a key by giving it a second argument: .data(data, d => d[0])
  • Instead of separate .enter() and .exit(), you can use .join():
d3.selectAll("circle")
  .data(data)
  .join(
    enter => enter.append("circle"),
    update => update.append("circle"),
    exit => exit.append("circle"),
  )

You can also use it to replace .enter().append():

d3.selectAll("circle")
  .data(data)
  .join("circle")

DOM Nodes

  • Append with .append(item)
  • Prepend with .insert(item, itemToPrepend)
  • Remove with .remove()
  • Set and get attributes with .attr()
    • Setter will also take a function, called with d, index, and nodes
  • Set and get styles with .style()
  • Set and get non-attribute properties (like checked) with .property()
  • Selectively add classes with .classed(classNames, addOrRemoveBoolean)
  • Set text with .text()
  • Set .innerHTML with .html()
  • Set the data with .datum()
  • Sort with .sort(comparator)
  • Apply a function to every element with .call(someFunction, arguments)

Scales

const scaleX = d3.scaleLinear()
  .domain(d3.extent(data, d => d.x))  // Range of data
  .range(0, height)                   // Range of display
  .nice()
  • d3.extent() takes an array and returns an array with the highest and lowest value.
  • .nice() rounds the values

Lines

const line = d3.line()
  .x(d => scaleX(d.x))
  .y(d => scaleY(d.y))

d3.select("#some-svg-g")
  .append("path")
  .attr("fill", "none")
  .attr("stroke", "red")
  .attr("d", linemaker(data))

Curves

const line = d3.line()
  .curve(d3.curveNatural)
  .x()
  .y()

Axes

svg.append("g")
  .call(d3.axisBottom(scaleX))
  .attr("transform", `translate(${width},0)`)

svg.append("g")
  .call(d3.axisLeft(scaleY))
  .attr("transform", `translate(${width},0)`)

Events

d3.selectAll("li")
  .data(data)
  .enter()
  .append("li")
  .text(d => d)
  .on("click", (d, index) => {
    // Whatever
  })
  • In the handler, event is the DOM event, d is the data, this is the DOM element. this binding isn't magicked over, you can use d3.event.target in an arrow function.
  • .on("click.someLabel", whatever) can be used to attach multiple listeners for the same event
  • .on("click.someLabel focus", whatever) can be used to attach multiple listeners for different events
  • d3.mouse(someElement) returns the [x, y] relative to someElement
  • Add built-in event handlers (like drag/drop) with behavior components:
drag(d3.selectAll("some-selector"))
  .call(
    d3.drag()
      .on("start", whatever)
      .on("drag", whatever)
      .on("end", whatever)
  )

Animations

d3.select(this)
  .transition()
  .duration(2000)
  .style("color", "blue")
  • .transition() tweens the values
  • .delay()
  • .ease() applies an easing function
  • Transitions can be chained