2.4. Drag and Drop

The drag-and-drop metaphor is a common UI approach for various tasks, such as selecting items from a list. It's a more visual approach to the problem and is therefore popular in the GUI world.

Modern RIAs are expected to expose this sort of "advanced" UI metaphor, and any good library these days will provide the basics for you at a minimum. Ext JS is definitely no exception.

Take a peek at Listing 2-3. This is a full, working example of drag and drop.

Example 2.3. An Example of Drag and Drop with Ext JS
<html>

  <head>

    <title>Chapter 2 Drag and Drop Example</title>

    <style>

      /* Style for a drop zone container. */
      .cssDDContainer {
        border : 1px solid #000000;
        width : 200px;
        height : 422px;
        background-color : #d0d0ff;
        overflow : auto;
        margin : 2px;
      }

      /* Style for a draggable item. */
      .cssDDItem {
        font-size : 8pt;
        font-weight : bold;
        font-family : arial;
        margin : 2px;
        border : 1px solid #000000;
        background-color : #ffd0d0;
        padding : 1px;
        cursor : move;
        z-index : 9999;
      }

      /* Style for when an item is hovering over a drop target. */
      .cssDDHover {
        background-color: #c0ffc0;
      }

    </style>

    <script type="text/javascript" src="ext/adapter/ext/ext-base.js"></script>
    <script type="text/javascript" src="ext/ext-all.js"></script>

