Search code examples
d3.jslinegraph

Remove old points from line graph in d3


I am trying to create a multi-line graph and updating the data on button click. On each line, I want to highlight the intersection point with a cirlce. Now on button click, I was able to update the line path, but the old highlighted intersection points are not removed from the svgContainer(for eaxmple clicking update2 then update1 do not removes the last set of circles which are not connected to any line).

 <input type="button" onclick="update1()" value="Update" />
  <input type="button" onclick="update2()" value="UpdateDimension_T" />
  <div id="outputViz">

  </div>


<script type="text/javascript">    
var data =  [
    [{'index':1,'score':0},{'index':2,'score':5},{'index':3,'score':10},{'index':4,'score':0},{'index':5,'score':6}],
    [{'index':1,'score':1},{'index':2,'score':6},{'index':3,'score':11},{'index':4,'score':1},{'index':5,'score':7}],
    [{'index':1,'score':2},{'index':2,'score':7},{'index':3,'score':12},{'index':4,'score':2},{'index':5,'score':8}],
    [{'index':1,'score':3},{'index':2,'score':8},{'index':3,'score':13},{'index':4,'score':3},{'index':5,'score':9}],
    [{'index':1,'score':4},{'index':2,'score':9},{'index':3,'score':14},{'index':4,'score':4},{'index':5,'score':10}]
  ];

  var data_O =  [
    [{'index':1,'score':1},{'index':2,'score':6},{'index':3,'score':11},{'index':4,'score':1},{'index':5,'score':7},{'index':6,'score':12}],
    [{'index':1,'score':2},{'index':2,'score':7},{'index':3,'score':12},{'index':4,'score':2},{'index':5,'score':8},{'index':6,'score':13}],
    [{'index':1,'score':3},{'index':2,'score':8},{'index':3,'score':13},{'index':4,'score':3},{'index':5,'score':9},{'index':6,'score':14}],
    [{'index':1,'score':4},{'index':2,'score':9},{'index':3,'score':14},{'index':4,'score':4},{'index':5,'score':10},{'index':6,'score':15}],
    [{'index':1,'score':5},{'index':2,'score':10},{'index':3,'score':15},{'index':4,'score':5},{'index':5,'score':11},{'index':6,'score':16}]
  ];

  var data_T =  [
    [{'index':1,'score':5},{'index':2,'score':10},{'index':3,'score':15},{'index':4,'score':5},{'index':5,'score':12},{'index':6,'score':20},{'index':7,'score':15}],
    [{'index':1,'score':6},{'index':2,'score':11},{'index':3,'score':16},{'index':4,'score':6},{'index':5,'score':13},{'index':6,'score':21},{'index':7,'score':16}],
    [{'index':1,'score':7},{'index':2,'score':12},{'index':3,'score':17},{'index':4,'score':7},{'index':5,'score':14},{'index':6,'score':22},{'index':7,'score':17}],
    [{'index':1,'score':8},{'index':2,'score':13},{'index':3,'score':18},{'index':4,'score':8},{'index':5,'score':15},{'index':6,'score':23},{'index':7,'score':18}],
    [{'index':1,'score':9},{'index':2,'score':14},{'index':3,'score':19},{'index':4,'score':9},{'index':5,'score':16},{'index':6,'score':24},{'index':7,'score':19}]
  ];

  var colors = [
    'steelblue',
    'green',
    'red',
    'purple',
    'black'
  ];
  var dataset = ["","Or","Se","Tr","De","Cc"];
  var dataset_O = ["","O_1","O_2","O_3","O_4","O_5","O_6"];
  var dataset_T = ["","T_1","T_2","T_3","T_4","T_5","T_6","T_7"];


  var margin = {top: 20, right: 30, bottom: 30, left: 50},
      width = 960 - margin.left - margin.right,
      height = 500 - margin.top - margin.bottom,
      padding = 30;

  var x = d3.scale.linear()
                  .domain([0, dataset.length])
                  .range([0, width]);

  var y = d3.scale.linear()
                  .domain([-1, 16])
                  .range([height, 0]);

  var xAxis = d3.svg.axis()
                    .scale(x)
                    .tickFormat(function(d) { return dataset[d]; })
                    .tickSize(-height)
                    .tickPadding(10)  
                    .tickSubdivide(false)  
                    .orient("bottom");  

  var yAxis = d3.svg.axis()
                    .scale(y)
                    .tickPadding(10)
                    .tickSize(-width)
                    .tickSubdivide(false)  
                    .orient("left");


    var svg = d3.select("body").append("svg")
                              .attr("width", width + margin.left + margin.right)
                              .attr("height", height + margin.top + margin.bottom)
                              .append("g")
                              .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);

    svg.append("g")
        .attr("class", "y axis")
        .call(yAxis);

    svg.append("g")
      .attr("class", "y axis")
      .append("text")
      .attr("class", "axis-label")
      .attr("transform", "rotate(-90)")
      .attr("y", (-margin.left) + 10)
      .attr("x", -height/2)
      .text('Axis Label');  

    svg.append("clipPath")
      .attr("id", "clip")
      .append("rect")
      .attr("width", width)
      .attr("height", height);

    var line = d3.svg.line()
                    .interpolate("linear")  
                    .x(function(d) { return x(d.index); })
                    .y(function(d) { return y(d.score); });   

    svg.selectAll('.line')
        .data(data)
        .enter()
        .append("path")
        .attr("class", "line")
        .attr('stroke', function(d,i){      
          return colors[i%colors.length];
        })
        .attr("d", line);

    var points = svg.selectAll('.dots')
                    .data(data)
                    .enter()
                    .append("g")
                    .attr("class", "dots")

    points.selectAll('.dot')
        .data(function(d, index){     
          var a = [];
          d.forEach(function(point,i){
            a.push({'index': index, 'point': point});
          });   
          return a;
        })
        .enter()
        .append('circle')
        .attr('class','dot')
        .attr("r", 2.5)
        .attr('fill', function(d,i){  
          return colors[d.index%colors.length];
        })  
        .attr("transform", function(d) { 
          return "translate(" + x(d.point.index) + "," + y(d.point.score) + ")"; }
        );

    function update1(){
      var x = d3.scale.linear()
                      .domain([0, dataset_O.length])
                      .range([0, width]);

      var y = d3.scale.linear()
                      .domain([-1, 16])
                      .range([height, 0]).nice();

      xAxis = d3.svg.axis()
                    .scale(x)
                    .tickFormat(function(d) { return dataset_O[d]; })
                    .tickSize(-height)
                    .tickPadding(10)  
                    .tickSubdivide(false)  
                    .orient("bottom"); 

      yAxis = d3.svg.axis()
                    .scale(y)
                    .tickPadding(10)
                    .tickSize(-width)
                    .tickSubdivide(false)  
                    .orient("left");

      var line = d3.svg.line()
                      .interpolate("linear")  
                      .x(function(d) { return x(d.index); })
                      .y(function(d) { return y(d.score); }); 

      svg.selectAll('.line')
        .data(data_O)
        .transition(750)
        .attr("d", line)
        .attr("class", "line");



      // change the x axis
      svg.select(".x.axis").call(xAxis);

      // change the y axis
      svg.select(".y.axis").call(yAxis);

          var points = svg.selectAll('.dots').data(data_O); 

          //UPDATE - HANDLE the current count
          points.selectAll('.dot')
                .data(function(d, index){     
                  var a = [];
                  d.forEach(function(point,i){
                    a.push({'index': index, 'point': point});
                  });   
                  return a;
                })
                .attr("transform", function(d) { 
                  return "translate(" + x(d.point.index) + "," + y(d.point.score) + ")"; 
                });



          //ENTER - add the newly added count
          points.selectAll('.dot')
                .data(function(d, index){     
                  var a = [];
                  d.forEach(function(point,i){
                    a.push({'index': index, 'point': point});
                  });   
                  return a;
                })
                .enter()
                .append('circle')
                .attr('class','dot')
                .attr("r", 2.5)
                .attr('fill', function(d,i){  
                  return colors[d.index%colors.length];
                })  
                .attr("transform", function(d) { 
                  return "translate(" + x(d.point.index) + "," + y(d.point.score) + ")"; 
                });


           d3.selectAll('g.dots').data(data_O).exit().remove();



    }

    function update2(){
      var x = d3.scale.linear()
                      .domain([0, dataset_T.length])
                      .range([0, width]);

      //var yExtents = d3.extent(d3.merge(data_T), function (d) { return d.score; });
      var y = d3.scale.linear()
                      .domain([-1, 29])
                      .range([height, 0]).nice();

      xAxis = d3.svg.axis()
                    .scale(x)
                    .tickFormat(function(d) { return dataset_T[d]; });

      var line = d3.svg.line()
                      .interpolate("linear")
                      .x(function(d) { return x(d.index); })
                      .y(function(d) { return y(d.score); });

      svg.selectAll('.line')
          .data(data_T)
          .transition(750)
          .attr("d", line)
          .attr("class", "line");

      svg.select(".x.axis").call(xAxis);

      svg.select(".y.axis").call(yAxis);

      var points = svg.selectAll('.dots').data(data_T);

      //ENTER - add the newly added count
      points.selectAll('.dot')
            .data(function(d, index){     
              var a = [];
              d.forEach(function(point,i){
                a.push({'index': index, 'point': point});
              });   
              return a;
            })
            .enter()
            .append('circle')
            .attr('class','dot')
            .attr("r", 2.5)
            .attr('fill', function(d,i){  
              return colors[d.index%colors.length];
            })  
            .attr("transform", function(d) { 
              return "translate(" + x(d.point.index) + "," + y(d.point.score) + ")"; 
            });


      //UPDATE - HANDLE the current count
      points.selectAll('.dot')
            .data(function(d, index){     
              var a = [];
              d.forEach(function(point,i){
                a.push({'index': index, 'point': point});
              });   
              return a;
            })
            .attr("transform", function(d) { 
              return "translate(" + x(d.point.index) + "," + y(d.point.score) + ")"; 
            });
    }
</script>

Here is the link to fiddle: https://jsfiddle.net/aakashjain/1dc57aL7/1/


Solution

  • You'll need an "exit" selection:

    points.selectAll('.dot')
        .data(function(d, index){     
            var a = [];
            d.forEach(function(point,i){
                a.push({'index': index, 'point': point});
            });   
                return a;
            })
        .exit()
        .remove();
    

    Here is the update fiddle:. https://jsfiddle.net/1dc57aL7/2/

    (Just a tip: you have a lot of duplicated code here. Your "update1" and "update2" functions could be way smaller)