or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

dom-manipulation.mdeditor-state.mdfile-handling.mdindex.mdspecialized-utilities.mdtree-traversal.md
tile.json

tree-traversal.mddocs/

0

# Tree Traversal

1

2

Comprehensive tree traversal algorithms and node navigation utilities for exploring the Lexical node tree structure. These functions provide efficient methods for walking through the editor's document tree, finding specific nodes, and performing complex tree operations.

3

4

## Capabilities

5

6

### Depth-First Search (DFS)

7

8

Performs depth-first tree traversal starting from a specified node, returning all nodes with their depth information.

9

10

```typescript { .api }

11

/**

12

* "Depth-First Search" starts at the root/top node of a tree and goes as far as it can down a branch end

13

* before backtracking and finding a new path. Consider solving a maze by hugging either wall, moving down a

14

* branch until you hit a dead-end (leaf) and backtracking to find the nearest branching path and repeat.

15

* It will then return all the nodes found in the search in an array of objects.

16

* @param startNode - The node to start the search, if omitted, it will start at the root node.

17

* @param endNode - The node to end the search, if omitted, it will find all descendants of the startingNode.

18

* @returns An array of objects of all the nodes found by the search, including their depth into the tree.

19

* {depth: number, node: LexicalNode} It will always return at least 1 node (the start node).

20

*/

21

function $dfs(

22

startNode?: LexicalNode,

23

endNode?: LexicalNode

24

): Array<DFSNode>;

25

26

interface DFSNode {

27

readonly depth: number;

28

readonly node: LexicalNode;

29

}

30

```

31

32

**Usage Examples:**

33

34

```typescript

35

import { $dfs } from "@lexical/utils";

36

import { $getRoot } from "lexical";

37

38

// Traverse entire document tree

39

const allNodes = $dfs();

40

allNodes.forEach(({ node, depth }) => {

41

const indent = ' '.repeat(depth);

42

console.log(`${indent}${node.getType()}`);

43

});

44

45

// Traverse from specific node

46

const paragraphNode = $getNodeByKey('paragraph-key');

47

const paragraphDescendants = $dfs(paragraphNode);

48

49

// Traverse between specific nodes

50

const startNode = $getNodeByKey('start-key');

51

const endNode = $getNodeByKey('end-key');

52

const nodesBetween = $dfs(startNode, endNode);

53

54

// Find all text nodes in document

55

const textNodes = $dfs()

56

.filter(({ node }) => node.getType() === 'text')

57

.map(({ node }) => node);

58

```

59

60

### DFS Iterator

61

62

Memory-efficient iterator for depth-first search that computes nodes on-demand with O(1) memory usage.

63

64

```typescript { .api }

65

/**

66

* $dfs iterator (left to right). Tree traversal is done on the fly as new values are requested with O(1) memory.

67

* @param startNode - The node to start the search, if omitted, it will start at the root node.

68

* @param endNode - The node to end the search, if omitted, it will find all descendants of the startingNode.

69

* @returns An iterator, each yielded value is a DFSNode. It will always return at least 1 node (the start node).

70

*/

71

function $dfsIterator(

72

startNode?: LexicalNode,

73

endNode?: LexicalNode

74

): IterableIterator<DFSNode>;

75

```

76

77

**Usage Examples:**

78

79

```typescript

80

import { $dfsIterator } from "@lexical/utils";

81

82

// Memory-efficient traversal for large documents

83

for (const { node, depth } of $dfsIterator()) {

84

if (node.getType() === 'heading') {

85

console.log(`Found heading at depth ${depth}:`, node.getTextContent());

86

break; // Can exit early without computing remaining nodes

87

}

88

}

89

90

// Process nodes in chunks

91

const iterator = $dfsIterator();

92

const batch: DFSNode[] = [];

93

for (const dfsNode of iterator) {

94

batch.push(dfsNode);

95

if (batch.length === 10) {

96

// Process batch of 10 nodes

97

processBatch(batch);

98

batch.length = 0;

99

}

100

}

101

```

102

103

### Reverse DFS

104

105

Performs right-to-left depth-first search traversal.

106

107

```typescript { .api }

108

/**

109

* Performs a right-to-left preorder tree traversal.

110

* From the starting node it goes to the rightmost child, than backtracks to parent and finds new rightmost path.

111

* @param startNode - The node to start the search, if omitted, it will start at the root node.

112

* @param endNode - The node to end the search, if omitted, it will find all descendants of the startingNode.

113

* @returns An array of DFSNode objects in reverse traversal order.

114

*/

115

function $reverseDfs(

116

startNode?: LexicalNode,

117

endNode?: LexicalNode

118

): Array<DFSNode>;

119

120

function $reverseDfsIterator(

121

startNode?: LexicalNode,

122

endNode?: LexicalNode

123

): IterableIterator<DFSNode>;

124

```

