Drag and Drop table content with JavaScript

More...

Content of HTML table cells can be dragged to another table 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 destination table cell. Attaching onMouseOver handler on table cells will not work, because browser doesn't fire events to the elements beneath the dragged object.

Anyway, after taking care of the current scroll position and calculating table cells positions, here is example that should work in FF3, IE8, Google Chrome, Safari 5 and Opera 11. Please try to drag green, blue or orange bordered DIV elements, change properties (radio button and check-boxes) and click on "Save" button.
Version 4.6.16
Download redips2.tar.gz
You can not drop here
Drag
and
drop
content
with
JavaScript
Smile
Table2
and
Drag
drop
table
with
JavaScript
Table3
Clone
(1) Clone
(2) Clone
Trash
Save content of the first table (plain query string)
Save content of the first table (JSON format)
Enable dropping to already taken table cells
Disable dropping to already taken table cells
Switch content
Switching content continuously
Overwrite content
Remove cloned object if dragged outside of any table
Delete confirmation
Enable dragging

"Save" button will scan tables, create query string and send to the PHP page. Demo shows how to collect table content and accept parameters on the server side. More about accepting parameters you can read in my post Reading multiple parameters in PHP."Clone" elements (orange in this demo) will be duplicated first because of "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). Script has built in autoscroll and option to forbid landing to non empty cells or cells named with class "forbid". Table can contain rowspan / colspan cells 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="drag"> to enable content dragging
  • place <div class="drag">Hello World</div> to the table cell

Other features of redips-drag.js:

  • functions and data structure are defined in namespace (easier integration with other JS frameworks)
  • JSLint: No problems found in redips-drag.js (tough one, huh)
  • REDIPS.drag documentation generated with JsDoc Toolkit
  • drag and drop table rows
  • movable div element can contain form elements, images and links
  • forbidding or allowing table cells marked with class name "mark" (depends on "marked_cell" parameter)
  • option to define exceptions and allow certain DIV elements to the marked table cell
  • option to define single content cell on the table declared with "multiple" drop option
  • cloning
    • for unlimited cloning add "clone" class name to the div object
      <div class="drag clone">Hello World</div>
    • to limit cloning and transform last object to the ordinary movable object add 'climit1_X' class name
      <div class="drag clone climit1_4">Hello World</div>
    • to limit cloning and transform last object to immovable object add 'climit2_X' class name
      <div class="drag 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 table content
  • switching content continuously
  • overwrite table cell content with dropped element
  • shift table content
  • table cell with "trash" class name becomes trashcan
  • enabled handlers to place custom code on events: clicked, moved, not moved, dropped, switched, changed, cloned, cloned end1, cloned end2, not cloned, deleted 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.js works?

Script will search for div elements (with class name "drag") inside tables closed in <div id="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 "left" and "top" styles of the object. 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 user holds left mouse button on div element.

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 table cell coordinates (with scroll page offset) and store them to arrays. Arrays are searched inside onMouseMove handler and after left mouse button is released, object will drop to the current (highlighted) table cell.

In redips2.tar.gz (130KB) package you will find 24 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.

Happy dragging and dropping!

This entry was posted on April 6, 2009 and is filed under Drag and Drop, JavaScript

Related posts

756 Responses to Drag and Drop table content with JavaScript

  1. Richard says:

    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. dbunic says:

    @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. Richard says:

    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. Tru says:

    @dbunic
    Fantastic!
    Thanks!

  5. amu says:

    Thanks a lot dbunic!!!.All my issues are resolved.

  6. dbunic says:

    @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!

  7. Vikram says:

    Hi Darko,

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

    Thanks for saving my job..

    real fan of your's

    Vikram

  8. Fred Carnac says:

    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

  9. Yvan says:

    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

  10. Yvan says:

    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

  11. Yaro says:

    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.

  12. Yaro says:

    Also Fred's idea is a good one.

  13. 123doing says:

    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.

  14. dbunic says:

    @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';
      }
    }

  15. Yvan says:

    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

  16. kaigou says:

    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!

  17. Raul says:

    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!!

  18. dbunic says:

    @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!

  19. Rat says:

    Hi, I have a realy problem with add text input in table cell, than its not dragable

    http://www.pslib.cz/ondrej.srut/PHP/drag%20and%20drop/example1/indexik.php

    if I create that input in PHP its ok, but if I create it with JS innerHTML than its not moveable, dont you know why it could be? you can try in on my page, link beyond. New input is create if you click on table cell and than click on Pridej textove pole.

  20. Alfredo says:

    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 10x10.

    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 Reply

Your email address will not be published. Required fields are marked *

*

In case of posting HTML tags or JavaScript code please convert special characters to HTML entities.
Especially pay attention to convert "<" to "&lt;" character!