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
Advanced drag and drop support with customizable constraints, foreign element support, and visual feedback during operations. The DnD plugin enables intuitive tree manipulation through mouse and touch interactions.
Configuration options for drag and drop behavior.
/**
* Drag and Drop plugin configuration
*/
interface DnDConfig {
/** Enable copy mode (hold Ctrl to copy instead of move) */
copy?: boolean;
/** Time before opening closed nodes during drag (ms, default: 500) */
open_timeout?: number;
/** Time before moving node to new position (ms, default: 500) */
move_timeout?: number;
/** Function or boolean to determine if node is draggable */
is_draggable?: boolean|function;
/** Check constraints while dragging (default: true) */
check_while_dragging?: boolean;
/** Allow dragging multiple selected nodes (default: true) */
drag_selection?: boolean;
/** Enable touch support (boolean or "selected" for selected nodes only) */
touch?: boolean|string;
/** Larger drop targets for easier dropping (default: false) */
large_drop_target?: boolean;
/** Larger drag handles for easier dragging (default: false) */
large_drag_target?: boolean;
/** Use HTML5 drag and drop API (default: true) */
use_html5?: boolean;
}
// Usage in tree initialization
const config = {
"plugins": ["dnd"],
"dnd": {
"copy": false,
"check_while_dragging": true,
"drag_selection": true,
"touch": true,
"large_drop_target": false
}
};Usage Examples:
// Initialize tree with drag and drop
$("#tree").jstree({
"core": {
"data": [
"Item 1", "Item 2",
{"text": "Folder", "children": ["Sub Item 1", "Sub Item 2"]}
],
"check_callback": true // Enable modifications
},
"plugins": ["dnd"],
"dnd": {
"copy": false, // Move by default
"check_while_dragging": true,
"drag_selection": true
}
});
// Get instance for DnD operations
const tree = $("#tree").jstree(true);Methods and configuration for controlling which nodes can be dragged.
/**
* Function to determine if a node is draggable
* @param nodes - Array of nodes being dragged
* @param event - Original drag event
* @returns True if nodes can be dragged
*/
type IsDraggableFunction = (nodes: Array<object>, event: Event) => boolean;
/**
* Configuration for draggable behavior
*/
interface DraggableConfig {
/** Global draggable setting */
is_draggable: boolean|IsDraggableFunction;
/** Per-node draggable setting in node data */
node_draggable?: boolean;
}Usage Examples:
// Global draggable function
$("#tree").jstree({
"plugins": ["dnd"],
"dnd": {
"is_draggable": function(nodes, e) {
// Only allow dragging leaf nodes
return nodes.every(node => tree.is_leaf(node));
}
}
});
// Per-node draggable setting
$("#tree").jstree({
"core": {
"data": [
{"text": "Draggable Item", "draggable": true},
{"text": "Fixed Item", "draggable": false},
{"text": "Default Item"} // Uses global setting
]
},
"plugins": ["dnd"]
});
// Dynamic draggable control
$("#tree").jstree({
"plugins": ["dnd"],
"dnd": {
"is_draggable": function(nodes, e) {
// Allow dragging only if user has permission
return nodes.every(node =>
node.li_attr && node.li_attr['data-user-can-move'] === 'true'
);
}
}
});Configuration for controlling where nodes can be dropped.
/**
* Check callback function for validating drop operations
* @param operation - Type of operation ("move_node", "copy_node")
* @param node - Node being moved/copied
* @param parent - Target parent node
* @param position - Position within parent
* @param more - Additional operation data including DnD info
* @returns True if operation is allowed
*/
type CheckCallback = (
operation: string,
node: object,
parent: object,
position: number|string,
more: object
) => boolean;Usage Examples:
// Restrict drop targets with check_callback
$("#tree").jstree({
"core": {
"check_callback": function(operation, node, parent, position, more) {
// Allow drops only into folders
if (operation === "move_node") {
return parent.type === "folder";
}
return true;
}
},
"plugins": ["dnd", "types"],
"types": {
"folder": {"icon": "fa fa-folder"},
"file": {"icon": "fa fa-file"}
}
});
// Complex drop validation
$("#tree").jstree({
"core": {
"check_callback": function(op, node, parent, pos, more) {
if (op === "move_node") {
// Prevent dropping parents into their children
if (more.core && more.dnd) {
return !tree.is_ancestor(node, parent);
}
// Prevent certain node types from being parents
if (parent.type === "file") {
return false;
}
// Limit number of children
if (tree.get_children_dom(parent).length >= 10) {
return false;
}
}
return true;
}
},
"plugins": ["dnd", "types"]
});Configuration and behavior for copy and move operations.
/**
* Copy/Move operation configuration
*/
interface CopyMoveConfig {
/** Default operation mode */
copy: boolean;
/** Modifier keys for copy mode */
copy_modifier?: string; // "ctrl", "alt", "shift", "meta"
/** Always copy specific node types */
always_copy?: Array<string>;
/** Never copy specific node types */
never_copy?: Array<string>;
}Usage Examples:
// Copy by default, move with Shift key
$("#tree").jstree({
"plugins": ["dnd"],
"dnd": {
"copy": true, // Copy by default
"copy_modifier": "shift" // Hold Shift to move instead
}
});
// Copy specific node types automatically
$("#tree").jstree({
"plugins": ["dnd", "types"],
"dnd": {
"copy": false, // Move by default
"always_copy": ["template", "reference"] // Always copy these types
},
"types": {
"template": {"icon": "fa fa-copy"},
"reference": {"icon": "fa fa-link"},
"regular": {"icon": "fa fa-file"}
}
});
// Listen for copy/move operations
$("#tree").on("move_node.jstree", function(e, data) {
if (data.original) {
console.log("Node moved from", data.original.parent, "to", data.parent);
}
});
$("#tree").on("copy_node.jstree", function(e, data) {
console.log("Node copied:", data.node.text);
});Configuration for touch devices and mobile interactions.
/**
* Touch support configuration
*/
interface TouchConfig {
/** Enable touch support */
touch: boolean|"selected";
/** Touch drag threshold (pixels) */
touch_threshold?: number;
/** Long press duration for drag start (ms) */
long_press_timeout?: number;
/** Enable touch feedback */
touch_feedback?: boolean;
}Usage Examples:
// Full touch support
$("#tree").jstree({
"plugins": ["dnd"],
"dnd": {
"touch": true,
"large_drop_target": true, // Easier targeting on mobile
"large_drag_target": true
}
});
// Touch support for selected nodes only
$("#tree").jstree({
"plugins": ["dnd"],
"dnd": {
"touch": "selected", // Only selected nodes are draggable on touch
"drag_selection": true
}
});Support for dragging elements from outside the tree.
/**
* Foreign element configuration
*/
interface ForeignElementConfig {
/** Allow dropping foreign elements */
accept_foreign?: boolean;
/** Selector for acceptable foreign elements */
foreign_selector?: string;
/** Function to process foreign element data */
foreign_processor?: function;
}Usage Examples:
// Accept foreign elements
$("#tree").jstree({
"plugins": ["dnd"],
"dnd": {
"accept_foreign": true,
"foreign_selector": ".draggable-item",
"foreign_processor": function(element, target, position) {
// Convert foreign element to tree node
return {
"text": element.text(),
"data": element.data(),
"type": element.attr("data-type") || "default"
};
}
}
});
// Make external elements draggable to tree
$(".draggable-item").draggable({
"helper": "clone",
"revert": "invalid"
});
// Handle foreign drops
$("#tree").on("create_node.jstree", function(e, data) {
if (data.node.original && data.node.original.foreign) {
console.log("Foreign element dropped:", data.node.text);
}
});Configuration for drag and drop visual indicators.
/**
* Visual feedback configuration
*/
interface VisualFeedbackConfig {
/** Show drop zones during drag */
show_drop_zones?: boolean;
/** Highlight valid drop targets */
highlight_targets?: boolean;
/** Custom drag helper element */
drag_helper?: function;
/** Drop marker styling */
drop_marker?: object;
}Usage Examples:
// Enhanced visual feedback
$("#tree").jstree({
"plugins": ["dnd"],
"dnd": {
"large_drop_target": true,
"show_drop_zones": true,
"highlight_targets": true
}
});
// Custom drag helper
$("#tree").jstree({
"plugins": ["dnd"],
"dnd": {
"drag_helper": function(nodes) {
const helper = $('<div class="custom-drag-helper">');
helper.text(nodes.length + " item(s) selected");
return helper;
}
}
});Events triggered during drag and drop operations.
// DnD-specific events
"dnd_start.vakata": DnDStartEvent;
"dnd_move.vakata": DnDMoveEvent;
"dnd_stop.vakata": DnDStopEvent;
interface DnDStartEvent {
element: jQuery;
target: jQuery;
helper: jQuery;
}
interface DnDMoveEvent {
element: jQuery;
target: jQuery;
helper: jQuery;
event: Event;
}
interface DnDStopEvent {
element: jQuery;
target: jQuery;
helper: jQuery;
event: Event;
}Usage Examples:
// Listen for DnD events
$(document).on("dnd_start.vakata", function(e, data) {
console.log("Drag started");
$("body").addClass("dragging");
});
$(document).on("dnd_move.vakata", function(e, data) {
// Update custom drop indicators
updateDropZones(data.target);
});
$(document).on("dnd_stop.vakata", function(e, data) {
console.log("Drag stopped");
$("body").removeClass("dragging");
});
// Tree-specific DnD events
$("#tree").on("move_node.jstree", function(e, data) {
console.log("Node moved:", {
node: data.node.text,
old_parent: data.old_parent,
new_parent: data.parent,
old_position: data.old_position,
new_position: data.position
});
});Advanced configuration options for complex drag and drop scenarios.
/**
* Advanced DnD configuration
*/
interface AdvancedDnDConfig {
/** Custom drag start validation */
drag_start?: function;
/** Custom drop validation */
drop_finish?: function;
/** Multi-tree support */
multi_tree?: boolean;
/** Cross-frame dragging */
cross_frame?: boolean;
/** Drag constraints */
constraints?: object;
}Usage Examples:
// Multi-tree drag and drop
$("#tree1, #tree2").jstree({
"plugins": ["dnd"],
"dnd": {
"multi_tree": true,
"check_while_dragging": true
}
});
// Custom drag constraints
$("#tree").jstree({
"plugins": ["dnd"],
"dnd": {
"constraints": {
"max_depth": 5,
"allowed_parents": ["folder", "category"],
"forbidden_children": ["readonly"]
},
"drag_start": function(nodes, event) {
// Custom validation before drag starts
return nodes.every(node => !node.li_attr.readonly);
}
}
});
// Cross-frame dragging setup
$("#tree").jstree({
"plugins": ["dnd"],
"dnd": {
"cross_frame": true,
"use_html5": true
}
});// DnD-specific node extensions
interface DraggableNode extends TreeNode {
draggable?: boolean;
droppable?: boolean;
dnd_data?: {
origin?: string;
foreign?: boolean;
[key: string]: any;
};
}
// DnD plugin settings
interface DnDSettings {
copy: boolean;
open_timeout: number;
move_timeout: number;
is_draggable: boolean|function;
check_while_dragging: boolean;
drag_selection: boolean;
touch: boolean|string;
large_drop_target: boolean;
large_drag_target: boolean;
use_html5: boolean;
}
// Drag operation data
interface DragData {
nodes: Array<object>;
event: Event;
helper: jQuery;
origin: string;
is_copy: boolean;
is_foreign: boolean;
}