Drag and Drop table content with JavaScript

Content of HTML table cells can be dragged to another cell or another table. It isn’t difficult to define onMouseMove handler and change top / left element styles to move the object. In case with tables, you will have to determine somehow target cell. Attaching onMouseOver handler on TD elements will not work, because browser doesn’t fire events to the elements below the dragged object.

Anyway, after taking care of the current scroll position and calculating TD positions, REDIPS.drag should work in recent major browsers like Google Chrome, Firefox, Safari, Internet Explorer, Opera and mobile devices as well. Click on image below, will open live demo where you can drag green, blue or orange bordered DIV elements, change properties (radio button and check-boxes) and click on “Save” button.

Download latest version redips2.tar.gz


REDIPS.drag example01

In this example “Save” button will scan table content, create query string and send to PHP page. Demo shows how to collect content and accept parameters on the server side. More about accepting parameters you can read at Reading multiple parameters in PHP. “Clone” elements (orange in this demo) will be duplicated first because of “redips-clone” keyword contained in class name. If you drop object on cell named “Trash”, object will be deleted from the table (with or without confirmation). Library has built in autoscroll and option to forbid landing to non empty cells or cells named with class “redips-mark”. Table can contain rowspan / colspan TDs and different background color for every cell.

Here are minimal steps to enable content dragging in table:

  • put <script type=”text/javascript” src=”redips-drag-min.js”></script> to the head section
  • initialize REDIPS.drag library: <body onload=”REDIPS.drag.init()”>
  • place table(s) inside <div id=”redips-drag”> to enable content dragging
  • place <div class=”redips-drag”>Hello World</div> to the table cell

Other features of REDIPS.drag library:

  • methods and data structure are defined in namespace (easier integration with other JS frameworks)
  • all JavaScript code is checked with ESLint
  • REDIPS.drag documentation generated with JsDoc Toolkit
  • drag and drop table rows
  • movable DIV element can contain other HTML code (images, forms, tables …)
  • forbidding or allowing TDs marked with class name “redips-mark”
  • option to define exceptions and allow dropping certain DIV elements to the marked cell
  • option to define single content cell on the table declared with “multiple” drop option
  • cloning
    • for unlimited cloning add “redips-clone” class name to the DIV object
      <div class=”redips-drag redips-clone”>Hello World</div>
    • to limit cloning and transform last object to the ordinary movable object add ‘climit1_X’ class name
      <div class=”redips-drag redips-clone climit1_4″>Hello World</div>
    • to limit cloning and transform last object to immovable object add ‘climit2_X’ class name
      <div class=”redips-drag redips-clone climit2_4″>Hello World</div>
    • where X is integer and defines number of cloned elements (in previous examples, each climit will allow only 4 cloned elements)
  • unlimited nested tables support
  • dropping objects only to empty cells
  • switch cell content
  • switching cell content continuously
  • overwrite TD content with dropped element
  • shift table content
  • table cell with “redips-trash” class name becomes trashcan
  • enabled handlers to place custom code on events: changed, clicked, cloned, clonedDropped, clonedEnd1, clonedEnd2, dblClicked, deleted, dropped, droppedBefore, finish, moved, notCloned, notMoved, shiftOverflow, relocateBefore, relocateAfter, relocateEnd, rowChanged, rowClicked, rowCloned, rowDeleted, rowDropped, rowDroppedBefore, rowDroppedSource, rowMoved, rowNotCloned, rowNotMoved, rowUndeleted, switched and undeleted
  • deleting cloned DIV if the cloned DIV is dragged outside of any table
  • enabling / disabling dragging
  • animation (move element/row to the destination cell/row)
  • added support for touch devices (touchstart, touchmove, touchend)

How REDIPS.drag works?

Script will search for DIV elements (with class name “redips-drag”) inside tables closed in <div id=”redips-drag”> and attach onMouseDown event handler. When user clicks with left mouse button on DIV element, onMouseMove and onMouseUp handlers will be attached to the document level.

While dragging DIV element, script changes its “left” and “top” styles. This is function of the onMouseMove handler. When user releases left mouse button, onMouseUp event handler will unlink onMouseMove and onMouseUp event handlers. This way, browser will listen and process mousemove events only when DIV element is dragged.

As I mentioned, onMouseDown is defined on the elements you want to drag. Elements beneath the dragged object will not be able to catch onMouseOver event. Why? Because you are dragging object and that object only can catch the onMouseOver event.

