Drag and drop table rows with JavaScript

REDIPS.drag was initially built to drag and drop table content. After publishing first version of REDIPS.drag, I received a lot of questions about dragging table rows also. Now is possible to drag and drop table rows as well as table content. First column and contained DIV element in this demo is a row handler, so you can try to drag table rows.


REDIPS.drag example15

It is very easy to define a row handler. Actually, it is only needed to place DIV element to the column (first, last or any other) and to define class=”drag row”. This DIV element will become a row handler. When row dragging begins, source row will change color and content will become transparent. This way, source row as start point is visible and obvious. It is possible to return row to the start point – in this case, event.rowDroppedSource() will be fired. In a moment when row is dropped to the destination table, source row will be removed. How REDIPS.drag works? The trick is to clone a mini table from the source table and to remove all rows except selected row. It looks like a row, but it is a table with only one row – a mini table. New functionality also brings new event handlers:

  1. event.rowChanged
  2. event.rowClicked
  3. event.rowCloned
  4. event.rowDeleted
  5. event.rowDropped
  6. event.rowDroppedBefore
  7. event.rowDroppedSource
  8. event.rowMoved
  9. event.rowNotCloned
  10. event.rowNotMoved
  11. event.rowUndeleted

Each event handler has access to the obj and objOld objects. For example, event.RowClicked() sees only obj object and this is reference to the source row. event.rowMoved() is fired in a moment when dragging starts and in this case, obj is reference to the mini table (previously mentioned) while objOld is reference to the source table row.

REDIPS.drag has a new method: rowOpacity(el, opacity, color) to change color and opacity of the source row and mini table. This way it is only needed to call rowOpacity() method in event handlers to have row effects like in this demo. Here is code for event.rowMoved() used in this demo:

rd.event.rowMoved = function () {
    // set opacity for moved row
    // rd.obj is reference of cloned row (mini table)
    rd.rowOpacity(rd.obj, 85);
    // set opacity for source row and change source row background color
    // rd.objOld is reference of source row
    rd.rowOpacity(rd.objOld, 20, 'White');
    // display message
    msg.innerHTML = 'Moved';
};

REDIPS.drag takes care about background color of table cells and table rows. When dragging begins, color of each table cell is saved to the array and returned in a moment of dropping or highlighting current table row. Source code of REDIPS.drag library with examples can be download from “download icon” below post title. If you want to see more drag and drop examples based on REDIPS.drag, click on Drag and Drop category.

Happy dragging and dropping!

