d3.js节点没有正常退出

时间:2017-03-21 16:20:36

标签: jquery d3.js dataset

所以这里。我花了很多时间在这一个 - 我真的很累 - 所以希望愚蠢的事情没有让我失望。

我正在使用数据集在图表中创建几行。之后,使用图例我想隐藏线条同时也改变数据集 - 所以改变不透明度不会削减它。

我按照在数据集中的每个对象上添加了一个键的路径,并将其设置为false,以便能够过滤要隐藏的对象。但是,它无法按预期工作,因为我无法正确退出()行。完全删除线条并再次绑定数据集可以完成工作,但会将线条颜色映射到图例项目。

问题出在redraw()函数中。

希望有人能够理清这场噩梦!

https://jsfiddle.net/2en21Lqh/2/

我创造了一个小提琴

2 个答案:

答案 0 :(得分:1)

我理解你的感受......我希望这个答案能帮到你。

您的代码中存在一些问题,因此我修复了主要的问题。 请记住,在我的版本中仍有很多可以改进的版本,直到让你做得更好。

我将通过一些评论来更新答案,详细说明我对如何处理这段代码的想法以及指向有用文章的链接或在此处回答。

同时检查此fiddle和以下代码

// .. code above is unchanged 
line = d3.line()
  .x(function(d) {
    return x(d.date);
  }).y(function(d) {
    return y(d.value);
  });

// notice I deleted the part where you created the groups and the path the first time 
// You don't need it anymore

var legendItem = d3.select(".legend")
  .selectAll("li")
  .data(dataGroup)
  .enter()
  .append("li")
  .on('click', function(d) {
    if (d.enabled) {
      d.enabled = false;
    } else {
      d.enabled = true;
    }
    redraw();
  });

legendItem
  .append("span")
  .attr("class","color-square")
  .style("background", function(d, i) {
    // This is basically a hack for this demo 
    // You should find a better way to assign an index to a color
    var idx = +d.key.slice(-1);  
    return colorScale(idx - 1);
  });

legendItem
  .append("span")
  .text(function(d) {
    return (d.values[0].name)
  });

redraw = function() {

  y.domain([
    d3.min(dataGroup, function(c) {
      return d3.min(c.values, function(d) {
        return d.value;
      });
    }),
    // In the demo I don't rescale after the datasets change, so no need to filter
    // by enabled sets. If you want the max to be calculated only for enabled items
    // you will also have to redraw the axis, otherwise you will display incorrect data.
    d3.max(dataGroup, function(c) { 
      return d3.max(c.values, function(d) {
        return d.value;
      });
    })
  ]);

  // Notice that i filter the data passed to the .data() function,
  // not after it.
  lines = svg.selectAll("g.d3-group")
    .data(dataGroup.filter(d => d.enabled))

  // This is the Enter + update selection
  var linesEnter = lines
    .enter()
    .append("g")
    .merge(lines)
    .attr("class", "d3-group")

  // We want the data to go from the <g> to its child, the <path> element
  // To do so, we need to rebind the data.
  var pathJoin = linesEnter
    .selectAll('path')
    .data(d => [d]) // <= this is very important

  // Enter the path and update it
  var pathEnter = pathJoin
    .enter()
    .append("path")
    .attr("class", "d3-line")
    .merge(pathJoin)    
    .attr('d', function(d) {
      return line(d.values);
    })
    // Notice how "i" is not used. If you log it you will see it is always 0
    // as it is always the first child of its parent, the <g class="group">
    .style("stroke", function(d, i) {
      var idx = +d.key.slice(-1);
      return colorScale(idx - 1);
    });


    lines.exit().remove(); 

}

redraw();

如果您按照输入,更新,退出模式进行操作,则不需要使用两种不同的功能来绘制和更新图表。操作将是相同的,因此这意味着更少的代码重复。 因此,我删除了您在页面加载时绘制它们的第一部分

var myline = svg.selectAll(".d3-group")...

要做到的一件重要事情是将数据从父(<g class="d3-group">)传递到子(<path>)。这发生在这里:

linesEnter
    .selectAll('path')
    .data(d => [d]) // <= here

这样,您的路径将始终由d3根据您传递给各自父母的数据绘制,并始终保持同步。

另外,映射图例/线条混乱的原因在于你如何分配颜色,即。通过使用索引。

图例中始终有3个标签,但SVG中的行数可变(1,2或3),因此您无法依靠索引来指定颜色。我使用的方法远非完美,但可以完成工作。您将希望找到更具可扩展性和可靠性的产品。

如果您需要任何澄清,请告诉我。