125

126

### Node Navigation Functions

127

128

#### Get Adjacent Caret

129

130

Gets the adjacent caret in the same direction.

131

132

```typescript { .api }

133

/**

134

* Get the adjacent caret in the same direction

135

* @param caret - A caret or null

136

* @returns caret.getAdjacentCaret() or null

137

*/

138

function $getAdjacentCaret<D extends CaretDirection>(

139

caret: null | NodeCaret<D>

140

): null | SiblingCaret<LexicalNode, D>;

141

```

142

143

#### Get Node Depth

144

145

Calculates the depth of a node in the tree hierarchy.

146

147

```typescript { .api }

148

/**

149

* Calculates depth of a node in the tree

150

* @param node - The node to calculate depth for

151

* @returns The depth level (0 for root)

152

*/

153

function $getDepth(node: null | LexicalNode): number;

154

```

155

156

**Usage Examples:**

157

158

```typescript

159

import { $getDepth } from "@lexical/utils";

160

161

const someNode = $getNodeByKey('node-key');

162

const depth = $getDepth(someNode);

163

164

// Use depth for indentation or styling

165

if (depth > 3) {

166

// Apply deep nesting styles

167

addClassNamesToElement(element, 'deeply-nested');

168

}

169

```

170

171

#### Get Next Sibling or Parent Sibling

172

173

Finds the next sibling node or the closest parent sibling with depth information.

174

175

```typescript { .api }

176

/**

177

* Returns the Node sibling when this exists, otherwise the closest parent sibling. For example

178

* R -> P -> T1, T2

179

* -> P2

180

* returns T2 for node T1, P2 for node T2, and null for node P2.

181

* @param node - LexicalNode.

182

* @returns An array (tuple) containing the found Lexical node and the depth difference, or null, if this node doesn't exist.

183

*/

184

function $getNextSiblingOrParentSibling(

185

node: LexicalNode

186

): null | [LexicalNode, number];

187

```

188

189

#### Get Next Right Preorder Node

190

191

Gets the next node in right-to-left preorder traversal.

192

193

```typescript { .api }

194

/**

195

* Performs a right-to-left preorder tree traversal.

196

* From the starting node it goes to the rightmost child, than backtracks to parent and finds new rightmost path.

197

* It will return the next node in traversal sequence after the startingNode.

198

* The traversal is similar to $dfs functions above, but the nodes are visited right-to-left, not left-to-right.

199

* @param startingNode - The node to start the search.

200

* @returns The next node in pre-order right to left traversal sequence or null, if the node does not exist

201

*/

202

function $getNextRightPreorderNode(

203

startingNode: LexicalNode

204

): LexicalNode | null;

205

```

206

207

### Node Search Functions

208

209

#### Find Nearest Node of Type

210

211

Searches up the tree to find the nearest ancestor of a specific type.

212

213

```typescript { .api }

214

/**

215

* Takes a node and traverses up its ancestors (toward the root node)

216

* in order to find a specific type of node.

217

* @param node - the node to begin searching.

218

* @param klass - an instance of the type of node to look for.

219

* @returns the node of type klass that was passed, or null if none exist.

220

*/

221

function $getNearestNodeOfType<T extends ElementNode>(

222

node: LexicalNode,

223

klass: Klass<T>

224

): T | null;

225

```

226

227

**Usage Examples:**

228

229

```typescript

230

import { $getNearestNodeOfType } from "@lexical/utils";

231

import { ListNode, ListItemNode } from "@lexical/list";

232

233

const currentNode = $getSelection()?.getNodes()[0];

234

if (currentNode) {

235

// Find containing list

236

const listNode = $getNearestNodeOfType(currentNode, ListNode);

237

if (listNode) {

238

console.log('Inside a list:', listNode.getListType());

239

}

240

241

// Find containing list item

242

const listItemNode = $getNearestNodeOfType(currentNode, ListItemNode);

243

if (listItemNode) {

244

console.log('Inside list item:', listItemNode.getValue());

245

}

246

}

247

```

248

249

#### Find Nearest Block Element Ancestor

250

251

Returns the nearest block element ancestor or throws an error if none exists.

252

253

