# Tree Node (diagram) Links: [Home](../index.md) An org-chart style tree: every node is a `` element with a content box and a children container, connectors are drawn in pure CSS (and light up in blue on hover), and a small `ApplicationPrototype` per node gives you `append()`, `remove()`, `parent()`, `parents()`, `childs()` and `siblings()`. Written by SGApps.IO. ## Loading the Module ```js Application.require("elements/tree-node").then(function (TreeNode) { var root = TreeNode(); root.content().textContent = 'CEO'; var cto = TreeNode(); cto.content().textContent = 'CTO'; root.append(cto); document.getElementById('chart').appendChild(root.node()); }); ``` **Dependencies:** `uri-load` (links `elements/tree-node/style.css`), `extensions/prototype` (the `attrdata` bag on elements, where each element keeps a reference to its node object). ## Markup ```html node.node() -- inline-block, centred in its parent CEO node.content() -- grey rounded box with a shadow (#efefef, padding 10px 15px) node.childsContainer() -- position: absolute, 100 px below; hidden when empty ... one per child, side by side (white-space: nowrap) ``` Every `` element carries its node object in `element.attrdata.treeNode`, which is how `childs()` and `parent()` walk the DOM. ## API ### `TreeNode()` Returns a **node**, an `ApplicationPrototype` (a detached `` element with an empty content box). | Method | Returns | Description | |---|---|---| | `node()` | `HTMLElement` | The `` element (plain method) | | `content()` | `HTMLElement` | The `` box -- set its text or append your own markup | | `childsContainer()` | `HTMLElement` | The `` element (plain method) | | `append(child)` | -- | Detaches `child` (a node object) from wherever it is and appends its element to this node's children container | | `remove()` | -- | Detaches this node's element from its parent (subtree included) | | `childs()` | node[] | The direct children, in DOM order (plain method) | | `parent()` | node or `null` | The node whose children container holds this element | | `parents()` | node[] | Ancestors, nearest first | | `siblings()` | node[] | The children of the parent, **this node included**; `[]` for a root | `content()`, `parent()`, `parents()` and `siblings()` are bound with `before` / `on` hooks (`"bf on"` / `"bf"`), `append()` and `remove()` with the full lifecycle; `node()`, `childs()`, `childsContainer()` are plain. ## Layout The connectors are CSS pseudo-elements on `tree-node-childs` (a 100 px vertical line from the parent down to a dot, growing on hover) and on each child (`::before` a 50 px vertical stub, `::after` the horizontal bar, shortened to a half for the first and last child, hidden for an only child). Constants in the stylesheet: 100 px parent-to-children connection, 50 px child stub, 10 px horizontal node margin, 3 px lines. Because `tree-node-childs` is `position: absolute`, **a tree takes no vertical space in the flow**: only the root's content box does. Give the container an explicit height (about 150 px per level) and `overflow: auto`. Sub-trees are centred under their parent independently of their cousins, so wide sub-trees of neighbouring nodes overlap -- keep the branching modest or add horizontal margin to `tree-node > tree-node-childs > tree-node` for your data. ## Use Cases ### Building a tree from data ```js Application.require("elements/tree-node").then(function (TreeNode) { var build = function (item) { var node = TreeNode(); node.content().textContent = item.name; (item.children || []).forEach(function (child) { node.append(build(child)); }); return node; }; var root = build({ name: 'CEO', children: [ { name: 'CTO', children: [{ name: 'Frontend' }, { name: 'Backend' }] }, { name: 'CFO', children: [{ name: 'Accounting' }] } ] }); document.getElementById('chart').appendChild(root.node()); }); ``` ### Selecting a node and walking the tree ```js document.getElementById('chart').addEventListener('click', function (ev) { var content = ev.target.closest('tree-node-content'); if (!content) return; var node = content.parentNode.attrdata.treeNode; console.log('path:', node.parents().map(function (p) { return p.content().textContent; }).reverse().join(' / ')); console.log('children:', node.childs().length, 'siblings:', node.siblings().length - 1); }); ``` ### Moving a sub-tree ```js var backend = findNode('Backend'); findNode('COO').append(backend); // append() detaches it from the CTO first ``` ## Notes - Nodes are plain DOM: `node.content().appendChild(...)` accepts any markup (avatars, badges, buttons). - `remove()` only detaches the element; keep the node object to `append()` it elsewhere. - Hovering a children container highlights its connectors and the hovered child's own connector chain in `royalblue`. - Live demo: [playground](playground.html#module=tree-node ':ignore :target=_blank').