178 thoughts on “Drag and drop table rows with JavaScript”

  1. I have some code just like example8 with two separate drags being initialized, the move method fails if moving more than one row successively. If I comment out the first drag init:

    //rd.init('drag1');
    rd.init('drag2');
    

    then things start to work again. I think that there is some confusion in the program about which table is in scope for the move method especially if moving within the same table (i.e. source and target tables are identical).

  2. Thank you so much for the awesome library.

    I want to use it in a single page AJAX app where widgets come in and out. If a widget is created that runs rd.init, how can i deallocate the resources that were claimed by the init method when the widget goes away? I want to avoid memory leaks.

    There are also scenarios where a table needs to be added later and needs to work with older tables. Often i don’t want them to share a parent. An example of this is dragging from a floating pane (attached to the body) to a rendering region (attached somewhere deep in the dom).

    It would be great if we had something like this:

    // same as now
    var d = rd.init('foo');
    // this adds 'bar' to the same dnd as foo - from a completely different div
    d.init('bar');
    // deallocate memory
    d.destroy();
    
  3. @Jany – REDIPS.move_object() method will run in context of the last initialized drag container. Every drag container can contain several tables so source/target table index is defined only for tables within current drag container. REDIPS.init() method has two purposes, it initializes drag container and defines current working set. Therefore, it’s possible to call REDIPS.init() method more than once to change the current drag container/context.

    If you manually drag and drop DIV elements or table rows in separated drag containers, switching context will be automatically executed. Before using REDIPS.move_object() please call REDIPS.init(‘drag1’) or REDIPS.init(‘drag2’) to define drag container. I hope that I succeeded answer the question, if not don’t hesitate to contact me (zipped example in attachment will be more than welcome). And at the end, new version 4.6.22. has added feature to reset “tables” array in case of switching drag containers (this should resolve problems regarding separated drag containers with different number of tables).

    @Rouben – REDIPS.drag uses internally only one array that can be considered for cleaning. Adding onclick events to the DIV elements and setting custom properties to DOM elements is not relevant in this case. “tables” array contains table references within drag container. Number of elements is the same as the number of tables. REDIPS.init() can be called several times and in this process this array is reused with resetting at the first. In the last REDIPS.drag version 4.6.22. is added the following line:

    tables.length = 0;
    

    to reset “tables” array. This can be important in the case with several drag containers with different number of tables. Anyway, this array is the only leftover after using REDIPS.drag library so my guess is that is not needed to be destroyed manually. GC should take care for unused variables.

    In the case when new table will be added dynamically, it’s only needed to call REDIPS.init() again to re-scan drag container and recreate “tables” array. Example0 AJAX / jQuery modification is exactly the described case. New table is added on button click:

    http://www.redips.net/my/preview/REDIPS_drag/example00/index2.html

  4. Thanks for that solution. Your drag and drop tool has been such a blessing to me. I really appreciate this well thought out and implemented solution. Hopefully I haven’t been a pain in asking a million and one questions.

    I’ll definitely be making a donation to support this site. Very cool!

  5. @Jany – No problem at all. Your questions are welcome and will be more than useful for others. Thanks for using REDIPS.drag library. Cheers!

  6. Hi,
    I’m using multiple nested tables. Case you drop a row inside a nested table I wanna get the row inserted above on first parent table with classname “outer-table” – how can I do that?
    Thank you in advance

  7. @peter – row_position public property defines where row will be placed in other tables. So if you want to drop rows before highlighted bottom row, just define the following line in initialization function:

    rd.row_position = 'before';
    

    Actually, “before” is default value so you can delete “row_position” line with “after” value to have the same effect as “before” definition. Hope this was your question. Anyway, please see Example 19 – Groups and table rows to see how rows can be dragged between inner tables. In this example the trick is in dynamic enabling / disabling tables …

  8. Hej Darko,

    Many thanks for the fantastic library you have written. I have a problem in IE8. What my code is doing is it generates vml objects inside a table cell with absolute positioning (in IE8). These are a set of pieCharts.

    When I drop the charts into another cell the color disappears.

    Drag and drop on rows is not possible.

    It works perfect in IE9+, Chrome and Firefox.

    This is what I’ve got:

    That is what I have i the table cells surrounded by a drag div of course

    Any ideas?

    Thanks agian

  9. Hello.
    Awesome lib you’ve created.. ;)

    I got a question.

    I got a table of, for example, 10 rows.
    I want to save the new order to mysql database by clicking a button but i cant seem to figure out how the save_content works..
    Is there any other way to save or …??
    Thanks in advance

  10. @Felix – Maybe you found a bug in REDIPS.drag library because IE8 browser should be supported. I will be very grateful if you can prepare, zip and send offline example to me so I can help you. If bug really exists, lib will be upgraded and fixed. Thanks!

    @Artis – save_content() method scans only DIV elements in a table. Table rows are not currently taken into account (hopefully future release will have that feature built-in). Anyway, I will suggest two ways how to save order of table rows to the server:

    1) Take out save_content() from the redips-drag-source.js file and make your own save method. Each row should have “row handler” (a DIV with “drag row” class names) that can be collected. You can also assign ID to the DIV so it will be easy to take care of “row handler” DIV and rowIndex of parentNode (TR element).

    2) Use myhandler_row_dropped event handler and on each row drop call backend script via AJAX and save the change.

    Hope this tips will be helpful.

  11. I’m using ASP classic and building a table with SQL data that I’m pulling using jQuery after the page has already loaded. I have the rowhandler entered as the class of the TD and drag row as the class of the DIV in the TD.

    However, once the table loads, it has the row handler, but the rows aren’t able to be dragged or dropped. Any ideas how to resolve my issue?

  12. @Devon – When table is dynamically loaded it’s needed to initialize DIV elements / row handlers. Actually, initialization will only attach event listeners to all DIV elements inside drag container. So the solution is to call init() method after table is loaded:

    // initialize REDIPS.drag
    REDIPS.drag.init();
    
    // or if rd is already defined with "var rd = REDIPS.drag;" line
    rd.init();
    
  13. Hi

    I used your suggestion and put some ajax code into the row_dropped event to save changed made by drag and dropping rows to a DB.

    Anyways I had a little hard time getting the rows that where affected. I though REDIPS.drag.obj and REDIPS.drag.objOld would be set to the dragged row and the row dropped upon, but that didn’t work. In the end I used pos = getPosition(); pos[1] and pos[4] to get the rows through table.rows

    Is there a better way to do this?

    Thanks for the great lib.

  14. Hi Armin!

    rowDropped event handler contains optional input parameters (please see docs for input parameter details):

    rowDropped(targetRow, sourceTable, sourceRowIndex)
    

    This way, dropped TR can be referenced directly from input parameter targetRow. Row dropped upon can be found in rowChanged event handler. Well, it seems that your version with getPosition() is better than combination with two event handlers …

  15. @Ace – Unfortunately, drag and drop table rows will work only with “plain” tables. Colspan and rowspan are not supported.

  16. Hi,

    First of all thanks for a great ToolKit.

    So I have a table thats 5000 px wide (for example) and naturally it doesnt all fit in the page at one. Users need to scroll to get across it. I have an element in a cell i want to drag and move across the table to parts which arent visible. How can i make this happen?

    Image below.

    http://i.imgur.com/eGqMuZf.png

    Thanks

  17. @Ian Daz – Just make sure that drag container is wrapping whole table. This can be checked with the following CSS rule:

    #drag {
        border: 2px dashed LightBlue;
        /* display: table; */
    }
    

    … next try to uncomment “display: table” to wrap table inside drag container. If table is placed properly inside drag container, then user should be able to drag-n-drop DIV elements to the most right table cells (with a little help of autoscroll).

  18. Hi,

    I love your scripts. I want to be able to move the whole column if possible, not just the row. Is this possible?

    Thanks,

    George

Leave a Comment