I use the JSTreegraph plugin to draw a tree structure. But now I need a drag, drop and dock function in which I can drag any node of the tree and attach to any other node, and then all the children of the first node will now be the great children of the new node (to which it is attached).
As far as I know, this plugin does not seem to have this feature. It simply draws a structure based on the data object passed to it.
The plugin basically assigns the Node class to all nodes (divs) of the tree and to another NodeHover class in the node when it hangs. No id is assigned to these divs.
So, I tried using jQuery Draggable to see if any node can be moved by doing this
$('.Node').draggable(); $('.NodeHover').draggable();
But it does not seem to work. So can someone help me with this.
How can I get the drag and drop functions?
* EDIT :: Sorry I'm not so good at using Fiddle, so share a sample code for your use: *
HTML file: which will draw the sample tree
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"> <head> <link href="css/JSTreeGraph.css" rel="stylesheet" type="text/css" /> <script src="js/JSTreeGraph.js" type="text/javascript"></script> <script src="js/jquery-1.7.1.min.js" type="text/javascript"></script> <script src="js/jquery.ui.position.js" type="text/javascript"></script> <style type="text/css"> .Container { position: absolute; top: 100px; left: 50px; id: Container; } </style> </head> <body> <div id="tree"> Ctrl+Click to Add Node <br /> Double Click to Expand or Collapse <br /> Shift+Click to Delete Node <br /> <select id="dlLayout" onchange="ChangeLayout()"> <option value="Horizontal"> Horizontal </option> <option value="Vertical" selected> Vertical </option> </select> <div class="Container" id="dvTreeContainer"></div> <script type="text/javascript"> var selectedNode; // Root node var rootNode = { Content: "Onida", Nodes:[] }; // First Level rootNode.Nodes[0] = { Content: "Employee Code", navigationType: "0"}; rootNode.Nodes[1] = { Content: "Problem Area", navigationType: "1" }; // Second Level rootNode.Nodes[1].Nodes = [{ Content : "ACC-HO", Collapsed: true /* This node renders collapsed */ }, { Content : "ACC-SALES" }, { Content : "BUSI. HEAD", /*This node looks different*/ ToolTip: "Click ME!" }, { Content : "CEO"}, { Content : "HO-ADMIN"}, { Content : "HO-FACTORY"}, { Content : "SALES"}]; // Third Level rootNode.Nodes[1].Nodes[0].Nodes = [{ Content: "Billing" }, { Content: "Credit Limit" }, { Content: "Reconciliation" }]; rootNode.Nodes[1].Nodes[1].Nodes = [{ Content: "Billing" }, { Content: "Others" }]; rootNode.Nodes[1].Nodes[2].Nodes = [{ Content: "AC" }, { Content: "CTV" }, { Content: "DVD" }, { Content: "Washing Machine" }]; rootNode.Nodes[1].Nodes[6].Nodes = [{ Content: "Appointments" }, { Content: "Resignations" }, { Content: "Others" }]; // Draw the tree for the first time RefreshTree(); function RefreshTree() { DrawTree({ Container: document.getElementById("dvTreeContainer"), RootNode: rootNode, Layout: document.getElementById("dlLayout").value, OnNodeClickFirefox: NodeClickFF, OnNodeClickIE: NodeClickIE, OnNodeDoubleClick: NodeDoubleClick }); } //function function NodeClickFF(e) { if (e.shiftKey){ // Delete Node if (!this.Node.Collapsed) { for(var index=0; index<this.Node.ParentNode.Nodes.length; index++) { if(this.Node.ParentNode.Nodes[index].Content == this.Node.Content) { this.Node.ParentNode.Nodes.splice(index, 1); break; } } RefreshTree(); } // return false; } else if (e.ctrlKey) { // Add new Child if Expanded if (!this.Node.Collapsed) { if (!this.Node.Nodes) this.Node.Nodes = new Array(); var newNodeIndex = this.Node.Nodes.length; this.Node.Nodes[newNodeIndex] = new Object(); this.Node.Nodes[newNodeIndex].Content = this.Node.Content + "." + (newNodeIndex + 1); RefreshTree(); } // return false; } else{ fnNodeProperties(this.Node); } } function NodeClickIE() { if (typeof(event) == "undefined" && event.ctrlKey) { // Add new Child if Expanded if (!this.Node.Collapsed) { if (!this.Node.Nodes) this.Node.Nodes = new Array(); var newNodeIndex = this.Node.Nodes.length; this.Node.Nodes[newNodeIndex] = new Object(); this.Node.Nodes[newNodeIndex].Content = this.Node.Content + "." + (newNodeIndex + 1); RefreshTree(); } } else if (typeof(event) == "undefined" && event.shiftKey) { // Delete Node if (!this.Node.Collapsed) { for(var index=0; index<this.Node.ParentNode.Nodes.length; index++) { if(this.Node.ParentNode.Nodes[index].Content == this.Node.Content) { this.Node.ParentNode.Nodes.splice(index, 1); break; } } RefreshTree(); } } else{ fnNodeProperties(this.Node); } } function NodeDoubleClick() { if (this.Node.Nodes && this.Node.Nodes.length > 0) { // If has children this.Node.Collapsed = !this.Node.Collapsed; RefreshTree(); } } function ChangeLayout() { RefreshTree(); } </script> </div> </body> </html>
JSTreeGraph JS file : js plugin file
function DrawTree(options) { // Prepare Nodes PrepareNode(options.RootNode); // Calculate Boxes Positions if (options.Layout == "Vertical") { PerformLayoutV(options.RootNode); } else { PerformLayoutH(options.RootNode); } // Draw Boxes options.Container.innerHTML = ""; DrawNode(options.RootNode, options.Container, options); // Draw Lines DrawLines(options.RootNode, options.Container); } function DrawLines(node, container) { if ((!node.Collapsed) && node.Nodes && node.Nodes.length > 0) { // Has children and Is Expanded for (var j = 0; j < node.Nodes.length; j++) { if(node.ChildrenConnectorPoint.Layout=="Vertical") DrawLineV(container, node.ChildrenConnectorPoint, node.Nodes[j].ParentConnectorPoint); else DrawLineH(container, node.ChildrenConnectorPoint, node.Nodes[j].ParentConnectorPoint); // Children DrawLines(node.Nodes[j], container); } } } function DrawLineH(container, startPoint, endPoint) { var midY = (startPoint.Y + ((endPoint.Y - startPoint.Y) / 2)); // Half path between start en end Y point // Start segment DrawLineSegment(container, startPoint.X, startPoint.Y, startPoint.X, midY, 1); // Intermidiate segment var imsStartX = startPoint.X < endPoint.X ? startPoint.X : endPoint.X; // The lower value will be the starting point var imsEndX = startPoint.X > endPoint.X ? startPoint.X : endPoint.X; // The higher value will be the ending point DrawLineSegment(container, imsStartX, midY, imsEndX, midY, 1); // End segment DrawLineSegment(container, endPoint.X, midY, endPoint.X, endPoint.Y, 1); } function DrawLineV(container, startPoint, endPoint) { var midX = (startPoint.X + ((endPoint.X - startPoint.X) / 2)); // Half path between start en end X point // Start segment DrawLineSegment(container, startPoint.X, startPoint.Y, midX, startPoint.Y, 1); // Intermidiate segment var imsStartY = startPoint.Y < endPoint.Y ? startPoint.Y : endPoint.Y; // The lower value will be the starting point var imsEndY = startPoint.Y > endPoint.Y ? startPoint.Y : endPoint.Y; // The higher value will be the ending point DrawLineSegment(container, midX, imsStartY, midX, imsEndY, 1); // End segment DrawLineSegment(container, midX, endPoint.Y, endPoint.X, endPoint.Y, 1); } function DrawLineSegment(container, startX, startY, endX, endY, lineWidth) { var lineDiv = document.createElement("div"); lineDiv.style.top = startY + "px"; lineDiv.style.left = startX + "px"; if (startX == endX) { // Vertical Line lineDiv.style.width = lineWidth + "px"; lineDiv.style.height = (endY - startY) + "px"; } else{ // Horizontal Line lineDiv.style.width = (endX - startX) + "px"; lineDiv.style.height = lineWidth + "px"; } lineDiv.className = "NodeLine"; container.appendChild(lineDiv); } function DrawNode(node, container, options) { var nodeDiv = document.createElement("div"); nodeDiv.style.top = node.Top + "px"; nodeDiv.style.left = node.Left + "px"; nodeDiv.style.width = node.Width + "px"; nodeDiv.style.height = node.Height + "px"; if (node.Collapsed) { nodeDiv.className = "NodeCollapsed"; } else { nodeDiv.className = "Node"; } if (node.Class) nodeDiv.className = node.Class; if (node.Content) nodeDiv.innerHTML = "<div class='NodeContent'>" + node.Content + "</div>"; if (node.ToolTip) nodeDiv.setAttribute("title", node.ToolTip); nodeDiv.Node = node; // Events if (options.OnNodeClickIE){ //alert('OnNodeClick'); nodeDiv.onclick = options.OnNodeClickIE; } // Events if (options.OnNodeClickFirefox){ //alert('OnNodeClick'); nodeDiv.onmousedown = options.OnNodeClickFirefox; } //on right click if (options.OnContextMenu){ //alert('OnContextMenu'); nodeDiv.oncontextmenu = options.OnContextMenu; } if (options.OnNodeDoubleClick) nodeDiv.ondblclick = options.OnNodeDoubleClick; nodeDiv.onmouseover = function () { // In this.PrevClassName = this.className; this.className = "NodeHover"; }; nodeDiv.onmouseout = function () { // Out if (this.PrevClassName) { this.className = this.PrevClassName; this.PrevClassName = null; } }; container.appendChild(nodeDiv); // Draw children if ((!node.Collapsed) && node.Nodes && node.Nodes.length > 0) { // Has Children and is Expanded for (var i = 0; i < node.Nodes.length; i++) { DrawNode(node.Nodes[i], container, options); } } } function PerformLayoutV(node) { var nodeHeight = 30; var nodeWidth = 100; var nodeMarginLeft = 40; var nodeMarginTop = 20; var nodeTop = 0; // defaultValue // Before Layout this Node, Layout its children if ((!node.Collapsed) && node.Nodes && node.Nodes.length > 0) { for (var i = 0; i < node.Nodes.length; i++) { PerformLayoutV(node.Nodes[i]); } } if ((!node.Collapsed) && node.Nodes && node.Nodes.length > 0) { // If Has Children and Is Expanded // My Top is in the center of my children var childrenHeight = (node.Nodes[node.Nodes.length - 1].Top + node.Nodes[node.Nodes.length - 1].Height) - node.Nodes[0].Top; nodeTop = (node.Nodes[0].Top + (childrenHeight / 2)) - (nodeHeight / 2); // Is my top over my previous sibling? // Move it to the bottom if (node.LeftNode && ((node.LeftNode.Top + node.LeftNode.Height + nodeMarginTop) > nodeTop)) { var newTop = node.LeftNode.Top + node.LeftNode.Height + nodeMarginTop; var diff = newTop - nodeTop; /// Move also my children MoveBottom(node.Nodes, diff); nodeTop = newTop; } } else { // My top is next to my top sibling if (node.LeftNode) nodeTop = node.LeftNode.Top + node.LeftNode.Height + nodeMarginTop; } node.Top = nodeTop; // The Left depends only on the level node.Left = (nodeMarginLeft * (node.Level + 1)) + (nodeWidth * (node.Level + 1)); // Size is constant node.Height = nodeHeight; node.Width = nodeWidth; // Calculate Connector Points // Child: Where the lines get out from to connect this node with its children var pointX = node.Left + nodeWidth; var pointY = nodeTop + (nodeHeight/2); node.ChildrenConnectorPoint = { X: pointX, Y: pointY, Layout: "Vertical" }; // Parent: Where the line that connect this node with its parent end pointX = node.Left; pointY = nodeTop + (nodeHeight/2); node.ParentConnectorPoint = { X: pointX, Y: pointY, Layout: "Vertical" }; } function PerformLayoutH(node) { var nodeHeight = 30; var nodeWidth = 100; var nodeMarginLeft = 20; var nodeMarginTop = 50; var nodeLeft = 0; // defaultValue // Before Layout this Node, Layout its children if ((!node.Collapsed) && node.Nodes && node.Nodes.length>0) { for (var i = 0; i < node.Nodes.length; i++) { PerformLayoutH(node.Nodes[i]); } } if ((!node.Collapsed) && node.Nodes && node.Nodes.length > 0) { // If Has Children and Is Expanded // My left is in the center of my children var childrenWidth = (node.Nodes[node.Nodes.length-1].Left + node.Nodes[node.Nodes.length-1].Width) - node.Nodes[0].Left; nodeLeft = (node.Nodes[0].Left + (childrenWidth / 2)) - (nodeWidth / 2); // Is my left over my left node? // Move it to the right if(node.LeftNode&&((node.LeftNode.Left+node.LeftNode.Width+nodeMarginLeft)>nodeLeft)) { var newLeft = node.LeftNode.Left + node.LeftNode.Width + nodeMarginLeft; var diff = newLeft - nodeLeft; /// Move also my children MoveRigth(node.Nodes, diff); nodeLeft = newLeft; } } else { // My left is next to my left sibling if (node.LeftNode) nodeLeft = node.LeftNode.Left + node.LeftNode.Width + nodeMarginLeft; } node.Left = nodeLeft; // The top depends only on the level node.Top = (nodeMarginTop * (node.Level + 1)) + (nodeHeight * (node.Level + 1)); // Size is constant node.Height = nodeHeight; node.Width = nodeWidth; // Calculate Connector Points // Child: Where the lines get out from to connect this node with its children var pointX = nodeLeft + (nodeWidth / 2); var pointY = node.Top + nodeHeight; node.ChildrenConnectorPoint = { X: pointX, Y: pointY, Layout:"Horizontal" }; // Parent: Where the line that connect this node with its parent end pointX = nodeLeft + (nodeWidth / 2); pointY = node.Top; node.ParentConnectorPoint = { X: pointX, Y: pointY, Layout: "Horizontal" }; } function MoveRigth(nodes, distance) { for (var i = 0; i < nodes.length; i++) { nodes[i].Left += distance; if (nodes[i].ParentConnectorPoint) nodes[i].ParentConnectorPoint.X += distance; if (nodes[i].ChildrenConnectorPoint) nodes[i].ChildrenConnectorPoint.X += distance; if (nodes[i].Nodes) { MoveRigth(nodes[i].Nodes, distance); } } } function MoveBottom(nodes, distance) { for (var i = 0; i < nodes.length; i++) { nodes[i].Top += distance; if (nodes[i].ParentConnectorPoint) nodes[i].ParentConnectorPoint.Y += distance; if (nodes[i].ChildrenConnectorPoint) nodes[i].ChildrenConnectorPoint.Y += distance; if (nodes[i].Nodes) { MoveBottom(nodes[i].Nodes, distance); } } } function PrepareNode(node, level, parentNode, leftNode, rightLimits) { if (level == undefined) level = 0; if (parentNode == undefined) parentNode = null; if (leftNode == undefined) leftNode = null; if (rightLimits == undefined) rightLimits = new Array(); node.Level = level; node.ParentNode = parentNode; node.LeftNode = leftNode; if ((!node.Collapsed) && node.Nodes && node.Nodes.length > 0) { // Has children and is expanded for (var i = 0; i < node.Nodes.length; i++) { var left = null; if (i == 0 && rightLimits[level]!=undefined) left = rightLimits[level]; if (i > 0) left = node.Nodes[i - 1]; if (i == (node.Nodes.length-1)) rightLimits[level] = node.Nodes[i]; PrepareNode(node.Nodes[i], level + 1, node, left, rightLimits); } } }
JSTreeGraph CSS File:
.NodeContent { font-family:Verdana; font-size:small; } .Node { position:absolute; background-color: #CCDAFF; border: 1px solid #5280FF; text-align:center; vertical-align:middle; cursor:pointer; overflow:hidden; } .NodeHover { position:absolute; background-color: #8FADFF; border: 1px solid #5280FF; text-align:center; vertical-align:middle; cursor:pointer; overflow:hidden; } .NodeCollapsed { position:absolute; background-color: #8FADFF; border: 2px solid black; text-align:center; vertical-align:middle; cursor:pointer; overflow:hidden; } .NodeLine { background-color: #000066; position:absolute; overflow:hidden; }