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. Kjeld – your example looks like ground plan – very nice! Let’s go to the question. Yes, it’s possible to retrieve informations from the server without reloading whole page. You said that you’re not so good in JavaScript, but your question requires AJAX technique. Please pay attention to the AJAX progress bar where you can see how to construct pulling mechanism on your page and how to parse responded XML. It also implies to define simple service for returning object positions in XML format. And at last, request handler will have to append new element to the HTML table. This can be seen at JavaScript Drag and Drop example 3 after checking checkbox in upper left corner (script automatically creates school objects for the week). I know it doesn’t sound easy, but I hope this guidelines will help you.

    Kind Regards!
    Darko

  2. Hello Darko
    Thank´s for your reply. I´ve looked into your exampels, and I understand some of it ;~)
    But when I wan´t to change the tablecontent, it is the full content, and not just an addition.
    Maybee I could change the code from your schoolprojekt, to check if the cell is empty,and so on.
    When I saw your reply I was in the middle of trying another solution:
    Due to the string-manipulation on top af the page, I already had the content stored i an array, to be able to manipulate it again, if the user pressed the cancell (Fortryd in Danish).
    I then found out a way to load the drag-div-table without content, and from a iframe-file tranfer the relevant data to the same array, and load/reload it to the tables with a funktion like this:

    function hent() {
        drg.ans.value = akt['ans'];
        drg.tut.value = akt['tut'];
        drg.rec.value = akt['rec'];......etc....
        var x = document.getElementById('table1').rows
        var y = x[1].cells
        y[1].innerHTML = aktt[1]
        y[2].innerHTML = aktt[2]
        y[3].innerHTML = aktt[3]
        var y = x[2].cells
        y[0].innerHTML = akt[7]
        y[1].innerHTML = aktt[6]
        y[2].innerHTML = aktt[7]....etc....
    
        var rd = REDIPS.drag;
        rd.init();
    }
    

    But I must ask you – is it allright to “restart” your library with:

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

    It seems to work – even with the good old iframe-refresh-thing – you can see it here http://www.33137086.dk/REDIPS/

    Kind Regards!
    Kjeld

  3. Kjeld – I’m glad you found the solution for displaying table content. If iframe-refresh works then it’s not worth to complicate with AJAX.

    Calling REDIPS.drag.init() will scan tables and table rows inside dragging container. It’s a preferred way to initialize tables after table content is changed. Initialization is fast and overhead is small.

    Your example is really good looking.
    Cheers!!

  4. Hi again – and thanks for your help…
    There is only one minor problem left – even after I call REDIPS.drag.init(); the tablecontent stays in the cell while moving, but jumps to the right place when releasing mousebutton.
    Have I messed up something by loading/reloading the stuff to the table as I descriped yesterday ?
    The edited ex. is here http://www.33137086.dk/REDIPS/ and the cellmovingthing was the same before I changed it ;~) So I probably just need an extra call – but which ?

    Kind Regard!
    Kjeld

  5. Dear Darko,

    I recently found your great Drag&Drop-library on http://www.redips.net, which is some really cool stuff.

    I’m especially interested in some kind of ranking: I need a table with switchable list, whose number of elements grows dynamically. The growth process should again be realized by drag and drop, i.e. more elements are dragged into the switchable list.

    Of course I would enable drop-option: ‘switchable’; for one table, which works perfect using your library. Unfortunately, if I add a second table consisting of elements that might be moved into the first table, switching is automatically enabled for both tables, i.e. elements might “jump” between the two tables.

    I hope you understand my problem and are willing to help me – is it possible, that upper ranking functionality is realized using your library?

    Thank you very much in advance
    best regards
    Thomas

  6. @Kjeld – You said the problem occurs after refreshing table content … Can you please prepare problematic example and allow me peek to the described situation? I’m not sure where’s the catch but I will help you to solve the problem.

    @Thomas – If I understood well, you have two tables. One for ranking and other with elements to drag to the first table. It’s understandable when element from the second table is dropped to the empty cell in the ranking table. Element will successfully land to the table. But what will happen if user want to drop element to the non empty cell (I mean what is application logic)? Of course, with drop_option=”switch” elements will exchange positions and this is unwanted case. But, do you want to disable this scenario (elements from the second table can pick only free cells) or element will drop to the same cell (you will have two elements in the ranking table cell)?

  7. dbunic – Thank you for giving me all this help ;-)
    I´ve found out that it´s browserspecific – in IE7-8 it show´s – not in FF and SF.
    The example is http://www.33137086.dk/REDIPS/.
    Here is the structure of the:

    function hent() {
        var y = x[8].cells
        y[0].innerHTML = akt[10]
        y[1].innerHTML = akt[12]
        y[5].innerHTML = akt[13]
        y[6].innerHTML = akt[14]
        REDIPS.drag.init();
    };
    

    which I use to to fill the table content from an iframe.
    The akt[] and aktt[] arrays is filled beforehand with stuff from MySql – and that part works in IE, others can´t save (but that okay). The whole thing works fine i IE – except that the cell-content stay in place – when moving around the table – until the mouse is relased – then it jumps to the right place.

    Best Regards!
    Kjeld

  8. Thanks a lot for your answer, Darko!

    What I want to realize is a bit hard to describe… As you understood correctly, I have two tables. Table one is the ranking table. Usually, the table is empty in the beginning. Step by step items are dragged from table two to table one. If an element is dragged to an empty cell, everything works fine. If an element is dragged to an non-empty cell, the element of this non-empty cell should automatically be moved to the cell below. If the cell below is non-empty, too, its content should also be moved downwards, and so on.

    In other words, I need a table with drop-option: ‘switchable’; like the index.html-Page of your Drag and Drop examples. But I want to add elements to this table dynamically, i.e. only table 1 is switchable and table 2 is not…

    I hope you understand what I mean…

  9. @Kjeld- I think I found the solution. Please try to define DOCTYPE in your HTML:

    <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>

    This should be the very first line in HTML. After adding DOCTYPE, dragging should start behave normally like in FF or Chrome. You will also notice a little mess up in upper table, but I hope this can be fixed with CSS.

    @ts – Let’s clarify the rules of your application with two tables.
    1)
    If user drags element from table two then element can be placed only to the empty cells of ranking table.
    2)
    If user drags element from the ranking table, then element can’t be placed to the table two and should switch with element on the ranking table.

    So, if you agree with this, please give me few day and I will try to prepare working example.

  10. those two rules sound good – I’m really looking forward to your working example :-)

    just one comment to 2) – I would prefer the “switching content continuously”-option for table two…

    Thank you very very much for your help, again!

  11. @ts – Please download latest redips2.tar.gz package. After unpacking you will find example9 – single and switching mode. This example will give a hint how to combine drop options regarding dragging elements from different tables. I hope it will be easier for you to go on.

    drag.js is also modified. Custom element properties are renamed to prevent namespace conflicts with other script components that might add arbitrary properties. Properties “enabled” and “container” are renamed to “redips_enabled” and “redips_container”.

  12. @dbunic: Thanks alot for your help. Example 9 is great!

    Up to now I was using jquery’s draggable / sortable to realize the ranking. But your script is so much better than that :-)

  13. What a stupid mistake I´ve made – it probably happend when I took the code from the iframe and placed it in the index.php instead. I had to change a lot in the css to make it look nice again (as you wrote). And it only works i IE – not at all i FF – and not good in SF – but that doesn´t matter, because it´s made for a specific intranet, and only with IE < 6.

    You can see he result at http://www.33137086.dk/REDIPS/ if you know someone with a PC and IE7/8 ;~)

    Thanks again for all your help…Kjeld

  14. Guys, I’m glad it works. Thank you for using my REDIPS.drag lib and greetings from Croatia!

    @Kyeld – As I can see dragging works in Chrome – saving doesn’t. Anyway, in case when you will need a cross browser application let me know and I will try to help ;)

  15. There is a bug in the JavaScript for this superb drag ‘n drop implementation. If the user has used a mouse event to increase the dimensions of the table (for example, in onMouseUp, to allow more space for a “bucket” drag ‘n drop operation), then any cells beyond the target table limits that are classed as “mark” (cannot be target of drop), become targetable on the next drag ‘n drop operation. The fix is easy :-

    ===============================
    Change :-
    ===============================

    // onmousedown handler
    handler_onmousedown = function (e) {
        var evt = e || window.event,
            // define event (cross browser)
            offset, // object offset
            mouseButton; // start drag if left mouse button is pressed
        // enable control for form elements
        if (elementControl(evt)) {
            return true;
        }
    

    ===============================
    To :-
    ===============================

    // onmousedown handler
    handler_onmousedown = function (e) {
        var evt = e || window.event,
            // define event (cross browser)
            offset, // object offset
            mouseButton; // start drag if left mouse button is pressed
        // enable control for form elements
        if (elementControl(evt)) {
            return true;
        }
        calculate_cells(); // <-- add this line
    

    Regards,
    MJ

  16. Hi,

    Is it possible to select multiple cells in a table.
    I would like to have a funcionality similar to calendar. I need to select a couple of days and drag them to another table.

    Best regards

  17. Hi,

    first of all – i’m so glad that i finally found what i was looking for with this lib.

    Now my question: when more than one element is dropped into a cell, they are placed beneath each other. i am looking for a way to have them placed next to each other (floating?) as long as the cell is wide enough.
    I tried to find my way through the code but couldn’t find a way to start.

    Would you help me out with that?

    Best,
    Mat

  18. dooh…figured it out by my self – a simple floating tag in the div’s style definition does the trick…

    m.

  19. One more thing:
    Let’s say i have a source table and a target table. The Elements of the source table are to be dropped in the target table, one target cell can have more than one element.
    The target table has two colums, the items dropped in the left column are to float left, the items dropped in the reight column are to float right.

    My question: is it possible to change/set the “float:##” attribute of the div element depending on where i drop it?

    Thanks for a little input!

    Mat

Leave a Comment