<script>
  var presidents = [ "George Washington", "John Adams", "Thomas Jefferson",
    "James Madison", "James Monroe", "John Quincy Adams", "Andrew Jackson",
    "Martin Van Buren", "William Harrison", "John Tyler", "James Polk",
    "Zachary Taylor", "Millard Fillmore", "Franklin Pierce",
    "James Buchanan", "Abraham Lincoln", "Andrew Johnson",
    "Ulysses S. Grant", "Rutherford B. Hayes", "James Garfield",
    "Chester A. Arthur", "Grover Cleveland", "Benjamin Harrison",
    "Grover Cleveland", "William McKinley", "Theodore Roosevelt",
    "William Howard Taft", "Woodrow Wilson", "Warren Harding",
    "Calvin Coolidge", "Herbert Hoover", "Franklin Delano Roosevelt",
    "Harry S. Truman", "Dwight D. Eisenhower", "John F. Kennedy",
    "Lyndon B. Johnson", "Richard Milhous Nixon", "Gerald Ford",
    "Jimmy Carter", "Ronald Reagan", "George Bush", "Bill Clinton",
    "George W. Bush"
  ];

  // Override drag-and-drop events as necessary.
  Ext.override(Ext.dd.DDProxy, {
    // Event when the user starts dragging an item.
    startDrag : function(inX, inY) {
      // Show contents of item when dragging so it looks nicer.
      var item = Ext.get(this.getDragEl());
      var el = Ext.get(this.getEl());
      item.update(el.dom.innerHTML);
      item.addClass(el.dom.className + " dd-proxy");
    },
    // Event when an item hovers over a drop target.
    onDragOver : function(inElement, inTargetID) {
      // Only do something if item is over the drop target.
      if (inTargetID == "destinationContainer") {
        // Record this as the drop target for when dragging stops.
        var dropTarget = Ext.get(inTargetID);
        this.lastTarget = dropTarget;
        // Style the drop target.
        dropTarget.addClass("cssDDHover");
      }
    },
    // Event when an item leaves a drop target.
    onDragOut : function(inElement, inTargetID) {
      // Clear the recorded drop target.
      this.lastTarget = null;
      if (inTargetID == "destinationContainer") {
        // If leaving the destination container, remove the hover style.
        Ext.get(inTargetID).removeClass("cssDDHover");
      }

},
  // Event when the user stops dragging an item.
  endDrag : function() {
    // Only do something if the item is over a drop target.
    if (this.lastTarget) {
      // Append the item to the drop target and remove the style.
      var item = Ext.get(this.getEl());
      Ext.get(this.lastTarget).appendChild(item);
      this.lastTarget.removeClass("cssDDHover");
      this.lastTarget = null;
     }
    }
  });


  /**
  * The appInit() function fires when the page loads. It creates all the
  * draggable items and defines the drop zones.
  */
  function appInit() {

    // Create items that can be dragged and insert them into the DOM.
    var presidentsContainer = Ext.getDom("sourceContainer");
    for (var i = 0; i < presidents.length; i++) {
      var newDiv = document.createElement("div");
      newDiv.className = "cssDDItem";
      newDiv.id = "president" + i;
      newDiv.innerHTML = presidents[i];
      presidentsContainer.appendChild(newDiv);
      var divElem = Ext.getDom("president" + i);
      divElem.dd = new Ext.dd.DDProxy("president")
    }

    // Register drop zone.
    var dz2 = new Ext.dd.DropZone("destinationContainer";

  } // End appInit().

   Ext.onReady(appInit);

 </script>

</head>

<body>

<!-- List of available presidents. -->
    <div style="position:absolute;left:2px;top:2px;">
      <center><h2>U.S. Presidents</h2></center>
      <div class="cssDDContainer" id="sourceContainer">
    </div>

    <!-- List of presidents the user likes. -->
    <div style="position:absolute;left:230px;top:2px;">
      <center><h2>The&nbsp;ones&nbsp;I&nbsp;like</h2></center>
    <div class="cssDDContainer" id="destinationContainer">
    </div>

  </body>

</html>

Even though this isn't going to win the award for great-looking applications, let's have a look anyway, in Figure 2-29.

Figure 2.29. The drag-and-drop example

Let's not quibble over the fact that we're using styles and JavaScript in the same file as the markup, a practice generally frowned on these days. Sometimes it's nice to have a single HTML page that is all inclusive, and for a basic example like this I think it's fine.

Anyway... the way this works is pretty simple. First, we have two <div>s that are absolutely positioned. Inside each is another <div>. The inner <div>s will contain other <div>s, each representing a US president. All of them start out in the <div> on the left with the ID sourceContainer. Users can drag the ones they want over to the <div> on the right with the ID destinationContainer to indicate which presidents they like.

You create the <div>s for each president via code in the appInit() function:

for (var i = 0; i < presidents.length; i++) {
  var newDiv = document.createElement("div");
  newDiv.className = "cssDDItem";
  newDiv.id = "president" + i;
  newDiv.innerHTML = presidents[i];
  presidentsContainer.appendChild(newDiv);
  var divElem = Ext.getDom("president" + i);
  divElem.dd = new Ext.dd.DDProxy("president" + i)
}

This is just some basic DOM manipulation code to create the <div>s and insert them as children of sourceContainer using the names of the presidents found in the presidents array. Note that each <div> has a class specified as cssDDItem (using the JavaScript className attribute name for the usual class attribute as it would appear on a <div> tag itself). This style is found in the <style> section and sets up such things as the cursor style that will be used for this element.

These are just plain old <div>s at this point, of course. The part that makes them draggable via Ext JS is the following line:

divElem.dd = new Ext.dd.DDProxy("president" + i)

An Ext.dd.DDProxy is an object that conceptually mimics the element you tell it to (the argument passed to it, which you've noticed works out at runtime to be the same value as that set for the id of the created <div>). More specifically, it creates an empty, bordered <div> that knows how to follow the mouse as you move it around after clicking on the element that it proxies (one of our president <div>s in this case). This is much more lightweight than trying to drag around the actual element.

The other task performed in appInit() is to register a drop target so that Ext JS knows where a draggable element can be dropped. This snippet does that:

var dz2 = new Ext.dd.DropZone("destinationContainer");

Once again we instantiate a class, Ext.dd.DropZone this time, that basically wraps a DOM node, destinationContainer here. Ext JS now knows that this element should react to draggable items dropped onto it.

But how exactly does it react to anything? The answer lies in the code in the Ext.override() call. If you removed that statement and ran the example, you'd find that the president items can be dragged, but you'd also see that dropping doesn't work. That's where the Ext.override() statement comes into play.

Ext.override() in general allows you to add a list of functions to the prototype of an existing class, overwriting any existing methods with the same name. One such object floating around is that Ext.dd.DDProxy class. This class contains a number of event handlers, and we need to override some of those to make everything work.

Four events in particular are of interest to us: startDrag, which fires when you click a draggable item and start dragging it around; onDragOver, which fires whenever a dragged item is hovering over a drop target; onDragOut, which is the opposite of onDragOver and thus fires when a draggable item leaves a drop target; and endDrag, which fires when the item is dropped.

First let's talk about the code that executes in response to the startDrag event. If you run the code with the Ext.override() statement removed, you'll notice that when you drag an item, all you see is a border being dragged—you don't see the contents of the original <div>. This may be fine in some cases, but wouldn't it be a little better if we saw what we were actually dragging? I think so! To accomplish this, we write the following code in the startDrag event handler:

var item = Ext.get(this.getDragEl());
var el = Ext.get(this.getEl());
item.update(el.dom.innerHTML);
item.addClass(el.dom.className + " dd-proxy");

The getDragEl() method returns a reference to the linked element (the <div> created by the proxy, in other words). Remember that this function is attached to the proxy, so the keyword this references the proxy itself. The getEl() method, on the other hand, returns a reference to the actual element being dragged. Then we set the contents of the empty proxy <div> using its update() method to the innerHTML of the real element. This allows us to see what we're dragging. Then we add the dd-proxy style class (supplied by Ext JS itself) to the object so it's styled properly. The addClass() method is good for this purpose (and is a handy method to remember since it is available on many objects when working with Ext JS).

So now that things look like we want, how do make it work like we want? It begins with the onDragOver event handler. When this event fires we have a relatively simple task: determine whether or not the dragged item is hovering over a valid drop target. To do this, we examine the ID of the target that is passed in to this event handler. If it matches the ID of our drop target, destinationContainer in this case, we get a reference to the Element underlying the drop target and store it in the lastTarget attribute of the proxy. We also add the cssDDHover class to it so the background color changes to indicate the item can be dropped there.

The next event to handle is endDrag. When the endDrag event fires, the code examines the lastTarget attribute to see if it's null. If it's not, that means the item is hovering over the drop target. In that case, we get a reference to the original <div>, and we then append it to the Element underlying the drop target. In other words, we move the DOM node from the sourceContainer <div> to the destinationContainer <div>. Finally, we remove the cssDDHover class from the drop target and make sure we clear the lastTarget attribute on the proxy.

The final event handler handles the onDragOut event. We have little to do here: set lastTarget to null so we know the item isn't hovering over a target, and if the target passed into the event handler is the destinationContainer, we also remove the cssDDHover class from it.

To see it all in action, take a look at Figure 2-30. In this screenshot you can see that I've dragged a few presidents over already and am in the process of dragging another. The destinationContainer is highlighted (you won't be able to see that too well on the printed page, although you may be able to discern a subtle difference in shades of gray).

Figure 2.30. The drag-and-drop example in action

As you can see, implementing drag and drop with Ext JS is a piece of cake. You can build some powerful UIs with these simple capabilities, and they'll save you a ton of work along the way!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.12.36.87