D3.js 是否使用.update、.enter和.exit更新条形图?

D3.js 是否使用.update、.enter和.exit更新条形图?,d3.js,D3.js,我做了一个简单的条形图。我想通过在数据的不同子集之间切换来更新条形图(动态)。据我所见,似乎有两种方法: 绘制图表,然后在按钮上单击(或其他)删除整个图表并重新绘制 使用不同的数据选择,通过单击按钮从图表中“更新”、“输入”和“退出”条形图 我正在尝试选择2。这是我的密码: // set dimensions var margin = {top: 10, right: 20, bottom: 50, left: 100}; var height = 600 -margin.top - margi

我做了一个简单的条形图。我想通过在数据的不同子集之间切换来更新条形图(动态)。据我所见,似乎有两种方法:

  • 绘制图表,然后在按钮上单击(或其他)删除整个图表并重新绘制
  • 使用不同的数据选择,通过单击按钮从图表中“更新”、“输入”和“退出”条形图
  • 我正在尝试选择2。这是我的密码:

    // set dimensions
    var margin = {top: 10, right: 20, bottom: 50, left: 100};
    var height = 600 -margin.top - margin.bottom;
    var width = 600 - margin.left - margin.right;
    
    // set ranges
    var x = d3.scale.linear().range([width, 0]);
    var y = d3.scale.ordinal().rangeRoundBands([0, height], .05);
    
    // define y axis
    var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left");
    
    // add svg container
    var svg = d3.select("#chart")
    .append("svg")
    .attr("height", height + margin.top + margin.bottom)
    .attr("width", width + margin.left + margin.right)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    
    // load data
    d3.json("data.json", function(data) {
    
      // format data
      data.forEach(function(d) {
        d.enrols = +d.enrols;
      });
    
      // register click event
      d3.selectAll(".toggle").on("click", function() {
        render(this.id);
      });
    
      // initialise
      render("group-1");
    
      function render(selection) {
    
        // filter data
        var subset = data.filter(function(d) { return d.group === selection} );
    
        // scale range of data
        x.domain([0, d3.max(subset, function(d) { return d.enrols; })]);
        y.domain(subset.map(function(d) { return d.month; }));
    
        // update selection
        var bars = svg.selectAll(".bars")
        .data(subset, function(d) { return d.month; });
    
        // add bars
        bars.enter()
        .append("g")
        .attr("class", "bar")
        .attr("transform", function (d, i) {
          return "translate(0," + y(d.month) + ")";
        });
    
        bars.append("rect")
        .attr("height", y.rangeBand())
        .attr("width", function (d) {
          return width - x(d.enrols);
        });
    
        bars.append("text")
        .attr("x", function (d) {
          return width - x(d.enrols) - 15;
        })
        .attr("y", y.rangeBand() / 2)
        .attr("dy", ".35em")
        .text(function (d) {
          return d.enrols;
        });
    
        bars.exit().remove();
    
        // add y axis
        svg.append("g")
        .attr("class", "y axis")
        .call(yAxis);
    
      }
    
    });
    
    问题是我的旧数据从未退出,新条被附加,但旧条仍然存在。我读过d3选择的理论,但我发现它们很难在实践中实现


    这是一本书。这里是。

    很好,您选择了第二个选项而不是第一个选项:删除所有元素并再次绘制图表是一种非常懒惰的编码方式

    但是,在您的情况下,由于数据的长度不变,因此不需要“输入”、“更新”和“退出”选项

    如果将一些不变的部分移到
    渲染
    函数之外,则一切都会变得更简单:

    y.domain(data.map(function(d) {
        return d.month;
    }));
    
    var bars = svg.selectAll(".bars")
        .data(data, function(d) {
            return d.month;
        }).enter()
        .append("g")
        .attr("class", "bar")
        .attr("transform", function(d, i) {
            return "translate(0," + y(d.month) + ")";
        });
    
    var rects = bars.append("rect")
        .attr("height", y.rangeBand())
        .attr("width", function(d) {
            return width - x(d.enrols);
        });
    
    var texts = bars.append("text")
        .attr("x", function(d) {
            return width - x(d.enrols) - 15;
        })
        .attr("y", y.rangeBand() / 2)
        .attr("dy", ".35em")
        .text(function(d) {
            return d.enrols;
        });
    
    svg.append("g")
        .attr("class", "y axis")
        .call(yAxis);
    
    只保留真正改变的东西:

    • 你的x标度范围
    • 绑定到组的数据
    • 矩形和文本的过渡
    它会小得多:

    function render(selection) {
    
        // filter data
        var subset = data.filter(function(d) {
            return d.group === selection
        });
    
        // scale range of data
        x.domain([0, d3.max(subset, function(d) {
            return d.enrols;
        })]);
    
        bars.data(subset, function(d) {
            return d.month;
        });
    
        rects.transition().duration(500).attr("width", function(d) {
            return width - x(this.parentNode.__data__.enrols);
        });
    
        texts.transition().duration(500).attr("x", function(d) {
                return width - x(this.parentNode.__data__.enrols) - 15;
            })
            .text(function(d) {
                return this.parentNode.__data__.enrols;
            });
    
    }
    
    这是你的密码笔:


    PS:如果你去掉这些组,直接添加矩形和文本,这会更容易。

    你的条形图只需要非常简单的修改。以下是缺失的拼图:

    当我们使用
    data()
    函数时,D3会在现有DOM元素和您的数据值之间执行转换。将此操作的结果存储在变量
    栏上
    。我们可以在
    条上调用多个方法

    方法
    exit()
    返回要删除的元素。如果DOM上有3个条,并且我们使用一个只有2个数据值的数组调用
    data()
    函数,
    exit()
    将返回将被删除的条

    同样,方法
    enter()
    返回要添加的元素。如果我们在DOM上有2个条,并且调用带有3个数据值的数组的
    data()
    函数,
    enter()
    将返回要添加的新条

    那么,将要更新的条形图呢?它们是存储在
    条中的变量

    将其转换为代码非常简单:

    这:

    变成这样:

    var newBars = bars.enter()
      .append("g")
      ...
    
    newBars.append(...)
    
    然后每次出现时:

    bars.append(...)
    
    变成这样:

    var newBars = bars.enter()
      .append("g")
      ...
    
    newBars.append(...)
    
    每次有一个新的条,我们就
    向DOM追加
    一些东西

    每次我们需要更新一个条时,我们都从DOM中选择一些内容

    指将要更新的条。因此,我们添加此代码以更新现有条形图:

    bars.select('rect')
      .attr("width", function (d) {
        return width - x(d.enrols);
      });
    
    bars.select('text')
      .attr("x", function (d) {
        return width - x(d.enrols) - 15;
      })
      .text(function (d) {
        return d.enrols;
      });
    

    selectAll(“.bar”)
    是错误的,因为没有具有类
    bar
    的元素,只有具有类
    bar
    的元素。您正在创建多个轴。您还向同一组
    g
    元素添加了多个条。看看代码笔中生成的SVG。你的意思是:?似乎没用?很好!您几乎解决了代码笔中的所有问题。每次调用
    render
    函数时,您仍在向图表中添加一个新轴。你应该解决这个问题。在任何情况下,我现在都可以向您解释d3的数据联接。
    this.parentNode.\uuuu data\uuuuu.enrols
    ?不
    d.注册
    ?不,不
    d
    ,除非我们在这里创建“输入”、“更新”和“退出”选项,这正是我要避免的。谢谢。这对我来说是一个学习练习-因此,如果数据的长度发生了变化,即我在第二个数据子集中没有11月和12月的数据,该怎么办?在这种情况下,您需要选择“输入”、“更新”和“退出”(顺便说一句,没有组更容易).我遵循您的逻辑:
    保存连接的dom元素和数据;将要添加的新条存储在变量中,无需删除任何内容,只需使用
    selectAll
    更新即可。所有我得到的是,虽然我的酒吧宽度增加了一点,每切换??更新代码笔:对不起,我弄错了。在我写的
    selectAll
    中,应该是
    select
    。我更新了我的答案。顺便说一句,D3V3和D3V4中的数据联接有一些不同。如果你对理解这些差异感兴趣,看看是什么。