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. First, I would like to apologize for delayed answer. In the past few weeks I was pretty busy and didn’t have time to reply promptly. I’m hoping that next period will be easier – but you never know ;)

    @s n k – Unfortunately, drag more than one DIV element is not possible now. However, there is a trick where you can move more elements at once. Please see example12. It’s not exactly what you are looking for, but the closest to your question.

    @Davide – As you described, dropped DIV element will be placed to the last available (already highlighted) table cell. For most scenarios this behavior is expected because highlighted cell shows where dragged DIV will be dropped. Currently, REDIPS.drag doesn’t have ability to know whether DIV element is over forbidden cell or not. Hope this feature will be implemented in the future releases.

    And regarding your colorTd question. I took example01 and right after init() line, I placed hover color definition:

    // initialization
    rd.init();
    rd.hover.colorTd = 'red';
    

    After dropMode is changed (with radio button), hover color was “red” in all cases. I can’t find out where is the problem. Do you have the similar situation or you change hover color deeper in your code? I’m just guessing, but maybe hover color is somehow overwritten in your JS code. If you still have problem, don’t hesitate to ask me. I’ll try to respond much quicker this time.

    @Marco – This kind of task can be accomplished with custom code inside droppedBefore() event handler. Just place JS code inside and test which table is source. In case of not allowed combination, you only have to return “false” from droppedBefore() method and DIV element will be returned to the source position. In REDIPS.drag documentation is described how to read source and target table cell position.

    @Sanjeev kumar – REDIPS.drag is primarily developed for dragging single DIV element on top of the HTML table. If you want to stretch DIV element, the better option is to merge TD before and then drop DIV element. For this purpose, I have developed REDIPS.table library which have all HTML table methods. Just type in “REDIPS.table” to the blog search filed on top of the page and open first two search results. REDIPS.drag and REDIPS.table can be combined and you will have a good start point for playing with HTML table and its content.

    @Frits – For “autosave” functionality please see inside “ajax” directory of example03. This is modification of original example where “save” function is called on every element drop. And in your save function (which should be triggered from rd.event.dropped) you have reference to the dropped DIV element (rd.obj.id). Here is how you can read “id” attribute – not “name” attribute because name attribute is used for form handling:

    // save - after element is dropped
    rd.event.dropped = function () {
        // get element position (method returns array with current and source positions - tableIndex, rowIndex and cellIndex)
        var pos = rd.getPosition();
        // save table content
        sendRequest('ajax/db_save.php?p=' + rd.obj.id + '_' + pos.join('_'));
    };
    

    This code is actually copy&paste from example03/ajax/script.js file.

    @Adnan – Table cells can be merged with REDIPS.table library. Please see live demo on Merge and split table cells with JavaScript post. Hope this will give you an idea how you can merge/split table cells.

  2. Hello,

    I want to drag multiple content into single cell and then retrieve its content with those strings in CSV format. @example: if i do have into one cell the labels “Drag,Drop and Table”, I want to get cell content like “Drag,Drop,Table”. Any ideas on this? Thank you in advance. Regards!

  3. @ionescu – Unfortunately, REDIPS.drag library doesn’t support dragging more than one DIV element at a time. But, maybe example12 “Example 12: Select and move more elements” can give you a hint how to solve this requirement. Idea is to select several elements before and with dragging last element, all selected elements will be moved to the dropped table cell. To list all examples from redips2.tar.gz package, click on “Preview” link below post title.

    @wilson barrera – To make your example working properly, just add:

    position: relative;
    

    … to the DIV element with class=”fixedTable”. This way DIV element will appear in the offsetParent chain and top and left offsets will be calculated correctly.

  4. @dbunic
    Is it possible to create a right angle triangle in place of rectangle in example 27 ?

  5. Hopefully this isn’t the a stupid question!

    I’m trying to set up a game board so that when the player attempts to hover over an “invalid” square or element, it will change the background of the hovered-over td to an X symbol.

    How would I do that? Which event handler should I use?

  6. @Mohammad Shiblee – I’m not sure if DIV element can be styled as triangle, but you can always create image and put it inside DIV element. This can do the trick!

    @Shon Pan – You can try with rd.event.changed(). In documentation you will see how to obtain current TD position, and in example05 how to change background color. I would advise to try with changing background color first and in next step with “X” symbol. For forbidden cell you will have to return boolean “false” from rd.event.droppedBefore() also. This way DIV element will be returned to the source position.

  7. Hi dbunic,
    i have three DIV successively positioning between my td; with a left position that varies with my need; I put a drag on the second DIV,
    when i clicked on the second DIV without moving it, change of bigin td
    I would love to avoid the event of clicked without moving?????

  8. Is there any way to make few tables undraggable ?

    <div id='drag'>
    <table></table>
    <table class='non-draggable'></table>
    <table></table>
    </div>
    
  9. @red1 – The problem might be related to your CSS. DIV element will be pull out from TD in the moment when user starts to drag it. Click and hold (left mouse button) should not have any impact on DIV element. If you drop several DIV elements to the same TD on this live example and click on one of them, you will see that nothing will happen.

    @Kush – REDIPS.drag library is based on using HTML table as main drag&drop layout (HTML tables are simple and it’s very easy to make construction). Inner tables are also supported so you can create a really complex structures. OK, if you want to place a table inside drag container and treat it as ordinary table (without possibility to accept and DIV element) just add nolayout class name.

    <table class="nolayout">
     ...
    </table>
    

    Please read more details here:

    REDIPS.drag documentation – Appendix A

    @xiongxu – IE8+ browser family is supported and all examples should work in IE8 too. You can prepare jsFiddle example to show me where is the problem and I’ll try to fix it.

  10. Hello – Has anyone created or previously asked about creating a Drag and Drop product table which works within magento ecommerce?

    Looking to do this – any help would be appreciated

    David

  11. If someone has experience with “magento ecommerce” it’s more than welcome to post a comment to this site.

  12. HI!!! thanks for this amazing Drag&drop was excatly what i was looking for… but i trying to make it work in wordpress
    i notice that “rd.init(); ” it’s not working :( does some body have some experience with wordpress PLEASEEE!!!!

  13. @Marieta – rd should be a reference to the REDIPS.drag namespace. Please check does your code have the following line at the beginning:

    var rd = REDIPS.drag;
    
  14. sorry for my bad English.
    My question is: is it possible to coexist in the same moment, two setMode (single and switch)?
    My problem is this:

    I have a table in which, taking the data from a DB, I fill the cells (and these boxes can not be occupied by another DIV.) So this function is fine by setting the setMode in SINGLE.

    The boxes where there are no records (ie empty), I fill it with the button by clicking it opens a pop-up that I will need to enter the information that I occupy that cell.

    Now, if I drag the contents of a cell to another cell filled full (by setting the setMode in SINGLE) does not allow me to do … and that’s okay.

    If I move it in a cell “blank” then where is the button, it should make a switch with the cell in which I want to change.

    This type of operation it is possible to do it?

Leave a Comment