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. @Markus Kirstein – saveContent() method read and return DIV elements from table. For each DIV element is prepared:

    id - element id
    r  - row index
    c  - cell index
    n  - class names
    t  - DIV innerText
    

    This data is prepared as query string or JSON format and send out from saveContent() method. In case when input element is placed inside DIV (text, checkbox, menu …) , value of element is ignored. If value of input element should be post to server then this is part of form submission or custom JS code that can prepare form element values as well.

    @Paul – When DIV element is cloned then all content inside is cloned too. Difference between first DIV element and cloned is in DIV id. REDIPS.drag will generate unique id attribute for each DIV element. In example #1 try to drag two cloned elements to first table and click save – id for each DIV element will be different. Other approach can be to use event.cloned() to change input name in moment when DIV element is cloned. Inside event handler exists reference to cloned object so it’s possible to change all contained objects and their properties …

  2. Hi,
    and congrats for the great work you have done with the library!

    Would it be possible to have example22 equipped with double-click and close button?

    Details: Addition to drag&drop it would be convenient to have person details displayed with a simple double-click event. Also a ‘close’ button would be nice so that you don’t have to drag the box back to the original location.

    I hope you have time and interest to add above functionalities…:)

    Kind Regards,
    Vesa

  3. hi in example01 Save content of the first table it cannot get the value of image(Smile) ,if i used image on that cell is there any method to get that value

  4. Please really make a demo or tutorial example of Drag and Drop table content with JavaScript that can input text and data can be saved to the mysqli database … to make it easier to understand
    I’ve tried but can’t always fail.
    Please help. Thank you sir. darko

  5. Hello i saw the example 29, there’s a way to move div with maybe same id (or other way to identify)? i’m doing a scheduler and for example an appointment have 2 cells because busy 2 hrs and if i move the appointment both cells must move not only the div\cells that i click\move

  6. @daniele – Example29 shows how to move more than one DIV element at time. The main method in this example is moveObject() and with input parameters such as DIV id and target cell, DIV element will move. So, it is possible to prepare some logic in event handler to pick needed DIV elements and move it to destination cell.

    Please take a look documentation for moveObject() method for calling examples.

  7. wow thank you for the quick answer! very gentle… i saw this but i saw that do the check if cells available just for the first one when i drop it not for the cells down (if i move 3 cells for example do just for the cell where i drop it not for the cells down them).. and go to unify the div inside the cell even i setup the single div cell

  8. So if I understand correctly, your idea is to automatically move more DIV elements to the same target TD (where dragged DIV element is dropped). In that case please look example12. In example12 you can check (lets say) DIV A and drag DIV B. In that case DIV A will be moved to the same cell as DIV B. So with some custom JS inside event handler you can produce any scenario needed for your project. As in example29, main method is moveObject() and the rest JS code is just wrapped around this method.

  9. sorry i didnt explain the right way… about the example12 when moved the div go all inside 1 cell but in my case imagine the table in excel if i’v cell a1 and a2 that i must move under column C if under column C i’v not 2 cells free but just 1 i can’t move.. but in the example the second cells will be merged with the cell full

  10. @daniele
    Preventing dropping DIV element is possible with event.droppedBefore() event handler. User will as usual drag DIV element and try to drop it. In that case assume the following event handler code:

    // how to cancel dropping DIV
    rd.event.droppedBefore = function () {
        //
        // JS logic to test is there enough room
        //
        // cancel DIV drop
        if (notEnoughRoom) {
            return false;
        }
    };
    

    That means for each DIV dropping your JS code will do “the magic” and test if there is enough room for other DIV elements to move below target. If that is not the case then returning “false” will indicate REDIPS.drag to return dragged DIV element to source TD. Return “false” in event.droppedBefore() also means that event.dropped() will not be executed.

    @Guillermo
    Hi, I tried to open page on stackoverflow.com but it doesn’t exist any more. I hope you managed to solve the problem.

  11. @gaoxiaowei
    REDIPS.drag is JavaScript library and developing such lib for Android platform would be quite another story.

    Cheers

  12. Hi there. REDIPS.drag has been working well in my project up till now. However, I noticed that when I experimented with setting the table’s TD elements to use relative positioning, then I was no longer able to drop anything into those cells.

    I’m just wondering if you have any idea why this might be. All that I’ve done is to set position: relative in the CSS, and this for each TD element in the body of the table to which I had been dragging things. I haven’t adjusted the actual position of the TD elements, only switched from static to relative positioning.

    This isn’t a huge deal, but it seemed that the relative position thing was going to set me up for styling the table in a certain way that would have been visually appealing, so I’m a little disappointed that it doesn’t seem to interact well with REDIPS.drag.

  13. Hi Erin,
    REDIPS.drag library calculates every TD position by adding offsetTop and offsetLeft property all way up to the document element. Therefore is important that every element has position style set to something other than the default “static” otherwise this element will not appear in the offsetParent chain (and position will be wrongly calculated). This is how browser interprets/sees DOM …

    Few more words can be read here:
    https://www.redips.net/javascript/redips-drag-documentation-appendix-a/#redips_noautoscroll

    I hope this reply explains a bit why “relative” settings should be used.

    Anyway, I’m glad to hear that REDIPS.drag is useful for you and I wish you all the best in 2021!

  14. I downloaded the REDIPS pack and tried it out. It is awesome.

    There’s only one bug I run into;
    I want to create a table that has 20 columns. When I create the table and want to drag an element to (for example) the 17th row, the cell doesn’t highlight and the object can not be dropped.

    Do you guys have any idea how to solve this?

    thanks in advance,
    Joost

  15. Hi Joost,
    your problem might be related to the dragging container. To start debugging, please make your dragging container visible with the following CSS style:

    #redips-drag {
        border: 1px solid lime;
    }
    

    This will help you to see dragging container and to verify if all tables are correctly placed inside container.

  16. Hi DBunic,

    Thanks for the reply!
    I tried it out and the dragging container is less wide than the amount of columns. I have a feeling there is a bug in Redips. Or do I have to set the amount of columns somewhere?

    thanks in advance,
    Joost

  17. Hi Joost,
    drag container is area where all the magic is happening. It is important to properly define container range with width and height. You can try to expand width in CSS so complete table will fit inside redips-drag container. Other approach is to change “display” type of redips-drag DIV element. Please try to set redips-drag container without any dimensions but with “display” property set to “table” like this:

    #redips-drag {
        border: 1px solid lime;
        display: table;
    }
    

    This should nicely wrap around table and drag-n-drop will work on all table columns.

  18. I am getting the “Uncaught ReferenceError: REDIPS is not defined
    at onload (10:1) ” even added the reference like below,

    Do you guys have any idea how to solve this?

Leave a Comment