HOW-TO: Add drag/drop behavior to a YUI tree

August 9th, 2006

Today we continue the adventures of “Getting the Yahoo Interface Library to play with Rails”. Though today’s episode will be much more about the yui than rails. In fact, the techniques mentioned can be applied to a variety of technology stacks.

Before you begin with this tutorial, make sure you have gone through HOW-TO: Integrate a YUI tree with Rails. This tutorial picks up where that one left off.

When you are done with this tutorial, you will build a simple tree that you can invoke with: htt://0.0.0.0:3000/tree/display

You will be able to drag a category over another category (and yes, I did switch over to my brand new spiffy iMac between the last screen shot and this one — w00t)

And finally, when you drop the category, you will get a dialog informing you of the drag object id and the drop object id.

Modify yui_build_tree to use an HTMLNode instead of a TextNode

The yui drag drop mechanism relies on creating a drag drop object and binding it to a dom id. The problem with the yui tree is that the dom id is pretty tricky to get at and based on an internally generated id (FYI — when working with the YUI the DOM Inspector is your friend as much of the DOM is generated after the page load).

One way to solve this problem is to extend TreeView and TreeNode as discussed on this ydn-javascript discussion thread. The example in the thread has the drag drop interaction modifying the tree AND THEN posting the modified tree to the server. I was interested in a much simpler interaction.

My interests were simply in capturing the id of the source and target object and then send those ids to the server for processing. In this tutorial, I take you as far as grabing the source and target ids. In the next tutorial I will delve into what to do with those ids.

The method I opted for was to use “out-of-the-box” yui tree classes and build a simple DragDrop class. Since my key problem with a TextNode yui tree is that I didn’t have an easily available dom id per tree node, I decided to use HTMLNode. That way I was able to embed a div tag with each node.

To support this, I needed to change yui_build_tree to use an HTMLNode instead of a TextNode.

For your convinience, the new tree_helper.rb is:

module TreeHelper
  def yui_build_tree(tree_id, tree_data)

  return "//<![CDATA[ n" +
       "function addChildrenNodes(currLevel, nodeIndex, parent) { n" +
	   "var lastNode;n"+
	   "var level = currLevel;n"+
	   "while (nodeIndex < #{tree_data}.length) {n"+
	       "var level = #{tree_data}[nodeIndex];n"+
	       "if (level == currLevel) {n"+
	           "nodeIndex++;n"+
	           "lastNode = new YAHOO.widget.HTMLNode(#{tree_data}[nodeIndex++], parent, false, true);n"+
	       "} else if (level < currLevel) {n"+
	           "return nodeIndex;n"+
	       "} else {n"+
	           "nodeIndex = addChildrenNodes(level, nodeIndex, lastNode);n"+
	       "}n"+
	   "}n"+
	   "return nodeIndex;n"+
	"}n"+
	"function #{tree_id}_Init() {n"+
		"var tree = new YAHOO.widget.TreeView("#{tree_id}");n"+
		"addChildrenNodes(0, 0, tree.getRoot());n"+
		"tree.draw();n"+
	"}n"+
    "YAHOO.util.Event.addListener(window, "load", #{tree_id}_Init);n"+
"//]]>";

  end
end

The next thing I had to do was open up display.html and update the test_data declaration to use html data attributes instead of label:

//<![CDATA[
	var test_data =
	            [0, { html:"<div id='node_1'>Cat 1</div>"},
	             1, { html:"<div id='node_2'>Cat 1.1</div>"},
	             1, { html:"<div id='node_3'>Cat 1.2</div>"},
	             2, { html:"<div id='node_4'>Cat 1.2.1</div>" },
	             2, { html:"<div id='node_5'>Cat 1.2.2</div>" },
	             1, { html:"<div id='node_6'>Cat 1.3</div>" },
	             0, { html:"<div id='node_7'>Cat 2</div>"}];
//]]>

You can see here how I use the div tags to create my own dom ids that I will use when I create the drag drop objects. Go ahead and test this. Drag drop isn’t enabled, but make sure you tree is still functioning.

Add in the drag drop behavior

If you aren’t familiar with how the yui does drag drop, check out Yahoo’s documentation on the drag and drop API. To test out drag and drop, add the following javascript to display.rhtml:

    new YAHOO.util.DD("node_1");

The above code should make Category 1 draggable. Try it out.

The base class doesn’t do much. If you want to add behavior to drag and drop, you need to extend the bases classes the yui provides. Create a new file called DDSend.js and save it in public/javascripts/yui. I am calling it DDSend because its eventuall purpose will be to send a message to the server when a source object drops on a valid target object. Make sure to update your javascript includes to include the new file. Copy the following text to DDSend.js:

