javascript - 对 d3 的 selectAll 方法的好奇心

标签 javascript d3.js dom selector selectall

我想了解 d3 的方法是如何工作的。我想我已经完全掌握了 d3,当然是基本水平,但是关于 selectAll 方法有一件奇怪的事情我不明白。因此,当我尝试创建 dom 节点并将其附加到选定的 dom 元素时,无论它是否存在,有时它会创建四个或两个节点,或者在其他情况下创建六个节点。为了清楚地说明问题,我将使用简单的示例。

HTML:

<!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>Document</title>
  </head>
  <body>
     <svg></svg>
  </body>
</html>

JS:

    dummyData = [
         {
           name: 'A',
           age: 50
         },
         {
           name: 'B',
           age: 20
         }
       ]

    svg = d3.select('svg')
            .attr('width','500')
            .attr('height', '300')
          .append('g')
            .attr('transform','translate(40, 40)');


function example_1() {

    svg.selectAll('circle')
      .data(dummyData)
      .enter()
    .append('circle')
      .attr('transform', d => `translate(${d.age}, 20)`)
      .attr('cx',32).attr('cy',53)
      .attr('r',15);

 }


function example_2() {

    svg.selectAll('g')
      .data(dummyData)
      .enter()
    .append('circle')
      .attr('transform', d => `translate(${d.age}, 20)`)
      .attr('cx',32)
      .attr('cy',53)
      .attr('r',15);
}

function example_3() {

    svg.selectAll('div')  // This div is an arbitrary value. It can be any html tag to output the same result 
      .data(dummyData)
      .enter()
    .append('circle')
      .attr('transform', d => `translate(${d.age}, 20)`)
      .attr('cx',32)
      .attr('cy',53)
      .attr('r',15);

 }

function example_4() {

   svg.selectAll('g')
      .data(dummyData)
      .enter()
    .append('circle')
      .attr('transform', d => `translate(${d.age}, 20)`)
      .attr('cx',32)
      .attr('cy',53)
      .attr('r',15);

    svg.selectAll('g')
      .data(dummyData)
      .enter()
    .append('path')
      .attr('stroke','#000')
      .attr('d',`M5,5H500`)
}

function example_5() {

   svg.selectAll('g')
      .data(dummyData)
      .enter()
    .append('circle')
      .attr('transform', d => `translate(${d.age}, 20)`)
      .attr('cx',32)
      .attr('cy',53)
      .attr('r',15);

    svg.selectAll('g')
      .data(dummyData)
      .enter()
    .append('g')
    .append('path')
      .attr('stroke','#000')
      .attr('d',`M5,5H500`)
}


example_1(); // This one creates DOM as: 

<svg>
  <g>
    <circle></circle>
    <circle></circle>
  </g>
</svg>

example_2(); // This one creates DOM as:

<svg>
  <g>
    <circle></circle>
    <circle></circle>
    <circle></circle>
    <circle></circle>
  </g>
</svg>

example_3(); // This one creates DOM as:

<svg>
  <g>
    <circle></circle>
    <circle></circle>
    <circle></circle>
    <circle></circle>
  </g>
</svg>

example_4(); // This one creates DOM as:

<svg>
  <g>
    <circle></circle>
    <circle></circle>
    <path></path>
    <path></path>
    <circle></circle>
    <circle></circle>
    <path></path>
    <path></path>
  </g>
</svg>

example_5(); // This one creates DOM as:

<svg>
  <g>
    <circle></circle>
    <circle></circle>
    <g>
      <path></path>
    </g>
    <g>
      <path></path>
    </g>
  </g>
</svg>

在我的示例中,变量 svg 是一个 dom 节点 g 作为 svg 的子节点。这意味着它既没有 circle 也没有 g 也没有 div 作为它的 child 。那么selectAll方法是用来做什么的呢?为什么我们不能写成

svg.data(dummyData)
  .enter()
.append('circle')
  .attr('transform', d => `translate(${d.age}, 20)`)
  .attr('cx',32).attr('cy',53)
  .attr('r',15);

虽然我尝试了更多不同的例子,但它们的行为都不同,但我真的看不到幕后发生了什么。 请帮助我至少理解一点。我很困惑。

最佳答案

让我们先回答你的最后一个问题。你为什么不能:

svg.data(dummyData).enter()

d3 使用进入、更新、退出模式。有大量关于该模式的读物,我强烈建议您研究它。但快速的是,它不仅仅是创建初始元素,还包括稍后更新它们。 .selectAll 是为我找到与我的选择器匹配的所有元素。 .data 是将数据绑定(bind)到它们。 .enter 是告诉我 在我的 .selectAll 中的所有数据元素。因此,如果没有 .selectAll,您的问题的答案就变成了,d3 无法计算哪些数据正在进入,也没有附加任何内容.在经典用法中,在初始渲染时,.selectAll 返回空(它们都不存在),所以它们都在进入。

现在,有了基础知识,让我们按照您的示例 5:

svg.selectAll('g') //<-- no gs exist in svg
  .data(dummyData)
  .enter() //<-- so, since data is two elements
.append('circle') //<-- you get two circles
  .attr('transform', d => `translate(${d.age}, 20)`)
  .attr('cx',32)
  .attr('cy',53)
  .attr('r',15);

svg.selectAll('g') //<-- no gs still exist in svg
  .data(dummyData)
  .enter() //<-- so since data is two elements
.append('g') //<-- you get two gs
.append('path') //<-- each with a path under them
  .attr('stroke','#000')
  .attr('d',`M5,5H500`)

现在,您所做的在这里没有任何意义。如果您关心圆圈,为什么选择 g?通常,我什至不建议选择元素。如果我想要一个圆来代表我的每个数据,我会这样做:

svg.selectAll('.my_cool_circle')
  .data(dummyData)
  .enter()
.append('circle')
.attr('class', 'my_cool_circle')
...

通过使用类,我知道那些圆圈代表我的dummyData。此外,如果我需要稍后回来更新我的圈子(因为我的数据现在是:

dummyData = [
     {
       name: 'A',
       age: 50
     },
     {
       name: 'B',
       age: 20
     },
     {
       name: 'C',
       age: 30
     }          
   ]

).

然后:

 svg.selectAll('.my_cool_circle')
  .data(dummyData)
  .enter()

只会返回“C”数据,我只会附加 1 个新圆。

关于javascript - 对 d3 的 selectAll 方法的好奇心,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51002070/

相关文章:

javascript - 在 Chrome 开发者工具中调试时分割 JavaScript 长度的任何方法

javascript - 跨浏览器javascript下载功能

javascript - 如何遍历html表格中的特定td列并替换innerText以更改日期格式?

javascript - 如何从 <table><tr><td></td></tr></table> 获取特定的 HTML 控件并将所选控件附加到同一个 <td> 中?

javascript - React 修改显示的值而不改变输入表单的值

javascript - AngularJS以不同的格式显示输入值,同时在模型中保持相同的格式

javascript - D3.js 在指定 nodeSize 时获取树的渲染尺寸

javascript - Javascript 导入的麻烦

javascript - D3 world-50m.json 投影填充错误

javascript - IE 因在闭包中引用 DOM 元素而导致内存泄漏?