D3.js:添加基于数据名称的类

问题描述 投票:0回答:3

D3 有 2 种设置类别的方法:

selection.attr("class", (d) => d.isFoo ? "foo" : "");
selection.classed("foo", (d) => d.isFoo)

我正在寻找一种方法来添加根据数据命名的类。 IE。像这样的东西:

selection.classed((d) => "node" + d.id, true);
  • 我不想使用
    id
    ,因为多个 DOM 元素将共享该类。
  • 我不想使用
    attr
    ,因为它可能会覆盖已有的其他类。

所以我可以做

selection.attr("class", (d) => d3.select(this).attr("class") + " node" + d.id);

感觉有点hackish。

还有更好的解决方案吗?或者正在进行中的功能?

javascript css d3.js
3个回答
9
投票

虽然 Gerardo 的 answer 是正确的,并且有正确的解释,但我认为,搞乱元素的类的最简单方法是求助于它从

classList
接口继承的
Element
属性。

Element.classList
是一个只读属性,它返回元素的类属性的实时
DOMTokenList
集合。

虽然属性本身是只读的,但它提供了用于添加、删除和切换类的方法。调用

.add()
将自动处理先前设置的类,并检查要添加的类是否已分配给元素:

添加(字符串[,字符串])

添加指定的类值。如果这些类已经存在于元素的属性中,那么它们将被忽略。

因此可以使用

selection.each()
:

来添加基于数据的类
divs.each(function(d) {
  this.classList.add("node" + d.name);
});

.each()
方法中,
this
指向实际元素,可用于直接访问
classList
属性。

var data = [{name: "foo"}, 
            {name: "bar"},
            {name: "baz"}];

var body = d3.select("body");

var divs = body.selectAll("myDivs")
    .data(data)
    .enter()
    .append("div")
      .attr("class", "someClass");

divs.each(function(d) {
  this.classList.add("node" + d.name);
});

//log the classes, you can see the previous class ("someClass") was not overwritten:
divs.each(function() {
    console.log(d3.select(this).attr("class"))
})
<script src="https://d3js.org/d3.v4.min.js"></script>


5
投票

不幸的是,您无法按照您想要的方式将函数传递给

classed()
(即作为第一个参数)。 文档清楚地说明了
classed()

选择.classed(名称[,值])

如果指定了值,则通过设置 class 属性或修改 classList 属性在所选元素上分配或取消分配指定的 CSS 类名称,并返回此选择。指定的名称是由空格分隔的类名的string。 (强调我的)

因此,您不能将函数传递给类名。那里的类名必须是固定

话虽如此,您现在正在做的事情是添加一个类名而不覆盖以前存在的类...

selection.attr("class", (d) => d3.select(this).attr("class") + " node" + d.id);

...似乎是 D3 编码人员的标准方式,即使您觉得这是一种 hack。但是,您需要在这里进行一些小修改:不要将

this
与箭头函数一起使用,它不会起作用(检查此示例 d3 v4 当 `this` 不可用时从拖动回调中检索拖动 DOM 目标)。因此,这是正确的片段:

selection.attr("class", function (d){
    d3.select(this).attr("class") + " node" + d.id);
});

但是,可以按照您想要的方式使用

classed()
,不是直接使用(同样,您不能将函数传递给它),而是以一种奇怪的解决方法。只是为了向您展示一种使用
classed()
实现此操作的方法,出于好奇,我创建了一个比您的解决方案“更黑客”的演示代码。看看吧:

var data = [{name: "foo"}, {name: "bar"}, {name: "baz"}]; var body = d3.select("body"); var divs = body.selectAll("myDivs") .data(data) .enter() .append("div") .attr("class", "someClass"); //here comes the hack: divs.each(function(d) { d3.select(this).classed("node" + d.name, () => d3.select(this).datum().name == d.name) }); //log the classes, you can see the previous class ("someClass") was not overwritten: divs.each(function() { console.log(d3.select(this).attr("class")) })
<script src="https://d3js.org/d3.v4.min.js"></script>

由于

d.name

是由

each()
函数提供的,因此第一个参数 (
"node" + d.name
) 实际上是一个字符串。
    


0
投票
document.styleSheets

添加类,但这也很hacky并且是一个罕见的用例)。

所以,不要尝试做类似的事情:

.cls-type-1 { ... } .cls-type-2 { ... }

selection.classed(d => d.className, true)
我最终通过属性选择器切换类,所以 js 部分很简单。

*[someAttr="cls-type-1"] { ... } *[someAttr="cls-type-2"] { ... }

selection.attr("someAttr", d => d.className)
我能想到的唯一缺点是你需要硬编码属性名称。

© www.soinside.com 2019 - 2024. All rights reserved.