Interactive and responsive Tree Diagram in D3.js
The tree diagram is a very intuitive visualization to display the structure and relation between parent and child nodes. To give users a better interaction between different nodes, we could utilize the diagram from Interactive d3.js tree diagram, make it more interactive and responsive.
See the Pen tree diagram expand by Henry_Z (@HanchengZhao-1471862086) on CodePen.
Prepare hierarchical tree node structure
The default hierarchy of a tree structure would be :
1 | { |
the default function to traverse the children is:
1 | function children(d) { |
so if we want to pass a different structure, we need to pass a different function accordingly.
For our use case, we have an array of tags
1 | var fields = |
Here’s our function to build a tree from it:
1 | const buildTree = (fields) => { |
Once we have the requested tree data, we can initialize our tree:
1 | var root = d3.hierarchy(treeData, function(d) { return d.children; }); |
Normalize for the current depth
In the Interactive d3.js tree diagram, the height between the parent and child nodes would not change in regards to the tree height. As a result, the width of the tree chart will expand out of the screen width if there are too many layers. However, this is not responsive and we do not want a scroll bar at the bottom, so we want the whole tree to ‘shrink’ according to the tree height.
Get current tree depth
We need to know the tree depth if we want to change the width dynamically. We could do this by a quick top-down traversal:
1 | /* |
Set each node’s y position based on its current depth
1 | // inside update function |
Handle window resize
The tree chart should resize when we change the inner window size. Firstly, we set the tree chart width to be
1 | var width = window.innerWidth - margin.left - margin.right |
Then we add a listener to the window resizing, we can debounce it a little by using _.debounce
from lodash.js
1 | window.addEventListener('resize', _.debounce(function(){ |
Handle collapse
We could collapse all but root node:
1 | // Collapse the node and all it's children |
Then expand the nodes on the given path or key:
1 | //Expand the tree based on key |
Handle click
We want to display 2 effects:
- highlight the path and node when clicking on the leaf node
- expand/collapse the subtree when clicking on non-leaf nodes
1 | // Toggle children on click. |
Get path
1 | // return the path from root to this node |
Highlight the clicked node
1 | // Update the node attributes and style |
Highlight the matched path
If we are given a path var key = "tag.kubernetes.pod.label.release";
, we could mark each node on the path as node.class = 'match'
when we expand the tree. Then we highlight the link between those marked nodes:
1 | // Transition back to the parent element position |
Integrate D3 with react
A lot of web apps are built with react, what if you want to render a D3 chart inside react app?
Both libraries are declarative and provide a wrapper on top of native dom. We might have conflicts when we are trying to integrate them. So a common way to do is to expose dom through ref
in react to D3. It’s like isolating a land for D3 to play with itself.
In the meantime, we can put some attributes in theReact state to trigger re-rendering when necessary.
Inside render()
function, set
1 | <div ref={node => (this.node = node)} /> |
then in d3:
1 | createTreeChart = fields => { |