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. Hello darko,
    can you tell me how i can modify your class that requests me when i use the save function the whole table entries even the empty cells,

    i want the ouput p[]=id_2_1_1_div_textdiv_value&id_2_1_2_div_textdiv_value .. even if the cell is empty

    i have this kind of table,

    text1text2

    i have modified it and i can get the id of the div with the class=”textdiv” but when iam trying to get the innerhtml he is saying undefined.

    Thanks for your help

    Haris

  2. my table formation is missing.

    its like this

    table-
    tr

    (td) (div1 class=”textdiv” value= text)(div2 class=”textdiv” value=text) (/td)

    /tr

  3. Hi
    Redips.drag is exactly that I was looking for. Just perfect, thanks for sharing! I have written a tiny learning app, where the students have to order some values. Unfortunatly i am not a real programmer so I dont know, how to do this: After the first round is done, I will generate a new one and reset the table. Is there a function which can be called without reloading the whole page?
    Kurt

  4. @Haris – I’m not sure but JavaScript is case sensitive so you should use innerHTML property, not innerhtml. Maybe that’s the catch – just guessing. If it helps then fine, if not then post a comment and I will search further.

    @kurt – Unfortunately REDIPS.drag doesn’t contain function to return moved DIV elements to the initial positions. Anyway, it shouldn’t be complicated to implement a such functionality. enable_drag() should be modified to store initial positions inside each DIV element (like redips_container reference is stored to DIV elements during initialization). And finally you will have to create “reset_position()” function. This function should collect DIV elements at current state, read stored initial positions and with REDIPS.drag.move_object() relocate to the initial positions … So, this will look like reseting app. Hope this info will somehow help.

  5. Hi darko,
    thanks, it works now. I got a another question. when i send the form with the query from the save_button,

    form action=”checktable.php?p[]1_1_1_äüäüdata”

    the problem is, when i parse the query p in checktable and i output it he is not showing me the output in utf-8 for example öädat. Firefox Opera and Safari is showing me the output right but IE not. I don’t know why. Maybe you have an answer

    I set the header type with php to utf-8 in checktable.php.

    Thanks
    Haris

  6. Hi darko,
    i forgot to say that the parameters in the url are right formated and he is showing characters like ä ö ü (utf-8) right also in IE the parameters of the query are right in the URL but the OUTPUT is showing me instead of einführung einf[]hrung.

    Thanks

  7. @Haris – Is the HTML page where you create URL with parameters also defined as UTF-8? I mean do you have defined content type like:

    <meta http-equiv=”content-type” content=”text/html; charset=UTF-8″>

    Did you try to peek to the Web server’s log file to see how parameters actually looks from IE8, FF3 and Chrome? I had problems with IE8 and AJAX where I had to define UTF-8 charset in service returning data. You have opposite situation where IE8 prepares and sends parameters to the server. After googling, I found encodeURIComponent – a function that encodes a URI component. Maybe this can help.

  8. Hi darko,
    i have fount the same function. But i solved it in an another way, i put the data in a hidden textarea, then it worked fine.

    I tried also the meta tag and put a php header with uft-8 but it didnt worked.
    Thanks for all.

    Thanks a lot.

    Haris

  9. Hi!

    There is a really masterpiece code you made! But I am wondering if it is possible to implement multiple items selection by holding SHIFT key for series and CTRL for one item?

    Thx in advance!

    Shabba

  10. @Haris – I’m glad you solved your problem. Cheers!

    @Shabba – Selecting more than one DIV element should not be complicated. It could be done with shift as you suggested, but the problems will be:
    1) dragging / moving more elements at once (performance impact)
    2) dropping all elements (where to drop elements)

    If you have some JavaScript skills, then you can write simple code to select more then one DIV element and drop them all to the same table cell. For example, you can place checkbox in DIV elements to enable selecting DIV elements instead of selecting with shift key (solution with shift key is complicated). With some CSS, checkboxes can have nice design to fit well. Next, after checkboxes are checked, REDIPS.drag will drag and drop normally one DIV elements – nothing special. In a moment of dropping, custom code in myhandler_dropped() can collect checked DIV elements and relocate them to the target table cell … This solution is possible without modifying or upgrading current REDIPS.drag library. Just small piece of JavaScript code will do the job. Hope this guidelines will be helpful.

  11. re: dbunic re @vaidhy.
    Nice tool you have going on. I have the same question as Vaidhy.

    //tables_nodeList = div_drag.getElementsByTagName('table');
    tables_nodeList.push(document.getElementById('tableID')); // no sauce
    
  12. @Shabba – In the meantime you can download latest redips2.tar.gz package and try example12 – Select and move more elements. I played with JavaScript and prepared new example where you can select (with checkboxes) DIV elements and drop them all to the target table cell at once. Hope this functionality is what you are looking for.

    @Matt – Nested tables are not trivial to handle. The main problem is to somehow exclude table cells that contains nested tables and then this might work. In the init phase, REDIPS.drag will search for nested tables and that tables will not be included to the “tables” array. With modification inside “init_tables” function and marking table cell containing nested tables you can enable support for nested tables. Anyway, in the following period a week or two, I will try to apply mentioned idea to the REDIPS.drag library. Result will be announced on the twitter.com/dbunic

  13. Hi,

    First of all, thanks to your greate work. I have a question that when I plugged the library into richfaces, firefox is working fine, but IE8 didnt show the dragging when I drag from cell 1 to cell 10? Why?

  14. Announcement: – added support for nested tables in version 3.0.0. Package also contains example 13 to show nested tables functionality.

    @buddy – Is there any error message in IE8 that can give me a hint what’s wrong with IE8 and richfaces component library? Maybe the catch is in initialization. Instead of

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

    try with attaching onload event to the window document:

    if (window.addEventListener) {
        window.addEventListener('load', REDIPS.drag.init, false);
    }
    else if (window.attachEvent) {
        window.attachEvent('onload', REDIPS.drag.init);}
    else {
        window.onload = REDIPS.drag.init;
    }
    

    I’m just guessing – please let me know if this helps.

  15. I just found this site and it’s awesome. I’m learning so much about JS from this site.
    Can’t thank you enough.

    Clay

  16. Thanks for your reply dbunic. I have changed what you suggested but javascript error happened because of document.getElementById(‘drag’) equals null. It seems that the document is not ready so that the drag element can not be referenced.

    And I should make my problem more clear. Actually, the functionalities of your dnd lib is working fine under IE8 (I could drag a div inside a table from cell 1 to cell 10), except the value/elements (\A) inside div could not be shown while in dragging.

    For debugging, I only copied the example from your distribution to xhtml, so there is no richfaces component inside. What is the difference between plain html and xhtml is that xhtml included richfaces css (should not be an issue as the page didnt have richfaces component) and a skinning.js. The skinning.js code is below:

    {
    var mediaName = "rich-extended-skinning";
    var userAgent = navigator.userAgent;
    var skipNavigator = window.opera ||
        (userAgent.indexOf('AppleWebKit/') > - 1 && userAgent.indexOf('Chrome/') == -);
    if (!skipNavigator) {
        var resetMedia = function (elt) {
            var media = elt.getAttribute('media');
            if (mediaName == media) {
                elt.removeAttribute('media');
            }
        };
        if (!window._RICH_FACES_SKINNING_ADDED_TO_BODY) {
            var getElementByTagName = function (elt, name) {
                var elements;
                try {
                    elements = elt.selectNodes(".//*[local-name()=\"" + name + "\"]");
                } catch (ex) {
                    try {
                        elements = elt.getElementsByTagName(name);
                    } catch (nf) {}
                }
                return elements;
            };
            var f = function () {
                if (window.RICH_FACES_EXTENDED_SKINNING_ON) {
                    var styles = getElementByTagName(document, 'link');
                    if (styles) {
                       var l = styles.length;
                       for (var i = 0; i < l; i++) {
                            var elt = styles[i];
                            resetMedia(elt);
                        }
                    }
                }
            };
            if (window.addEventListener) {
                window.addEventListener( 'load', f, false);
            } else {
                window.attachEvent( 'onload', f);
            }
            window._RICH_FACES_SKINNING_ADDED_TO_BODY = true;
        }
        if (!window._RICH_FACES_SKINNING_ADDED_TO_AJAX && typeof A4J != 'undefined' && A4J.AJAX) {
            A4J.AJAX.AddHeadElementTransformer(function (elt) {
                if (window.RICH_FACES_EXTENDED_SKINNING_ON) {
                    if (elt.tagName && elt.tagName.toLowerCase() == 'link') {
                        resetMedia(elt);
                    }
                }
            });
            window._RICH_FACES_SKINNING_ADDED_TO_AJAX = true;
        }
    }
    };
    

    Hope it can help …………………………………….

    Thank you very much dbunic 0Z

  17. Thanks for your reply. I have changed what your suggested and javascript error happened because of document.getElementById(‘drag’) equal to null. It seems that the document was not ready.

    And I should make my problem more clear. Actually, the functionalies of your dnd lib is working fine under IE8 except the dragging source cell content C e.g. div C div was not shown while in dragging but the source cell could drop onto a target cell. For experiment, I only copied the example from your distribution into xhtml. The difference between running plain html and xhtml is xhtml included extra richfaces css (should not be an issue as no richfaces component inside) and one skinning.js. Therefore, I dig into the skinning.js but couldnt find a clue. ???!!!!………

    Finally, I want to post out the skinning.js to see if you can help but how can I post source code like your reply?

  18. @buddy – Few months ago I have discussion about IE8 and “tablecontent stays in the cell while moving, but jumps to the right place”. The solution was to define DOCTYPE in HTML page like:

    <!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, but maybe you will have to fix styles …

Leave a Comment