如何通过javascript(d3)更改节点CSS的样式

时间:2014-11-20 15:19:50

标签: javascript html css node.js d3.js

这是我的css

.node.selectedNode {
    width:50px;
    height:50px;
    stroke-width: 3px;
    stroke: #f00;
}

.node.unselectedNode {
    width:35px;
    height:35px;
    stroke-width: 3px;
    stroke: #000;
}

我想单击选中的节点(为其选择属性),再次单击并取消选中。 这是一段代码,我检查一个节点的id是否在一个数组中,如果它不是它也添加它,让我能够轻松打印出选定的节点。

if(selectedNodesArray.indexOf(d.coreId)==-1){
       selectedNodesArray.push(d.coreId);
       d.selectedNode = true; //change style
       d.unselectedNode = false;
       d3.select(this).classed("selectedNode", true);
       console.log("clicked");
}else{
        selectedNodesArray.pop(d.coreId);
        d.unselectedNode = true;
        d.selectedNode = false;
        d3.select(this).classed("unselectedNode", true);
        console.log("pulled"); 
}

你可能会注意到我尝试改变风格。现在这工作了两次;当我选择它并取消选择它时。之后它再也不起作用了。有什么想法吗?

此外,我创建了一个按钮,以便清除所有突出显示的节点。就像我在这里,当我点击节点时,它将其属性更改为固定

function dragstart(d) {
  d.fixed = true;
  d3.select(this).classed("fixed", true);

我想在单击按钮时执行此操作,因此它会更改节点的css属性。我不知道如何挑选出所有的节点,并给它们所有相同的css属性,而不是通过D3并通过这种方式改变.style。

对于这篇冗长的文章,我很抱歉,我希望尽可能多地为您提供详细信息,让每个人都更轻松。

1 个答案:

答案 0 :(得分:1)

看起来你做得差不多了。问题是您的代码假定在节点上不调用.classed("selectedNode", true)会导致它没有应用.selectedNode类。但实际上(并且您可以在浏览器的“元素”面板中看到此内容),如果您取消选择某个节点,它将会.unselectedNode,但也会.selectedNode,因为没有删除.selectedNode类。并且,任何后续的选择/取消选择都不会再修改内容,因为该节点已经有两个类。

因此,您需要在每次交互发生时删除不适用的类,方法是调用

d3.select(this).classed("selectedNode", false);

d3.select(this).classed("unselectedNode", false);

在适当的地方。而那将会奏效。

但现在你有机会重构一些事情。

首先,我的建议是完全忘记unselectedNode类,只使用类.node来设置取消选择状态的样式。这样,您只有一个selectedNode类,它是默认样式的修饰符,并且您的代码将更加简单。

最后,我猜测你正在使用强制布局,所以你已经有了tick()函数或类似的东西,它每秒都会多次更新所有节点。所以,把它们放在一起,这就是你如何从这个方法中做到的一切:

var selectedNodesArray = [];

function tick() {
  var nodes = d3.selectAll('.node').data(force.nodes);

  // ENTER
  nodes.enter()
    .append('circle')// or 'rect' or whatever
    .attr('class', 'node')
    .on('click', function(d) {
      // here you set the selected-ness, but not the visual representation

      d.selectedNode = !d.selectedNode; // flip the selected-ness from true->false or vice versa
      console.log(d.selectedNode ? "clicked" : "pulled");

      // here you manage the array
      if(!d.selectedNode) {
        // note that pop() is actually unsafe here, bc it
        // removes the last-selected node, but what if you
        // deselect something from 2 clicks ago?
        selectedNodesArray.pop();
      }
      else {
        // Here I recommend pushing d, instead of its d.coreId, because
        // then you can use this array to get the actual datums of the 
        // selectedNodes, rather than just their id
        selectedNodesArray.push(d);
      }
    })
    ...// do whatever else you need to do to the entering nodes

  // UPDATE
  nodes
    // Here you take care of the representation of selected-ness
    .classed('selectedNode', function(d) {
      return d.selectedNode
      // or: return selectedNodesArray.indexOf(d)==-1
    })
    // do whatever else you need to do to the updating nodes (position, etc)
}

关于你的第二个问题:我不确定你究竟在那里问什么,但我认为如果你认为你的tick()方法是基于a更新表示的东西纯数据模型或状态(例如selectedNodesArrayd.selectedNode),然后任何交互都可以修改该状态,让tick()使表示更快。例如,这是取消选择所有内容的大量方法:

// loop through the array and set selected to `false`
selectedNodesArray.forEach(function(d) {
  d.selectedNode = false;
})
selectedNodesArray = [];// clear the array
tick();// To update the visuals

最后一件事:在2个地方(selectedNodesArrayd.selectedNode)维护有关选定信息的信息有点奇怪。如果您可以选择并使用这两种方式中的一种来表示选择性,那么您将更容易向前发展。