jQuery Drag and Drop to sort Tree
Update: This interface can be seen in a fully functioning state in the VizReader RSS reader.
I just finished off a nice interface where an arbitrary amount of nested lists are used to represent a tree. Any item can be dragged and dropped on any other item as a way of moving them and placing them as children beneath another item/branch. The functionality mimics that of any file browser that can display folder structures as a tree.
Check out the demo here, drag and drop texts on other texts. The goal here is to sort various tags in an arbitrary hierarchy, and then save it to a database by way of Ajax and jQuery JSON. In order to do that we have to loop through the whole tree in a recursive manner, more on that after the code listing.
I’ve taken the basic functionality from the Interface drag and drop tree. Note how their interface is a lot more complicated as it resembles a real file browser. Below all of that has been ripped out as we only want the basic sorting here. I’m also using the latest version of the jQuery UI components whereas they’re using somewhat dated versions.
Note that I’m also making use of the print_r jQuery plugin in the demo.
function parseTree(ul){
var tags = [];
ul.children("li").each(function(){
var subtree = $(this).children("ul");
if(subtree.size() > 0)
tags.push([$(this).attr("id"), parseTree(subtree)]);
else
tags.push($(this).attr("id"));
});
return tags;
}
$(document).ready(function(){
$("li.tree_item span").droppable({
tolerance : "pointer",
hoverClass : "tree_hover",
drop : function(event, ui){
var dropped = ui.draggable;
dropped.css({top: 0, left: 0});
var me = $(this).parent();
if(me == dropped)
return;
var subbranch = $(me).children("ul");
if(subbranch.size() == 0) {
me.find("span").after("<ul></ul>");
subbranch = me.find("ul");
}
var oldParent = dropped.parent();
subbranch.eq(0).append(dropped);
var oldBranches = $("li", oldParent);
if (oldBranches.size() == 0) { $(oldParent).remove(); }
}
});
$("li.tree_item").draggable({
opacity: 0.5,
revert: true
});
$("#save").click(function(){
/*
var tree = $.toJSON(parseTree($("#tag_tree")));
$.post("@saveTags", {tags: tree}, function(res){
$("#printOut").html(res);
});
*/
$.debug.print_r(parseTree($("#tag_tree")), "printOut", false);
});
});
Let’s walk the droppable code first as it’s the most complicated:
1.) We begin by setting two options (tolerance and hoverClass) and the callback (drop). Tolerance -> pointer will result in a properly set drop condition if the mouse pointer hovers over the drop target while we drag something, a nice hover class is good to have too that will be assigned to the drop target to indicate that yes we can drop now.
2.) The callback will first set top to 0 and left to 0 on the dropped element, I don’t know why I have to do this but it didn’t work properly without that line, the jQuery UI code assigned these two properties to any moved element making them end up in the strangest of places.
3.) Since we are in effect dropping on only the spanned text elements, not the list elements themselves (doing that would create unresolvable ambiguities), we have to get at the parent list element of the text in question. That would be the parent().
4.) I’m not sure we need that test to check for self-drops, couldn’t hurt though.
5.) Next we check if we have a child ul element already, otherwise we create it, and append the dropped object to its inner html.
6.) We also save the prior parent (ul) of the dropped list. If it doesn’t contain any elements anymore it will be removed.
When the Save button is pressed we will loop through the whole tree recursively with the help of the parseTree function which will call itself whenever a list contains an ul element.
Finally we could convert the resultant array to a JSON string and send it to the server by way of Ajax which is done in the commented out code here. Instead we use the print_r function to output the whole structure for inspection.
Related Posts
Tags: ajax, Javascript, jquery, json, tree