score:2

Here is what I ended up doing in my load method:

load: function() {
    var $container = this.container;
    var color = this.color;
    var treemap = this.treemap;
    var position = this.position;
    var tooltip = this.tooltip;
    var url = this.base_url + "?" + this.url_fragment();

    console.log("D3View.load: " + url);

    d3.json(url, function(error, root) {
        var tooltip_mouseover = function(d) {
            tooltip.html(d.name + ": " + Math.floor(d.value))
              .style("visibility", "visible");
            this.style.cursor = "hand";
        };
        var tooltip_mouseout = function(){
            tooltip.style("visibility", "hidden");
        };
        var background_color = function(d) { return d.children ? color(d.name) : null; };
        var text_format = function(d) { return d.children ? null : d.name; };

        /*
         * Refresh sticky bit
         * https://github.com/mbostock/d3/wiki/Treemap-Layout
         * "Implementation note: sticky treemaps cache the array of nodes internally; therefore, it
         * is not possible to reuse the same layout instance on multiple datasets. To reset the
         * cached state when switching datasets with a sticky layout, call sticky(true) again."
         */
        treemap.sticky(true);

        /* 'root' actually means the data retrieved by the xhr call */
        var nodes = $container.datum(root)
            .selectAll(".node")
            .data(treemap.nodes);

        var enter = nodes.enter().append("div")
            .attr("class", "node")
            .call(position)
            .style("background", background_color)
            .text(text_format)
            .on("mouseover", tooltip_mouseover)
            .on("mouseout", tooltip_mouseout);

        var exit = nodes.exit().remove();

        var update = nodes.style("background", background_color)
            .call(position)
            .text(text_format);

        d3.selectAll("input").on("change", function change() {
            var functions = {
                count: function(d) { return d.count; },
                size: function(d) { return d.size; },
                avg: function(d) { return d.size / d.count; }
            };
            var value = functions[this.value];

            $container.selectAll(".node")
                .data(treemap.value(value).nodes)
            .transition()
                .duration(1500)
                .call(position);
        });
    });

There are two important changes:

  1. Reset treemap sticky bit
  2. Use enter/exit/update selections for new/missing/changed nodes/data

Notice also that enter nodes have the side effect of adding a div of class node and the exit and update nodes do not reference that div-class -- except they do, in the creation of nodes. If add a further selection on node-class in those places, your selection will be empty and the exit and update code will be no-ops.

Thanks to AmeliaBR for posting a really helpful comment with a link to an SO answer she composed.

Other helpful reads:


Related Query