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。
还有更好的解决方案吗?或者正在进行中的功能?
虽然 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>
不幸的是,您无法按照您想要的方式将函数传递给
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
) 实际上是一个字符串。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)
我能想到的唯一缺点是你需要硬编码属性名称。