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. @dbunic – I figured it out. I needed to send init() to each drag div and add them into the css. I am dynamically creating them, so I wonder if I need to figure out a max number and just pre-populate the CSS

    Great work! Thank you!

  2. @dbunic – I figured it out, I need to initialize the drag divs. I dynamically create the tables any idea of how I can initialize only what I. Or if I only plan to have a max of 8 tables, initialize all 8 with a try/catch clause to catch the error if a div doesn’t exist?

    Thanks for your help.

  3. @Al – Initialization of drag containers doesn’t have to be at the beginning (I mean in onload event). It is possible to dynamically create tables (with or without DIV elements) and then call REDIPS.drag.init() method. This way, tables and content will be scanned and prepared for drag and drop.

  4. Howto change background color in example: enable dropping to already taken table cells?
    Drag colored cell to already taken cell…
    Not changing table cells content but only color?

  5. @edgar – I’m afraid that I didn’t understand your question completely. Anyway, if your question is how to change highlight color for occupied cells here is code snippet:

    // set default highlight color
    rd.hover.colorTd = 'blue';
    
    // event fired on every cell change
    rd.event.changed = function (td) {
        // test current TD (empty or not)
        var empty = rd.emptyCell(td, 'test');
        // if cell is empty then set default background color
        if (empty) {
            rd.hover.colorTd = 'blue';
        }
        // cell is not empty - set red color
        else {
            rd.hover.colorTd = 'red';
        }
    };
    
  6. Sorry my bad english…
    Thank you for this answer, but i want to do in example 3 this.
    Example: drag and drop school subject “English” to the timetable…
    After that drag and drop teacher (background colorised table cell) to ocupied “English” cell in timetable.
    Each teacher has a different color in database and after second drag and drop timetable shows subjects different background colors.
    English = teacher1 = red
    English = teacher2 = green
    and so on…

  7. Zillion thx for this! Quick question:

    after ana running ajax on my page, the elements are not draggable anymore… Any ideas on that?
    I can supply the code if needed.

    Cheers,

    C

  8. @edgar – OK, here is another snippet. If “teacher” DIV elements are declared as clone elements:

    <td><div id="t1" class="drag clone">Teacher1</div></td>
    <td><div id="t2" class="drag clone">Teacher2</div></td>
    <td><div id="tt" class="drag clone">Reset</div></td>
    

    Then the following JS code in event.droppedBefore() can do the trick:

    // event fired before DIV element is dropped to the table
    rd.event.droppedBefore = function (td) {
        // id of dropped DIV element and local variables
        var id = rd.obj.id,
            empty,
            teacher,
            color;
        // test if dragged DIV el is teacher
        // (id of teacher DIV should begin with "t" - t1, t2, t3, t4 ...)
        if (id.substr(0, 1) === 't') {
            // dropped DIV element is teacher, so test if target TD is empty
            empty = rd.emptyCell(td, 'test');
            // set first two characters for teacher (cloned DIV will have id like "t1c2")
            teacher = id.substr(0, 2);
            // if target cell is not empty then set default background color
            if (!empty) {
                switch (teacher) {
                    // teacher 1
                    case 't1':
                        color = 'blue';
                        break;
                    // teacher 2
                    case 't2':
                        color = 'lime';
                        break;
                    // reset color (cloned DIV with id="tt")
                    // suppose that table has white background
                    case 'tt':
                        color = 'white';
                        break;
                    // default color
                    default:
                        color = 'red';
                        break;
                }
                // set background color
                td.style.backgroundColor = color;
            }
            // teacher DIV element should not be dropped
            return false;
        }
    };
    

    As you can see from the code, teacher color is hardcoded in event handler. But you can read class name from the origin DIV element and append it to the destination TD (more elegant way). Anyway, hope this lines will give you directions how to solve the problem.

    @Chimele – AJAX and REDIPS.drag should work nicely as you can see from several examples in redips2.tar.gz package. Just search for “AJAX” keyword across directories to see where and how is used. If you have online example to show (you can prepare example on http://jsfiddle.net/), I will gladly peek to the code and try to help.

  9. @dbunic – it is very, very good job…
    But I do not understand how a teachers colors could be attributed to the database?

  10. hi dbunic. Thanks for posting this useful script. I wonder if anyone has use this drag and drop feature to create an ad hoc report for the user.i.e: user can pick and choose which field they want to display and what order they should be on to create a report.

    Thanks,

  11. @edgar – One way can be to dynamically create “color” object on the server side (e.g. with PHP or any other script) and to use in presented function. Here is example how to create global variable:

    // prepare teacher colors
    var color = {
        t1 : 'blue',
        t2 : 'lime',
        t3 : 'red',
        tt : 'white'};
    

    … and how to use it in function.

    ...
    // if target cell is not empty then set default background color
    if (!empty) {
        // set background color
        td.style.backgroundColor = color[teacher];
    }
    ...
    

    Other way can be to load colors via AJAX but it is a bit complicated if you are not familiar with AJAX.

    @Rizal – REDIPS.drag is useful for building any kind of user interface. Personally, didn’t use it for report definition, but this should not be a problem. Hope someone will post case similar to your question …

  12. Is it possible to include a select option with as many dragable elements but user selects number of elements to work with? Example i want a user to select 2 dragable elements to work with, then 5 then 10 etc with ability to hide if not needed? Example I have a select option. User selects 5 drag able objects 5 divs shows up on the screen each is individual with hide ability and possibly with a unique name like in checkers sort of because objects are drag able and disappear. Sorry hard to explain but that’s what I mean.

  13. Thank you very much for very interesting scripts!
    I have two questions for the static version of Example 3 (timetable):
    1) Is there a way to save the changes of the timetable in static version?
    2) Can the fields on the left of the timetable (Arts, Biology, …) be set variable, such as the field “content” in Example 1?
    Thanks a lot!
    Olaf

  14. @Frank – Your question is related more to logic that goes out of REDIPS.drag scope. Anyway, DIV elements can be dynamically create/cloned and placed to the table. After DIV element is created it should be initialized with enableDrag() method. On the other hand, you can use cloneObject() method to manually clone DIV element. Here is code snippet from example03 to clone and place DIV element to the table row:

    // clone DIV element
    objNew = rd.cloneObject(objOld);
    // append to the table cell
    targetRow.cells[i].appendChild(objNew);
    

    If you have some JS skills, you can write code to read dropdown selected option and to place DIV elements on the table with cloneObject() method.

    @Olaf – Hi!
    1)
    If you want to avoid server side logic and to have “smart” client then it’s possible with more JS code and AJAX. In example03 you will find “ajax” variant of the demo where each DIV drop is saved via AJAX. Just replace script.js with ajax/script.js and you will see how changes are saved after DIV element is dropped. index.php is needed when user initially loads page.

    2)
    Elements on the left can contain text field but you will have to work out on logic to save such element to the database (like reading text element content and sending to the server side). In example03, first two chars of DIV element defines school subject. REDIPS.drag will attach cNN to each DIV element ID after element is cloned (NN is integer). In a moment when table content is saved, only ID of DIV elements and their position will be send to the db_save.php script. Example24 is a bit complicated but it can you give direction how to send more data in JSON format.

  15. Hello,
    quick question, I have a problem when I want to have two tables, side by side, so I can drag-n-drop rows between them. When there is no float on table containers, it works, but when I put float on them, it stops working. I am completely baffled, because in your examples, it works. Would you know what could cause the problem?

    Pozdrav, and thank you in advance :)

  16. @Dusan – Maybe the problem is related to drag container. Please make drag container visible with the following CSS style(s):

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

    Next try to uncomment “display: table” to wrap all tables inside drag container. Hope this will help and if not, do not hesitate to contact me again. Pozdrav! ;)

    @Frank – You can set any background image for table with some CSS styles. Here is page for background-image: url() style with several examples (hope this will give you an idea how to solve the problem):

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

    If you need checkerboard then it’s not needed image because cells can be simply colored with CSS background-color style. Cheers!

  17. Thank you! Your method solved the problem, but it caused another one in IE9. The dragable rows are showing under the tables while being dragged, but I solved that problem in another way.
    Now what I would like to do is be able to grab the whole row, anywhere I want, and drag it to the other table. I tried to add rowhandler and drag row to more than one td, but it doesn’t seem to work. Is it in any way possible with your library, or did I do something wrong?

    Again, thank you very much in advance for your answer, and thank you for this great library! :)

Leave a Comment