YUI 3.x Home -

YUI Library Examples: Drag & Drop: List reorder w/Scrolling

Drag & Drop: List reorder w/Scrolling

This example shows how to make a sortable list using Custom Event Bubbling and Node scrolling.

  • Item #1
  • Item #2
  • Item #3
  • Item #4
  • Item #5
  • Item #6
  • Item #7
  • Item #8
  • Item #9
  • Item #10
  • Item #11
  • Item #12
  • Item #13
  • Item #14
  • Item #15
  • Item #16
  • Item #17
  • Item #18
  • Item #19
  • Item #20
  • Item #1
  • Item #2
  • Item #3
  • Item #4
  • Item #5
  • Item #6
  • Item #7
  • Item #8
  • Item #9
  • Item #10
  • Item #11
  • Item #12
  • Item #13
  • Item #14
  • Item #15
  • Item #16
  • Item #17
  • Item #18
  • Item #19
  • Item #20

Setting up the lists

First we will make a list that we want to make sortable.

  1. <div id="play">
  2. <ul id="list1">
  3. <li class="list1">Item #1</li>
  4. <li class="list1">Item #2</li>
  5. <li class="list1">Item #3</li>
  6. <li class="list1">Item #4</li>
  7. <li class="list1">Item #5</li>
  8. <li class="list1">Item #6</li>
  9. <li class="list1">Item #7</li>
  10. <li class="list1">Item #8</li>
  11. <li class="list1">Item #9</li>
  12. <li class="list1">Item #10</li>
  13. <li class="list1">Item #11</li>
  14. <li class="list1">Item #12</li>
  15. <li class="list1">Item #13</li>
  16. <li class="list1">Item #14</li>
  17. <li class="list1">Item #15</li>
  18. <li class="list1">Item #16</li>
  19. <li class="list1">Item #17</li>
  20. <li class="list1">Item #18</li>
  21. <li class="list1">Item #19</li>
  22. <li class="list1">Item #20</li>
  23. </ul>
  24. <ul id="list2">
  25. <li class="list2">Item #1</li>
  26. <li class="list2">Item #2</li>
  27. <li class="list2">Item #3</li>
  28. <li class="list2">Item #4</li>
  29. <li class="list2">Item #5</li>
  30. <li class="list2">Item #6</li>
  31. <li class="list2">Item #7</li>
  32. <li class="list2">Item #8</li>
  33. <li class="list2">Item #9</li>
  34. <li class="list2">Item #10</li>
  35. <li class="list2">Item #11</li>
  36. <li class="list2">Item #12</li>
  37. <li class="list2">Item #13</li>
  38. <li class="list2">Item #14</li>
  39. <li class="list2">Item #15</li>
  40. <li class="list2">Item #16</li>
  41. <li class="list2">Item #17</li>
  42. <li class="list2">Item #18</li>
  43. <li class="list2">Item #19</li>
  44. <li class="list2">Item #20</li>
  45. </ul>
  46.  
  47. </div>
<div id="play">
    <ul id="list1">
        <li class="list1">Item #1</li>
        <li class="list1">Item #2</li>
        <li class="list1">Item #3</li>
        <li class="list1">Item #4</li>
        <li class="list1">Item #5</li>
        <li class="list1">Item #6</li>
        <li class="list1">Item #7</li>
        <li class="list1">Item #8</li>
        <li class="list1">Item #9</li>
        <li class="list1">Item #10</li>
        <li class="list1">Item #11</li>
        <li class="list1">Item #12</li>
        <li class="list1">Item #13</li>
        <li class="list1">Item #14</li>
        <li class="list1">Item #15</li>
        <li class="list1">Item #16</li>
        <li class="list1">Item #17</li>
        <li class="list1">Item #18</li>
        <li class="list1">Item #19</li>
        <li class="list1">Item #20</li>
    </ul>
    <ul id="list2">
        <li class="list2">Item #1</li>
        <li class="list2">Item #2</li>
        <li class="list2">Item #3</li>
        <li class="list2">Item #4</li>
        <li class="list2">Item #5</li>
        <li class="list2">Item #6</li>
        <li class="list2">Item #7</li>
        <li class="list2">Item #8</li>
        <li class="list2">Item #9</li>
        <li class="list2">Item #10</li>
        <li class="list2">Item #11</li>
        <li class="list2">Item #12</li>
        <li class="list2">Item #13</li>
        <li class="list2">Item #14</li>
        <li class="list2">Item #15</li>
        <li class="list2">Item #16</li>
        <li class="list2">Item #17</li>
        <li class="list2">Item #18</li>
        <li class="list2">Item #19</li>
        <li class="list2">Item #20</li>
    </ul>    
 
