3

I have following function for wrapping text in D3 taken from this answer:

function wrap(text, width) {
    text.each(function () {
        var text = d3.select(this),
            words = text.text().split(/\s+/).reverse(),
            word,
            line = [],
            lineNumber = 0,
            lineHeight = 1.1, // ems
            x = text.attr("x"),
            y = text.attr("y"),
            dy = 0, //parseFloat(text.attr("dy")),
            tspan = text.text(null)
                        .append("tspan")
                        .attr("x", x)
                        .attr("y", y)
                        .attr("dy", dy + "em");
        while (word = words.pop()) {
            line.push(word);
            tspan.text(line.join(" "));
            if (tspan.node().getComputedTextLength() > width) {
                line.pop();
                tspan.text(line.join(" "));
                line = [word];
                tspan = text.append("tspan")
                            .attr("x", x)
                            .attr("y", y)
                            .attr("dy", ++lineNumber * lineHeight + dy + "em")
                            .text(word);
            }
        }
    });
}

I initialize the nodes in a following manner:

const nodes = [1, 2, 3]
const nodesTextsEnter = vis.g.selectAll('text.node').data(nodes).enter()
const nodesTextsUpdate = vis.g.selectAll('text.node').data(nodes)
vis.g.selectAll('text.node').data(nodes).exit().remove()
        
nodesTextsEnter.append('text')
                .attr('x', (d, i) => i*30)
                .attr('y', (d, i) => i*30)
                .attr('text-anchor', 'middle')
                .attr('dominant-baseline', 'middle')
                .attr('font-size', 12)
                .style('cursor', 'pointer')
                .attr('class', 'node')
                .attr('opacity', '0')
                .transition()
                .attr('opacity', '1')
                .attr('x', (d, i) => i*30)
                .attr('y', (d, i) => i*30)
                .text('This is a long text')
                .call(wrap, 30)

nodesTextsUpdate
                .transition(transitionDuration)
                .attr('x', (d, i) => i*30)
                .attr('y', (d, i) => i*30)
                .text('This is a long text')
                .call(wrap, 30)

The problem is that while debugging wrap function, I see all the wrapping changes happening to the nodes, but then at some point (specifically, I figured it is happening on timerFlush() in wake function of timer.js) content of nodes gets flushed as though it was never wrapped.

Why this could be happening? Any ideas are welcome.

altern
  • 5,829
  • 5
  • 44
  • 72

1 Answers1

2

Your code works if you set the text contents before the transition:

nodesTextsEnter.append('text')
    .attr('x', (d, i) => i*30)
    .attr('y', (d, i) => i*30)
    .attr('text-anchor', 'middle')
    .attr('dominant-baseline', 'middle')
    .attr('font-size', 12)
    .style('cursor', 'pointer')
    .attr('class', 'node')
    .attr('opacity', '0')
    .text('This is a long text') // this line was moved up
  .transition()
    .attr('opacity', '1')
    .attr('x', (d, i) => i*30)
    .attr('y', (d, i) => i*30)
    .call(wrap, 30);

Otherwise, the value of d3.select(this).text() in wrap is empty and therefore words only contains the empty string.

Dan
  • 1,501
  • 9
  • 8