```typescript { .api }

254

/**

255

* Returns the element node of the nearest ancestor, otherwise throws an error.

256

* @param startNode - The starting node of the search

257

* @returns The ancestor node found

258

*/

259

function $getNearestBlockElementAncestorOrThrow(

260

startNode: LexicalNode

261

): ElementNode;

262

```

263

264

#### Find Matching Parent

265

266

Searches up the tree for a parent node that matches a test function.

267

268

```typescript { .api }

269

/**

270

* Starts with a node and moves up the tree (toward the root node) to find a matching node based on

271

* the search parameters of the findFn. (Consider JavaScripts' .find() function where a testing function must be

272

* passed as an argument. eg. if( (node) => node.__type === 'div') ) return true; otherwise return false

273

* @param startingNode - The node where the search starts.

274

* @param findFn - A testing function that returns true if the current node satisfies the testing parameters.

275

* @returns A parent node that matches the findFn parameters, or null if one wasn't found.

276

*/

277

function $findMatchingParent<T extends LexicalNode>(

278

startingNode: LexicalNode,

279

findFn: (node: LexicalNode) => node is T

280

): T | null;

281

function $findMatchingParent(

282

startingNode: LexicalNode,

283

findFn: (node: LexicalNode) => boolean

284

): LexicalNode | null;

285

```

286

287

**Usage Examples:**

288

289

```typescript

290

import { $findMatchingParent } from "@lexical/utils";

291

292

const currentNode = $getSelection()?.getNodes()[0];

293

if (currentNode) {

294

// Find nearest editable element

295

const editableParent = $findMatchingParent(

296

currentNode,

297

(node) => $isElementNode(node) && node.isEditable()

298

);

299

300

// Find nearest node with specific attribute

301

const styledParent = $findMatchingParent(

302

currentNode,

303

(node) => node.hasFormat('bold')

304

);

305

306

// Type-safe search with type predicate

307

const tableParent = $findMatchingParent(

308

currentNode,

309

(node): node is TableNode => node instanceof TableNode

310

);

311

}

312

```

313

314

### Child Iterator Functions

315

316

Safe iteration over element children that preserves sibling references during mutation.

317

318

```typescript { .api }

319

/**

320

* Return an iterator that yields each child of node from first to last, taking

321

* care to preserve the next sibling before yielding the value in case the caller

322

* removes the yielded node.

323

* @param node - The node whose children to iterate

324

* @returns An iterator of the node's children

325

*/

326

function $firstToLastIterator(node: ElementNode): Iterable<LexicalNode>;

327

328

/**

329

* Return an iterator that yields each child of node from last to first, taking

330

* care to preserve the previous sibling before yielding the value in case the caller

331

* removes the yielded node.

332

* @param node - The node whose children to iterate

333

* @returns An iterator of the node's children

334

*/

335

function $lastToFirstIterator(node: ElementNode): Iterable<LexicalNode>;

336

```

337

338

**Usage Examples:**

339

340

```typescript

341

import { $firstToLastIterator, $lastToFirstIterator } from "@lexical/utils";

342

343

const parentElement = $getNodeByKey('parent-key') as ElementNode;

344

345

// Safe forward iteration (allows removal during iteration)

346

for (const child of $firstToLastIterator(parentElement)) {

347

if (child.getType() === 'text' && child.getTextContent().trim() === '') {

348

child.remove(); // Safe to remove during iteration

349

}

350

}

351

352

// Safe backward iteration

353

for (const child of $lastToFirstIterator(parentElement)) {

354

if (shouldProcessChild(child)) {

355

processChild(child);

356

}

357

}

358

```

359

360

### Filter Function

361

362

Filters an array of nodes using a transform function.

363

364

```typescript { .api }

365

/**

366

* Filter the nodes

367

* @param nodes - Array of nodes that needs to be filtered

368

* @param filterFn - A filter function that returns node if the current node satisfies the condition otherwise null

369

* @returns Array of filtered nodes

370

*/

371

function $filter<T>(

372

nodes: Array<LexicalNode>,

373

filterFn: (node: LexicalNode) => null | T

374

): Array<T>;

375

```

376

377

**Usage Examples:**

378

379

```typescript

380

import { $filter, $dfs } from "@lexical/utils";

381

import { $isTextNode } from "lexical";

382

383

// Get all text nodes from traversal

384

const allNodes = $dfs();

385

const textNodes = $filter(allNodes, ({ node }) =>

386

$isTextNode(node) ? node : null

387

);

388

389

// Transform and filter in one step

390

const headingTexts = $filter(allNodes, ({ node }) => {

391

if (node.getType() === 'heading') {

392

return node.getTextContent();

393

}

394

return null;

395

});

396

```