</div>

Make it scrollable

Now add some CSS to make it scrollable.

  1. #play ul {
  2. /* Snipped */
  3. overflow: auto;
  4. }
#play ul {
    /* Snipped */
    overflow: auto;
}

Setting up the YUI Instance

Now we need to create our YUI instance and tell it to load the dd-constrain, dd-proxy, dd-drop and dd-scroll, modules.

  1. YUI().use('dd-constrain', 'dd-proxy', 'dd-drop', 'dd-scroll', function(Y) {
YUI().use('dd-constrain', 'dd-proxy', 'dd-drop', 'dd-scroll', function(Y) {

Making the Nodes Drag Instances and Drop Targets

Now we have our YUI instance ready, we can make the list items draggable. We will do this using Y.Node.all

We will be passing the selector string #play ul li to Y.all to have it return us a NodeList of the li's in our 2 lists. Using this selector syntax we will be able to add new list markup to the #play div and not have to change our code.

Then we will walk that NodeList and create our draggable Nodes.

Also note that we are using the Node's parentNode as our node in the DDNodeScroll plugin.

  1. //Get the list of li's in the lists and make them draggable
  2. var lis = Y.all('#play ul li');
  3. lis.each(function(v, k) {
  4. var dd = new Y.DD.Drag({
  5. node: v,
  6. //Make it Drop target and pass this config to the Drop constructor
  7. target: {
  8. padding: '0 0 0 20'
  9. }
  10. }).plug(Y.Plugin.DDProxy, {
  11. //Don't move the node at the end of the drag
  12. moveOnEnd: false
  13. }).plug(Y.Plugin.DDConstrained, {
  14. //Keep it inside the #play node
  15. constrain2node: '#play'
  16. }).plug(Y.Plugin.DDNodeScroll, {
  17. node: v.get('parentNode')
  18. });
  19. });
//Get the list of li's in the lists and make them draggable
var lis = Y.all('#play ul li');
lis.each(function(v, k) {
    var dd = new Y.DD.Drag({
        node: v,
        //Make it Drop target and pass this config to the Drop constructor
        target: {
            padding: '0 0 0 20'
        }
    }).plug(Y.Plugin.DDProxy, {
        //Don't move the node at the end of the drag
        moveOnEnd: false
    }).plug(Y.Plugin.DDConstrained, {
        //Keep it inside the #play node
        constrain2node: '#play'
    }).plug(Y.Plugin.DDNodeScroll, {
        node: v.get('parentNode')
    });
});

Making the List Drop Target's too

We need to make the UL nodes a Drop Target so we can catch drops on the empty space of the list. Using this selector syntax we will be able to add new list markup to the #play div and not have to change our code.

  1. //Create simple targets for the 2 lists..
  2. var uls = Y.all('#play ul');
  3. uls.each(function(v, k) {
  4. var tar = new Y.DD.Drop({
  5. node: v
  6. });
  7. });
//Create simple targets for the 2 lists..
var uls = Y.all('#play ul');
uls.each(function(v, k) {
    var tar = new Y.DD.Drop({
        node: v
    });
});

Using Event Bubbling

By default, all Drag and Drop instances bubble their event's up to the DragDropMgr. In this example we are assuming that there are no other Drag Operations in this YUI Instance.

Start Drag Event

The first thing we will do is handle the drag:start event. In this event, we will setup some styles to apply to the node and dragNode of the current Drag instance.

We will also be copying the innerHTML of the node and copying that to the innerHTML of the dragNode.

It should be noted, that doing this will also copy any id's of the nodes inside the node. So if you are using this on something that is id based, you may need to remove the id's of the nodes inside the node that is being dragged.

  1. Y.DD.DDM.on('drag:start', function(e) {
  2. //Get our drag object
  3. var drag = e.target;
  4. //Set some styles here
  5. drag.get('node').setStyle('opacity', '.25');
  6. drag.get('dragNode').set('innerHTML', drag.get('node').get('innerHTML'));
  7. drag.get('dragNode').setStyles({
  8. opacity: '.5',
  9. borderColor: drag.get('node').getStyle('borderColor'),
  10. backgroundColor: drag.get('node').getStyle('backgroundColor')
  11. });
  12. });
Y.DD.DDM.on('drag:start', function(e) {
    //Get our drag object
    var drag = e.target;
    //Set some styles here
    drag.get('node').setStyle('opacity', '.25');
    drag.get('dragNode').set('innerHTML', drag.get('node').get('innerHTML'));
    drag.get('dragNode').setStyles({
        opacity: '.5',
        borderColor: drag.get('node').getStyle('borderColor'),
        backgroundColor: drag.get('node').getStyle('backgroundColor')
    });
});

End Drag Event

In this event, we will reset some of the styles set in the drag:start event.

  1. Y.DD.DDM.on('drag:end', function(e) {
  2. var drag = e.target;
  3. //Put out styles back
  4. drag.get('node').setStyles({
  5. visibility: '',
  6. opacity: '1'
  7. });
  8. });
Y.DD.DDM.on('drag:end', function(e) {
    var drag = e.target;
    //Put out styles back
    drag.get('node').setStyles({
        visibility: '',
        opacity: '1'
    });
});

Drag Event

In this event, we will track the up/down movement for later use.

  1. Y.DD.DDM.on('drag:drag', function(e) {
  2. //Get the last y point
  3. var y = e.target.lastXY[1];
  4. //is it greater than the lastY var?
  5. if (y &lt; lastY) {
  6. //We are going up
  7. goingUp = true;
  8. } else {
  9. //We are going down..
  10. goingUp = false;
  11. }
  12. //Cache for next check
  13. lastY = y;
  14.  
  15. //Added to support scrolling
  16. Y.DD.DDM.syncActiveShims(true);
  17. });
Y.DD.DDM.on('drag:drag', function(e) {
    //Get the last y point
    var y = e.target.lastXY[1];
    //is it greater than the lastY var?
    if (y &lt; lastY) {
        //We are going up
        goingUp = true;
    } else {
        //We are going down..
        goingUp = false;
    }
    //Cache for next check
    lastY = y;
 
    //Added to support scrolling
    Y.DD.DDM.syncActiveShims(true);    
});

Over Drop Event

In this event, know which Target we are over, so we add the Drag node to the list either above or below the current Drop Target.

Here was also chnage the node which needs to be scrolled. Since we are changing lists, we need to tell the scrolling plugin instance that it needs to check against another node.

  1. Y.DD.DDM.on('drop:over', function(e) {
  2. //Get a reference to out drag and drop nodes
  3. var drag = e.drag.get('node'),
  4. drop = e.drop.get('node');
  5.  
  6. //Are we dropping on a li node?
  7. if (drop.get('tagName').toLowerCase() === 'li') {
  8. //Are we not going up?
  9. if (!goingUp) {
  10. drop = drop.get('nextSibling');
  11. }
  12. //Add the node to this list
  13. e.drop.get('node').get('parentNode').insertBefore(drag, drop);
  14. //Set the new parentScroll on the nodescroll plugin
  15. e.drag.nodescroll.set('parentScroll', e.drop.get('node').get('parentNode'));
  16. //Resize this nodes shim, so we can drop on it later.
  17. e.drop.sizeShim();
  18. }
  19. });
Y.DD.DDM.on('drop:over', function(e) {
    //Get a reference to out drag and drop nodes
    var drag = e.drag.get('node'),
        drop = e.drop.get('node');
 
    //Are we dropping on a li node?
    if (drop.get('tagName').toLowerCase() === 'li') {
        //Are we not going up?
        if (!goingUp) {
            drop = drop.get('nextSibling');
        }
        //Add the node to this list
        e.drop.get('node').get('parentNode').insertBefore(drag, drop);
        //Set the new parentScroll on the nodescroll plugin
        e.drag.nodescroll.set('parentScroll', e.drop.get('node').get('parentNode'));        
        //Resize this nodes shim, so we can drop on it later.
        e.drop.sizeShim();
    }
});

Drop Hit Event

In this event, we check to see if the target that was dropped on was not an LI node. If it wasn't, then we know it was dropped on the empty space of the UL.

  1. Y.DD.DDM.on('drag:drophit', function(e) {
  2. var drop = e.drop.get('node'),
  3. drag = e.drag.get('node');
  4.  
  5. //if we are not on an li, we must have been dropped on a ul
  6. if (drop.get('tagName').toLowerCase() !== 'li') {
  7. if (!drop.contains(drag)) {
  8. drop.appendChild(drag);
  9. //Set the new parentScroll on the nodescroll plugin
  10. e.drag.nodescroll.set('parentScroll', e.drop.get('node'));
  11. }
  12. }
  13. });
Y.DD.DDM.on('drag:drophit', function(e) {
    var drop = e.drop.get('node'),
        drag = e.drag.get('node');
 
    //if we are not on an li, we must have been dropped on a ul
    if (drop.get('tagName').toLowerCase() !== 'li') {
        if (!drop.contains(drag)) {
            drop.appendChild(drag);
            //Set the new parentScroll on the nodescroll plugin
            e.drag.nodescroll.set('parentScroll', e.drop.get('node'));            
        }
    }
});

Full Javascript Source

  1. YUI().use('dd-constrain', 'dd-proxy', 'dd-drop', 'dd-scroll', function(Y) {
  2. //Listen for all drop:over events
  3. //Y.DD.DDM._debugShim = true;
  4.  
  5. Y.DD.DDM.on('drop:over', function(e) {
  6. //Get a reference to out drag and drop nodes
  7. var drag = e.drag.get('node'),
  8. drop = e.drop.get('node');
  9.  
  10. //Are we dropping on a li node?
  11. if (drop.get('tagName').toLowerCase() === 'li') {
  12. //Are we not going up?
  13. if (!goingUp) {
  14. drop = drop.get('nextSibling');
  15. }
  16. //Add the node to this list
  17. e.drop.get('node').get('parentNode').insertBefore(drag, drop);
  18. //Set the new parentScroll on the nodescroll plugin
  19. e.drag.nodescroll.set('parentScroll', e.drop.get('node').get('parentNode'));
  20. //Resize this nodes shim, so we can drop on it later.
  21. e.drop.sizeShim();
  22. }
  23. });
  24. //Listen for all drag:drag events
  25. Y.DD.DDM.on('drag:drag', function(e) {
  26. //Get the last y point
  27. var y = e.target.lastXY[1];
  28. //is it greater than the lastY var?
  29. if (y &lt; lastY) {
  30. //We are going up
  31. goingUp = true;
  32. } else {
  33. //We are going down..
  34. goingUp = false;
  35. }
  36. //Cache for next check
  37. lastY = y;
  38. Y.DD.DDM.syncActiveShims(true);
  39. });
  40. //Listen for all drag:start events
  41. Y.DD.DDM.on('drag:start', function(e) {
  42. //Get our drag object
  43. var drag = e.target;
  44. //Set some styles here
  45. drag.get('node').setStyle('opacity', '.25');
  46. drag.get('dragNode').set('innerHTML', drag.get('node').get('innerHTML'));
  47. drag.get('dragNode').setStyles({
  48. opacity: '.5',
  49. borderColor: drag.get('node').getStyle('borderColor'),
  50. backgroundColor: drag.get('node').getStyle('backgroundColor')
  51. });
  52. });
  53. //Listen for a drag:end events
  54. Y.DD.DDM.on('drag:end', function(e) {
  55. var drag = e.target;
  56. //Put out styles back
  57. drag.get('node').setStyles({
  58. visibility: '',
  59. opacity: '1'
  60. });
  61. });
  62. //Listen for all drag:drophit events
  63. Y.DD.DDM.on('drag:drophit', function(e) {
  64. var drop = e.drop.get('node'),
  65. drag = e.drag.get('node');
  66.  
  67. //if we are not on an li, we must have been dropped on a ul
  68. if (drop.get('tagName').toLowerCase() !== 'li') {
  69. if (!drop.contains(drag)) {
  70. drop.appendChild(drag);
  71. //Set the new parentScroll on the nodescroll plugin
  72. e.drag.nodescroll.set('parentScroll', e.drop.get('node'));
  73. }
  74. }
  75. });
  76.  
  77. //Static Vars
  78. var goingUp = false, lastY = 0;
  79.  
  80. //Get the list of li's in the lists and make them draggable
  81. var lis = Y.all('#play ul li');
  82. lis.each(function(v, k) {
  83. var dd = new Y.DD.Drag({
  84. node: v,
  85. target: {
  86. padding: '0 0 0 20'
  87. }
  88. }).plug(Y.Plugin.DDProxy, {
  89. moveOnEnd: false
  90. }).plug(Y.Plugin.DDConstrained, {
  91. constrain2node: '#play'
  92. }).plug(Y.Plugin.DDNodeScroll, {
  93. node: v.get('parentNode')
  94. });
  95. });
  96.  
  97. //Create simple targets for the 2 lists..
  98. var uls = Y.all('#play ul');
  99. uls.each(function(v, k) {
  100. var tar = new Y.DD.Drop({
  101. node: v
  102. });
  103. });
  104.  
  105.  
  106. });
YUI().use('dd-constrain', 'dd-proxy', 'dd-drop', 'dd-scroll', function(Y) {
    //Listen for all drop:over events
    //Y.DD.DDM._debugShim = true;
 
    Y.DD.DDM.on('drop:over', function(e) {
        //Get a reference to out drag and drop nodes
        var drag = e.drag.get('node'),
            drop = e.drop.get('node');
 
        //Are we dropping on a li node?
        if (drop.get('tagName').toLowerCase() === 'li') {
            //Are we not going up?
            if (!goingUp) {
                drop = drop.get('nextSibling');
            }
            //Add the node to this list
            e.drop.get('node').get('parentNode').insertBefore(drag, drop);
            //Set the new parentScroll on the nodescroll plugin
            e.drag.nodescroll.set('parentScroll', e.drop.get('node').get('parentNode'));                        
            //Resize this nodes shim, so we can drop on it later.
            e.drop.sizeShim();
        }
    });
    //Listen for all drag:drag events
    Y.DD.DDM.on('drag:drag', function(e) {
        //Get the last y point
        var y = e.target.lastXY[1];
        //is it greater than the lastY var?
        if (y &lt; lastY) {
            //We are going up
            goingUp = true;
        } else {
            //We are going down..
            goingUp = false;
        }
        //Cache for next check
        lastY = y;
        Y.DD.DDM.syncActiveShims(true);
    });
    //Listen for all drag:start events
    Y.DD.DDM.on('drag:start', function(e) {
        //Get our drag object
        var drag = e.target;
        //Set some styles here
        drag.get('node').setStyle('opacity', '.25');
        drag.get('dragNode').set('innerHTML', drag.get('node').get('innerHTML'));
        drag.get('dragNode').setStyles({
            opacity: '.5',
            borderColor: drag.get('node').getStyle('borderColor'),
            backgroundColor: drag.get('node').getStyle('backgroundColor')
        });
    });
    //Listen for a drag:end events
    Y.DD.DDM.on('drag:end', function(e) {
        var drag = e.target;
        //Put out styles back
        drag.get('node').setStyles({
            visibility: '',
            opacity: '1'
        });
    });
    //Listen for all drag:drophit events
    Y.DD.DDM.on('drag:drophit', function(e) {
        var drop = e.drop.get('node'),
            drag = e.drag.get('node');
 
        //if we are not on an li, we must have been dropped on a ul
        if (drop.get('tagName').toLowerCase() !== 'li') {
            if (!drop.contains(drag)) {
                drop.appendChild(drag);
                //Set the new parentScroll on the nodescroll plugin
                e.drag.nodescroll.set('parentScroll', e.drop.get('node'));                                
            }
        }
    });
 
    //Static Vars
    var goingUp = false, lastY = 0;
 
    //Get the list of li's in the lists and make them draggable
    var lis = Y.all('#play ul li');
    lis.each(function(v, k) {
        var dd = new Y.DD.Drag({
            node: v,
            target: {
                padding: '0 0 0 20'
            }
        }).plug(Y.Plugin.DDProxy, {
            moveOnEnd: false
        }).plug(Y.Plugin.DDConstrained, {
            constrain2node: '#play'
        }).plug(Y.Plugin.DDNodeScroll, {
            node: v.get('parentNode')
        });
    });
 
    //Create simple targets for the 2 lists..
    var uls = Y.all('#play ul');
    uls.each(function(v, k) {
        var tar = new Y.DD.Drop({
            node: v
        });
    });
 
 
});

Copyright © 2009 Yahoo! Inc. All rights reserved.

Privacy Policy - Terms of Service - Copyright Policy - Job Openings