简体   繁体   中英

D3: dataset update does not remove elements from DOM

I have a linechart, and by default I need to show just a subset of elements, then show a different subset on dropdown event.

Here is initial code:

 varlines = svg.append("g")
    .attr('id', 'lines')
    .selectAll('g')
    .data(datum.filter(function(d) {
            return d.group == 'default'
        }),
        function(d) {
            return d.name
        }
    ).enter()
    .append("g")
    .attr("class", 'varline')
    .append("path")
    .attr("class", "line")
    .attr("id", function(d) {
        return 'line_' + d.name
    })
    .attr("d", function(d) {
        return line(d.datum);
    });

And it works fine. Now, here is the code that meant to update my selection:

$('.dropdown-menu a').click(function(d) {
    var g = this.text;
    dd.select("button").text(g) // switch header

    var newdata = datum.filter(function(d) {
        return d.group == g
    }); # datalines for selected group

    varlines.data(newdata, function(d) { return d.name })
        .enter()
        .merge(varlines)
        .append("g")
        .attr("class", 'varline')
        .attr("id", function(d) {
            return 'line_' + d.name
        })
        .append("path")
        .attr("class", "line")
        .attr("d", function(d) {
            return line(d.datum);
        });

    varlines.exit().remove()

And it works weird. While it adds stuff and does not duplicate dom elements, it doesn't remove old ones.

Also, when I console.log(varlines); at any step, it shows only two initial elements in the _groups Array, and no _enter and _exit properties, even having 3 lines in the svg.

I am using d3v4, jquery3.1.1.slim

If you look at your varlines , you'll see that it is just an enter selection:

varlines = svg.append("g")
    .attr('id', 'lines')
    .selectAll('g')
    .data(datum.filter(function(d) {
            return d.group == 'default'
        }),
        function(d) {
            return d.name
        }
    ).enter()
    .append("g")
    //etc...

And, of course, you cannot call exit() on an enter selection. Therefore, this:

varlines.exit().remove()

... is useless.

Solution : make varlines an "update" selection by breaking it (I'm using var here, so we avoid it being a global):

var varlines = svg.append("g")
    .attr('id', 'lines')
    .selectAll('g')
    .data(datum.filter(function(d) {
            return d.group == 'default'
        }),
        function(d) {
            return d.name
        }
    );

varlines.enter()
    .append("g")
    //etc...

Pay attention to this fact: since you're using D3 v4, you have to use merge() , otherwise nothing will show up the first time the code runs. Alternativelly, you can just duplicate your code:

varlines = svg.append("g")
    .attr('id', 'lines')
    .selectAll('g')
    .data(datum.filter(function(d) {
            return d.group == 'default'
        }),
        function(d) {
            return d.name
        }
    )

varlines.enter()
    .append("g")
    //all the attributes here

varlines.//all the attributes here again

EDIT: the problem in your plunker is clear: when you do...

.attr("class", "line")

... you are overwriting the previous class. Therefore, it should be:

.attr("class", "varline line")

Here is the updated plunker: https://plnkr.co/edit/43suZoDC37TOEfCBJOdT?p=preview

Here is my new code following Gerardo's recommendations as well as this Mike's tutorial: https://bl.ocks.org/EmbraceLife/efb531e68ce46c51cb1df2ca360348bb

function update(data) {

  var varlines = svg.selectAll(".varlines")
    .data(data, function(d) {return d.name});


  // ENTER
  // Create new elements as needed.
  //
  // ENTER + UPDATE
  varlines.enter().append("g")
      .attr("class", 'varline')
      .attr("id", function(d) {
                    return 'line_' + d.name
                })
                .append("path")
                .attr("class", "line")
                .attr("d", function(d) {
                    return line(d.datum);
                })
                .style('stroke', function(d) {
                    return z(d.name)
                });
    // .merge(varlines); does not help if uncomment and move after .enter()

  // EXIT
  // Remove old elements as needed.
  varlines.exit().remove();
}

and then in the initial code (queue.await(...)):

svg.append("g")
   .attr('id', 'lines');
var data = datum.filter(function(d){return (d.group == 
settings['groups_settings']['default_group']) && (d.tp == tp)});
update(data)

and the same on dropdown change event:

var newdata = datum.filter(function(d){return (d.group == g) && (d.tp == tp)});
update(newdata);

Behaviour remains the same - correctly displays first batch, but does not remove lines on any changes (just keeps adding lines)

on print .selectAll returns this:

Selection {_groups: Array(1), _parents: Array(1), _enter: Array(1), _exit: Array(1)}
_enter:[Array(6)]_exit
:[Array(0)]_groups
:[Array(6)]
_parents:[svg]
__proto__:Object]

here is the full code: https://gist.github.com/Casyfill/78069927d2b95bf4856aa8048a4fa4be

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