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)
- for unlimited cloning add “redips-clone” class name to the DIV object
- 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!
@Anders – First sorry for late reply … I was on vacation for a week. ;) Anyway here is modification that will search for input box inside DIV element and add value to the JSON or Query string output (don’t forget to define/declare tb and v variables at the function top). Just add modification to the save_contet() method. Hope it will be helpful. If you will have further questions, I will gladly help you.
@amnon – Thanks! :)
I swear – This is the best script of this type I have ever seen – Thanks
JavaScript drag and drop is a great thing! I believe if your web apps can utilise drag ‘n drop, they should. It makes your web apps look more professional, become more user-friendly and helps give you that polished look.
Interestingly, I’ve recently created an article on creating drag and drop elements in JavaScript. My code allows for any element to have a class “draggable” attached it and will convert said element to a freely draggable element. Very useful stuff. If you’re interested, check it out at http://www.sitesupplier.co.uk/index.php/blog/86-tutorials/javascripttutorials/170-javascript-drag-and-drop
It’s really an amazing library of drag’ndrop ! Big big thanks from France :)
I noticed there seems to be a problem when setting the first few columns with class=”mark”. For example, when preceding the cells of the 1st a table in example01 with 3 columns marked as such, and then adding a few more columns at the end of the table (I’ve tried with a total of 11 cells per row), the drag and drop stops behaving nicely when you try and drag a div to one of the last two columns. Is there something I’m missing ?
This is the best work. Hats off!
Basically I’m a beginner to web applications. I just wanted to make a table with interchangeable and drag/drop contents. I am using notepad to use this script. Working fine but I want to use the feature SWITCH and SAVE. When I’m giving these radio buttons on my page, the switch functionality is not working, but this feature is running in the examples provided in the package. I tried everything but the switch content feature is not working in my page.
One thing I also noticed that in your examples you have not initialized the body like this: But it is working fine in your page.
My sample code is as follows in which switch content is not working although I have provided the “redips-drag-min.js” script.
My First JavaScript
This is a paragraph.
1
2
3
4
5
6
7
8
9
Switch content
Guys, I’m glad you like REDIPS.drag and thank you very much for supporting my work!
:)
@amnon – If drag region is not set wide enough then dragging in the last columns will not work. Try to make DIV id=”drag” visible by adding border style to the #drag class:
Next you can extend drag region to cover whole table, or you can define drag region styles like:
Hope this will solve the problem and dragging will work in all columns.
@Mark – Here is how to define “switch” mode (complete code):
Please see how to safely initialize REDIPS.drag library. This way it will only add REDIPS to the init process and will not overwrite any existing initialization on the page. And please convert special characters to HTML entities so they will not be filtered when posting a comment (see lines below comment box) – thanks!
First off all, sorry for my English, I’m a frenchie :p
I have a problem…. I use your technic to drag’n drop <div> .
It works but I want to save the id of the <div> which receive the dragged <div>.
I tried with jQuery UI but it doesn’t work.
In your “redips-drag-min.js” I don’t know how to do to say :
@dbunic
Thanks mate. You are champ :D
One more thing. Can this javascript work with DataGridView of Asp.net?
I might be going about this the wrong way, but I’m running into an odd issue.
I have two separate drag elements with different IDs. However, neither exists when the page loads, only when the user performs a particular function — and they get regenerated with dynamic content each time the user uses that particular function. So I am currently using an AJAX call to generate the content, then a call following that to REDIPS.drag.init() for each of the two elements.
So far, that’s working fine for the first load and first use of the function. However, it doesn’t work when I call the function again (which re-creates the two drag elements and re-calls REDIPS.drag.init() for both). Any idea why that would be? There’s no error messages appearing, it just fails silently — the new content just isn’t draggable even though the structure is identical.
If there’s a better approach to this (init on multiple elements that don’t exist prior to an AJAX call) then please let me know!
I took it one step further and instead of re-creating the two drag containers every time the function is called I create them initially in a holding div, init them and when the function is called move them to the correct place. Still no luck though. The content is loading fine, and the first time they’re populated with content it functions perfectly, it’s only after the content is regenerated that it fails.
Tried calling enable_drag() manually after the content is generated, that doesn’t work either.
@dje – Your English is OK and I am not a English native speaker also ;)
In the moment when DIV element is dropped to the table cell it is possible to hook up a JS code to the event handler. So, your custom code can be executed on every DIV element drop. Here is simple example how to display DIV id to the console:
You will find more examples in redips2 package and please see code snippets in documentation of REDIPS.drag library.
@Mark – REDIPS.drag is a frontend library and can be used in combination with other JS frameworks (like jQuery). I haven’t any experience with DataGridView but if HTML table is wrapped in DIV id=”drag” and REDIPS.drag.init() is called after page is loaded then dragging will probably work. REDIPS.drag setting is easy so you can try and experiment to see if this combination will work.
@Paul – After dynamically created DIV element is added to the table it should be initialized. Actually event listeners are attached to the newly added DIV element. REDIPS.init() is a heavy artillery and it should work, but better approach is to call enable_drag() to avoid overhead. Here is a code snippet from documentation how to call REDIPS.drag.enable_drag() for your case:
“rd” is reference to the REDIPS.drag library while “myElement” is reference to the DIV element added to the HTML table. Yes, it sounds odd that only first time of calling REDIPS.drag.init() works. You can open console in Web developer tools and there should be displayed error message that might help to narrow down the problem.
Thanks for the reply dbunic.
That’s exactly the code I’ve tried, and it works the first time but not after that. No error messages appear in the web dev console in Chrome or Firefox. I’ll see if I can create a simple test case for you to replicate the error as the code I’ve got is too complex and involved.
@Paul – Small demo will be fine so I can make tests and fix the problem. If you are familiar with Web inspector tool (in Chrome or FF), you can set breakpoint to see if enable_drag() is called second time. Next step is to load redips-drag.js (instead of redips-drag-min.js) and trace the error in enable_drag() (if there is any) …
Hi, I’m in the process of making a visit diary, So far all your functions have been exactly what I am needing – I do however have a small question: Is it possible to have the divs sorted within a cell ?
I Am using elements from your save function on example 1 with elements from the school timetable. I have four table rows for four quarters of the day – Appointments (The divs I am attempting to sort) are dropped into a quarter(Cell) so a cell can contain many appointments. – What I am basicly saying is I need the divs to stay where I put them within the cell and remember via save where they were put within the cell – As it is the divs are hard to arrange.
Cheers
@Andy – REDIPS.drag doesn’t have option to sort DIV elements within a table cell. There is only multiple_drop property to define where dropped DIV element will be placed (at the cell top or bottom regarding other DIV elements in cell). Maybe you can create several cells (define maximum number of possible events) for appointments and style them to have invisible borders. Other solution can be to have inner table and to dynamically append cells as user drops DIV element to appointments. These are just hints …
@dbunic
Thanks for your suggestions :) I will try this javascript with GridView.
My question is, if we drop a content on another cell which has already an existing content, can these two DIVs be merged together so when we move one of them again, the another one is bound to it, so it would move along with it too. Is it possible through your existing functions?
Another way to view this problem is, when we move a content to another, these two DIVs should merge into a single content, having the values of both contents and so on. How can we achieve it?
How can i save each modification directly without form ?
If i drop my div in the div “test”, i want to save automatically in base this new position.
really great post thanks for the post dude
@Mark – If you want to merge content of two DIVs (say DIV A is dropped to the occupied cell with DIV B and the result should be DIV AB) then you can try to set drop_option=”overwrite”. In this case dropped DIV element will overwrite any content in destination TD. But for merging DIV content, you will need some magic in myhandler_dropped_before() event handler. myhandler_dropped_before(target_cell) has target cell as input parameter and it will be called before existing DIV element is deleted. It seems that 4.7.2 version will not call this event handler in “overwrite” mode so I will have to check it to be sure. And reference for the dragged element is rd.obj so it’s only needed to concatenate innerHTML of two DIV elements.
@dje – Please see example03. Inside this directory is modification of saving DIV position on each drop via AJAX call. You only have to change the path in example to activate this demo mode.