DDSend = function(id, sGroup, config) {

    if (id) {
        // bind this drag drop object to the
        // drag source object
        this.init(id, sGroup, config);
        this.initFrame();
    }

    var s = this.getDragEl().style;
    s.borderColor = "transparent";
    s.backgroundColor = "#f6f5e5";
    s.opacity = 0.76;
    s.filter = "alpha(opacity=76)";
};

// extend proxy so we don't move the whole object around
DDSend.prototype = new YAHOO.util.DDProxy();

DDSend.prototype.onDragDrop = function(e, id) {
    // this is called when the source object dropped
    // on a valid target
    alert("dd " + this.id + " was dropped on " + id);
    // send a request to the server to handle the drag drop
}

DDSend.prototype.startDrag = function(x, y) {
    // called when source object first selected for dragging
    // draw a red border around the drag object we create
    var dragEl = this.getDragEl();
    var clickEl = this.getEl();

    dragEl.innerHTML = clickEl.innerHTML;
    dragEl.className = clickEl.className;
    dragEl.style.color = clickEl.style.color;
    dragEl.style.border = "1px solid red";

};

DDSend.prototype.onDragEnter = function(e, id) {
    var el;

    // this is called anytime we drag over
    // a potential valid target
    // highlight the target in red
    if ("string" == typeof id) {
        el = YAHOO.util.DDM.getElement(id);
    } else {
        el = YAHOO.util.DDM.getBestMatch(id).getEl();
    }

    el.style.border = "1px solid red";
};

DDSend.prototype.onDragOut = function(e, id) {
    var el;

    // this is called anytime we drag out of
    // a potential valid target
    // remove the highlight
    if ("string" == typeof id) {
        el = YAHOO.util.DDM.getElement(id);
    } else {
        el = YAHOO.util.DDM.getBestMatch(id).getEl();
    }

    el.style.border = "";
}

DDSend.prototype.endDrag = function(e) {
   // override so source object doesn't move when we are done
}

Now replace your display.rhtml with the following code (the main change we are doing here is creating all the DDSend objects):

<html>
<head>
  <title>YUI Tester: <%= controller.action_name %></title>
  <%= javascript_include_tag "yui/yahoo",
                             "yui/dom",
                             "yui/event",
                             "yui/connection",
                             "yui/dragdrop",
                             "yui/DDSend",
                             "yui/treeview"%>
    <%= stylesheet_link_tag  'yui/tree'%>

    <%= stylesheet_link_tag 'appstylesheet'%>

 </head>

<body>
    <div id="mytree"></div>

<script type="text/javascript">

//<![CDATA[
	var test_data =
	            [0, { html:"<div id='node_1'>Cat 1</div>"},
	             1, { html:"<div id='node_2'>Cat 1.1</div>"},
	             1, { html:"<div id='node_3'>Cat 1.2</div>"},
	             2, { html:"<div id='node_4'>Cat 1.2.1</div>" },
	             2, { html:"<div id='node_5'>Cat 1.2.2</div>" },
	             1, { html:"<div id='node_6'>Cat 1.3</div>" },
	             0, { html:"<div id='node_7'>Cat 2</div>"}];
//]]>
<%= yui_build_tree("mytree", "test_data") %>
</script>    

    <script type="text/javascript">
        new DDSend("node_1");
        new DDSend("node_2");
        new DDSend("node_3");
        new DDSend("node_4");
        new DDSend("node_5");
        new DDSend("node_6");
        new DDSend("node_7");
    </script>

</body>
</html>

Now test it all out. You should be able to drag and drop your tree nodes and generate an alert that tells you the id of the source and target nodes.

Next Steps

Well, I am still hard coding the data, so I still owe you some helpers or example off how to generate the table using real data. The next thing I will write up for you, however, is the server side processing. I will also look into AJAX and see if I can find a way to have DDSend send an AJAX request and then have the response update the tree.

Entry Filed under: How-to, Ruby, YUI

Trackbacks

