Alexander Tsepkov
Alexander Tsepkov

Reputation: 4186

How to prevent/minimize reflows for this d3 example?

I have the following d3 logic for rendering individual objects:

svg.selectAll("path")
    .data(hugePathDataset)
    .enter().append("path")
    .attr("class", (d) => d.properties.cls )
    .attr("id", (d) => d.properties.name )
    .each(... canvas render logic ...)

For performance reasons, the svg element above is set to display: none, the real rendering happens on canvas via d3's projection logic. The svg element is still needed, however, for later updates to canvas (such as changing color of each path individually).

My dataset includes over 60,000 paths and above code takes around 30 seconds to run. Testing it in Chrome's profiler I noticed that 90% of this time is spent in reflow. This made no sense to me since canvas doesn't reflow and the SVG with display: none shouldn't reflow the DOM. As I continued looking into it, I realized that the reflow is not triggered by appending elements to invisible SVG, but by setting class and id attributes on these elements. Sure enough, if I remove lines 4 and 5, the reflow slowdown completely disappears. Setting other attributes (i.e. data-something) does not cause a slowdown/reflow.

The problem is that I'm then unable to manipulate these paths individually later, as described above. My questions are:

Upvotes: 2

Views: 155

Answers (1)

Alexander Tsepkov
Alexander Tsepkov

Reputation: 4186

Reading D3 documentation I realized that selection.append("path") is equivalent to selection.append(() => document.createElement("path")). Since document.createElement does not yet attach the element to the DOM, it should be safe to set properties on it without a reflow. I rewrote the above logic differently and the issue went away:

svg.selectAll("path")
    .data(hugePathDataset)
    .enter().append((d) => {
        let element = document.createElement("path");
        element.id = d.properties.name;
        element.className = d.properties.cls;
        return element;
    })
    .each(... canvas render logic ...)

I still don't understand why class/id change on invisible element causes a reflow, however, but I'm no longer blocked by this.

Upvotes: 2

Related Questions