为什么此D3 exit()代码无法按预期工作?

时间:2019-02-21 18:39:50

标签: d3.js

运行下面的代码段。

按“ update1”-出现圆圈。

按“ update2”-没有明显变化。

在update2上,我希望将(左上)黑色圆圈删除,并在另一端(右下方)替换为圆圈-因为函数dataKey返回的键值对于修改后的数据为“ 140:140”原始数据为“ 20:20”。也就是说,不再有与​​“ 20:20”相对应的数据项,并且有一个键为“ 140:140”的新项。

编辑

自从第一次发布以来,我注意到,如果我替换对象而不是修改对象的属性,那么事情将按预期工作。(使用按钮update1B和update2B)..尽管这仍然需要dataKey函数。

let svg = null ;
let space = 20 ;
let dataIndex = 1 ;
let data = [
    {x:space*dataIndex,y:space*dataIndex++,c:"black"},
    {x:space*dataIndex,y:space*dataIndex++,c:"green"},
    {x:space*dataIndex,y:space*dataIndex++,c:"blue"},
    {x:space*dataIndex,y:space*dataIndex++,c:"red"},
    {x:space*dataIndex,y:space*dataIndex++,c:"yellow"},
    {x:space*dataIndex,y:space*dataIndex++,c:"purple"}
];

function init(){
    svg = document.getElementById("svg") ;
    svg.setAttribute("height",`${space*dataIndex+10}px`);
    svg.setAttribute("width",`${space*dataIndex+10}px`);     
 }
function dataKey(d){
  // console.log(`${d.x}:${d.y}`);
   return `${d.x}:${d.y}`;
}

function updateData(data){
    
    let d3svg = d3.select(svg);

    let nodes = d3svg.selectAll(".node").data(data,dataKey) ;
    
    nodes
    .enter()
    .append("circle")
    .attr("cx",function(d){ return d.x; })
    .attr("cy",function(d){ return d.y; })
    .attr("r","10px")
    .attr("class","node")
    .attr("fill",function(d){ return d.c; });
    
    nodes.exit().remove();
    
}

function update1(){
    data[0].x = space;
    data[0].y = space;    
    updateData(data) ;
}
function update2(){
    data[0].x = space*dataIndex;
    data[0].y = space*dataIndex;
    updateData(data) ;
}

function update1B(){
    data[0] = {x:space,y:space};   
    updateData(data) ;
}
function update2B(){
    data[0] = {x:space*dataIndex,y:space*dataIndex};
    updateData(data) ;
}


onload = init ;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg id="svg"></svg>
<button onclick="update1();">update1</button>
<button onclick="update2();">update2</button>
<button onclick="update1B();">update1B</button>
<button onclick="update2B();">update2B</button>

2 个答案:

答案 0 :(得分:0)

关于update1 / update2-数据由索引绑定(如果未指定键),并且由于数据数组的长度没有减少,因此出口选择为空(事实上,enter更新时的选择也应为空,因为数据长度也未增加)。取而代之的是,您现有的元素更改了数据,但是您根本不对它做出反应(即您对nodes.enter()进行了某些操作,对nodes.exit()进行了某些操作,而仅对nodes进行了更改)

通常,您只希望对append选择使用styleenter(并且可选地,如果使用过渡,则是其他属性的起始值),然后合并在更新新数据的属性之前,将其与update选择一起使用。

这是您的代码,固定了updateData函数

let svg = null ;
let space = 20 ;
let dataIndex = 1 ;
let data = [
    {x:space*dataIndex,y:space*dataIndex++,c:"black"},
    {x:space*dataIndex,y:space*dataIndex++,c:"green"},
    {x:space*dataIndex,y:space*dataIndex++,c:"blue"},
    {x:space*dataIndex,y:space*dataIndex++,c:"red"},
    {x:space*dataIndex,y:space*dataIndex++,c:"yellow"},
    {x:space*dataIndex,y:space*dataIndex++,c:"purple"}
];

function init(){
    svg = document.getElementById("svg") ;
    svg.setAttribute("height",`${space*dataIndex+10}px`);
    svg.setAttribute("width",`${space*dataIndex+10}px`);     
 }
function dataKey(d){
  // console.log(`${d.x}:${d.y}`);
   return `${d.x}:${d.y}`;
}

function updateData(data){
    
    let d3svg = d3.select(svg);

    let nodes = d3svg.selectAll(".node").data(data,dataKey) ;
    
    nodes
    .enter()
    .append("circle")
    .attr("class","node")
    .merge(nodes)
    .attr("cx",function(d){ return d.x; })
    .attr("cy",function(d){ return d.y; })
    .attr("r","10px")
    .attr("fill",function(d){ return d.c; });
    
    nodes.exit().remove();
    
}

function update1(){
    data[0].x = space;
    data[0].y = space;    
    updateData(data) ;
}
function update2(){
    data[0].x = space*dataIndex;
    data[0].y = space*dataIndex;
    updateData(data) ;
}

function update1B(){
    data[0] = {x:space,y:space};   
    updateData(data) ;
}
function update2B(){
    data[0] = {x:space*dataIndex,y:space*dataIndex};
    updateData(data) ;
}


onload = init ;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg id="svg"></svg>
<button onclick="update1();">update1</button>
<button onclick="update2();">update2</button>
<button onclick="update1B();">update1B</button>
<button onclick="update2B();">update2B</button>

答案 1 :(得分:0)

我的困惑在于我期望按键如何工作;

我曾想过,自上次连接以来其键已更改的对象将被标识为新数据。

至关重要的是,在连接时计算出的键值不会存储在以后的连接中以进行比较;

在连接时计算得出的键值不存储/不可用于后续连接中的比较,因此不会将同一对象标识为新数据或已删除数据。