jQuery tree plugin for creating interactive tree components with drag & drop, inline editing, checkboxes, search, and customizable node types
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
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.
Fundamental events triggered during tree lifecycle and operations.
/**
* Tree lifecycle events
*/
// Initialization events
"init.jstree": InitEvent;
"loading.jstree": LoadingEvent;
"loaded.jstree": LoadedEvent;
"ready.jstree": ReadyEvent;
"destroy.jstree": DestroyEvent;
// Rendering events
"redraw.jstree": RedrawEvent;
"refresh.jstree": RefreshEvent;
interface InitEvent {
instance: jsTree;
}
interface LoadingEvent {
instance: jsTree;
}
interface LoadedEvent {
instance: jsTree;
}
interface ReadyEvent {
instance: jsTree;
}
interface DestroyEvent {
instance: jsTree;
}
interface RedrawEvent {
nodes: Array<string>;
instance: jsTree;
}
interface RefreshEvent {
instance: jsTree;
}Usage Examples:
// Listen for lifecycle events
$("#tree").on("init.jstree", function(e, data) {
console.log("Tree initialized");
});
$("#tree").on("ready.jstree", function(e, data) {
console.log("Tree is ready for interaction");
// Safe to call tree methods here
const tree = data.instance;
tree.open_all();
});
$("#tree").on("destroy.jstree", function(e, data) {
console.log("Tree destroyed, cleaning up...");
// Cleanup code here
});
// Loading state management
$("#tree").on("loading.jstree", function(e, data) {
$("#loading-indicator").show();
});
$("#tree").on("loaded.jstree", function(e, data) {
$("#loading-indicator").hide();
});Events triggered by node-specific operations and state changes.
/**
* Node operation events
*/
// Node state events
"open_node.jstree": NodeEvent;
"close_node.jstree": NodeEvent;
"select_node.jstree": SelectEvent;
"deselect_node.jstree": SelectEvent;
"changed.jstree": ChangedEvent;
// Node modification events
"create_node.jstree": CreateEvent;
"rename_node.jstree": RenameEvent;
"delete_node.jstree": DeleteEvent;
"move_node.jstree": MoveEvent;
"copy_node.jstree": CopyEvent;
// Node interaction events
"activate_node.jstree": ActivateEvent;
"hover_node.jstree": HoverEvent;
"dehover_node.jstree": HoverEvent;
interface NodeEvent {
node: object;
instance: jsTree;
}
interface SelectEvent {
node: object;
selected: Array<string>;
event: Event;
instance: jsTree;
}
interface ChangedEvent {
selected: Array<string>;
event: Event;
instance: jsTree;
}
interface CreateEvent {
node: object;
parent: string;
position: number;
instance: jsTree;
}
interface RenameEvent {
node: object;
text: string;
old: string;
instance: jsTree;
}
interface DeleteEvent {
node: object;
parent: string;
instance: jsTree;
}
interface MoveEvent {
node: object;
parent: string;
position: number;
old_parent: string;
old_position: number;
is_multi: boolean;
old_instance: jsTree;
new_instance: jsTree;
instance: jsTree;
}
interface CopyEvent {
node: object;
original: object;
parent: string;
position: number;
old_parent: string;
old_position: number;
is_multi: boolean;
old_instance: jsTree;
new_instance: jsTree;
instance: jsTree;
}
interface ActivateEvent {
node: object;
event: Event;
instance: jsTree;
}
interface HoverEvent {
node: object;
instance: jsTree;
}Usage Examples:
// Node selection events
$("#tree").on("select_node.jstree", function(e, data) {
console.log("Selected node:", data.node.text);
console.log("All selected:", data.selected);
// Update UI based on selection
updateSelectionInfo(data.selected);
});
$("#tree").on("deselect_node.jstree", function(e, data) {
console.log("Deselected node:", data.node.text);
if (data.selected.length === 0) {
clearSelectionInfo();
}
});
// Node modification events
$("#tree").on("create_node.jstree", function(e, data) {
console.log("Created node:", data.node.text, "in parent:", data.parent);
// Save new node to server
saveNodeToServer(data.node);
});
$("#tree").on("rename_node.jstree", function(e, data) {
console.log("Renamed node from", data.old, "to", data.text);
// Update server
updateNodeOnServer(data.node.id, {text: data.text});
});
$("#tree").on("delete_node.jstree", function(e, data) {
console.log("Deleted node:", data.node.text);
// Confirm with user and update server
if (confirm("Permanently delete this item?")) {
deleteNodeOnServer(data.node.id);
}
});
// Node movement events
$("#tree").on("move_node.jstree", function(e, data) {
console.log("Moved node:", {
node: data.node.text,
from: data.old_parent,
to: data.parent,
oldPos: data.old_position,
newPos: data.position
});
// Update server with new structure
updateNodeParent(data.node.id, data.parent, data.position);
});
// Node state events
$("#tree").on("open_node.jstree", function(e, data) {
console.log("Opened node:", data.node.text);
// Load additional data when node opens
if (data.node.children.length === 0) {
loadNodeChildren(data.node.id);
}
});Events specific to individual plugins.
/**
* Checkbox plugin events
*/
"check_node.jstree": CheckboxEvent;
"uncheck_node.jstree": CheckboxEvent;
interface CheckboxEvent {
node: object;
selected: Array<string>;
event: Event;
instance: jsTree;
}
/**
* Search plugin events
*/
"search.jstree": SearchEvent;
"clear_search.jstree": ClearSearchEvent;
interface SearchEvent {
nodes: Array<string>;
str: string;
res: Array<object>;
instance: jsTree;
}
interface ClearSearchEvent {
instance: jsTree;
}
/**
* DnD plugin events (using vakata namespace)
*/
"dnd_start.vakata": DnDEvent;
"dnd_move.vakata": DnDEvent;
"dnd_stop.vakata": DnDEvent;
interface DnDEvent {
element: jQuery;
target: jQuery;
helper: jQuery;
event?: Event;
}
/**
* State plugin events
*/
"state_ready.jstree": StateEvent;
interface StateEvent {
instance: jsTree;
}Usage Examples:
// Checkbox events
$("#tree").on("check_node.jstree", function(e, data) {
console.log("Checked:", data.node.text);
console.log("All checked nodes:", data.selected);
// Update summary
updateCheckedSummary(data.selected);
});
$("#tree").on("uncheck_node.jstree", function(e, data) {
console.log("Unchecked:", data.node.text);
updateCheckedSummary(data.selected);
});
// Search events
$("#tree").on("search.jstree", function(e, data) {
console.log(`Found ${data.nodes.length} matches for "${data.str}"`);
$("#search-results").text(`${data.nodes.length} results`);
if (data.nodes.length === 0) {
$("#no-results").show();
} else {
$("#no-results").hide();
}
});
$("#tree").on("clear_search.jstree", function(e, data) {
$("#search-results").text("");
$("#no-results").hide();
});
// DnD events
$(document).on("dnd_start.vakata", function(e, data) {
console.log("Drag started");
$("body").addClass("dragging");
});
$(document).on("dnd_stop.vakata", function(e, data) {
console.log("Drag stopped");
$("body").removeClass("dragging");
});
// State events
$("#tree").on("state_ready.jstree", function(e, data) {
console.log("State restored");
// Tree state has been restored from storage
});Common patterns for binding and managing events.
/**
* Event binding utilities and patterns
*/
interface EventBindingPatterns {
/** Bind multiple events at once */
bindMultiple: (events: object) => void;
/** Bind with context */
bindWithContext: (event: string, handler: function, context: object) => void;
/** One-time event binding */
bindOnce: (event: string, handler: function) => void;
/** Conditional event binding */
bindConditional: (event: string, condition: function, handler: function) => void;
}Usage Examples:
// Multiple event binding
$("#tree").on({
"select_node.jstree": function(e, data) {
handleNodeSelection(data);
},
"deselect_node.jstree": function(e, data) {
handleNodeDeselection(data);
},
"open_node.jstree": function(e, data) {
handleNodeOpen(data);
},
"close_node.jstree": function(e, data) {
handleNodeClose(data);
}
});
// Event delegation for dynamic trees
$(document).on("select_node.jstree", ".dynamic-tree", function(e, data) {
// Handle selection for any tree with class 'dynamic-tree'
console.log("Dynamic tree selection:", data.node.text);
});
// One-time event binding
$("#tree").one("ready.jstree", function(e, data) {
// This will only run once when tree is first ready
initializeTreeUI(data.instance);
});
// Conditional event handling
$("#tree").on("delete_node.jstree", function(e, data) {
// Only handle if user has permission
if (hasDeletePermission(data.node)) {
confirmAndDelete(data.node);
} else {
e.preventDefault();
showPermissionError();
}
});
// Event namespacing for cleanup
$("#tree").on("select_node.jstree.myapp", function(e, data) {
handleSelection(data);
});
// Later, remove only this namespace
$("#tree").off(".myapp");Creating and triggering custom events within jsTree context.
/**
* Custom event creation and triggering
*/
interface CustomEvents {
/** Trigger custom event */
trigger: (event: string, data?: object) => void;
/** Create custom event type */
createEventType: (name: string, handler: function) => void;
}
// Custom event data structure
interface CustomEventData {
custom: boolean;
data: any;
instance: jsTree;
[key: string]: any;
}Usage Examples:
// Define custom events
$("#tree").on("custom_validation.jstree", function(e, data) {
console.log("Custom validation triggered:", data);
// Perform custom validation logic
const isValid = validateNodeData(data.node);
if (!isValid) {
e.preventDefault();
showValidationError(data.node);
}
});
// Trigger custom events
const tree = $("#tree").jstree(true);
tree.element.trigger("custom_validation.jstree", {
node: tree.get_node("some_node"),
validationType: "business_rules",
timestamp: Date.now()
});
// Custom event for business logic
$("#tree").on("node_audit.jstree", function(e, data) {
// Log node operations for audit trail
auditLog.record({
action: data.action,
nodeId: data.node.id,
userId: getCurrentUser().id,
timestamp: new Date().toISOString()
});
});
// Usage in tree operations
$("#tree").on("create_node.jstree", function(e, data) {
// Trigger audit event
$(this).trigger("node_audit.jstree", {
action: "create",
node: data.node
});
});Best practices for event handling performance.
/**
* Performance optimization patterns
*/
interface EventPerformance {
/** Debounced event handling */
debounce: (handler: function, delay: number) => function;
/** Throttled event handling */
throttle: (handler: function, limit: number) => function;
/** Batch event processing */
batchProcess: (events: Array<object>) => void;
}Usage Examples:
// Debounced search
let searchTimeout;
$("#tree").on("search.jstree", function(e, data) {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(function() {
updateSearchAnalytics(data.str, data.nodes.length);
}, 300);
});
// Throttled selection updates
let lastSelectionUpdate = 0;
$("#tree").on("changed.jstree", function(e, data) {
const now = Date.now();
if (now - lastSelectionUpdate > 100) { // Throttle to 10fps
updateSelectionUI(data.selected);
lastSelectionUpdate = now;
}
});
// Batch processing for multiple operations
let pendingUpdates = [];
$("#tree").on("move_node.jstree copy_node.jstree delete_node.jstree", function(e, data) {
pendingUpdates.push({
event: e.type,
data: data,
timestamp: Date.now()
});
// Process in batches
if (pendingUpdates.length >= 10) {
processBatch(pendingUpdates);
pendingUpdates = [];
}
});
// Process remaining updates periodically
setInterval(function() {
if (pendingUpdates.length > 0) {
processBatch(pendingUpdates);
pendingUpdates = [];
}
}, 1000);
// Memory cleanup
$("#tree").on("destroy.jstree", function() {
// Clear timeouts and intervals
clearTimeout(searchTimeout);
clearInterval(batchProcessor);
// Remove global event listeners
$(document).off(".tree-app");
$(window).off(".tree-app");
});Events for error handling and debugging.
/**
* Error events and handling
*/
"error.jstree": ErrorEvent;
interface ErrorEvent {
error: string;
plugin: string;
id: string;
reason: string;
data: any;
instance: jsTree;
}Usage Examples:
// Global error handling
$("#tree").on("error.jstree", function(e, data) {
console.error("jsTree error:", {
error: data.error,
plugin: data.plugin,
reason: data.reason,
data: data.data
});
// Show user-friendly error message
showErrorNotification("An error occurred while updating the tree");
// Log to error tracking service
errorTracker.log("jstree_error", {
error: data.error,
plugin: data.plugin,
reason: data.reason,
url: window.location.href,
userAgent: navigator.userAgent
});
});
// Plugin-specific error handling
$("#tree").on("error.jstree", function(e, data) {
if (data.plugin === "search") {
$("#search-error").show().text("Search failed: " + data.reason);
} else if (data.plugin === "core" && data.error === "load_node") {
showRetryDialog("Failed to load tree data. Retry?", function() {
// Retry loading
data.instance.refresh();
});
}
});// Base event interface
interface TreeEvent {
type: string;
target: Element;
currentTarget: Element;
instance: jsTree;
timeStamp: number;
}
// Event data union type
type EventData =
| InitEvent
| NodeEvent
| SelectEvent
| CreateEvent
| RenameEvent
| DeleteEvent
| MoveEvent
| CopyEvent
| SearchEvent
| CheckboxEvent
| ErrorEvent;
// Event handler type
type EventHandler<T = EventData> = (event: jQuery.Event, data: T) => void;
// Event binding options
interface EventBindingOptions {
namespace?: string;
once?: boolean;
passive?: boolean;
capture?: boolean;
}