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 Darko,
    I have a problem with the IE8. I’m loading via HTML Request with AJAX and I’m replacing content in the actual window. The first time when I’m loading the content which contains your script initialization and the handlers like dropped etc. works fine with IE8 but when I’m loading the same Thing again with a second click on the getContent button he is making the same thing again, loading content via AJAX but now he is throwing an error in the drag.js script at this line –> box = box.offsetParent; unknown error. I don’t really know where I have to look at to avoid this error. I thought there must be something wrong with the object creation.

    Hope you can help me.

    Thanks
    haris

  2. @Haris – box_offset method calculates object offset (top, right, bottom, left) and it’s used across whole REDIPS.drag library. It seems that something isn’t correctly initialized. Did you call REDIPS.init() method after loading content with AJAX? During initialization process, library scans tables in dragging container and prepares array with offset of table rows. If page content is dynamically changed, you need to call REDIPS.init() to sync page state with internal array of row offsets. Maybe that’s the solution for your problem …

  3. Hi Darko,

    new Request.HTML({
        url: $('vorl_fac_select').getProperty('action') +
        '?fac_param=' + $('select_fac').getSelected().get('value') +
        '&sem_param=' + $('select_sem').getSelected().get('value'),
        // methods
        onRequest: function (response) {
            $('spinner_vorlplan_php_id').setStyle('display', 'block');
        },
        onSuccess: function (responseTree, responseElements, responseHTML, responseJavaScript) {
            if (checkServerData(responseHTML)) {
                $('vorl_plan_content_id').set('html', '');
                $('vorl_plan_content_id').set('html', responseHTML);
                eval(responseJavaScript);
                $('spinner_vorlplan_php_id').setStyle('display', 'none');
            } else {
                $('spinner_vorlplan_php_id').setStyle('display', 'none');
            }
        },
        onFailure: function () {
            $('spinner_vorlplan_php_id').setStyle('display', 'none');
        }
    }).send($('vorl_fac_select'));
    

    As you can see this is my AJAX request and on success he evaluates the JavaScript in the called php file. In my php file at the top there is the initialization of REDIPS library.

    // initialization
    if ($('main_container')) {
        REDIPS.drag.init();
        init_vorlesungen_tip();
    }
    // enable dropping to already taken table cells
    REDIPS.drag.drop_option = 'multiple';
    // set hover color
    REDIPS.drag.hover_color = '#dfdfdf';
    // don't ask on delete
    REDIPS.drag.trash_ask = false;
    // elements could be cloned with pressed CTRL key
    REDIPS.drag.clone_ctrlKey = true;
    

    In FireFox everything works fine, even Opera and Safari :) but IE8 NO hehe.

    Thanks
    haris

  4. @Haris – it seems that IE8 doesn’t create DOM well for $(‘vorl_plan_content_id’) or DOM is not ready in a moment when when eval(responseJavaScript) is executed. I tried to search if MooTools has element.ready() event handler to attach to the #vorl_plan_content_id element but there was not luck (or I did’t search hard enough). Anyway, I found a page Fixing innerHTML where author explains problems in IE and simple trick with createElement to avoid it.

    IMHO, the solution might be in attaching “ready” event handler (if there is any) on element that contains dragging tables – but I will try the createElement trick first. It’s worth to try.

  5. Hi Darko,
    I tried even a trick, I put a click button like init DRAG into the requested page just to avoid the problem if the DOM isn’t ready. But IE throws also that kind of problem. I WILL try the FIXING innterHTML thing and then I will tell you if it worked.

    Thank you for your help, I forgot to copy paste the code above the initialization then you can see the function of MooTools that adds a Event of domready.

    window.addEvent('domready', function () {
    
        if ($('main_container')) {
            REDIPS.drag.init();
            init_vorlesungen_tip();
        }
        // enable dropping to already taken table cells
        REDIPS.drag.drop_option = 'multiple';
        // set hover color
        REDIPS.drag.hover_color = '#dfdfdf';
        // don't ask on delete
        REDIPS.drag.trash_ask = false;
        // elements could be cloned with pressed CTRL key
        REDIPS.drag.clone_ctrlKey = true;
    

    Thanks
    haris

  6. Hi Darko,

    I solved the problem with the fixing innerHTML by creating a new element and replacing it with the old element. Then it worked. A great workshop of jeremey.

    Thanks for you help Darko.
    Haris

  7. Hello.
    How to make to switching only in first rows those are th and only switching th. I have to diffrent tabels and I wanted to switching a th in each of those tables. I used example8. I dont know why those th dont wont to switching I can’t drag&drop them. Plz help me.

  8. Hi Darko.
    Can you help with problem about drag&drop mix. I have 2 tables with data and I wanted to switching in each table th heads.I try to use example8 to drag&drop in each tabel but nothing is going on. structure looks like this :
    1122 .. soma data
    3344 .. soma data
    I put the code

    var rd = REDIPS.drag; // reference to the REDIPS.drag class
    // DIV container initialization
    rd.init('dragi1');
    rd.drop_option = 'switching';
    rd.init('dragi2');
    rd.drop_option = 'switching';
    rd.clone_ctrlKey = false;
    

    Plz Help me with that. reagrds

  9. @Pablo – TH cells are not different (in context of REDIPS.drag) compared to TD cells. You can create TH row and DIV elements should be able to drop to the TH cells. To define “switching” mode for both tables, just set drop_option once after initialization like this:

    // reference to the REDIPS.drag class
    var rd = REDIPS.drag;
    // DIV container initialization
    rd.init('dragi1');
    rd.init('dragi2');
    rd.drop_option = 'switching';
    rd.clone_ctrlKey = false;
    

    Both tables will behave in “switching” mode regardless to TD or TH table cell.

  10. Hi Darko, Hi Haris,

    i am very new to javascript, so i am sorry to bug you with a question like this

    i use example01 to pass along the changed table data to the multiple-parameters.php and writig everything in a database

    my question is, how do i have to modify the script to pass along the text in the tablecell (innerText or innerHTML) to the php file

    i tried to copy the part from the trash function, and also played around with the tbl_cell.childNodes. part in the save function but i dont really get it

    thank you
    andi

  11. Hi andi,

    what you have to do is, search for the this IF function and change it like this:

    if (tbl_cell.childNodes[d].tagName === 'DIV') { // and yes, it should be uppercase
        divid = tbl_cell.childNodes[d];
        div_value = divid.innerHTML;
        query += 'p[]=' + tbl_cell.childNodes[d].id + '_' + t + '_' + r + '_' + div_value + '&';
    }
    

    before you can try it you have to declare the variables divid and div_value (you can name them like you want), after the declaration of the function. divid will contain your id of the childnode and div_value will contain the value of the innerHTML of your tablecell.

    as Darko progged it in the multiple_parameter.php everything is done with his example.

    list($id, $tbl, $row, $col) = explode(‘_’, $p);

    $col will be now your innerHTML data.

    hope this will help you.

    Haris

  12. @Haris – Your assistance is welcome. Thank you very much!

    @andi – Just apply modification mentioned by Haris and you will get content of DIV elements to the PHP script. Cheers!

  13. Hi Darko,

    I found your Drag&Drop script very useful, great stuff!

    I’m trying to achieve the `clone_ctrlKey` behavior to work with ‘REDIPS.only’ set.

    Is there any workaround to get it to work? Or should it be rewritten?

    10x

  14. Hello,

    Can we add ID numbers for the timetables in the Example 3. I want it to choose schedule with index.php?id=1. Such as “It’s my School timetable and index.php?id=2 is timetable of a friend of mine. How can we do that?

  15. dbunic,

    Yes, you are right. The “tablecontent stays in the cell while moving, but jumps to the right place” issue under IE8 was caused by IBM Webseal junction added javascript before the line . Is there any other workaround way to solve the problem as the javascript is automatically added by IBM Webseal junction????

    Thanks dbunic, you are always helpful ^^

  16. @Chanan – Currently it is not possible to enable ctrl-key cloning for defined group of DIV elements. REDIPS.drag.clone_ctrlKey parameter is applied to all elements. But you can modify redips-drag.js script and in line 531 add additional condition to exclude cloning for some elements. Anyway, your question gives me an idea to include a new event handler myhandler_beforecloning() with default returning “true”. There can be placed a logic to return false and element will not be cloned …

    In a next few weeks I will try to include that improvement.
    Thank you!

    @andi – I’m happy your problem is solved. Cheers!

    @Aaron – If you want to manage more separated schedules in example03, than you will need additional attribute in redips_timetable table. This attribute can be foreign key of “user” table (you will have to create it) or just a flag to differentiate schedules.

    In index.php, “id” parameter should be accepted and passed to the timetable() function as a third parameter. Inside timetable() function add that parameter to the where part of SQL.

    And finally, “id” parameter should be sent to the save.php also. Just expand insert with a new parameter.

    Hope this tips will help you.

    @buddy – The trick is to switch IE8 to standard mode – not to stay in quirks rendering mode. DOCTYPE line – the very first line on the HTML page – will trigger browser to the desired rendering mode. If IE8 stays in quirks mode, than box model will be handled differently – and that is the problem with dragging elements. Unfortunately, I’m not sure that there exists another way to force standard rendering mode except defining document type declaration in the page top. If asked workaround exists, don’t hesitate to correct me.

  17. First, great work with this, it has been an BIG help with a scheduler that I’m creating.

    Second, I having a problem with dragging from a scrollable DIV. When I scroll down the DIV and mousedown on a draggable DIV there is an offset between the cursor and the DIV. The farther I scroll down the bigger the offset. I’m using redips 2.8.1.

    Thanks a bunch!

  18. @Jim – Please make sure that scrollable DIV container has a position style set to other than the default “static” (perhaps you can set “position: relative”). This way, scrollable container will appear in the offsetParent chain, and offset calculating should be correct. You can read more about element positioning on page Element position with scrolling offsets.

Leave a Comment