添加到D3条形图中的最后一个元素未排序

时间:2019-12-05 11:41:23

标签: javascript d3.js

我是D3 / Javascript菜鸟,正在尝试创建一个图表,该图表可以添加条形图(通过“更新”>“输入”选择),然后对其进行重新排序。

但是,最后添加的栏拒绝参与重新排序。

有人可以告诉我: a)为什么会这样 b)如何修复

请参阅Codepen:https://codepen.io/benjamesdavis/pen/JjodJwy?editors=1010

            //Width and height
            var w = 900;
            var h = 450;

            var array = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
                            11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ];

            dataset=[];             
            for(let i=0; i<array.length; i++)
                {dataset[i] = {"key":i, "value": array[i]}}

            var xScale = d3.scaleBand()
                            .domain(d3.range(dataset.length))
                            .rangeRound([0, w])
                            .paddingInner(0.05);

            var yScale = d3.scaleLinear()
                            .domain([0, d3.max(dataset, d=> d.value)])
                            .range([0, h]);

            //Create SVG element
            var svg = d3.select("body")
                        .append("svg")
                        .attr("width", w)
                        .attr("height", h);



            //Create bars
            svg.selectAll("rect")
               .data(dataset)
               .enter()
               .append("rect")
               .attr("x", function(d, i) {
                    return xScale(i);
               })
               .attr("y", function(d) {
                    return h - yScale(d.value);
               })
               .attr("width", xScale.bandwidth())
               .attr("height", function(d) {
                    return yScale(d.value);
               })
               .attr("fill", function(d) {
                    return "rgb(0, 0, " + Math.round(d.value * 10) + ")";
               });




            //On click, update with new data            
            d3.select("#newBar")
                .on("click", function() {

                    //Add one new value to dataset
                    var maxValue = 25;
                    var newNumber = Math.floor(Math.random() * maxValue);
                    newNumber = {"key":dataset.length, "value": newNumber}
                    dataset.push(newNumber);

                    //Update scale domains
                    xScale.domain(d3.range(dataset.length));
                    yScale.domain([0, d3.max(dataset, d=> d.value)]);

                    //Select…
                     bars = svg.selectAll("rect")
                        .data(dataset)

                    //Enter…
                    bars.enter()
                        .append("rect")
                        .attr("x", w)
                        .attr("y", function(d) {
                            return h - yScale(d.value);
                        })
                        .attr("width", xScale.bandwidth())
                        .attr("height", function(d) {
                            return yScale(d.value);
                        })
                        .attr("fill", function(d,i) {
                            return "rgb(0, 0, " + Math.round(d.value * 10) + ")";
                        })
                        .merge(bars)
                        .transition()
                        .duration(500)
                        .attr("x", function(d, i) {
                            return xScale(i);
                        })
                        .attr("y", function(d) {
                            return h - yScale(d.value);
                        })
                        .attr("width", xScale.bandwidth())
                        .attr("height", function(d) {
                            return yScale(d.value);
                        });

                });


            //Re-Sort Bars
            d3.select("#sortBars").on("click",function(){
                d3.shuffle(dataset)

                 bars.data(dataset,d => d.key)
                    .transition(2000)
                    .attr("x", function(d, i) {             
                        return xScale(i);
                    })                                  
                })

2 个答案:

答案 0 :(得分:1)

我认为这是事实,当您定义钢筋时,它在add-bars函数的范围内,而不是re-sort函数,因此,首先,您需要每次进行选择。然后,您需要确保两次都使用密钥。这是我对您的代码的看法:

//宽度和高度         var w = 500;         var h = 200;

    var array = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
                    11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ];

    dataset=[];     

    for(let i=0; i<array.length; i++)
        {dataset[i] = {"key":i, "value": array[i]}}

    var xScale = d3.scaleBand()
                    .domain(d3.range(dataset.length))
                    .rangeRound([0, w])
                    .paddingInner(0.05);

    var yScale = d3.scaleLinear()
                    .domain([0, d3.max(dataset, d=> d.value)])
                    .range([0, h]);

    //Create SVG element
    var svg = d3.select("body")
                .append("svg")
                .attr("width", w)
                .attr("height", h);



    //Create bars
    svg.selectAll("rect")
       .data(dataset)
       .enter()
       .append("rect")
       .attr("x", function(d, i) {
            return xScale(i);
       })
       .attr("y", function(d) {
            return h - yScale(d.value);
       })
       .attr("width", xScale.bandwidth())
       .attr("height", function(d) {
            return yScale(d.value);
       })
       .attr("fill", function(d) {
            return "rgb(0, 0, " + Math.round(d.value * 10) + ")";
       });




    //On click, update with new data            
    d3.select("#newBar")
        .on("click", function() {

            //Add one new value to dataset
            var maxValue = 25;
            var newNumber = Math.floor(Math.random() * maxValue);
            newNumber = {"key":dataset.length, "value": newNumber}
            dataset.push(newNumber);

            //Update scale domains
            xScale.domain(d3.range(dataset.length));
            yScale.domain([0, d3.max(dataset, d=> d.value)]);

            //Select…
             bars = svg.selectAll("rect")
                       .data(dataset,d => d.key) //make sure you use the key here too!


            //Enter…
            bars.enter()
                .append("rect")
                .attr("x", w)
                .attr("y", function(d) {
                    return h - yScale(d.value);
                })
                .attr("width", xScale.bandwidth())
                .attr("height", function(d) {
                    return yScale(d.value);
                })
                .attr("fill", function(d,i) {
                    return "rgb(0, 0, " + Math.round(d.value * 10) + ")";
                })
                .merge(bars)
                .transition()
                .duration(500)
                .attr("x", function(d, i) {
                    return xScale(i);
                })
                .attr("y", function(d) {
                    return h - yScale(d.value);
                })
                .attr("width", xScale.bandwidth())
                .attr("height", function(d) {
                    return yScale(d.value);
                });

        });


    //Re-Sort Bars
    d3.select("#sortBars").on("click",function(){
        d3.shuffle(dataset)
        bars =  svg.selectAll("rect").data(dataset,d => d.key) //redefine bars, and use the key
            .transition(2000)
            .attr("x", function(d, i) {             
                return xScale(i);
            })                                  
        })

答案 1 :(得分:1)

具体的问题是,当您在第70行上bars bars选择并将其合并到83上的bars时,您并没有将新的合并选择保存回{ {1}}。结果,当您在第104行进行重新排序时,bars更新选择不包含最新的条。

通常,要记住的是,数据绑定选择始终是更新选择,并且选择(从D3v4开始)是不可变的,所以当您这样做时

bars.enter()
    .<do stuff to just the enter selection>
    .merge(bars) // merge the update and enter selections together
    .<do more stuff to the merged update+enter selections>

bars的值仍然是旧的更新选择。如果要稍后使用合并的内容,则必须明确保存合并的内容。

bars = bars.enter() // note the assignment to bars!
    .<do stuff to just the enter selection>
    .merge(bars) // merge the update and enter selections together
    .<do more stuff to the merged update+enter selections>