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

    @Kjell Wahlberg - Scrollable DIV containers should work. Please see Example 6: Scrollable and fixed containers. If you scroll right table to the right, DIV elements from middle table should be able to drag. The similar case will work if one table is above the other table. Table should be closed within DIV container with set style position:relative.
    Hope this tip will help you.

  2. Ron says:

    Hi Darko,

    I've poured through the documentation before bothering you with these questions but I haven't figured it out. The smaller question is how come I'm not getting the "dropped" event triggered ever? I have a test page on which I have set up handlers for all the different events and display them in a message area on the page (this is, of course, just for my learning purposes). I get all kind of events: moved, cloned dropped, dropped before, clicked, changed, etc. But no "dropped".

    The bigger question (and that may make the first question moot) is this. I have two columns (that are represented by two tables) and I can drag from the left column to the right column (so far so good) and I can trap "cloned dropped" to determine if it's a valid move. However, when I move things from one cell to the other WITHIN the target/right side I can't trap any event that is useful to determine if it's a valid drop. Hence the first question about the dropped event.

    So why am I not trapping "dropped" and is there a better way to do this anyway?

    Thanks!

    Ron

  3. Ron says:

    I didn't mean for you to guess why I don't get the event dropped. I can send example. I was just wondering if this something you've seen before and might know right off what I did wrong.

    Thanks aagain,

    Ron

  4. Ron says:

    I may have figured it out. Sorry for the messages.

  5. dbunic says:

    @Ron - No problem, I will just add a comment. myhandler_dropped is invoked after DIV element is dropped to the table cell. In source code redips-drag.js version 4.6.14 see line 1175. Right after this event handler is called, REDIPS.drag lib calls myhandler_cloned_dropped event handler. So, maybe displayed message from cloned_dropped overwrites message from dropped event handler (because in case of dropping cloned DIV element, both event handlers will be called).

  6. Alex says:

    Hi,
    really wonderful plugin!!!
    Unlucky i've a problem with using this plugin with a table with more than 20 columns with width set to 150 or more px....
    After the 4th or 5th column drag and drop not works anymore...
    can you help me please?

    thanks in advice!!
    Alex

  7. Brian Pedersen says:

    Great library!

    I have a few questions.

    1. Is it possible to control the ordering of elements, when dropping multiple div's in a single cell ?
    It seems they allways go to the bottom of the cell, no matter where they are dropped, and it is pretty hard to rearrange them inside the cell (for the same reason).

    2. Is it possible to change the orientation, when dropping multiple div's in a single cell ?
    It could be nice being able to "stack" them horizontially instead of vertically.

    See: http://bitwerk.dk/redips

    Regards
    Brian

  8. Alex says:

    Hi Darko,

    i've found the problem in this ccs class:

    /* drag container (contains two tables) */
    #drag {
        margin: auto;
        width: 730px;
    }
    

    if i set width: auto, the problem seems partially resolved, but persist with a table with scrollbars.
    Set with:10000px autoscroll not works properly;

    Thanks in Advice
    Alex

  9. Ruben says:

    Hi Darko,

    I have a question for you related to your wonderfull script :) :
    When dragging an item from one cell to another. Do you know if it is possible to recognize the source cell and the target cell? I can see the event 'myhandler_dropped', but i don't think that will return those values.

    Trying to save separate items via AJAX :) . Is that even doable? Or is it only possible to save the schedule as one large chunk in the database?

    thanks in advance! (you must be busy with all these questions :) )
    Ruben

  10. dbunic says:

    @Alex - Try to make drag container visible by adding border style:

    #drag {
        margin: auto;
        width: 730px;
        border: 1px solid lime;
    }
    

    After drag container is visible, set width to wrap tables properly. On the other hand, please try to set drag container on the following way:

    #drag {
        display: table;
        margin: auto;
        border: 1px solid lime;
    }
    

    If display style is set to table then drag container will be extended to include all content inside.

    @Brian Pedersen - Element order inside one table cell can be controlled but with additional JS code. My advice would be to use a table with several TD elements and to sort DIV elements by setting REDIPS.drag libt to shift mode. Please see example09 Single and shift mode. Table can have one TR element with many TD elements so it's irrelevant will it be vertically or horizontally. So, please try to avoid dropping DIV elements to one table cell if you need to control order of DIV elements.

    @Ruben - Inside myhandler_dropped event handler it's possible to find out source and target table cells:

    REDIPS.drag.myhandler_dropped = function (target_cell) {
        // define source table cell
        var source_cell = REDIPS.drag.source_cell;
        ...
        ...
    

    Target cell reference is sent during event handler call, while source cell is defined from public property. Hope this info will be useful for developing AJAX save. And you can try with REDIPS.drag.get_position() method like is used in example03/ajax/script.js script.

    Answering the questions is sometimes fun. It's helpful for me to see how REDIPS.drag can be improved or is documentation good enough - thanks! ;)

  11. Alex says:

    Hi Darko,

    thanks for the help but unfortunatly didn't work, the autoscroll problem after first scroll persist.

    I've build and example of the issue at : http://www.fantacompa.altervista.org/DragTest/index.html

    In this test i've apply your proposed solution, but without result :(

    Really thanks for you amazin plugin and your help!!

    Thanks in Advice!
    Alex

  12. dbunic says:

    @Alex - Please try to remove:

    rd.autoscroll = false;
    

    line from myhandler_dropped_before event handler in CrossTbl.js file. It seems that you turned off autoscroll feature on first DIV element drop - but it's unwanted action. ;)

  13. Alex says:

    Hi Darko,
    i've found the problem!

    I've wrongly put in the drop_before event the command to disable the autoscroll.
    Sorry about the time that I made you lose.

    Alex

  14. Scott says:

    Once again, great script!

    Couple questions.

    I have a 5x6 table that gets loaded with images that represent various objects. We let our users move the images around the table via your script. We also have the images inside href tags so when they are clicked on they get taken to an edit screen where various descriptive fields can be used to describe the image. In some cases (may be dependent on the browser) we are getting the edit screen come up after dropping the image either in a new location or if we start to drag and then drop back in its original location. What would be the best approach to identify a drag operation that would disable the click operation. Ideally we would like the click to only be recognized if no drag event preceded it.

    Second, on a delete event, if I don't want the included javascript alert box but instead my own, styled to match our site, type of confirmation window to pop up, do I have to edit your script to make that happen or are there any configurable items around the delete confirmation dialog?

    Thanks!

  15. dbunic says:

    @Scott - The problem is that both objects (inner image and outer DIV element) have registered onclick event listener. Event order can be capturing, bubbling or both - means that clicking on DIV element can be caught by listener attached to the image. So, hope the next trick will solve this problem. When user clicks on DIV element, drop_flag will be set to false. If DIV element will be moved, that drop_flag will be set to true. This flag (declared as global variable) should be used in your code executed on image click.

    redips_init = function () {
    	// reference to the REDIPS.drag lib
    	var rd = REDIPS.drag;
    	// initialization
    	rd.init();
    	// after DIV element is clicked, reset flag variable
    	rd.myhandler_clicked = function () {
    		drag_flag = false;
    	};
    	// after DIV element is moved then set flag variable
    	rd.myhandler_moved = function () {
    		drag_flag = true;
    	};
    };
    

    And later in function executed after user clicks on image:

    if (!drag_flag) {
    	...
    	...
    	...
    }
    

    And regarding delete confirmation. REDIPS.drag curenttly doesn't have option to define custom dialog box. You can open redips-drag.js source file and search for "confirm(" string - it should be at line 1066. After modification is tested, just minimize source code to speed up page loading.

  16. Bruce996 says:

    Great package. One comment, it would be nice if you had a copy function that would allow a target to be copied to with the option to replace any contents. This would be like the move function, except don't move the object, clone it, then move the cloned object. With the replace option, if the target div already has contents in it, first delete that object, then move the cloned object.

    Thanks.

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!