一些可能有用的链接:

答案 1 :(得分:1)

以下是使用以下修复程序重构的代码:

  1. 简化初始绘制并重绘为单一功能。
  2. 在此单一功能中正确处理输入,更新,退出模式。
  3. 引入key function来修复数据绑定(使其对数据保持唯一)。
  4. 使用d.key代替颜色而不是索引(以修复流浪的颜色)。
  5. 修正了一些常见的代码质量问题(缺少var关键字,过滤选择而不是数据等等)
  6. 我没有使用不透明度“切换”线条,尽管这样也可以。

    <!DOCTYPE html>
    <html>
    
    <head>
      <script data-require="d3@4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
      <script data-require="jquery@3.0.0" data-semver="3.0.0" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.js"></script>
      <style>
        .line-chart {
          width: 800px;
          height: 200px;
        }
        
        .d3-axis {
          font-family: 'Arial', sans-serif;
          font-size: 10px;
        }
        
        .d3-line {
          fill: none;
          stroke-width: 2px;
        }
        
        .d3-axis path {
          fill: none;
          stroke: #e6e6e6;
          shape-rendering: crispEdges;
          opacity: 0; // remove axes
        }
        
        .d3-axis line {
          fill: none;
          stroke: #eee;
          shape-rendering: crispEdges;
        }
        
        ul li {
          display: inline-block;
          margin-left: 10px;
        }
        
        .color-square {
          display: block;
          float: left;
          margin-right: 3px;
          width: 20px;
          height: 20px;
          border: 1px solid #000;
        }
      </style>
    </head>
    
    <body>
      <div class="line-chart"></div>
      <ul class="legend"></ul>
      <script>
        var data = [{
          "name": "line1",
          "date": "2016-10-07T23:59:07Z",
          "value": 67
        }, {
          "name": "line1",
          "date": "2016-10-15T11:35:32Z",
          "value": 57
        }, {
          "name": "line1",
          "date": "2017-02-09T07:13:41Z",
          "value": 11
        }, {
          "name": "line1",
          "date": "2016-11-16T21:18:03Z",
          "value": 12
        }, {
          "name": "line1",
          "date": "2016-05-01T03:08:22Z",
          "value": 71
        }, {
          "name": "line1",
          "date": "2016-10-01T08:15:08Z",
          "value": 64
        }, {
          "name": "line1",
          "date": "2016-07-27T09:58:43Z",
          "value": 25
        }, {
          "name": "line1",
          "date": "2016-04-15T12:20:35Z",
          "value": 15
        }, {
          "name": "line1",
          "date": "2016-11-01T11:51:14Z",
          "value": 69
        }, {
          "name": "line1",
          "date": "2016-10-05T23:27:50Z",
          "value": 12
        }, {
          "name": "line1",
          "date": "2016-11-11T21:53:45Z",
          "value": 87
        }, {
          "name": "line1",
          "date": "2017-01-22T17:22:10Z",
          "value": 10
        }, {
          "name": "line1",
          "date": "2016-07-18T23:33:03Z",
          "value": 27
        }, {
          "name": "line1",
          "date": "2017-01-04T14:35:53Z",
          "value": 6
        }, {
          "name": "line1",
          "date": "2016-11-10T07:17:06Z",
          "value": 91
        }, {
          "name": "line1",
          "date": "2016-04-18T00:40:18Z",
          "value": 56
        }, {
          "name": "line1",
          "date": "2016-06-23T11:27:18Z",
          "value": 22
        }, {
          "name": "line1",
          "date": "2016-08-10T12:53:00Z",
          "value": 18
        }, {
          "name": "line1",
          "date": "2016-09-02T17:24:51Z",
          "value": 89
        }, {
          "name": "line1",
          "date": "2016-06-08T03:09:20Z",
          "value": 27
        }, {
          "name": "line1",
          "date": "2016-10-30T17:54:30Z",
          "value": 38
        }, {
          "name": "line1",
          "date": "2017-01-22T01:56:44Z",
          "value": 99
        }, {
          "name": "line1",
          "date": "2016-06-02T19:58:44Z",
          "value": 48
        }, {
          "name": "line1",
          "date": "2016-07-12T01:04:56Z",
          "value": 68
        }, {
          "name": "line1",
          "date": "2016-09-23T07:30:45Z",
          "value": 11
        }, {
          "name": "line1",
          "date": "2016-11-08T05:18:12Z",
          "value": 29
        }, {
          "name": "line1",
          "date": "2017-01-24T03:46:43Z",
          "value": 19
        }, {
          "name": "line2",
          "date": "2016-04-17T06:36:39Z",
          "value": 44
        }, {
          "name": "line2",
          "date": "2016-03-27T17:40:29Z",
          "value": 29
        }, {
          "name": "line2",
          "date": "2016-09-13T02:11:44Z",
          "value": 55
        }, {
          "name": "line2",
          "date": "2016-12-24T10:47:49Z",
          "value": 54
        }, {
          "name": "line2",
          "date": "2016-11-12T21:17:27Z",
          "value": 74
        }, {
          "name": "line2",
          "date": "2016-07-17T10:18:03Z",
          "value": 55
        }, {
          "name": "line2",
          "date": "2016-10-15T10:46:42Z",
          "value": 24
        }, {
          "name": "line2",
          "date": "2016-08-25T12:10:23Z",
          "value": 63
        }, {
          "name": "line2",
          "date": "2017-01-22T18:08:27Z",
          "value": 88
        }, {
          "name": "line2",
          "date": "2016-05-04T09:47:14Z",
          "value": 44
        }, {
          "name": "line2",
          "date": "2016-10-19T18:45:20Z",
          "value": 74
        }, {
          "name": "line2",
          "date": "2017-01-16T19:03:01Z",
          "value": 46
        }, {
          "name": "line2",
          "date": "2017-01-03T18:05:28Z",
          "value": 32
        }, {
          "name": "line2",
          "date": "2016-09-22T14:32:07Z",
          "value": 93
        }, {
          "name": "line2",
          "date": "2016-08-26T16:07:09Z",
          "value": 22
        }, {
          "name": "line2",
          "date": "2016-08-12T07:03:45Z",
          "value": 52
        }, {
          "name": "line2",
          "date": "2016-10-09T11:12:56Z",
          "value": 52
        }, {
          "name": "line2",
          "date": "2016-10-11T00:13:01Z",
          "value": 39
        }, {
          "name": "line2",
          "date": "2016-10-23T16:35:20Z",
          "value": 58
        }, {
          "name": "line2",
          "date": "2016-07-06T05:18:24Z",
          "value": 95
        }, {
          "name": "line2",
          "date": "2017-02-03T08:49:39Z",
          "value": 51
        }, {
          "name": "line2",
          "date": "2016-07-21T00:03:27Z",
          "value": 100
        }, {
          "name": "line2",
          "date": "2016-08-27T07:23:05Z",
          "value": 71
        }, {
          "name": "line3",
          "date": "2016-11-11T21:53:45Z",
          "value": 87
        }, {
          "name": "line3",
          "date": "2017-01-22T17:22:10Z",
          "value": 220
        }, {
          "name": "line3",
          "date": "2016-07-18T23:33:03Z",
          "value": 24
        }, {
          "name": "line3",
          "date": "2017-01-04T14:35:53Z",
          "value": 65
        }, {
          "name": "line3",
          "date": "2016-11-10T07:17:06Z",
          "value": 9
        }, {
          "name": "line3",
          "date": "2016-04-18T00:40:18Z",
          "value": 54
        }, {
          "name": "line3",
          "date": "2016-06-23T11:27:18Z",
          "value": 72
        }, {
          "name": "line3",
          "date": "2016-08-10T12:53:00Z",
          "value": 88
        }, {
          "name": "line3",
          "date": "2016-09-02T17:24:51Z",
          "value": 89
        }, {
          "name": "line3",
          "date": "2016-06-08T03:09:20Z",
          "value": 27
        }, {
          "name": "line3",
          "date": "2016-10-30T17:54:30Z",
          "value": 38
        }, {
          "name": "line3",
          "date": "2017-01-22T01:56:44Z",
          "value": 99
        }, {
          "name": "line3",
          "date": "2016-06-02T19:58:44Z",
          "value": 48
        }, {
          "name": "line3",
          "date": "2016-07-12T01:04:56Z",
          "value": 68
        }, {
          "name": "line3",
          "date": "2016-09-23T07:30:45Z",
          "value": 51
        }, {
          "name": "line3",
          "date": "2016-11-08T05:18:12Z",
          "value": 49
        }, {
          "name": "line3",
          "date": "2017-01-24T03:46:43Z",
          "value": 89
        }, {
          "name": "line3",
          "date": "2016-04-17T06:36:39Z",
          "value": 54
        }, {
          "name": "line3",
          "date": "2016-03-27T17:40:29Z",
          "value": 27
        }, {
          "name": "line3",
          "date": "2016-09-13T02:11:44Z",
          "value": 58
        }, {
          "name": "line3",
          "date": "2016-12-24T10:47:49Z",
          "value": 24
        }, {
          "name": "line3",
          "date": "2016-11-12T21:17:27Z",
          "value": 54
        }, {
          "name": "line3",
          "date": "2016-07-17T10:18:03Z",
          "value": 55
        }, {
          "name": "line3",
          "date": "2016-10-15T10:46:42Z",
          "value": 24
        }, {
          "name": "line3",
          "date": "2016-08-25T12:10:23Z",
          "value": 63
        }]
    
        margin = {
          top: 20,
          right: 20,
          bottom: 20,
          left: 30
        };
    
        var containerwidth = $('.line-chart').width(),
            containerheight = $('.line-chart').height();
    
    
        var width = containerwidth - margin.left - margin.right,
           height = containerheight - margin.top - margin.bottom;
    
        var parseTime = d3.timeParse("%Y-%m-%dT%H:%M:%SZ")
    
        data.forEach(function(d) {
          d.date = parseTime(d.date);
          d.value = +d.value;
        });
    
        function sortByDateAscending(a, b) {
          return a.date - b.date;
        }
        dataset = data.sort(sortByDateAscending);
    
        var dataGroup = d3.nest()
          .key(function(d) {
            return d.name;
          })
          .entries(data);
    
        dataGroup.forEach(function(d) {
          d.enabled = true;
        });
        
        var svg = d3.select('.line-chart')
          .append('svg')
          .attr('width', containerwidth)
          .attr('height', containerheight)
          .append('g')
          .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
    
        var x = d3.scaleTime().range([0, width]),
            y = d3.scaleLinear().range([height, 0]);
    
        var colorScale = d3.scaleOrdinal(d3.schemeCategory10);
    
        var xAxis = d3.axisBottom(x).ticks(20).tickSizeInner(2).tickSizeOuter(0);
        var yAxis = d3.axisLeft(y).ticks(5).tickSizeInner(-width).tickSizeOuter(0);
        var xAxisGroup = svg.append('g').attr('class', 'x d3-axis').attr('transform', 'translate(0,' + height + ')');
        var yAxisGroup = svg.append('g').attr('class', 'y d3-axis').attr('transform', 'translate(0,0)');
    
        var line = d3.line()
          .x(function(d) {
            return x(d.date);
          }).y(function(d) {
            return y(d.value);
          });
    
        var legendItem = d3.select(".legend")
          .selectAll("li")
          .data(dataGroup)
          .enter()
          .append("li")
          .on('click', function(d) {
            d.enabled = !d.enabled;
            redraw();
          });
    
        legendItem
          .append("span")
          .attr("class", "color-square")
          .style("background", function(d, i) {
            return colorScale(d.key);
          });
    
        legendItem
          .append("span")
          .text(function(d) {
            return (d.values[0].name)
          });
    
        redraw();
    
        function redraw() {
    
          var fData = dataGroup.filter(function(d) {
            return d.enabled;
          });
          
          y.domain([
            d3.min(fData, function(c) {
              return d3.min(c.values, function(d) {
                return d.value;
              });
            }),
            d3.max(fData, function(c) {
              return d3.max(c.values, function(d) {
                return d.value;
              });
            })
          ]);
          
          x.domain([
            d3.min(fData, function(c) {
              return d3.min(c.values, function(d) {
                return d.date;
              });
            }),
            d3.max(fData, function(c) {
              return d3.max(c.values, function(d) {
                return d.date;
              });
            })
          ]);
          
          xAxisGroup.call(xAxis);
          yAxisGroup.call(yAxis);
    
          // update selection
          lines = svg.selectAll(".d3-group")
            .data(fData, function(d) {
              return d.key
            });
    
          // exit the whole group
          lines
            .exit().remove();
    
          // enter selection
          linesEnter = lines
            .enter()
            .append("g")
            .attr("class", "d3-group");
            
          // add path on enter
          linesEnter.append("path")
            .attr("class", "d3-line");
            
          // add text on enter
          linesEnter.append("text")
            .attr("class", "my-label")
            .attr("x", function(d,i){
              return i * 100;
            })
            .attr("y", 10);
            
          // update + enter
          lines = lines.merge(linesEnter);
          
          // adjust label
          lines.select(".my-label")
            .text(function(d,i){
              return "hi Mom " + d.key;
            });
    
          // adjust path
          lines.select(".d3-line")
            .attr('d', function(d) {
              return line(d.values);
            })
            .style("stroke", function(d, i) {
              return colorScale(d.key);
            });
    
        }
      </script>
    </body>
    
    </html>