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 I love your job. it look very fine. Actually how can I add it into a formbuilder plugin?

  2. i’ve a question for saving issue. for example i’ll drag and drop div elements in my calendar and save the new moved date into my database. each day has of
    <td id="2019-07-01" YYYY-MM-DD

    The dragged id is clear, i can catch it with div class="redips-drag" id="my_id2" but how can i find out the id of dropped td ?

  3. Here is example how to access target cell in event.dropped:

    rd.event.dropped = function (targetCell) {
        var id = targetCell.id;
        ...
        ...
    };
    

    Actually, target cell is optional input parameter in this event handler (and in clonedDropped, droppedBefore, switched) so all TD properties (including “id”) can be easily accessed.

  4. Hi,
    for moving elements or cloned elements on HTML table, please open example18. If you check “Cloned” checkbox in this example then DIV element will be cloned and placed to the random TD.

    https://www.redips.net/my/preview/REDIPS_drag/example18/

    For dynamically adding table cells and rows or merging / splitting table elements, please look my REDIPS.table library. In example post, TD cells can be marked by clicking (let say two neighbor cells) and then try to click merge. REDIPS.table contains some useful methods and maybe it will be helpful for your project:

    https://www.redips.net/javascript/table-td-merge-split/

    Cheers!

  5. Is there a way to get the id of a div element if you have its position (i.e., reverse of getPosition)?
    Thanks a lot,
    Ragheb

  6. Thank you for maintaining such a fine piece of code and making it available under a BSD license. I last used this on a personal project in 2011 before touch actions were included. I haven’t done any web development (hobbiest) since then, but have another hobby project that requires browser drag and drop. I was looking through JQuery UI, etc. but came back to this just in case it was still be maintained. I was very happy to see you’ve been keeping it updated all these years! Your documentation and examples are outstanding and extremely helpful to novice developers like me (and probably advanced coders as well ;). Just wanted to let you know your efforts are appreciated, keep up the good work!

  7. Hi Jim,
    thank you very much for supporting my site and this drag and drop library. I developed REDIPS.drag as a solution for calendar planning app in my company and at the end it was pretty nice lib. At that time I didn’t find any similar lib to combine HTML table and dragging DIV elements, so I decided to publish it on Github and make it public.

    Thank you for your feedback and I’m glad that REDIPS.drag is still useful.
    Cheers!

  8. Hi.
    Thanks for a really good script!
    I have a question that I find no answer to.
    I don’t know JavaScript so well and wonder if this can be done.
    I have a program where I can sort the archer to which target they should be on. It can have 4 shooters on each target.
    No problem so far.
    I would like to be able to change the background image of each shooter at the touch of a button (where each DIV id>0).
    It can be as many as 32 shooters.
    (8 targets with 4 shooters at every target)
    Let’s say I want to see what shooting style they have (eg barebow, recurve, compound longbow) so I would like to press a button called eg “see shooting style” and all shooters’ cells ( ) and everyone’s background image changes to what they shoot. (e.g a picture of an barebow or compound).
    Same thing if I want to see what kind of board they shoot, eg 3spot, 10 ring 40cm, 10 ring 60cm. Press a button called ‘see board’, for example, and all shooters’ backgrounds will be changed to the respective board.
    All data is in MySQL db and can be retrieved via PHP script.
    But how do you get it in so it works as intended, if it goes?

  9. Hi Magnus,
    first I would like to thank you for supporting this site – thank you very much. Changing style properties (like background color or image) for DIV element should not be a problem. I have prepared JSFiddle example with several DIV elements in table. Background color will be changed only for those DIV elements with defined ID. Method also accepts class name as input filter to show how color will be changed for green or orange elements. Here is method source code:

    // change background color of DIV elements
    redips.changeColor = function (divClassName, color) {
        // variables
        var tbl = document.getElementById('table1'), // ref to main table
            divs = tbl.getElementsByTagName('DIV'),  // collect all DIV in table
            div,
            i;
          
        // loop through DIV collection
        for (i = 0; i < divs.length; i++) {
            // define current DIV
            div = divs[i];
            // change background color if "id" of div element is
            // defined and class name matched
            if (div.id !== '' && div.className.indexOf(divClassName) > -1) {
                div.style.backgroundColor = color;
            }
        }
    };
    
    // how to call method
    redips.changeColor('orange', 'lightskyblue');
    redips.changeColor('green', 'gold');
    redips.changeColor('orange', ''); // reset orange elements
    

    Hope this example will give you hint / direction how to change object properties dynamically. How all together work can be seen on:

    https://jsfiddle.net/dbunic/hnbj65Lp/

    Cheers!

  10. Thank you so much for the answer.
    That’s not really what I want.
    If we take a1, a2 and a3 for example.
    If I press the ‘Division’ button I want to see what type of _all_ shooters are with a background image.
    The shooters a1, a2 can be compound and a3 is a barebow, so here are two different images at once.
    If I press the ‘Border’ button, I want to see all kinds of borders that all shooters use at once.
    The shooter a1 uses a 3spot, a2 and a3 use 10 rings 40cm.
    I think I have to create an index for each shooter with the type of border, division and so on.
    Here’s how I want (first element is shooter id and (?) div id) , a 2d array?
    pseudo code:

    Shooter one: type[55123]=[border1.jpg, division1.jpg]
    shooter two: type[55132]=[border2.jpg, division1.jpg]
    shooter three: type[56154]=[border2.jpg, division2.jpg]
    

    and the button ‘division’ calls changetype(2)

    changetype(i) {
    iterate throughout the table for each id==type[0] and set the background with setbackground[div id]=type[div id][i]
    }
    

    Sorry if this is not in scope for drag and drop.
    But if someone can point me in right direction I would be happy. :)
    Ofcourse I will check internet more for answers.

  11. Hi,
    instead of creating JS array maybe all properties can be stored to the data-* attributes for each DIV element. Here is a good start page to read more about data-* attributes:

    https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes

    I made modification of previous JSFiddle example to show how to read data-* attributes and how to display unique set:

    https://jsfiddle.net/dbunic/3qfojhb6/

    DIV elements can be prepared on the backend (PHP page) like this:

    <div id="a1" class="redips-drag" data-border="border1.jpg" data-division="division1.jpg">a1</div>
    <div id="a2" class="redips-drag" data-border="border2.jpg" data-division="division1.jpg">a2</div>
    <div id="a3" class="redips-drag" data-border="border2.jpg" data-division="division2.jpg">a3</div>
    

    So, each DIV element contains all needed properties. Next step is iteration through DIV elements to create unique list of values (for defined property – like “division”, “border” etc).

    // read values from "data-" attributes of dataName
    redips.getData = function (dataName) {
        // variables
        var tbl = document.getElementById('table1'), // ref to main table
            div = tbl.getElementsByTagName('DIV'),   // collect all DIV in table
            dataValue,
            arr = [],
            i;
          
        // loop through DIV collection
        for (i = 0; i < div.length; i++) {
            // read data value from current DIV element
            dataValue = div[i].dataset[dataName];
            // add value to the array if dataValue exists in HTML attribute
            // and array already doesnt contain that value
            if (dataValue !== undefined && arr.indexOf(dataValue) === -1) {
                arr.push(dataValue);
            }
        }
        // display uniq values from "data-" attributes
        alert(dataName + ' - ' + arr.toString());
    };
    
    // how to call method
    redips.getData('division');
    redips.getData('border');
    

    With this approach in HTML5, it is possible to store extra information in data-* attributes and values can be easily read or access in JavaScript.

    Hope this modification is now a bit closer to goals of your project :)
    Cheers!

  12. Hello.
    This made my day!
    With this and some more searching on internet and trial I found what I was looking for!
    It’s amazing what you can do with only css and with javascript it’s really powerful.

    Thanks!

  13. Yes, JavaScript is very powerful in combination with CSS. You can travel across DOM, communicate via JSON with backend and change elements and its properties on HTML page.

    Anyway, I’m glad it worked!
    Cheers :)

  14. thank you for the great stuff.
    i have a question how to add row handler dynamically.

    If you have static code like this

    i
    ts easy but how can you to this dynamically ?

  15. sorry the syntax was removed
    i meant : if you define a table with rows and cells in a static way you can add row handler with
    the class redips-rowhandler and redips-drag and redips-row.
    But how do you do this in case of a adding a row dynamically ?
    Thank you in advance for the answer !
    BR, Sascha

  16. At first it seems to me that event listener should be enabled for row handler (because DIV element is also dynamically added).

    <td class="redips-rowhandler">
        <div class="redips-drag redips-row"></div>
    </td>
    

    The safest way will be to call initialization after new row is added:

    // initialization
    rd.init();
    

    This way inner table array will be regenerated (because new row is added) and event listeners for DIV element (row handler) will be registered properly. To reduce overhead, it is possible to use initTables() and enableDrag() methods instead of calling init() but in general it will be OK.

  17. Hello Darko,

    your code works great and I am planning to use it in my program. Just one small question – maybe it is a bug or a misunderstanding from me:
    I use your example #01 with the three tables. Drag the dropdown select box with “table, drop, down, menu” to the first table and submit the data to the server. It will submit: “[…] tabledropdownmenu […]” which does not give me the possibility to know what option the user selected.

    Can you please provide information or a fix?

    Thank you and best regards

    Markus

  18. dbunic thanks for redits. It really is incredible.
    Excuse my question, if I make a clone of a cell that contains (for example an input, or options) how can I differentiate one value from another cloned value? From already thank you very much.

Leave a Comment