D3的一点理解

D3

用其官网的话来说:

D3.js is a JavaScript library for manipulating documents based on data. D3 helps you bring data to life using HTML, SVG, and CSS.

D3是一个用于数据可视化的JS库。其将数据与DOM绑定,通过建立数据与DOM元素样式之间的关系,来表现数据,实现可视化。它也是github上星第4多的JS库。

不用工具绘制一个柱状图

成果图如下

d301

代码如下

<style>
    .chart div {
      font: 10px sans-serif;
      background-color: steelblue;
      text-align: right;
      padding: 3px;
      margin: 1px;
      color: white;
    }
  </style>
  ...
  <body>
  <div class="chart"></div>
  <script>
    let data = [30, 86, 168, 281, 303, 365]
    let chart = document.getElementsByClassName('chart')[0]
    data.forEach(d => {
      let div = document.createElement('div')
      div.style.width = `${d}px`
      div.textContent = d
      chart.appendChild(div)
    })
  </script>
</body>
整理思路如下
  1. 选择画布

    let chart = document.getElementsByClassName('chart')[0]

  2. 创建元素,通过元素的样式来表现数据

    let div = document.createElement('div')
    div.style.width = `${d}px`
    div.textContent = d
  3. 将元素添加到画布上

    chart.appendChild(div)

用D3绘制一个柱状图

整个思路中,首先要注意数据与元素数量之间的关系

有多少组数据,就应该对应有多少个元素

其次就是建立数据与元素样式之间的函数关系

例如数据与元素宽度之间的函数关系

function (data) {
  return data + "px";
}

数据与元素文本内容之间的函数关系

function (data) {
  return data;
}

我个人觉得,d3的 Data-Driven Documents,主要体现就是以上两点

用d3实现同样效果如下

<script src="https://d3js.org/d3.v4.min.js"></script>
  <script>
    let data = [30, 86, 168, 281, 303, 365]
    d3.select(".chart") // 选择画布
      .selectAll("div") // 选择画布上的div元素
      .data(data) // 将画布上的div元素与数据绑定
      .enter().append("div") // 如果不够,补足
      .style("width", function (d) { // 数据与元素宽度之间的函数关系
        return d + "px";
      })
      .text(function (d) { // 数据与元素文本内容之间的函数关系
        return d;
      });
  </script>
理解数据与元素数量之间的关系

数据

let data = [30, 86, 168, 281, 303, 365]
本身就有对应数量的元素

如果在 .chart 中有对应数量的 div

<div class="chart">
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
  </div>

那么就不需要额外添加 div 元素

d3.select(".chart")
      .selectAll("div")
      .data(data)
      .style("width", function (d) {
        return d + "px";
      })
      .text(function (d) {
        return d;
      });
本身就没有元素

如果没有 div 元素,就需要通过 enter().append('div') 来添加相应数量的元素

<div class="chart">
  </div>

其中 enter()

Returns the enter selection: placeholder nodes for each datum that had no corresponding DOM element in the selection. (The enter selection is empty for selections not returned by selection.data.)
d3.select(".chart")
      .selectAll("div")
      .data(data)
      .enter().append('div')
      .style("width", function (d) {
        return d + "px";
      })
      .text(function (d) {
        return d;
      })
本身有过多数量的元素

如果在 .chart 中有过多数量的 div

<div class="chart">
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
  </div>

那么就需要将多余的 div 移除,通过 exit().remove() 来实现

其中 exit()

Returns the exit selection: existing DOM elements in the selection for which no new datum was found. (The exit selection is empty for selections not returned by selection.data.)
d3.select(".chart")
      .selectAll("div")
      .data(data)
      .style("width", function (d) {
        return d + "px";
      })
      .text(function (d) {
        return d;
      })
      .exit().remove()
更新数据与元素数量之间的关系

首先要知道在 d3 中,数据与 DOM 元素并不是双向绑定的,在将数据绑定到元素之后,再次修改数据,元素并不会产生对应的变化

所以数据更新要手动完成

function update(data) {
      d3.select(".chart")
        .selectAll("div")
        .data(data)
        .style("width", function (d) {
          return d + "px";
        })
        .text(function (d) {
          return d;
        })
     // 如果元素不够,补足
      d3.select(".chart")
        .selectAll("div")
        .data(data)
        .enter().append('div')
        .style("width", function (d) {
          return d + "px";
        })
        .text(function (d) {
          return d;
        })
    // 如果元素多余,移除
      d3.select(".chart")
        .selectAll("div")
        .data(data)
        .exit().remove()
    }

完整实验代码

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>D3.js</title>
  <style>
    .chart div {
      font: 10px sans-serif;
      background-color: steelblue;
      text-align: right;
      padding: 3px;
      margin: 1px;
      color: white;
    }
  </style>
</head>

<body>
  <div class="chart">
  </div>
  <script src="https://d3js.org/d3.v4.min.js"></script>
  <script>
    let data = [30, 86, 168, 281, 303, 365]
    d3.select(".chart")
      .selectAll("div")
      .data(data)
      .enter().append('div')
      .style("width", function (d) {
        return d + "px";
      })
      .text(function (d) {
        return d;
      })

    setTimeout(function () {
      data = [30, 86, 168, 281, 303, 365, 700]
      update(data)
    }, 1000)

    function update(data) {
      d3.select(".chart")
        .selectAll("div")
        .data(data)
        .style("width", function (d) {
          return d + "px";
        })
        .text(function (d) {
          return d;
        })

      d3.select(".chart")
        .selectAll("div")
        .data(data)
        .enter().append('div')
        .style("width", function (d) {
          return d + "px";
        })
        .text(function (d) {
          return d;
        })

      d3.select(".chart")
        .selectAll("div")
        .data(data)
        .exit().remove()
    }
  </script>
</body>

</html>

小结

d3提供的选择方法,能处理数据与元素数量不匹配的情况

  • 当元素数量不够时,通过enter()能选择到那些用于补足的元素
  • 当元素数量过多时,通过exit()能选择那些过剩的元素

d3方便了通过元素的样式来展现数据的过程,用户自己定义数据与元素样式之间的函数关系


nbb3210
436 声望31 粉丝

优雅地使用JavaScript解决问题