Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3 - Correct number of elements on update, but wrong values

I am populating a div with child divs containing the values from an array. On first pass, the array looks like this:

arr_subpop_unique = ["CPL", "NAP", "NPL", "SAP", "SPL", "TPL", "UMW", "WMT", "XER"]

My selection enter/update/exit looks like this:

var sizemapHeader = d3.select("#d3-sizemap-hr").selectAll("div")
        .data(arr_subpop_unique)

    sizemapHeader.enter().append("div")
        .attr("class", "sizemap-hr-title ellipsis scroll_on_hover")
        .html(function(d,i){ return d; });

    sizemapHeader.exit().remove();

This works fine, giving me a div for each element containing a string.

When I run the function again, my data array updates to this:

    arr_subpop_unique = ["MAN_MADE", "NATURAL"]

The update returns two divs, however, they contain "CPL" and "NAP" (index values 0 and 1). Can someone explain why this would not replace the first two indicies with "MAN_MADE" and "NATURAL"?

Thanks for the help...trying to get the hang of enter/update/exit data joins in D3!

like image 393
thefreeline Avatar asked Mar 31 '15 02:03

thefreeline


1 Answers

It's worth noting that the chaining is a little bit asymmetrical because enter() has the side-effect of merging into the update selection. This means, if you add features on to the update selection after the append(), they will also be added on the enter nodes. On the other hand, the update selection is not merged into the enter selection. So any features chained after enter() will only be on the enter selection.

In the case of the OP question, there are two update nodes and zero enter nodes. So the HTML is added on the non-existent enter nodes and the HTML of the two update nodes is not updated.

So, yes, this works...

var sizemapHeader = d3.select("#d3-sizemap-hr").selectAll("div")
            .data(arr_subpop_unique)

//ENTER
sizemapHeader
.enter()
    .append("div")
        .attr("class", "sizemap-hr-title ellipsis scroll_on_hover")
//UPDATE
sizemapHeader
    .html(function (d, i) { return d; })

//EXIT
sizemapHeader
.exit()
    .remove();

But this doesn't...

var sizemapHeader = d3.select("#d3-sizemap-hr").selectAll("div")
            .data(arr_subpop_unique)

//ENTER
//sizemapHeader
    .enter()
    .append("div")
        .attr("class", "sizemap-hr-title ellipsis scroll_on_hover")

//UPDATE
sizemapHeader
        .html(function (d, i) { return d; })

//EXIT
sizemapHeader
.exit()
    .remove();

And this breaks in the initial update (when the update selection is empty)...

var sizemapHeader = d3.select("#d3-sizemap-hr").selectAll("div")
            .data(arr_subpop_unique)

//UPDATE
sizemapHeader
    .html(function (d, i) { return d; })

//ENTER
sizemapHeader
.enter()
    .append("div")
        .attr("class", "sizemap-hr-title ellipsis scroll_on_hover")

//EXIT
sizemapHeader
.exit()
    .remove();

When you trace your code, you will see that the value of sizemapHeader changes after .enter() is called on it.

The reason is buried in the wiki...

The enter selection merges into the update selection when you append or insert. Rather than applying the same operators to the enter and update selections separately, you can now apply them only once to the update selection after entering the nodes. If you find yourself removing an entire selection's elements only to reinsert most of them, do this instead. For example:

var update_sel = svg.selectAll("circle").data(data)
update_sel.attr(/* operate on old elements only */)
update_sel.enter().append("circle").attr(/* operate on new elements only */)
update_sel.attr(/* operate on old and new elements */)
update_sel.exit().remove() /* complete the enter-update-exit pattern */
like image 128
Cool Blue Avatar answered Oct 05 '22 21:10

Cool Blue