简体   繁体   中英

D3 exit mutli-series line chart labels on transition

I'm trying to create a multi-series line chart (based off the Mike Bostock example) but transitioning between multiple data sets. I have gotten the lines to transition in and out, but the labels for each line stick around after they should have disappeared. Screenshot at this link.

Furthermore, the lines are transitioning in an odd way; almost like they are just extending the same line to create a new one (Second screenshot at this link) .

Here is the relevant part of my code (where I draw the lines and add labels):

  var line = d3.svg.line() .interpolate("basis") .x(function(d) { return x(d.Date); }) .y(function(d) { return y(d.candidate); }); var person = svg_multi.selectAll(".candidate") .data(people); var personGroups = person.enter() .append("g") .attr("class", "candidate"); person .enter().append("g") .attr("class", "candidate"); personGroups.append("path") .attr("class", "line") .attr("d", function(d) { return line(d.values); }) .style("stroke", function(d) { return color(d.name); }); var personUpdate = d3.transition(person); personUpdate.select("path") .transition() .duration(1000) .attr("d", function(d) { return line(d.values); }); person .append("text") .datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; }) .attr("transform", function(d) { return "translate(" + x(d.value.Date) + "," + y(d.value.candidate) + ")"; }) .attr("x", 3) .attr("dy", ".35em") .text(function(d) { return d.name; }); person.selectAll("text").transition() .attr("transform", function(d) { return "translate(" + x(d.value.Date) + "," + y(d.value.candidate) + ")"; }); person.exit().remove(); 

You are appending a new text element to each person every time you render and not removing the old ones. Presumably the code you posted gets run every time you want to draw the chart with new data, so you end up with more text elements every time, rather than updating the existing ones. You need to only append on the "enter" selection. You did this right on the path elements, so you just need to make the text work more like the path. I've updated your example with comments to highlight what I changed.

 var line = d3.svg.line() .interpolate("basis") .x(function(d) { return x(d.Date); }) .y(function(d) { return y(d.candidate); }); var person = svg_multi.selectAll(".candidate") // I added a key function here to make sure you always update the same // line for every person. This ensures that when you re draw with different // data, the line for Trump doesn't become the line for Sanders, for example. .data(people, function(d) { return d.name}); var personGroups = person.enter() .append("g") .attr("class", "candidate"); // This isn't needed. The path and text get appended to the group above, // so this one just sits empty and clutters the DOM // person // .enter().append("g") // .attr("class", "candidate"); personGroups.append("path") .attr("class", "line") // You do this down below, so no need to duplicate it here // .attr("d", function(d) { return line(d.values); }) .style("stroke", function(d) { return color(d.name); }); // Append the text element to only new groups in the enter selection personGroups.append("text") // Set any static attributes here that don't update on data .attr("x", 3) .attr("dy", ".35em"); var personUpdate = d3.transition(person); personUpdate.select("path") .transition() .duration(1000) .attr("d", function(d) { return line(d.values); }); person.select("text") // You don't have to do this datum call because the text element will have // the same data as its parent, but it does make it easier to get to the last // value in the list, so you can do it if you want .datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; }) .attr("transform", function(d) { return "translate(" + x(d.value.Date) + "," + y(d.value.candidate) + ")"; }) .text(function(d) { return d.name; }); // Remove this. You don't need it anymore since you are updating the text above // person.selectAll("text").transition() // .attr("transform", function(d) { return "translate(" + x(d.value.Date) + "," + y(d.value.candidate) + ")"; }); person.exit().remove(); 

The key to your question was really just doing personGroups.append('text') rather than person.append('text') . The rest was just me going overboard and pointing out some other ways to improve your code that should make it easier to understand and maintain.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM