philipnye
philipnye

Reputation: 695

Tree diagram - prevent labels overlapping nodes

I've made a collapsible tree diagram in d3 (v5), a simplified version of which can be found here: https://jsfiddle.net/philipnye/cwj9nkhr/.

Each node has a label, but labels are only displayed where there is enough space for them not to overlap each other or get too close to the edge of the SVG. In the linked example, on first load labels only appear for the first two depths of the tree diagram.

requiredSpacing is used to approximate how much space is needed, while minSpacing checks the minimum spacing that exists between nodes at each depth.

The following lines position the nodes and labels, with a transition effect:

var nodeUpdate=nodeEnter.merge(node)
    .transition()
    .duration(function() {
        if (loaded==0) {
            return 0;
        }
        else {
            return duration;
        }
    })
    .attr("transform", function(d) {
        return "translate(" + d.x + "," + d.y + ")";
    });

nodeUpdate.select("circle")
    .attr("r", function(d) {
        return radiuses[d.depth];
    })
    .attr("class", function(d) {
        if (d._children) {
            return "filled";
        } else {
            return "unfilled";
        }
    });

nodeUpdate.select("text")
    .style("fill-opacity", function(d) {
        if (minSpacing[d.depth]<requiredSpacing[d.depth]*2 || (d.data.position=='left' && Math.ceil(d.x/5)*5-radiuses[d.depth]-requiredSpacing[d.depth]<margin.left) || (d.data.position=='right' && Math.ceil(d.x/5)*5+radiuses[d.depth]+requiredSpacing[d.depth]>width)) {
            return 1e-6;
        }
        else {
            return 1;
        }
    })
    .attr("class", function(d) {
        if (minSpacing[d.depth]<requiredSpacing[d.depth]*2 || (d.data.position=='left' && Math.ceil(d.x/5)*5-radiuses[d.depth]-requiredSpacing[d.depth]<margin.left) || (d.data.position=='right' && Math.ceil(d.x/5)*5+radiuses[d.depth]+requiredSpacing[d.depth]>width)) {
            return "nodeLabel nonselectable";
        }
        else {
            return "nodeLabel";
        }
    })

I set the opacity of labels which there isn't space to show to 1e-6 (to enable a transition) and give them a class of nonselectable, which means the labels can't be selected. But I don't actually remove them - and I then have the problem that in some cases they overlap nodes and mean the nodes can't be clicked on to expand/collapse the tree below that point. Look at the nodes two or three levels deeper than those labelled Male and Female - only somewhere between a third and a half of the nodes can be clicked on. (Note, nodes in the bottom layer aren't expandable/collapsible, as they have no children nodes.)

How am I best removing these labels, or at least stopping them preventing you from clicking on the nodes?

Things I've considered:

.attr("font-size", function(d) {
    if (minSpacing[d.depth]<requiredSpacing[d.depth]*2 || (d.data.position=='left' && Math.ceil(d.x/5)*5-radiuses[d.depth]-requiredSpacing[d.depth]<margin.left) || (d.data.position=='right' && Math.ceil(d.x/5)*5+radiuses[d.depth]+requiredSpacing[d.depth]>width)) {
        return '0.1em';
    }
})

Or there may be another, better way of solving this.

How should I do it?

Upvotes: 1

Views: 763

Answers (1)

Gerardo Furtado
Gerardo Furtado

Reputation: 102218

Just set a pointer-events: none; to those labels:

.nonselectable {
    pointer-events: none;
    etc...
}

Actually, since I'm seeing that the labels are not clickable, you could set pointer-events: none; to all of them.

Here is the updated JSFidle: https://jsfiddle.net/e3j7ta4y/

Upvotes: 1

Related Questions