0
# Events and Callbacks
1
2
Comprehensive guide to jsTree's event system, including core events, plugin-specific events, and custom event handling. jsTree uses jQuery's event system to provide extensive hooks for tree interactions and state changes.
3
4
## Capabilities
5
6
### Core Events
7
8
Fundamental events triggered during tree lifecycle and operations.
9
10
```javascript { .api }
11
/**
12
* Tree lifecycle events
13
*/
14
15
// Initialization events
16
"init.jstree": InitEvent;
17
"loading.jstree": LoadingEvent;
18
"loaded.jstree": LoadedEvent;
19
"ready.jstree": ReadyEvent;
20
"destroy.jstree": DestroyEvent;
21
22
// Rendering events
23
"redraw.jstree": RedrawEvent;
24
"refresh.jstree": RefreshEvent;
25
26
interface InitEvent {
27
instance: jsTree;
28
}
29
30
interface LoadingEvent {
31
instance: jsTree;
32
}
33
34
interface LoadedEvent {
35
instance: jsTree;
36
}
37
38
interface ReadyEvent {
39
instance: jsTree;
40
}
41
42
interface DestroyEvent {
43
instance: jsTree;
44
}
45
46
interface RedrawEvent {
47
nodes: Array<string>;
48
instance: jsTree;
49
}
50
51
interface RefreshEvent {
52
instance: jsTree;
53
}
54
```
55
56
**Usage Examples:**
57
58
```javascript
59
// Listen for lifecycle events
60
$("#tree").on("init.jstree", function(e, data) {
61
console.log("Tree initialized");
62
});
63
64
$("#tree").on("ready.jstree", function(e, data) {
65
console.log("Tree is ready for interaction");
66
// Safe to call tree methods here
67
const tree = data.instance;
68
tree.open_all();
69
});
70
71
$("#tree").on("destroy.jstree", function(e, data) {
72
console.log("Tree destroyed, cleaning up...");
73
// Cleanup code here
74
});
75
76
// Loading state management
77
$("#tree").on("loading.jstree", function(e, data) {
78
$("#loading-indicator").show();
79
});
80
81
$("#tree").on("loaded.jstree", function(e, data) {
82
$("#loading-indicator").hide();
83
});
84
```
85
86
### Node Events
87
88
Events triggered by node-specific operations and state changes.
89
90
```javascript { .api }
91
/**
92
* Node operation events
93
*/
94
95
// Node state events
96
"open_node.jstree": NodeEvent;
97
"close_node.jstree": NodeEvent;
98
"select_node.jstree": SelectEvent;
99
"deselect_node.jstree": SelectEvent;
100
"changed.jstree": ChangedEvent;
101
102
// Node modification events
103
"create_node.jstree": CreateEvent;
104
"rename_node.jstree": RenameEvent;
105
"delete_node.jstree": DeleteEvent;
106
"move_node.jstree": MoveEvent;
107
"copy_node.jstree": CopyEvent;
108
109
// Node interaction events
110
"activate_node.jstree": ActivateEvent;
111
"hover_node.jstree": HoverEvent;
112
"dehover_node.jstree": HoverEvent;
113
114
interface NodeEvent {
115
node: object;
116
instance: jsTree;
117
}
118
119
interface SelectEvent {
120
node: object;
121
selected: Array<string>;
122
event: Event;
123
instance: jsTree;
124
}
125
126
interface ChangedEvent {
127
selected: Array<string>;
128
event: Event;
129
instance: jsTree;
130
}
131
132
interface CreateEvent {
133
node: object;
134
parent: string;
135
position: number;
136
instance: jsTree;
137
}
138
139
interface RenameEvent {
140
node: object;
141
text: string;
142
old: string;
143
instance: jsTree;
144
}
145
146
interface DeleteEvent {
147
node: object;
148
parent: string;
149
instance: jsTree;
150
}
151
152
interface MoveEvent {
153
node: object;
154
parent: string;
155
position: number;
156
old_parent: string;
157
old_position: number;
158
is_multi: boolean;
159
old_instance: jsTree;
160
new_instance: jsTree;
161
instance: jsTree;
162
}
163
164
interface CopyEvent {
165
node: object;
166
original: object;
167
parent: string;
168
position: number;
169
old_parent: string;
170
old_position: number;
171
is_multi: boolean;
172
old_instance: jsTree;
173
new_instance: jsTree;
174
instance: jsTree;
175
}
176
177
interface ActivateEvent {
178
node: object;
179
event: Event;
180
instance: jsTree;
181
}
182
183
interface HoverEvent {
184
node: object;
185
instance: jsTree;
186
}
187
```
188
189
**Usage Examples:**
190
191
```javascript
192
// Node selection events
193
$("#tree").on("select_node.jstree", function(e, data) {
194
console.log("Selected node:", data.node.text);
195
console.log("All selected:", data.selected);
196
197
// Update UI based on selection
198
updateSelectionInfo(data.selected);
199
});
200
201
$("#tree").on("deselect_node.jstree", function(e, data) {
202
console.log("Deselected node:", data.node.text);
203
204
if (data.selected.length === 0) {
205
clearSelectionInfo();
206
}
207
});
208
209
// Node modification events
210
$("#tree").on("create_node.jstree", function(e, data) {
211
console.log("Created node:", data.node.text, "in parent:", data.parent);
212
213
// Save new node to server
214
saveNodeToServer(data.node);
215
});
216
217
$("#tree").on("rename_node.jstree", function(e, data) {
218
console.log("Renamed node from", data.old, "to", data.text);
219
220
// Update server
221
updateNodeOnServer(data.node.id, {text: data.text});
222
});
223
224
$("#tree").on("delete_node.jstree", function(e, data) {
225
console.log("Deleted node:", data.node.text);
226
227
// Confirm with user and update server
228
if (confirm("Permanently delete this item?")) {
229
deleteNodeOnServer(data.node.id);
230
}
231
});
232
233
// Node movement events
234
$("#tree").on("move_node.jstree", function(e, data) {
235
console.log("Moved node:", {
236
node: data.node.text,
237
from: data.old_parent,
238
to: data.parent,
239
oldPos: data.old_position,
240
newPos: data.position
241
});
242
243
// Update server with new structure
244
updateNodeParent(data.node.id, data.parent, data.position);
245
});
246
247
// Node state events
248
$("#tree").on("open_node.jstree", function(e, data) {
249
console.log("Opened node:", data.node.text);
250
251
// Load additional data when node opens
252
if (data.node.children.length === 0) {
253
loadNodeChildren(data.node.id);
254
}
255
});
256
```
257
258
### Plugin Events
259
260
Events specific to individual plugins.
261
262
```javascript { .api }
263
/**
264
* Checkbox plugin events
265
*/
266
"check_node.jstree": CheckboxEvent;
267
"uncheck_node.jstree": CheckboxEvent;
268
269
interface CheckboxEvent {
270
node: object;
271
selected: Array<string>;
272
event: Event;
273
instance: jsTree;
274
}
275
276
/**
277
* Search plugin events
278
*/
279
"search.jstree": SearchEvent;
280
"clear_search.jstree": ClearSearchEvent;
281
282
interface SearchEvent {
283
nodes: Array<string>;
284
str: string;
285
res: Array<object>;
286
instance: jsTree;
287
}
288
289
interface ClearSearchEvent {
290
instance: jsTree;
291
}
292
293
/**
294
* DnD plugin events (using vakata namespace)
295
*/
296
"dnd_start.vakata": DnDEvent;
297
"dnd_move.vakata": DnDEvent;
298
"dnd_stop.vakata": DnDEvent;
299
300
interface DnDEvent {
301
element: jQuery;
302
target: jQuery;
303
helper: jQuery;
304
event?: Event;
305
}
306
307
/**
308
* State plugin events
309
*/
310
"state_ready.jstree": StateEvent;
311
312
interface StateEvent {
313
instance: jsTree;
314
}
315
```
316
317
**Usage Examples:**
318
319
```javascript
320
// Checkbox events
321
$("#tree").on("check_node.jstree", function(e, data) {
322
console.log("Checked:", data.node.text);
323
console.log("All checked nodes:", data.selected);
324
325
// Update summary
326
updateCheckedSummary(data.selected);
327
});
328
329
$("#tree").on("uncheck_node.jstree", function(e, data) {
330
console.log("Unchecked:", data.node.text);
331
updateCheckedSummary(data.selected);
332
});
333
334
// Search events
335
$("#tree").on("search.jstree", function(e, data) {
336
console.log(`Found ${data.nodes.length} matches for "${data.str}"`);
337
$("#search-results").text(`${data.nodes.length} results`);
338
339
if (data.nodes.length === 0) {
340
$("#no-results").show();
341
} else {
342
$("#no-results").hide();
343
}
344
});
345
346
$("#tree").on("clear_search.jstree", function(e, data) {
347
$("#search-results").text("");
348
$("#no-results").hide();
349
});
350
351
// DnD events
352
$(document).on("dnd_start.vakata", function(e, data) {
353
console.log("Drag started");
354
$("body").addClass("dragging");
355
});
356
357
$(document).on("dnd_stop.vakata", function(e, data) {
358
console.log("Drag stopped");
359
$("body").removeClass("dragging");
360
});
361
362
// State events
363
$("#tree").on("state_ready.jstree", function(e, data) {
364
console.log("State restored");
365
// Tree state has been restored from storage
366
});
367
```
368
369
### Event Binding Patterns
370
371
Common patterns for binding and managing events.
372
373
```javascript { .api }
374
/**
375
* Event binding utilities and patterns
376
*/
377
interface EventBindingPatterns {
378
/** Bind multiple events at once */
379
bindMultiple: (events: object) => void;
380
/** Bind with context */
381
bindWithContext: (event: string, handler: function, context: object) => void;
382
/** One-time event binding */
383
bindOnce: (event: string, handler: function) => void;
384
/** Conditional event binding */
385
bindConditional: (event: string, condition: function, handler: function) => void;
386
}
387
```
388
389
**Usage Examples:**
390
391
```javascript
392
// Multiple event binding
393
$("#tree").on({
394
"select_node.jstree": function(e, data) {
395
handleNodeSelection(data);
396
},
397
"deselect_node.jstree": function(e, data) {
398
handleNodeDeselection(data);
399
},
400
"open_node.jstree": function(e, data) {
401
handleNodeOpen(data);
402
},
403
"close_node.jstree": function(e, data) {
404
handleNodeClose(data);
405
}
406
});
407
408
// Event delegation for dynamic trees
409
$(document).on("select_node.jstree", ".dynamic-tree", function(e, data) {
410
// Handle selection for any tree with class 'dynamic-tree'
411
console.log("Dynamic tree selection:", data.node.text);
412
});
413
414
// One-time event binding
415
$("#tree").one("ready.jstree", function(e, data) {
416
// This will only run once when tree is first ready
417
initializeTreeUI(data.instance);
418
});
419
420
// Conditional event handling
421
$("#tree").on("delete_node.jstree", function(e, data) {
422
// Only handle if user has permission
423
if (hasDeletePermission(data.node)) {
424
confirmAndDelete(data.node);
425
} else {
426
e.preventDefault();
427
showPermissionError();
428
}
429
});
430
431
// Event namespacing for cleanup
432
$("#tree").on("select_node.jstree.myapp", function(e, data) {
433
handleSelection(data);
434
});
435
436
// Later, remove only this namespace
437
$("#tree").off(".myapp");
438
```
439
440
### Custom Events
441
442
Creating and triggering custom events within jsTree context.
443
444
```javascript { .api }
445
/**
446
* Custom event creation and triggering
447
*/
448
interface CustomEvents {
449
/** Trigger custom event */
450
trigger: (event: string, data?: object) => void;
451
/** Create custom event type */
452
createEventType: (name: string, handler: function) => void;
453
}
454
455
// Custom event data structure
456
interface CustomEventData {
457
custom: boolean;
458
data: any;
459
instance: jsTree;
460
[key: string]: any;
461
}
462
```
463
464
**Usage Examples:**
465
466
```javascript
467
// Define custom events
468
$("#tree").on("custom_validation.jstree", function(e, data) {
469
console.log("Custom validation triggered:", data);
470
471
// Perform custom validation logic
472
const isValid = validateNodeData(data.node);
473
474
if (!isValid) {
475
e.preventDefault();
476
showValidationError(data.node);
477
}
478
});
479
480
// Trigger custom events
481
const tree = $("#tree").jstree(true);
482
tree.element.trigger("custom_validation.jstree", {
483
node: tree.get_node("some_node"),
484
validationType: "business_rules",
485
timestamp: Date.now()
486
});
487
488
// Custom event for business logic
489
$("#tree").on("node_audit.jstree", function(e, data) {
490
// Log node operations for audit trail
491
auditLog.record({
492
action: data.action,
493
nodeId: data.node.id,
494
userId: getCurrentUser().id,
495
timestamp: new Date().toISOString()
496
});
497
});
498
499
// Usage in tree operations
500
$("#tree").on("create_node.jstree", function(e, data) {
501
// Trigger audit event
502
$(this).trigger("node_audit.jstree", {
503
action: "create",
504
node: data.node
505
});
506
});
507
```
508
509
### Event Performance and Optimization
510
511
Best practices for event handling performance.
512
513
```javascript { .api }
514
/**
515
* Performance optimization patterns
516
*/
517
interface EventPerformance {
518
/** Debounced event handling */
519
debounce: (handler: function, delay: number) => function;
520
/** Throttled event handling */
521
throttle: (handler: function, limit: number) => function;
522
/** Batch event processing */
523
batchProcess: (events: Array<object>) => void;
524
}
525
```
526
527
**Usage Examples:**
528
529
```javascript
530
// Debounced search
531
let searchTimeout;
532
$("#tree").on("search.jstree", function(e, data) {
533
clearTimeout(searchTimeout);
534
searchTimeout = setTimeout(function() {
535
updateSearchAnalytics(data.str, data.nodes.length);
536
}, 300);
537
});
538
539
// Throttled selection updates
540
let lastSelectionUpdate = 0;
541
$("#tree").on("changed.jstree", function(e, data) {
542
const now = Date.now();
543
if (now - lastSelectionUpdate > 100) { // Throttle to 10fps
544
updateSelectionUI(data.selected);
545
lastSelectionUpdate = now;
546
}
547
});
548
549
// Batch processing for multiple operations
550
let pendingUpdates = [];
551
$("#tree").on("move_node.jstree copy_node.jstree delete_node.jstree", function(e, data) {
552
pendingUpdates.push({
553
event: e.type,
554
data: data,
555
timestamp: Date.now()
556
});
557
558
// Process in batches
559
if (pendingUpdates.length >= 10) {
560
processBatch(pendingUpdates);
561
pendingUpdates = [];
562
}
563
});
564
565
// Process remaining updates periodically
566
setInterval(function() {
567
if (pendingUpdates.length > 0) {
568
processBatch(pendingUpdates);
569
pendingUpdates = [];
570
}
571
}, 1000);
572
573
// Memory cleanup
574
$("#tree").on("destroy.jstree", function() {
575
// Clear timeouts and intervals
576
clearTimeout(searchTimeout);
577
clearInterval(batchProcessor);
578
579
// Remove global event listeners
580
$(document).off(".tree-app");
581
$(window).off(".tree-app");
582
});
583
```
584
585
### Error Events
586
587
Events for error handling and debugging.
588
589
```javascript { .api }
590
/**
591
* Error events and handling
592
*/
593
"error.jstree": ErrorEvent;
594
595
interface ErrorEvent {
596
error: string;
597
plugin: string;
598
id: string;
599
reason: string;
600
data: any;
601
instance: jsTree;
602
}
603
```
604
605
**Usage Examples:**
606
607
```javascript
608
// Global error handling
609
$("#tree").on("error.jstree", function(e, data) {
610
console.error("jsTree error:", {
611
error: data.error,
612
plugin: data.plugin,
613
reason: data.reason,
614
data: data.data
615
});
616
617
// Show user-friendly error message
618
showErrorNotification("An error occurred while updating the tree");
619
620
// Log to error tracking service
621
errorTracker.log("jstree_error", {
622
error: data.error,
623
plugin: data.plugin,
624
reason: data.reason,
625
url: window.location.href,
626
userAgent: navigator.userAgent
627
});
628
});
629
630
// Plugin-specific error handling
631
$("#tree").on("error.jstree", function(e, data) {
632
if (data.plugin === "search") {
633
$("#search-error").show().text("Search failed: " + data.reason);
634
} else if (data.plugin === "core" && data.error === "load_node") {
635
showRetryDialog("Failed to load tree data. Retry?", function() {
636
// Retry loading
637
data.instance.refresh();
638
});
639
}
640
});
641
```
642
643
## Types
644
645
```javascript { .api }
646
// Base event interface
647
interface TreeEvent {
648
type: string;
649
target: Element;
650
currentTarget: Element;
651
instance: jsTree;
652
timeStamp: number;
653
}
654
655
// Event data union type
656
type EventData =
657
| InitEvent
658
| NodeEvent
659
| SelectEvent
660
| CreateEvent
661
| RenameEvent
662
| DeleteEvent
663
| MoveEvent
664
| CopyEvent
665
| SearchEvent
666
| CheckboxEvent
667
| ErrorEvent;
668
669
// Event handler type
670
type EventHandler<T = EventData> = (event: jQuery.Event, data: T) => void;
671
672
// Event binding options
673
interface EventBindingOptions {
674
namespace?: string;
675
once?: boolean;
676
passive?: boolean;
677
capture?: boolean;
678
}
679
```