So, to detect destination table cells, script calculates all cell coordinates (with scroll page offset) and store them to the array. Array is searched inside onMouseMove handler and after left mouse button is released, DIV will drop to the current (highlighted) table cell.

In redips2.tar.gz package you will find many examples including example of how to save/recall table using PHP and MySQL. Package also contains and redips-drag-min.js – a compressed version of REDIPS.drag library (compressed with Google Closure Compiler).

Happy dragging and dropping!

1,195 thoughts on “Drag and Drop table content with JavaScript”

  1. Hi,

    I have a question for you:

    I would like to know why it is not possible to select table cells with CTRL+click in IE and in Firefox I can do it? Could you give me some tips how to modify it?
    I have already added to each table cells event onClick where I am changing the style class of selected td element. But in IE it seems that, this event is not triggered. In IE there is no tool such as FireBug so it is hard for me to debug the page.

    Thx in advance for your help!!!

  2. Hi! I’ve downloaded your project, but there is no example of how to save/recall table using PHP and MySQL in the project. Can you teach me on how to do it?

  3. Hey, amazing javascript stuff you’ve made! This DnD table stuff is exactly what I was looking for, and a question: Is there some way (with javascript) to move a certain table cell by id to a certain column and row e.g. move “penguin” to (3,2)
    Some way to do this? Thanks in advance, and this is truly amazing!

  4. Hi it’s working fine in window.onload function
    but it was not working like this way of code

    window.onload = display();
    
    function display() {
        var rd = REDIPS.drag,
            // reference to the drag region
            div_drag = document.getElementById('drag');
        // DIV container initialization
        rd.init();
        // this function (event handler) is called after element is dropped
        rd.myhandler_dropped = function () {
            var div1, // DIV elements inside DIV id="drag" (collection)
            div2 = [],
                // references of DIV elements prepared from collection
                cb, i, j; // checkbox and loop variables
            // collect DIV elements from drag region
            div1 = div_drag.getElementsByTagName('div');
            // loop through collected DIV elements
            for (i = 0, j = 0; i 0 && cb[0].checked === true) {
                // uncheck checkbox
                cb[0].checked = false;
                // save reference of DIV element to the div2 array
                div2[j] = div1[i];
                // increment counter j
                j++;
            }
    
        }
        // loop through div2 array and move elements to the target table cell
        for (i = 0; i < div2.length; i++) {
            rd.target_cell.appendChild(div2[i]);
        }
    }
    

    it says div_drag is null….. please help me its urgent for me……

  5. @thea – Please look example03 – School timetable. Inside you will find database.sql needed for tables creation and config.php for database username and password definition. Static example only shows how timetable looks. So you should prepare your local MySQL database and Apache/PHP to view and test example03.

    @dharma – div_drag is not visible inside myhandler_dropped() handler. Either you should define global div_drag variable outside function (this is more efficient solution because it will be executed once) or you can collect DIV element inside handler like:

    div1 = document.getElementById(‘drag’).getElementsByTagName(‘div’);

  6. First my apologies for delay. Last week I was busy with redesigning and upgrading to the new WordPress version. Hope you like it …

    @qR – Well, it seems that there is no problem with selecting more then one DIV element (I have solution with checkbox in example12 – see inside redips2.tar.gz package) but moving all together is not possible yet. Instead of moving whole group of selected DIV elements, only one element is moved. In a moment of dropping, other elements will be relocated to the destination table cell. Maybe this solution will be good enough … So if you have any further questions do not hesitate to ask. I would gladly help.

    @Robert – REDIPS.drag lib contains move_object(from, to) method to move cell content from source to the destination table cell. At the bottom of source redips-drag.js you will find well commented method. It is used for “switching” mode (internally) and it is exposed as public method too. Hope this is what you are looking for.

  7. Hi~dbunic
    Thanks for your nice drag and drop table js library. I have a problem in your example13 (nested table). For some reason, I execute the init function two times, the source code similar follows:

    var rd = REDIPS.drag;
    rd.init();
    rd.init();
    

    then I got some error in Firefox 3.6.15:
    tables[table] is undefined at line 776
    Why the init function can’t init all state caused some error?

  8. @Alan Jhu – I tried to call twice in example13 and
    I can’t to force mentioned error. Just for testing, I called rd.init() twice in example13 and there is no error in FireFox 3.6.10 and Google Chrome 9.0.597.98 Maybe the reason is not in repeated calling of rd.init() … Do you have some example to show? I will help and try to fix the error if there is any.

  9. Thanks for your reply. My example is:

    window.onload = function () {
        var rd = REDIPS.drag;
        rd.init();
        rd.init();
        ...
    

    Just add a additional rd.init(); after the first init call.
    When the page is loaded and I try to move the B, then I got the error below:

    tables[table] is undefined
    [Break On This Error] tos[0] = tables[table].offset[0]; // top (line 776)

    If I try to move B second times, I got another error message below:

    el is undefined
    [Break On This Error] if (el.currentStyle) { (line 1473)

    This error also occurs in Google Chrome 9.0.597.107, but the error message is different.

    I dump the array tables before for (table = 0; table < tables.length; table++) in function set_trc.
    I found the difference between execute rd.init() once and twice. There is a undefined element at index 2 caused the error when I execute rd.init() twice.

    So I add a loop to clear the undefined elements for now. But I wish the bug can fix radically.

    Sorry about my poor English, my country is not use English be mother tongue.

    By the way, how to drag a div and it contain a table?

  10. @Alan Jhu – Thanks to your description I located error and bug is fixed. Please download latest version – 3.1.3 The problem was in increasing top_position variable without initial setting to 0.

    Whatever is placed to the <div class=”drag”></div> it will be dragged. The same will be with table placed inside DIV element – but this is currently unexplored territory :)

  11. Hello, you have a pretty nice script there!
    But I have one little problem when using it:
    I have a very long list of entries which I want to sort. (vertical list)
    You have this cool function, that the body scrolls automatically, when the mouse comes close to an end of the page. But if “Switching content continuously” is selected for example, often two entries are merging or there appears a gap in between. Of course this only happens when I´m dragging an object an scrolling at the same time. Maybe you have a solution for this :)

  12. @Fidelis – I made some code cleaning and improving autoscroll function. Here is snippet from the changes.txt file:

    3.2.0
    – code optimization in handler_onmousemove()
    set current table, row and cell:
    1) if mouse pointer is inside DIV id=”drag”
    2) and autoscroll is not working
    3) and current table contains nested table or cursor is outside of current cell
    – added example14 – Sort elements in long table
    – fixed bug in “switching” mode (two entries are merging or there appears a gap in between)
    – fixed settings of “current cell” in autoscroll mode (modified autoscrollX() and autoscrollY())
    – evt.clientX/evt.clientY replaced with local variables X/Y in handlers
    – added pointer object with pointer.x and pointer.y properties (properties are set in handlers)
    – modified set_trc() – removed input parameter (uses pointer object instead)
    – fixed bug with adding events in IE8
    – fixed bug for case where moved element changed cell dimensions

    Please try your example with latest REDIPS.drag library.
    Cheers!

  13. Hello i was wondering if anyone can help me.

    I have put a drag and drop feature into my site, (testing stage at the moment) and i need help with the save feature, here is the page on my site i am referring to http://www.extremehomepages.com/Hompage/4test/index.html .i would like a button next to the search button replacing the existing one saying save wich will save the divs position on the page so a user will come back to it where they left it,

    any help will be much appreciated

    thank you in advance, im not very good at web development but im learning a little bit at a time.

  14. How to move a nested table into other cell?
    In example 13, all nested tables can’t move.
    Thanks

  15. @Alan Jhu – Nested tables in example13 are used for drag-n-drop layout, so they can not move. You can try to place table in DIV element and table should become movable (but other object will not be able to drop in). Huh, nested tables are not easy to implement because of layout priority and overlapping. I hope future releases will have better support for nested tables.

  16. Example14: works perfectly now, thanks a lot! With the new version my Sort became a lot better!
    Have a nice day :-)

  17. Hi dbunic! Really, you’re doing a great work! Congratulations!

    I need to fire an event when a container receives some element. I have to identifiy the container, the element and perform a call to the server with these parameters.

    I have a question: I need in your opinion, what is the best way to implement this, using your library? I know how to call the server but how to fire an event immediatly after the drop?

    Many thanks!

  18. Hi
    I would like to use your script but the table will be automatically generated with unknown numbers of rows, so it’s hard to compute the results with the collum/row coordinates.

    What about use the id of the td for generate datas ?
    For example if the div “sea” is in the td “blue” the save_content function returns “p[]=sea_blue”

  19. Hi, dbunic, you did a pretty good job over here and i would like to use your script for my school work. Currently, my work is implemented using the Hello World and it means you can only drag one clone object to the table and the original object will not draggable anymore. So how can i change the code to make it draggable once i drag the clone object to the “Trash” button?

    Any form of help will be appreciated! Thank you so much!

Leave a Comment