这可能是一个愚蠢的问题,但我只是无法弄清楚何时在 D3 中使用 .join 以及何时使用 .append 。
就像在这个块中一样,我不知道为什么在这里使用 join 而不是追加。
elements.selectAll('circle')
.data(d=>d.values)
//.append('circle')
.join('circle')
.attr("class","dots")
.attr('r',2)
.attr('fill',d=>colorScale(d['track']))
.attr("cx", d=>dateScale(d['edate']))
.attr("cy", d=>valueScale(d['record_time']));
Run Code Online (Sandbox Code Playgroud)
有人能帮我理解吗?
And*_*eid 39
长话短说
\nSelection.append 本身只是将一个子元素附加到调用它的选择中的每个元素(继承其父元素的数据)。Selection.join() 与数据相关:它执行输入/更新/退出循环,以便 DOM 中匹配元素的数量与数据数组项的数量相匹配。
\n您拥有的代码表明您想要使用 .enter().append("circle") 而不是仅仅使用 .append("circle"):这完成了输入/更新/退出循环的 Enter() 部分,即也是通过使用.join()完成的。
\n您可以使用联接或单独的输入/退出/更新选择来实现相同的结果,联接只是一种方便,如文档中所述:
\n\n\n此方法是显式常规更新\n模式的便捷替代方案,替换了selection.enter、selection.exit、selection.append、\nselection.remove 和selection.order。(文档)
\n
输入/更新/退出
\n当您看到selectAll()后面的内容时,.data()我们正在选择所有匹配的元素,对于每个存在的元素,我们将数据数组中的一项绑定到它。使用 .data() 返回所谓的更新选择:它包含现有元素(如果有)以及绑定到这些现有项目的新提供的数据。
但是,如果所选元素的数量与 items\xc2\xb9 的数量不匹配,则 .data() 创建进入选择或退出选择。如果我们有多余的数据项,那么我们需要输入一个输入选择,其中每个需要添加的项都有一个元素,以便拥有相同数量的 DOM 元素和数据数组项。相反,如果我们有多余的 DOM 元素,那么我们就有一个退出选择。
\n在更新选择上调用 .enter() 返回输入选择。此选择包含占位符(“从概念上讲,输入选择 \xe2\x80\x99s 占位符是指向父元素文档的指针”),我们可以使用 .append("tagname") 来添加我们需要的元素。
\n相反,在更新选择上调用 .exit() 返回退出选择,通常可以使用 .exit().remove() 简单地删除它;
\n这种模式通常看起来像这样:
\n let circle = svg.selectAll("circle")\n .data([1,2,3,4])\n\n circle.exit().remove(); \n\n circle.enter()\n .append("circle") \n .attr...\n\n circle.attr(...\nRun Code Online (Sandbox Code Playgroud)\n首先我们选择所有的圆圈,假设有 2 个圆圈可供选择。
\n其次,我们使用 Selection.exit() 删除多余的元素:但是,由于我们有四个数据项并且只有两个匹配的 DOM 元素,因此没有任何内容可删除,因此选择为空,并且没有任何内容被删除。
\n第三,我们根据需要添加元素,以确保匹配的 DOM 元素的数量与数据数组项的数量相同。由于我们有四个数据项和只有两个匹配的 DOM 元素,因此输入选择包含两个占位符(指向父级的指针)。我们将圆圈附加到它们上并根据需要设置它们的样式。
\n最后,我们使用包含两个预先存在的圆圈的更新选择,并根据新数据根据需要设置它们的样式。
\n通常我们希望新元素和现有元素的样式相同,因此我们可以使用 merge 方法来组合输入和更新选择:
\n let circle = svg.selectAll("circle")\n .data([1,2,3,4])\n\n circle.exit().remove(); \n\n circle.enter()\n .append("circle") \n .merge(circle)\n .attr(...\nRun Code Online (Sandbox Code Playgroud)\n这稍微简化了我们的代码,因为我们不需要分别为输入和更新重复样式。
\n加入
\n那么 .join() 从哪里来呢?是为了方便。最简单的形式: .join("elementTagName") .join 将上面的代码替换为:
\n let circle = svg.selectAll("circle")\n .data([1,2,3,4])\n .join("circle")\n .attr(...\nRun Code Online (Sandbox Code Playgroud)\n这里 join 方法删除了退出选择并返回合并的更新选择和输入选择(包含新圆圈),我们现在可以根据需要设置样式。它本质上是一种速记方法,允许您编写更简洁的代码,但在功能上与第二个代码块相同\xc2\xb2。
\n你的代码
\n在您的代码中,您可以选择一个或多个元素(一个/某些父元素)。绑定数据包含一个子数组,您希望使用它来创建子元素。为此,您需要向 .data() 提供该子数据数组:
\nparentElements.selectAll("circle")\n .data(d=>d.values)\nRun Code Online (Sandbox Code Playgroud)\n您可以跟进.join():这将为每个父元素执行输入/更新/退出循环,以便它们每个都有适当数量的圆圈并返回所有这些圆圈的选择。
您不能只使用 .append(),因为这会将一个圆圈附加到每个父元素,并返回这些圆圈的选择。这不太可能是期望的结果。
\n相反,正如本答案顶部所述,您可以使用 .enter().append("circle") 以便正确使用该模式。
\n如果您创建元素一次并且从不更新数据,则只需要输入选择,否则,您将需要使用输入/更新/退出方法来处理多余的元素、多余的数据项和更新的元素。
\n最终,连接和输入/更新/退出之间的区别是代码偏好、风格、简洁性的问题,但除此之外,没有什么是你不能用其中一个做而不能用另一个做的。
\n\xc2\xb9 假设只向 .data() 提供一个参数 - 第二个可选参数是一个键控函数,它根据键匹配 DOM 元素和数据项。没有匹配数据的 DOM 元素放置在退出选择中,没有匹配 DOM 元素的数据数组项放置在输入选择中,其余的放置在更新选择中。
\n\xc2\xb2 假设 .join() 没有提供第二个或第三个参数,这允许更精细地控制进入/退出/更新周期。
\n