对 d3 的 selectAll 方法的好奇心
curiosity on d3's selectAll method
我想了解 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 的 child。这意味着它既没有圆也没有 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
是为我找到与我的 select 匹配的所有元素。 .data
是将数据绑定到它们。 .enter
是告诉我在我的 .selectAll
中 不是 的所有数据元素。因此,如果没有 .selectAll
,您的问题的答案就变成了 d3
无法计算哪些数据正在 进入 并且没有附加任何内容。在 classic 用法中,在初始渲染时,.selectAll
return 是空的(none 还存在),所以它们都在进入。
现在,有了基础知识,让我们按照您的示例 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`)
现在,您所做的在这里没有任何意义。如果您关心圈子,为什么 select g
?通常,我什至根本不会推荐 selecting 元素。如果我想要一个 圆来代表我的每个数据 ,我会这样做:
svg.selectAll('.my_cool_circle')
.data(dummyData)
.enter()
.append('circle')
.attr('class', 'my_cool_circle')
...
通过使用 class,我知道那些圆圈代表我的 dummyData
。另外,如果我需要稍后回来更新我的圈子(因为我的数据现在是:
dummyData = [
{
name: 'A',
age: 50
},
{
name: 'B',
age: 20
},
{
name: 'C',
age: 30
}
]
).
然后:
svg.selectAll('.my_cool_circle')
.data(dummyData)
.enter()
只会 return 'C' 数据,我只会追加 1 个新圆。
我想了解 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 的 child。这意味着它既没有圆也没有 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
是为我找到与我的 select 匹配的所有元素。 .data
是将数据绑定到它们。 .enter
是告诉我在我的 .selectAll
中 不是 的所有数据元素。因此,如果没有 .selectAll
,您的问题的答案就变成了 d3
无法计算哪些数据正在 进入 并且没有附加任何内容。在 classic 用法中,在初始渲染时,.selectAll
return 是空的(none 还存在),所以它们都在进入。
现在,有了基础知识,让我们按照您的示例 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`)
现在,您所做的在这里没有任何意义。如果您关心圈子,为什么 select g
?通常,我什至根本不会推荐 selecting 元素。如果我想要一个 圆来代表我的每个数据 ,我会这样做:
svg.selectAll('.my_cool_circle')
.data(dummyData)
.enter()
.append('circle')
.attr('class', 'my_cool_circle')
...
通过使用 class,我知道那些圆圈代表我的 dummyData
。另外,如果我需要稍后回来更新我的圈子(因为我的数据现在是:
dummyData = [
{
name: 'A',
age: 50
},
{
name: 'B',
age: 20
},
{
name: 'C',
age: 30
}
]
).
然后:
svg.selectAll('.my_cool_circle')
.data(dummyData)
.enter()
只会 return 'C' 数据,我只会追加 1 个新圆。