Skip to content

Events ​

The tree dispatches CustomEvents on the element it was created in. They bubble and they are cancelable, and their data is in event.detail:

js
const element = document.getElementById("tree1");

element.addEventListener("tree.click", (e) => {
  console.log(e.detail.node.name);
});

Everything you do in this tree is logged below it, most recent first:

Events

Click a node, open a folder, or drag something.

Because the events bubble, you can also listen higher up in the document, which is handy when the tree is re-created:

js
document.addEventListener("tree.select", (e) => {
  console.log(e.detail.node);
});

Lifecycle ​

tree.init ​

ts
"tree.init": undefined;

Dispatched once, after the tree is rendered for the first time. With dataUrl this is after the data has arrived, so it is the moment the api is safe to use:

js
element.addEventListener("tree.init", () => {
  tree.selectNode(tree.getNodeByName("node1"));
});

No detail.

With inline data, the tree is rendered inside the constructor, so this event has already fired by the time the constructor returns. Add the listener to the element before creating the tree:

js
const element = document.getElementById("tree1");

element.addEventListener("tree.init", () => {
  // ...
});

const tree = new TreeElement({ data, htmlElement: element });

tree.refresh ​

ts
"tree.refresh": undefined;

Dispatched after the tree has been re-rendered. No detail.

Mouse ​

tree.click ​

ts
"tree.click": object;

Dispatched when a node title is clicked. Call preventDefault() to stop the tree from selecting the node:

js
element.addEventListener("tree.click", (e) => {
  e.preventDefault();
  console.log("clicked", e.detail.node.name, "but not selected");
});
NameType
nodeNode
originalEventMouseEvent

tree.dblclick ​

ts
"tree.dblclick": object;

Dispatched when a node title is double clicked.

NameType
nodeNode
originalEventMouseEvent

tree.contextmenu ​

ts
"tree.contextmenu": object;

Dispatched on a right click on a node, unless useContextMenu is false. The browser's own menu is suppressed.

NameType
nodeNode
originalEventMouseEvent

Opening and closing ​

tree.open ​

ts
"tree.open": object;

Dispatched when a folder is opened, after the animation.

NameType
nodeNode

tree.close ​

ts
"tree.close": object;

Dispatched when a folder is closed, after the animation.

NameType
nodeNode

Selection ​

tree.select ​

ts
"tree.select": object;

Dispatched when a node is selected. A deselection is tree.deselect instead, so node is always a node here.

js
element.addEventListener("tree.select", (e) => {
  console.log("selected", e.detail.node.name);
});
NameTypeDescription
deselectedNodeNode | nullThe node that was selected before, if any. When several nodes were selected, this is the first; the others get a tree.deselect, dispatched before this event.
nodeNodeThe node that is now selected.

tree.deselect ​

ts
"tree.deselect": object;

Dispatched when the selected node is deselected, which happens when it is clicked again or passed to selectNode again with mustToggle: true. No tree.select is dispatched for it.

When several nodes are selected and a click or selectNode selects just one node, it is dispatched for each node that loses the selection, except the one in the deselectedNode of tree.select.

js
element.addEventListener("tree.deselect", (e) => {
  console.log("deselected", e.detail.node.name);
});

Selecting another node does not dispatch it — that is a tree.select with the previous node in deselectedNode. It is also not dispatched by selectNode without mustToggle: true, selectNode(null), removeFromSelection, removeNode, loadData and restoring a saved state.

NameType
nodeNode

Drag and drop ​

tree.move ​

ts
"tree.move": object;

Dispatched when a node is dropped after a drag.

detailType
moveInfo.movedNodeNode
moveInfo.targetNodeNode
moveInfo.position"before" | "after" | "inside"
moveInfo.previousParentNode | null
moveInfo.originalEventEvent
moveInfo.doMove() => void

Call preventDefault() to keep the tree as it is, and moveInfo.doMove() to apply the move later. See Drag and drop.

Loading data ​

tree.set_data ​

ts
"tree.set_data": object;

Dispatched when data is set on the tree, both from the data option and from a url.

NameTypeDescription
node?NodeThe node whose children were replaced, if it was a subtree.
treeData?NodeData[]-

tree.loading_data ​

ts
"tree.loading_data": object;

Dispatched when a request starts.

NameTypeDescription
elementHTMLElement-
node?NodeThe node whose children are loading, if it is a subtree.

tree.loaded_data ​

ts
"tree.loaded_data": object;

Dispatched when a request finishes. It is dispatched when the request fails too; a tree.load_failed follows it.

NameTypeDescription
elementHTMLElement-
node?NodeThe node whose children were loaded, if it was a subtree.

tree.load_failed ​

ts
"tree.load_failed": object;

Dispatched when a request returns an error status or fails with a network error, after tree.loaded_data:

js
element.addEventListener("tree.load_failed", (e) => {
  if (e.detail.response) {
    console.error("loading the tree failed", e.detail.response.status);
  } else {
    console.error("loading the tree failed", e.detail.error);
  }
});
NameTypeDescription
error?unknownThe error thrown by fetch, if the request failed with a network error.
response?ResponseThe response, if the request returned an error status.

Keyboard ​

tree.activate ​

ts
"tree.activate": object;

Dispatched when Enter is pressed on the selected node, or on the focused node when no node is selected. Use it for what a click on a link would do, like opening the edit page of the node:

js
element.addEventListener("tree.activate", (e) => {
  window.location.href = e.detail.node.url;
});
NameType
nodeNode
originalEventKeyboardEvent

Types ​

The package types the events by their name, so in TypeScript the detail needs no cast:

ts
element.addEventListener("tree.click", (e) => {
  console.log(e.detail.node.name, e.detail.originalEvent.button);
});

This works on elements, on document and on window, in any file that imports tree-element.

Each event has its own detail, so a listener only sees the properties of the event it listens to:

ts
element.addEventListener("tree.select", (e) => {
  console.log(
    "selected",
    e.detail.node.name,
    "instead of",
    e.detail.deselectedNode,
  );
});

element.addEventListener("tree.deselect", (e) => {
  console.log("deselected", e.detail.node.name);
});

The types are also exported, for a listener that is written separately:

ts
import type { MoveInfo, TreeEvent, TreeEventName, TreeEvents } from "tree-element";

const element = document.getElementById("tree1")!;

// TreeEvent<Name> is the CustomEvent, TreeEvents[Name] is its detail.
const onMove = (e: TreeEvent<"tree.move">): void => {
  const moveInfo: MoveInfo = e.detail.moveInfo;
  console.log(moveInfo.movedNode.name);
};

element.addEventListener("tree.move", onMove);

// TreeEventName is the name of any event: "tree.click" | "tree.close" | ...
const log = <Name extends TreeEventName>(
  name: Name,
  detail: TreeEvents[Name],
) => {
  console.log(name, detail);
};

Dispatching events differently ​

The overrideTriggerEventProvider option replaces the whole mechanism, if you want the tree to report to something other than DOM events. See Options.