21 Comments Add your own

  • 1. HowToWorkWithTheYUI in Ru&hellip  |  August 9th, 2006 at 12:22 am

    Kramer auto Pingback[…] HOW-TO: Add drag and drop behavior to a YUI tree […]

  • 2. RoR Wiki 翻訳 Wiki - HowT&hellip  |  August 10th, 2006 at 7:54 pm

    Kramer auto Pingback[…] HOW-TO: YUI ツリーにドラッグ&ドロップ機能を追加 Last modified:2006/08/11 11:53:51 Keyword(s):[Howto] References:[RailsWikiIndex] […]

  • 3. HowToWorkWithTheYUI (Vers&hellip  |  August 20th, 2006 at 8:39 pm

    Kramer auto Pingback[…] HOW-TO: Add drag and drop behavior to a YUI tree […]

  • 4. kga245  |  August 21st, 2006 at 10:55 pm

    Sanjaya - I met your brother at a birthday dinner for a mutual friend of ours in San Diego this weekend. He mientioned that I might do well to look you up since you and I travel in similar circles. So I looked you up and, yes, we do.

    You should check out Dandelife.com when you get a moment. Would love to get your feedback and talk shop. It’s a RoR site and with a lot of nice AJAXy goodness to boot.

    Sorry for the blog-spam. It was unfortunately, the only means I had available to me.

    Peace,
    K

    P.S. feel free to delete this comment since it’s just not at all germain to this post. I won’t be offended.

  • 5. sonjaya  |  August 23rd, 2006 at 7:41 pm

    You should have my email address now — I sent you an email. For spam reasons, I keep my email off this site — I figured if anyone wants to contact me they do what you you did — which is post a comment :) — so I am glad you did!

  • 6. ikaro75  |  November 3rd, 2006 at 3:01 pm

    Hi, Sonjaya, I’m newbie at YUI.. I’m programming serve-code side with ASP, and I find interesting and useful the YUI, but I haven’t used Ruby. How would it be implementing drag and drop to YUI treeview without integrating it with Ruby?

    Best regards and forgive my poor English

  • 7. sonjaya  |  November 3rd, 2006 at 3:31 pm

    ikaro75,

    Actually very little of what I have here is Ruby dependant. The key routine is the javascript function addChildrenNodes.

    In your server side code, you will want to have a function that produces the same javascript output as the ruby function: yui_build_tree. This function writes out the addChildrenNodes javascript routines plugging in the value of the tree id AND the variable name where you are putting the tree data.

    For the drag/drop to work, just fill out the tree data like I have — namely defining them as html tree nodes. The trick with the drag/drop are unique names on those div tags — that lets you use the DDSend javascript class we create here.

    You shouldn’t have any problems using DDSend as nothing in there is Ruby specific. Just make sure to provide your own implementation for DDSend.prototype.onDragDrop to handle the drop event.

  • 8. ryan  |  February 5th, 2007 at 2:21 pm

    Found this very useful, as Im implementing a server-side tree handler and wanted the drag & drop on the client side. FYI: for those who arent Ruby developers, heres a simple demo of the functionality in just Javascript (using the DDSend.js, of course):


    function treeInit() {
    var tree = new YAHOO.widget.TreeView("treeDiv1");
    var parent = new YAHOO.widget.HTMLNode({ html:"Container"}, tree.getRoot(), true, true);
    new YAHOO.widget.HTMLNode({ html:"Child 1"}, parent, true, true);
    new YAHOO.widget.HTMLNode({ html:"Child 2"}, parent, true, true);
    new YAHOO.widget.HTMLNode({ html:"Child 3"}, parent, true, true);

    new DDSend("node_1");
    new DDSend("node_2");
    new DDSend("node_3");
    new DDSend("node_4");

    tree.draw();
    }

    Make sure you have a div with the id “treeDiv1″ to match the TreeView declaration. Hope someone finds this useful.

  • 9. avenging avatar&hellip  |  March 4th, 2007 at 1:30 am

    links from Technorati [IMG ] my del.icio.us Amazon.com: Wii Charge Yui Form Animation Example - Accordion Sonjaya Tandon 損 HOW-TO: Add Awesome Box at Paul Armstrong Current TV // The TV Network ColorJack: Sphere LSL Wiki : HomePage How to emulate PHP5 object Solitaire - DHTML JavaScript CSS frameset generator Pictures, Photos of Lost

  • 10. Cheat Codes » Comme&hellip  |  March 4th, 2007 at 11:24 pm

    […] @Dolittle. The current version of the TreeView doesnt support DragDrop out of the box. But there is a pretty good how-to here: http://sonjayatandon.com/08-2006/how-to-add-dragdrop-behavior-to-a-yui-tree/ […]

  • 11. Davs Rants and Random Tho&hellip  |  March 5th, 2007 at 2:17 pm

    Kramer auto Pingback[…] But there is a pretty good how-to here: http://sonjayatandon.com/08-2006/how-to-add-dragdrop-behavior-to-a-yui-tree/ […]

  • 12. roopa  |  April 14th, 2007 at 2:58 am

    Drag Drop Tree example was very useful. Im working on a tree view with drag drop cababilities where i got the below mentioned
    issues.Please help me out.

    1. i’m generating the tree dynamically ie., only when i click a node the server gives me the corresponding child nodes. I’m capable of dragging the node. But it works only for the latest childnodes
    2. On dragging a node it is possible to drag only the text and on dropping i could not delete the icon of the dragged element.
    3.On dropping an element i’m not able to drop it as a child node of the destination element.
    Note: i’m using javascript

  • 13. roopa  |  April 14th, 2007 at 3:17 am

    Hi i’m working on implementing a dynamic tree view with drag n drop.Below is my code for generating the nodes (inside dynamic load) dynamically. in this case i’m able to drag only the latest childnodes ie., the nodes above this level couldn be dragged.
    On dropping i unable to drop the particular node as a child to the destination node.
    Please help me out.Thanks.

    if (menuItems != null)
    {
    if(menuItems.length != 0)
    {
    for(i =0; i”+item+”"};
    var tmpNode = new YAHOO.widget.HTMLNode(nodeItem , this, false, true);
    oTextNodeMap[tmpNode.contentElId] = tmpNode;
    }
    }
    for (var k = 1; k

  • 14. roopa  |  April 26th, 2007 at 10:33 pm

    Hi,
    With the use of your example, I have implemented a dynamic tree view with drag and drop capabilities.
    Thanks a lot.
    When i use HTMLNode, how will i expand or collapse a node on clicking tha label of the node?
    I tried to add an event to enable this.But i’m unable to do it properly.Is there any thing I’m missing out?
    Please help me out.
    Thanks

  • 15. iZaak  |  September 19th, 2007 at 10:53 am

    to roopa:
    Have you copied the highlighted row correctly? There is an additional parameter (true) at the end of that row. Changing TextNode to HTMLNode is not enough.

  • 16. sdevi_mantra  |  September 20th, 2007 at 3:38 am

    hi,

    i am very new to ajax.plz clearly explain following reqirement.

    I want construct the tree from top to bottom with drag and drop facility.the tree wiil be constructed from database.if we are adding new node i. e updated in database and again come to tree.

    updation or any connections for that we are using JSP ’s only

    i want source code also and demo.

    plz its very urgent.

  • 17. yui en Gennio&hellip  |  October 9th, 2007 at 6:00 am

    Kramer auto Pingback[…] Comments on: HOW-TO: Add drag/drop behavior to a YUI tree  http://sonjayatandon.com/08-2006/how-to-add-dragdrop-behavior-to-a-yui-tree/feed […]

  • 18. 若&hellip  |  October 24th, 2007 at 5:06 am

    Kramer auto Pingback[…] Sonjaya Tandon 鐃 HOW-TO: Add drag/drop behavior to a YUI tree […]

  • 19. Discover From Your Favori&hellip  |  November 3rd, 2007 at 5:10 am

    Kramer auto Pingback[…] […]

  • 20. santajack &hellip  |  December 1st, 2007 at 2:16 am

    Kramer auto Pingback[…] […]

  • 21. dragdroptree | sehr belie&hellip  |  March 24th, 2008 at 4:41 am

    Kramer auto Pingback[…] Blog 4.333.038 Bookmarks __utmSetVar(’Tags’); dragdroptree sehr beliebt brandneu aktuell popul辰r Sonjaya Tandon 損 HOW-TO: Add drag/drop behavior to a YU … drag drop yui tree dragdroptree yui vor >30 Tagen von da0, 1 Benutzer, mehr Info speichern dragdroptreeverwandt mit+ yui Jetzt registrieren und mitmachen! Kontakt | auf dieser Site werben | Blog | About | cn | en | es | fr | ru © 2008 mister-wong.de | AGB | Allgemeine Rechtshinweise | Datenschutz | Impressum Mister Wong bedankt sich bei für den Online Marketing Support und das Web 2.0 Development. _uacct = “UA-1269285-1″; urchinTracker(”"); var tm_sid = “4b2a7f32951488ef926bbd32570fd8e6″; var tm_pageCategory = “”; var tm_conversionName = “”; tm_tracker(); […]

Leave a Comment

You must be logged in to post a comment.

Trackback this post  |  Subscribe to the comments via RSS Feed


Calendar

August 2006
M T W T F S S
« Jul   Sep »
 123456
78910111213
14151617181920
21222324252627
28293031  

Most Recent Posts