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. Forget about my last comment I almost immediately recognised my mistake! I had forgotten to add drag to the classname when I changed it. Problem solved!

    I came up with another query though. I am trying to use your library as a sort of drag and drop calendar and wondered how I might get an “event” to span 2 days. Obviously I can do this by changing the structure of the table, but I need to be able to drag a 2 day event onto another 2 day period and free up the original 2 seperate days. Hence changing the structure of the table won’t work for me. Is this possible with your library? Many thanks.

    hgbreton

  2. Do you know your support has done magic for me Man !!!! .

    Thanks a ton for this otherwise I would have been dissapointed and I would have to think something different about this and my client may have been furious upon me.

  3. @hgbreton – Yes, you can drag one element and drop more than one element – playing with myhandler_dropped() handler. But unfortunately, dragging more than one element at the same time is not possible with my lib. Sorry.

    @Gautam – I’m glad my advice was helpful. One again, your application looks very nice! Thank you for using my lib. Cheers!

  4. Wow! Very cool. Never thought this could be done so smoothly with JS. I am thinking of using JS for my next project. Love it. Regards Mark

  5. Hello. This library is brilliant. I have already solved one tricky problem with it.
    But now i’m trying some deeper waters and I am stuck.

    The goal of this is to create fixtures for the championship. But these fixtures sometimes need to be adjusted manually (to avoid two teams of the same club to play at the same time and same place)

    The schema goes like that:

    div id=drag
       mysql query;
       foreach result {
         div style=”float: left”
         table id=table’.$id.’
           Results of fixtures script, consisting of one column and al lot of cells down
         /table
         /div // THE TRICKY DIV CLOSING
       }
    /div

    The result can be seen here: http://www.8ball.lu/joueurs_clubs?view=fixtures (in fact, this is the page from admin section, i just put it here temporarily)

    I have two problems:

    1. If i close THE TRICKY DIV CLOSING, then i get nice layout, but i cannot move any element (like now)
    but if I do not close it, i can move all elements freely, but in that case all tables make one long vertical column and dont go side by side

    2. I will need to save the results in all tables (4 in this case, can be mo, can be less). Is it possible to do?
    I would just need to which element is in which row.

    3. Is it possible to restrict moving of elements from one table to another?

    Any hint would be very appreciated.
    P.S. Sorry for my first comments. I didnt realize that html was switched of.

  6. @Mindaugas – thank you for using my library. To enable dragging, please try to make visible DRAG div. For example put

    border: 2px lime solid;

    to the #drag style. You will see that somehow DIV is short and tables aren’t inside DIV id=”drag”. So, if you define height, elements will start to drag normally. I made the following modification:

    #drag{
      border: 2px lime solid;
      height: 300px;
      margin: 2px;
    /* width: 100%; */
    }

    Saving results from all tables is possible. I will modify save_content() method to enable saving content of all tables in case if input parameter doesn’t exist.

    You can define target cells for DIV elements – in other words disable dropping to another tables. Please see my other examples with target cells definition …

  7. Thank you for the hint. I didnt even notice that “drag” div doesn’t enclose all the table cells. It is sort of strange behaviour: when you look at the code, everything in eclosed within “drag”. But alas.

    I applied solution, found here: http://www.positioniseverything.net/easyclearing.html. added this class to the “drag” div. It perfectly wraps everyting inside.

    .clearfix:after {
    content: “.”;
    display: block;
    height: 0;
    clear: both;
    visibility: hidden;
    }

    .clearfix {display: inline-block;} /* for IE/Mac */

    Gonna study other examples now. Cheers

  8. Hello again,

    I tried to put restrictions on the tables, setting, that only elements with the id=table1 could be placed on any td with a class=table1
    Since i am quite new to php and a 0 in javascript, it’s not easy for me.
    Naturally, it doesnt work :) Where am I wrong?

    window.onload = function () {
    // initialization
    REDIPS.drag.init();
    // only “smile” can be placed to the marked cell
    // REDIPS.drag.mark.exception.d8 = ‘smile’;
    REDIPS.only.div.table1 = ‘table1’; //
    REDIPS.only.div.table2 = ‘table2’; //
    REDIPS.only.div.table3 = ‘table3’; //
    REDIPS.only.div.table4 = ‘table4’; //

    Thank you

  9. Hi dbunic, have come across a very odd bug when using your library in IE. If I include it on a page in which I also have a nested list like

    A
    B

    B1

    then your library stops working properly, and several errors are thrown up by IE. This works perfectly in Chrome however! Have you any idea what might be going on?

  10. Hello again

    I coudnt manage to restrict moving elements from one table to another. But maybe its not the biggest issue now. I coloured them differently.

    The tricky part is saving. I tried adding all four tables into saving file, but the script only recognizes and saves the first table. Others it sees empty.

    function save(){
    // scan first table
    // var content = REDIPS.drag.save_content(0);
    var content = REDIPS.drag.save_content(1);
    // var content = REDIPS.drag.save_content(2);
    // var content = REDIPS.drag.save_content(3);
    // var content = REDIPS.drag.save_content(4);

    When i uncomment table 2 or 3 or 4 i get “Table is empty” message.

    By the way, if anyone is interested in setting fixtures, i can recommend the script which i found couple of years ago at http://bluebones.net/fixtures.php.

    Connecting that with “Drag and Drop Table” of yours, creates very nice tool. Thanks Darko!

  11. hi, thanks for the awesome lib. i have been playing with it all day. i have a quick question. i need to be able to initiate more than one drag object on one page. i have been playing with the following code but there is a problem with the tables objects: you will see i have modified the var rd = REDIPS.drag;
    // initialization
    rd.init(document.getElementById(‘drag’), ‘drag’);
    a little bit to try to get this working. i just replaced the parameters i’m passing to init to your drag.js file. could you please help me out though as i really need this to work for more than one instant at a time.

    thanks mate

    body {
      font-family: arial;
    }

    /* drag objects (DIV inside table cells) */
    .drag {
      /*filter:alpha(opacity=50);  
      opacity: 0.5;
      -moz-opacity:0.5;*/
      border: 0px solid #9B9EA2;
    }

    /* tables */
    .dragging table {
      background-color: #e0e0e0;
      border-collapse: collapse;
      width:700px;
    }

    /* table cells */
    .dragging td {
      height: 100px;
      width:135px;
      border: 1px solid white;
      text-align: center;
      font-size: 12px;
    }

    /* message cell (marked as forbidden) */
    .mark {
      color: white;
      background-color: #9B9EA2;  
    }


    // after page is loaded, initialize DIV elements inside tables
    window.onload = function () {
      var rd = REDIPS.drag;
      // initialization
      rd.init(document.getElementById(‘drag’), ‘drag’);
      // set hover color
      rd.hover_color = ‘#E7C7B2’;
      // set drop option to ‘switching’
      rd.drop_option = ‘multiple’;

      //rd.myhandler_dropped = function () { alert(rd.save_content(0)) }

      var rd2 = REDIPS.drag;
      // initialization
      rd2.init(document.getElementById(‘drag2’), ‘drag2’);
      // set hover color
      rd2.hover_color = ‘#E7C7B2’;
      // set drop option to ‘switching’
      rd.drop_option = ‘multiple’;
    }

    Pick the top three winners

    1st
    2nd
    3rd

    Participants
    Pick the winner

    1st

    Participants

  12. Great work, dbunic! I wanted to know if there’s a way to start the drag/drop function disabled, and then enabled by checkbox. I’ve tried several different ways to switch the enable_drag function, but it doesn’t seem to work at all. I would think that the first initialization would activate it, but later on, the checkbox work as intended, but the first initiation at window onload is what seems to trouble me. Thanks to anyone for the support.

  13. I’ve problems with IE7 and IE8, when i drag a box to the right, it can not be dropped. but, to the left yes.
    In IE 6 and previous it works fine!

    Do you know this problem?

  14. @Mindaugas – In new REDIPS package save_content() method will sent content of all tables if input parameter doesn’t exist. So, you only have to call save_content() without any parameter.

    Next, if you want to define DIV elements from the first table to drop only to the first table here is how to:

    1)
    Class name of the TD should have “mark”. Instead of class=”table1″ put class=”mark table1″ for the first table, class=”mark table2″ for the second table … in every TD.

    2)
    After function “save()”, place the following function to mark DIV elements:
    function mark_elements(tbl){
      // local variables
      var div, i, id;
      // collection of div elements from defined table
      div = document.getElementById(tbl).getElementsByTagName(‘div’);
      // loop through all DIV elements in table
      for (i = 0; i < div.length; i ) {     // div elements should have defined Id (ignore "Round X" div elements)     if (div[i].id !== '') {       id = div[i].id;       REDIPS.drag.mark.exception[id] = tbl;     }   } } 3) At the end of "window.onload = function () { ..." call mark_elements() function:   ...   ...   ...   // mark elements   mark_elements('table1');   mark_elements('table2');   mark_elements('table3');   mark_elements('table4'); } This modification will disable to drop elements from the first table to any other table then the first table itself. The same will be with the content of the second, third and fourth table. Hope this few lines will help you. Cheers! PS You will have to change "mark" class because it has special meaning.

  15. @hgbreton – Do you have prepared example to show me the problem? Just send me the code or show me the link and I will try to fix it. Thanks!

  16. Thank you Darko. It really helped. This script is extraordinary.
    I just have to tell, that in the beggining i have difficulties saving all the tables.

    The problem was that within div id=drag i had not only class=drag tables but also some other tables (every table id and also table content is queried from db.

    And those other tables, although they didnt have any id, they’d still influencend the script, pretending that they have an id number.

    example:

    table simple
    table id=1
    table simple
    table id=2

    script understands like:

    table id=0
    table id=1
    table id=2
    table id=3

    i just got rid of those extra tables and the script works like a breeze. THANK YOU! One can do amazing stuff with it.

  17. @Mindaugas – I’m glad it works – Cheers! … :)

    @john – Now REDIPS.drag library support more than one dragging container. Please download
    redips2.tar.gz package from the page top and test example8. You will see that every table is placed to it’s own container and elements from first table can’t be dropped to the second table and vice versa.

    @Habib Mustafa – Did you try to click on “Enable dragging” in this example? You will see how toggle_dragging() function (prepared for this example) calls REDIPS.drag.enable_drag() and how elements in table becomes unmovable. Input parameter is boolean true or false. I hope now you will manage to enable / disable dragging objects in table.

    @Gonzalo – Hm, I did’n notice problem with moving elements to the right in IE7+ … If you have prepared example, please show me / zip it and send and I will try to see where is the problem. Thank you for playing with my lib.

  18. Hi Darko
    thanks for your skript which is a high enrichment for the web.
    In table 2 you show that the content can also be dropped in a table with merged cells. What I am looking for is a skript which also brings flexibility in the table itself. Means merging / splitting of cells and adding / deleting of rows / columns at any point. With your post http://www.redips.net/javascript/adding-table-rows-and-columns/ you show how to add rows / columns at the end of the table. Do you have a hint if there exists a skript for these requirement?
    Best regards
    Jo

  19. @Jo – Inserting row at specific location or deleting specific row should not be a problem. In my example, row is inserted at the last position so it looks like it’s appended at the table end. It’s a little complicated with columns but is not so hard to implement. Unfortunately I didn’t play with merging or splitting table cells like you asked. At this point I will also have to search for existing examples of merging table cells …

    :(

    Anyway, thank you for a great discussion about advanced JavaScript / DOM skills.

  20. Hi Dbunic
    What a wonderfull piece you´ve done !
    I´ve been looking around for this, and was so pleased when I´ve found your site – thank you very much for charing this with us.

    I have made this little thing at http://www.33137086.dk/REDIPS/ whitch is placed on 12 computers as webcontent on the xp-desktop. It´s my workingcopy you´ll see, so feel free to edit/save.
    I’m not good at javascript at all – but wondering if it´s possible to redraw the table-content without having to reload the full page, when another pc edit/save the content.
    My question is not about MySQL – that part is working quite well right now.

    I have an iframe checking if the table has changed – and if so, the iframe reloads – but it would bee nice if just the new content was placed in the existing document. It would also make it possible to use my [Fortryd] button as a recall-thing, if a user did something wrong, and wanted to go back from scratch.

    Best regards
    Kjeld from Denmark

Leave a Comment