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 Dd
    how do you specify a number of cells to show? Example I want an 8×8 table show how do you do that? Also how do you fit an image in an 8×8? Thank you…

  2. I’m not find the uncompressed redips in this site, I don’t know where I can find it

  3. @Dusan – It should be possible to have more than one rowhandler in a row. I just replaced first row in example15 (Drag and drop table rows) with the following HTML. With this replacement, now is possible to drag row by clicking on circle (DIV element) in any TD:

    <tr class="rl">
        <td class="rowhandler"><div class="drag row"></div></td>
        <td class="rowhandler"><div class="drag row"></div></td>
        <td class="rowhandler"><div class="drag row"></div></td>
        <td class="rowhandler"><div class="drag row"></div></td>
        <td class="rowhandler"><div class="drag row"></div></td>
        <td class="rowhandler"><div class="drag row"></div></td>
    </tr>
    

    This is tested in Chrome and IE8 so I guess it should work in IE9 also. I’m glad you find REDIPS.drag useful – thanks for using it.

    @Frank – Number of cells in a table are defined with HTML. Here is simple example of 3×3 table:

    <table>
        <tr>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <td></td>
            <td></td>
            <td></td>
        </tr>
    </table>
    

    … and did you try examples for table background image from the following link:

    http://www.tagindex.net/css/table/background_image.html

    Hope this could give you some directions.

    @vic shang – redips2.tar.gz package can be download from the “Download” link below post title. Inside you will find compressed redips-drag-min.js file and well commented (verified with jsLint) source file.

  4. Good evening and thanks for this very nice drag and drop post…

    I have a question …

    Is it possible to update (through php) the object’s new position?

    I’d like to use this for a grid where quiz question numbers and quiz question rounds cross each other. So the first cell is the first question of the first round. After having dropped the object i’d like to save the new position. Where can I place the ajax event?

    Thanks in advance for your time!

  5. @lucio – Please download redips2.tar.gz package and in example03/ajax directory you will find modification of example03. Here is code snippet where you can see AJAX call in event.dropped():

    // save - after element is dropped
    rd.event.dropped = function () {
        // get element position (method returns array with current and source positions -
        // tableIndex, rowIndex and cellIndex)
        var pos = rd.getPosition();
        // save table content
        sendRequest('ajax/db_save.php?p=' + rd.obj.id + '_' + pos.join('_'));
    };
    

    sendRequest() and all other details are located in script.js file. This example is a good start point for your project.

  6. Hi, do you know if it’s possible to keep the column header always visible at the top of the page even if I scroll down ? I tried with position:fixed on my th elements but it doesn’t work (all th are fixed on the top but they are all gathered on the first column…)

    I also would like that my div element within a cell takes the whole td. For the moment, my div element doesn’t take the whole space within my td.

    Thanks for your help !

  7. Hi, it’s great, congratulations!
    I’d like to ask if I can get these functions:
    – Selection of cell ranges with drag (for large areas)
    – Moving the selected area, maintaining the same geometry
    – Connect to the database, for location update in db
    Thanks in advance

  8. @am – I have prepared and sent offline example to show fixed table header. It actually contains two tables (header table and content table). The trick is to scroll header table while user scrolls content table.

    And regarding occupying whole TD cell, please see example24 (Table editor) where dropped DIV element is stretched to the cell width (just peek to the rd.event.droppedBefore event handler in example24/script.js).

    @delfi – REDIPS.drag can only drag one DIV element at the same time. But you can create custom JS code like in example12 (select and move more elements) to move more elements at once. This demo doesn’t have option to preserve initial geometry.

    Saving table content to the database is possible. Please see example3 (school timetable) where timetable is saved to the MySQL database via PHP.

  9. Thanks Bunic
    Is there a function within redips to auto generate a table for you eg an 8×8 to fit an 8×8 image?
    For instance I ran into a sutuation where a background table image is not fully shown in a table either an 8×6 or 8×8 using redips. Do you have an email I can email you my work so you can see what I mean. Something similar to this…http://www.quackit.com/html/html_table_generator.cfm

  10. @Frank – REDIPS.drag is drag and drop library but REDIPS.table maybe could be helpful. Please see Merge and split table cells with JavaScript. The idea is to have minimal table and with “row” and “column” methods to expand table to 8×8. If you need to contact me, you will find my email address in “About” page.
    ;)

  11. Hi dbunic, ¿How do I get the final HTML source code, after move the DIV objects??

    Thks,

    Frederick

  12. @Frederick – After DIV element is dropped to the TD, here is code snippet to show HTML source of table cell. You will need to place id=”message” to some element on the page to see the result. Line with “replace” is needed to encode “<” for demo purpose otherwise it can be removed.

    rd.event.dropped = function (targetCell) {
        // target cell HTML source code
        var html = targetCell.innerHTML;
        // encode < character (this line is needed only for demo)
        html = html.replace(/</g, '&lt;');
        // display HTML source in some object on page
        document.getElementById('message').innerHTML = html;
    };

    Hope this was your question.

  13. Thats very interesting and useful scripts.
    I am learning Russian nowadays,and I use http://www.internetpolyglot.com for learning
    in the match game I wished to be able to extract those words I realy need to memorize and practice and exclude those rare ones, it will be very helpful if some of you guys work on that and give us a way (or page in a website) to fill in list of words with their English equivalent to play with.

  14. Hi Darko,

    I would like to know if it’s possible to find the original row of a div element in the dropped event ?

    Thanks again for your work/help !

  15. Another question :)

    In my droppedBefore event, I move an element (an element which is linked with my current dragged div) to the target row. In my dropped event, I resize the elements depending the content of the rows. I have noticed that my element moved in droppedBefore with rd.moveObject doesn’t belong to the target row in the dropped event. The result is that my linked element is not well resized. How can I handle that ? Maybe the use of moveObject in droppedBefore in inapropriate ? Or maybe could I call my resize function later ? Many thanks !

  16. @A M – Yes it’s possible. Here is simple code that will color source row in dropped event handler:

    rd.event.dropped = function (targetCell) {
        // define local variables
        var tr, td;
        // set source TD
        td = rd.td.source;
        // find TR
        tr = rd.findParent('TR', td);
        // change source row color
        tr.style.backgroundColor = 'lime';
    };
    

    In documentation you can find all details about rd.td object.

    event.droppedBefore is triggered before DIV element is dropped to the TD (before DIV element will belong to target TD). So, if you want to change DIV size to match TD dimensions, here is code snippet from example24 how to resize DIV width to the width of target TD:

    rd.event.droppedBefore = function (targetCell) {
        // set new width to the dropped DIV element
        var width = targetCell.offsetWidth;
        // set width and reset height value
        rd.obj.style.width = (width - 2) + 'px';
        rd.obj.style.height = '';
    };
    

    Similar should be with the height …

  17. Hi Darko,
    I am facing problems in the example24 of drag and drop. I have set up the application in my tomcat server and the application is coming up fine. After creating a sample table form when I click on “Save Form” I get the following error on my console.

    Uncaught TypeError: Cannot read property 'elements' of undefined script.js:258
    

    I am not getting any “FORM” tag in the dragged component’s div. As a result “frm” is undefined below:

    // set form reference (there shoud be only one form inside DIV component)
    frm = div.getElementsByTagName('FORM')[0];
    

    Please suggest what needs to be done?
    thanks,
    Gaurav

  18. @Gaurav – The problematic line is inside form2obj(frm) method, and that method is called from save(). So, if you are familiar with Web inspector tool (in Chrome, FF, IE10 …) you can set break point to line 215 where “frm” is defined:

    // set form reference (there shoud be only one form inside DIV component)
    frm = div.getElementsByTagName('FORM')[0]; // <-- line 215
    // call method to scan component form and return all form elements with their values
    component.form = redips.form2obj(frm);
    

    Each component (DIV element) contains FORM tag. Here is example for text component:

    <form>
        <table class="nolayout">
            <tr>
                <td>Explanatory text</td>
                <td><input type="text" name="t1"/></td>
            </tr>
            <tr>
                <td>Label</td>
                <td><input type="text" name="t2"/></td>
            </tr>
        </table>
    </form>
    

    So, the problem may be that your components doesn’t contain FORM like it is in example above. Inspecting application with Web inspector should give the right answer. Hope this answer will help you to carry on with this example.

Leave a Comment