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. Hey Darko!

    Thanks a lot for your script, it almost perfectly fits my needs. In the moment I am trying to adapt (with very limited Javascript knowledge) your script so I can organize classes between various teachers:
    (http://testdenivel.net78.net/redips2/www_redips_net/example3/index_static.html)

    I have various questions, if you could help me with them, I would be really grateful:

    1: My subjects are linked to the time of the day. How can I make, that the subject “ele//1900-2030” can only drop in the “19:00-20:30” line of each tab?

    2: I have a limited and defined number of subjects, so I would like them to disappear from the left table once dropped in the right table?

    Thanks again for the script,

    ri

  2. @Vikram & @amu – Autoscroll functionality for scrollable containers is finished. Hope this will resolve problems amu mentioned.

    @francisco – Please try to make DIV id=drag visible to see drag area, or you can set CSS for drag area like @drbob suggested:

    #drag {
    display: table;
    table-layout: auto;
    }

    This should fix the problem.

    @mirko – Described problem really exists in IE6. Thank you for reporting and I will try to fix the error.

    @Salman – redips2.tar.gz package contains drag.js file.

    @Tru – Script is fixed. TEXTAREA is editable …

    @Richard – Hi, I will try to help you.

    1) Did you mean that row “19:00-20:30” is reserved only for subject “ele//1900-2030”? If so, other subject will not be able to drop to row “19:00-20:30”. Please see new example6 in package where only element “A” can be placed to the last row.

    2) In example6 you will see how to define number of cloned elements.

  3. Hey Darko,

    Sorry for not expressing myself clear. I have, let’s say 8 subjects planned for 19-2030. Their names could be “ele//1900-2030”, “pre//1900-2030”, etc…, the last 8 digits always refer to their correspondent timetable, and I would like to prevent e.g. a 1600-1730 subject into a 2030-2200 cell.

    thanks for your quick reply,

    richard

  4. @Richard – I added new property named “only” to set DIV objects for defined table cells. drag.js is update and example6 also. Example6 (in package) shows A and B elements allowed to drop only to the last row. Other elements can’t be placed to the last row. In the same way you can define certain subjects for desired table row.

    @Tru & @amu – Guys, I’m glad it works. Cheers!

  5. Hi Darko,

    Thanks Man, lots of ppl must have said this, but…

    Thanks for saving my job..

    real fan of your’s

    Vikram

  6. Darko

    Thanks for making this code available. I am hoping to use it. Can you make it possible to select items from a select menu instead of from a table? This would be neater for me.

    Also, if the user misses any droppable cell, can an item to go back to it’s original position rather than dropping into the nearest droppable cell?

    You busted some big nuts for me! Thank you

    Fred

  7. I’ve successfully implemented this drag n’ drop javascript library on a website that I’m developing (for creating student progression plans — by dragging and dropping course names into table cells that represent school semesters) — and I am at the stage where I now need to figure out how post the inner html contents of my modified table to a page that will re-display the table and then convert it to a PDF file (using PHP and dompdf).

    I was NOT planning to use a database fore this part of the project, and was wondering — is it possible for me to post the inner html of my customized table / student progression plan to another page? If so, … how?

    Note that I do NOT need to recall the table data at a later time. I only need to somehow post the customized table data to another page. How can I accomplish this?

    Thanks,
    – Yvan

  8. Hmm .. I think I might have just found the solution:

    function save() {
    document.getElementById(‘innerhtmlcode’).value = document.getElementById(‘progression_plan’).innerHTML;
    }

    In the above function, .. “innerhtmlcode” is the id attribute of a textarea form element, and “progression_plan is the id attribute of my modified table.

    – yg

  9. Many thanks for this great script Darko.

    How would you make the style of the draged object change to the style of the objects in the new table??

    For example when object from table2 is clicked on it changes its style(border…) to the style of objects in table table1

    or if thats too much work then

    when object from table2 is droped into table1 its style(border…) would change to match those of the objects in table1.

    I have an idea about changing the className but the code is a bit complex even though its well explained and im not too sure about where to put it.

    Hope you can point me in the right direction. Cheers.

  10. It’s very good.
    I like this.
    Thanks for share.
    And I wrote something to introduce this project for my readers.
    You can find the post about this in my website.
    If something is wrong,pls figure it out.thanks.

  11. @mirko – Problem was with BODY margins in IE6 / IE7. Please try to put body{margin: 0px;} and the problem should disappear.

    @Vikram – Thanks :)

    @Fred Carnac – Selecting items from a select menu instead of from a table?
    I’m sorry, but this tool is table cell oriented and placing drag objects to the drop down menu is not possible. But you can place table in scrollable DIV container (please see example5 in package).

    And your second question. Unfortunately script currently doesn’t have option to return object to the original position rather than dropping into the nearest droppable cell (in case when user misses any droppable cell). Any way, your suggestion is appreciated – thank you!

    @Yvan – I’m glad you found the solution and commented to be visible to other visitors. Thanks.

    @Yaro – Changing style after dropping element can be done with myhandler_dropped() handler. Handler sees reference to the dragged element, so JS code might be look like:

    window.onload = function () {
      // initialization
      REDIPS.drag.init();
      // define dropped handler
      REDIPS.drag.myhandler_dropped = function () {
        REDIPS.drag.obj.style.borderColor = ‘red’;
      }
    }

  12. I’d like to add some custom javascript functionality to the student progression plan generating website that I’m developing. I’ve created a static version of my “drag and drop” screen for reference purposes:

    http://67.199.17.112/progressionplans.php.htm

    What I’d like to be able to do — if possible — is have it so that when you drag and drop a course into a cell in the student progression plan — the numbers in my “6 more semesters required” headings would decrease accordingly. Likewise, .. the opposite would take effect, meaning that if a course was dragged out of the student progression plan and back into one of the courses table cells, — the numbers in that courses table would increment accordingly.

    For example, .. if I was to drag a course out of the “Language Arts” courses table and drop it into a cell in the student progression plan, .. the “8 more semesters required” text would change to “7 more semesters required”, .. and if a 2nd Language Arts course was dropped into the progression plan, it would change to “6 more semesters required”.

    I suspect that I might need to change the number containers from span tags to text input fields to simplify the calculations, — but what kind of extra javascript would I need to introduce in order to achieve the type of functionality I’ve described? Is this even possible? If so, how?

    Thanks!
    – Yvan

  13. I’m building a survey, and this fits perfectly for several of the questions, where I want respondents to have a bunch of options, which they can then arrange into blocks to indicate frequency and preference. Ie, option#1 occurs more than X times and they like it, so it’d go in the top-left; option#2 occurs between Y and Z times and they’re lukewarm about it, so they can put it in the middle but rank it lower, and so on.

    Two problems: first, this is part of a larger form, so saving in-form would break usability, because it’d be the only place I’d ask them to save before continuing. Is there a way to make the ‘save’ occur immediately upon dropping something into a box? I can find the function for saving, but I’m not sure if I need to add something there to have it triggered by an onchange or a drop, or if the save action should be put into a function for dropping.

    Second, what’s selected becomes the basis for the next question, and that’s what’s led me on a merry chase through way too many scripts. Any chance it’s possible to have an onswitch/unhide triggered by moving a drag-box into a column (or more precisely, out of a column) — IOW, “if col != 4, div.this = unhide”? In terms of use, the table would first appear with all options in the ‘discard’ column (col=4), and if the respondent picks options A, B, and C, and moves them to any column other than #4, then the next question adds options A, B, and C rows to its table.

    Any ideas/suggestions at all would be greatly appreciated. This script, so far, is the most elegant and versatile, and didn’t give me even a lick of problems getting the AJAX to go through and come back again. All that’s left is figuring out how to create an auto-save kind of feature, but the switch function would be awfully nice to have.

    Thanks in advance!

  14. Hi there. I am using your impressive drag and drop code, but I’ve run into an issue: I can’t get the inner info out of the dragged cells. I mean I can handle the row and column numbers, but not the cell contents. What can I do? Thanks so much for the code and for your kind answer!

    … I forgot to mention I am using example number 1!!

  15. @Yvan – Updating number of remaining courses is not a problem. Please try the following code …

    window.onload = function () {
        // prepare span elements with remaining courses
        var courses_remaining = document.getElementById(‘courses_remaining’).getElementsByTagName(‘span’);
        // initialization
        REDIPS.drag.init();
        // dragged elements can be placed to the empty cells only
        REDIPS.drag.drop_option = ‘single’;
        // this function (event handler) is called after element is dropped
        REDIPS.drag.myhandler_dropped = function () {
            var i,    // used in main loop
                el;    // for DOM hierarchy climbing
            // open loop for each course
            for (i = 0; i < courses_remaining.length; i++) {             // set start element             el = courses_remaining[i];             // go up to the table             do {                 el = el.offsetParent;             }             while (el && el.nodeName !== 'TABLE');             // update number of remaining courses             courses_remaining[i].innerHTML = el.getElementsByTagName('div').length;         }     } } @kaigou - First thank you for a detailed description of your problem. If you are familiar with AJAX, than you only need to put "save" code to the myhandler_dropped(). This handler is executed on each element dropping and I suppose "target_cell" and obj will be the most interesting for you ... "target_cell" is table cell where element is dropped (so you can find parent table) and "obj" is reference of dropped element. Simple code for demonstration is: window.onload = function () {     // initialization     REDIPS.drag.init();     // this function (event handler) is called after element is dropped     REDIPS.drag.myhandler_dropped = function () {         var el, // for DOM hierarchy climbing             obj = REDIPS.drag.obj; // reference to the dragged object             target_cell = REDIPS.drag.target_cell; // reference to the Target cell                         // set start element         el = obj;         // go up to the table         do {             el = el.offsetParent;         }         while (el && el.nodeName !== 'TABLE');         // print         alert('div='+obj.innerHTML+' table='+el.id+' cell='+target_cell.cellIndex);     } } This code will be useful and for your second question. How to detect target table and where to place code to show addition table rows ... Hope this will help and if you have any additional questions please do not hesitate to ask. Cheers! @Raul - Please see example for @kaigou how to place some custom code in myhandler_dropped() and how to reference innerHTML for dropped object. Dropped object has "obj" reference. If you need further assistance I will gladly help. Bye!

  16. Hello!
    I’ve tried your code and it is excelent! Good work!

    I’m having some problems with it though:

    I took example 1, and removed tables 2 and 3. Everything works fine. Then I added more rows and cols, now I have it 10×10.

    Next I replaced all the (even all rows with other TDs) with numbers, kept the divs, all different from d1 to d100.

    The drag and drop works ok.

    THE PROBLEM:

    When I replace the simple text with input type text or textarea, I cannot always move the cells. I can move them up and down, but not on all columns.

    The code is:

    ABC4

    all other rows, in my example (for now only)
    have the other rows with td like this:
    ABC4

    the table name is table1 (like in example1) and the table is also inside a div.

    Please help :)

    Sorry to bother

Leave a Comment