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
State management, node types, sorting, context menus, and other specialized functionality. These plugins extend jsTree's core capabilities with additional features for enhanced tree management and user interaction.
Persistent state management for preserving tree state across sessions.
/**
* State plugin configuration
*/
interface StateConfig {
/** Storage key for state data (default: "jstree") */
key?: string;
/** Events that trigger state save (default: ["changed.jstree", "open_node.jstree", "close_node.jstree"]) */
events?: Array<string>;
/** Time to live for state data in seconds (default: 3600) */
ttl?: number;
/** Custom filter function to determine what to save */
filter?: function;
/** Preserve loaded state on refresh (default: false) */
preserve_loaded?: boolean;
}
/**
* Save current tree state to storage
*/
save_state(): void;
/**
* Restore tree state from storage
*/
restore_state(): void;
/**
* Get current tree state object
* @returns State object containing selected nodes, opened nodes, etc.
*/
get_state(): object;
/**
* Set tree state from object
* @param state - State object to restore
* @param callback - Function to call when state is restored
*/
set_state(state: object, callback?: function): void;
/**
* Clear saved state from storage
*/
clear_state(): void;Usage Examples:
// Initialize with state plugin
$("#tree").jstree({
"plugins": ["state"],
"state": {
"key": "my-tree-state",
"events": ["changed.jstree", "open_node.jstree", "close_node.jstree"],
"ttl": 7200, // 2 hours
"filter": function(state) {
// Only save certain parts of state
return {
core: {
selected: state.core.selected,
open: state.core.open
}
};
}
}
});
// Manual state operations
const tree = $("#tree").jstree(true);
// Save current state
tree.save_state();
// Get state object
const currentState = tree.get_state();
console.log("Current state:", currentState);
// Restore specific state
tree.set_state({
core: {
selected: ["node_1", "node_2"],
open: ["parent_1", "parent_2"]
}
});
// Clear saved state
tree.clear_state();Node type system with type-specific icons, behavior, and validation.
/**
* Types plugin configuration
*/
interface TypesConfig {
/** Default node type (default: "default") */
default?: string;
/** Type definitions */
types?: {[type: string]: TypeDefinition};
}
interface TypeDefinition {
/** Maximum number of children (default: -1 for unlimited) */
max_children?: number;
/** Maximum depth for this type (default: -1 for unlimited) */
max_depth?: number;
/** Valid children types (default: -1 for all types) */
valid_children?: Array<string>|number;
/** Icon class or URL for this type */
icon?: string|boolean;
/** Additional node attributes */
li_attr?: object;
/** Additional anchor attributes */
a_attr?: object;
}
/**
* Get node type
* @param obj - Node to get type for
* @returns Node type string
*/
get_type(obj: string|object): string;
/**
* Set node type
* @param obj - Node to set type for
* @param type - New type name
* @returns True on success
*/
set_type(obj: string|object, type: string): boolean;Usage Examples:
// Initialize with types
$("#tree").jstree({
"plugins": ["types"],
"types": {
"default": {
"icon": "fa fa-file"
},
"folder": {
"icon": "fa fa-folder",
"valid_children": ["folder", "file"],
"max_children": 50
},
"file": {
"icon": "fa fa-file-o",
"valid_children": [],
"max_children": 0
},
"image": {
"icon": "fa fa-image",
"valid_children": [],
"li_attr": {"class": "image-node"}
}
},
"core": {
"data": [
{"text": "Documents", "type": "folder", "children": [
{"text": "report.pdf", "type": "file"},
{"text": "photo.jpg", "type": "image"}
]}
]
}
});
// Type operations
const tree = $("#tree").jstree(true);
// Get node type
const nodeType = tree.get_type("node_1");
console.log("Node type:", nodeType);
// Set node type
tree.set_type("node_1", "folder");
// Create typed nodes
tree.create_node("#", {
"text": "New Folder",
"type": "folder"
});Automatic and manual sorting of tree nodes.
/**
* Sort plugin configuration
*/
interface SortConfig {
/** Custom sort function */
sort?: function;
/** Case sensitive sorting (default: false) */
case_sensitive?: boolean;
/** Natural sorting (handles numbers correctly) */
natural?: boolean;
}
/**
* Sort nodes alphabetically
* @param obj - Node to sort children of (default: root)
* @param deep - Also sort all descendants (default: false)
*/
sort(obj?: string|object, deep?: boolean): void;Usage Examples:
// Initialize with sorting
$("#tree").jstree({
"plugins": ["sort"],
"sort": {
"case_sensitive": false,
"natural": true,
"sort": function(a, b) {
// Custom sort logic
const nodeA = this.get_node(a);
const nodeB = this.get_node(b);
// Sort folders before files
if (nodeA.type === "folder" && nodeB.type !== "folder") return -1;
if (nodeA.type !== "folder" && nodeB.type === "folder") return 1;
// Then sort alphabetically
return nodeA.text.toLowerCase().localeCompare(nodeB.text.toLowerCase());
}
}
});
// Manual sorting
const tree = $("#tree").jstree(true);
tree.sort(); // Sort root level
tree.sort("folder_1", true); // Sort folder and all descendantsCustomizable right-click context menus with conditional items.
/**
* Context menu plugin configuration
*/
interface ContextMenuConfig {
/** Select node when showing context menu (default: true) */
select_node?: boolean;
/** Show menu at node position vs cursor position (default: true) */
show_at_node?: boolean;
/** Menu items configuration */
items?: object|function;
}
interface ContextMenuItem {
/** Display label */
label?: string;
/** Icon class */
icon?: string;
/** Action function */
action?: function;
/** Submenu items */
submenu?: {[key: string]: ContextMenuItem};
/** Separator */
separator_before?: boolean;
separator_after?: boolean;
/** Conditional visibility */
visible?: boolean|function;
/** Conditional enablement */
disabled?: boolean|function;
/** Custom class name */
_class?: string;
}
/**
* Show context menu programmatically
* @param obj - Node to show menu for
* @param x - X coordinate (optional)
* @param y - Y coordinate (optional)
* @param e - Original event (optional)
*/
show_contextmenu(obj: string|object, x?: number, y?: number, e?: Event): void;Usage Examples:
// Initialize with context menu
$("#tree").jstree({
"plugins": ["contextmenu"],
"contextmenu": {
"items": function(node) {
return {
"create": {
"label": "Create",
"icon": "fa fa-plus",
"action": function(data) {
const inst = $.jstree.reference(data.reference);
const obj = inst.get_node(data.reference);
inst.create_node(obj, {}, "last", function(new_node) {
setTimeout(function() { inst.edit(new_node); }, 0);
});
}
},
"rename": {
"label": "Rename",
"icon": "fa fa-edit",
"action": function(data) {
const inst = $.jstree.reference(data.reference);
const obj = inst.get_node(data.reference);
inst.edit(obj);
}
},
"delete": {
"label": "Delete",
"icon": "fa fa-trash",
"separator_before": true,
"action": function(data) {
const inst = $.jstree.reference(data.reference);
const obj = inst.get_node(data.reference);
if (inst.is_selected(obj)) {
inst.delete_node(inst.get_selected());
} else {
inst.delete_node(obj);
}
}
},
"properties": {
"label": "Properties",
"icon": "fa fa-info",
"separator_before": true,
"visible": function(key, opt) {
// Only show for certain node types
return node.type === "file";
},
"action": function(data) {
showNodeProperties(data.reference);
}
}
};
}
}
});
// Dynamic context menu
$("#tree").jstree({
"plugins": ["contextmenu", "types"],
"contextmenu": {
"items": function(node) {
const items = {};
// Add type-specific items
if (node.type === "folder") {
items.create_folder = {
"label": "New Folder",
"action": function(data) {
// Create folder logic
}
};
items.create_file = {
"label": "New File",
"action": function(data) {
// Create file logic
}
};
}
// Add common items
items.rename = {
"label": "Rename",
"action": function(data) {
const inst = $.jstree.reference(data.reference);
inst.edit(data.reference);
}
};
return items;
}
}
});Ensures unique node names within parent nodes.
/**
* Unique plugin configuration
*/
interface UniqueConfig {
/** Case sensitive uniqueness check (default: false) */
case_sensitive?: boolean;
/** Trim whitespace when checking (default: true) */
trim_whitespace?: boolean;
/** Custom duplicate callback */
duplicate?: function;
}Usage Examples:
// Initialize with unique names
$("#tree").jstree({
"plugins": ["unique"],
"unique": {
"case_sensitive": false,
"duplicate": function(name, counter) {
// Custom naming for duplicates
return name + " (" + counter + ")";
}
}
});
// Automatic duplicate handling
const tree = $("#tree").jstree(true);
tree.create_node("#", {"text": "New Item"}); // Creates "New Item"
tree.create_node("#", {"text": "New Item"}); // Creates "New Item (2)"
tree.create_node("#", {"text": "new item"}); // Creates "new item (3)" if case_sensitive: falseExtends clickable area to full row width for easier interaction.
/**
* Wholerow plugin configuration
*/
interface WholerowConfig {
/** Enable wholerow selection */
wholerow?: boolean;
}Usage Examples:
// Initialize with wholerow
$("#tree").jstree({
"plugins": ["wholerow"],
"wholerow": {
"wholerow": true
}
});
// The entire row becomes clickable, not just the text/iconTracks and reports changes to the tree structure.
/**
* Get array of changed node IDs since last call
* @param callback - Function to call with changed nodes
* @returns Array of changed node IDs
*/
get_changed(callback?: function): Array<string>;Usage Examples:
// Initialize with changed tracking
$("#tree").jstree({
"plugins": ["changed"]
});
// Get changed nodes
const tree = $("#tree").jstree(true);
const changedNodes = tree.get_changed();
console.log("Changed nodes:", changedNodes);
// Listen for changes
$("#tree").on("changed.jstree", function(e, data) {
console.log("Tree changed:", data.changed);
});Allows conditional node selection via callback functions.
/**
* Conditionalselect plugin configuration
*/
interface ConditionalselectConfig {
/** Function to determine if node can be selected */
conditionalselect?: function;
}
type ConditionalSelectFunction = (node: object, event: Event) => boolean;Usage Examples:
// Initialize with conditional selection
$("#tree").jstree({
"plugins": ["conditionalselect"],
"conditionalselect": {
"conditionalselect": function(node, event) {
// Only allow selection of leaf nodes
return this.is_leaf(node);
}
}
});
// More complex conditions
$("#tree").jstree({
"plugins": ["conditionalselect", "types"],
"conditionalselect": {
"conditionalselect": function(node, event) {
// Prevent selection based on node type and user permissions
if (node.type === "readonly") return false;
if (node.li_attr && node.li_attr["data-locked"] === "true") return false;
// Check user permissions
const userRole = getCurrentUserRole();
if (userRole === "viewer" && node.type === "admin") return false;
return true;
}
}
});Enables loading multiple nodes in a single request for performance optimization.
/**
* Massload plugin configuration
*/
interface MassloadConfig {
/** URL for mass loading endpoint */
url?: string;
/** Request data configuration */
data?: function;
}Usage Examples:
// Initialize with mass loading
$("#tree").jstree({
"plugins": ["massload"],
"massload": {
"url": "/api/nodes/massload",
"data": function(nodes) {
return {
"ids": nodes.join(","),
"timestamp": Date.now()
};
}
}
});
// Server endpoint should return data for multiple nodes at once
// instead of making individual requests for each nodeCommon patterns for combining multiple plugins effectively.
/**
* Comprehensive tree setup with multiple plugins
*/
interface ComprehensiveTreeConfig {
plugins: Array<string>;
core: CoreConfig;
checkbox?: CheckboxConfig;
contextmenu?: ContextMenuConfig;
dnd?: DnDConfig;
search?: SearchConfig;
state?: StateConfig;
types?: TypesConfig;
sort?: SortConfig;
unique?: UniqueConfig;
wholerow?: WholerowConfig;
conditionalselect?: ConditionalselectConfig;
}Usage Examples:
// Full-featured tree setup
$("#tree").jstree({
"plugins": [
"checkbox", "contextmenu", "dnd", "search",
"state", "types", "sort", "unique", "wholerow"
],
"core": {
"check_callback": true,
"themes": {"stripes": true}
},
"checkbox": {
"three_state": true,
"cascade": "up+down+undetermined"
},
"contextmenu": {
"items": contextMenuBuilder
},
"dnd": {
"check_while_dragging": true,
"drag_selection": true
},
"search": {
"fuzzy": true,
"show_only_matches": true
},
"state": {
"key": "full-tree-state"
},
"types": {
"folder": {"icon": "fa fa-folder"},
"file": {"icon": "fa fa-file"}
},
"sort": {
"case_sensitive": false
}
});
// Event coordination across plugins
$("#tree").on("ready.jstree", function() {
// Tree is fully initialized with all plugins
const tree = $(this).jstree(true);
// Restore state
tree.restore_state();
// Enable search UI
$("#search-input").on("keyup", function() {
tree.search($(this).val());
});
});// Plugin-enhanced node object
interface EnhancedTreeNode extends TreeNode {
// State plugin
state?: {
loaded?: boolean;
opened?: boolean;
selected?: boolean;
disabled?: boolean;
};
// Types plugin
type?: string;
// Checkbox plugin (when enabled)
state?: {
checked?: boolean;
undetermined?: boolean;
checkbox_disabled?: boolean;
};
// Plugin-specific data
data?: {
[key: string]: any;
};
}
// Combined plugin settings
interface PluginSettings {
[plugin: string]: any;
}