Yahoo! UI dragdrop sortable list 1
Dragdrop sortable lists are difficult. Deciding how the lists should work is the trickier part but also partitioning responsibility in the code is challenging. Here is my try.
The Yahoo! UI sortable list example has each element in the list behaving as a target. This makes it possible to detect above or below which item you would like to insert the dragged item. That is fine but I don't really think of the items in a list (ie. the <li>'s) as being the target. I think of the list itself (ie. the <ul>) as the target. With the Yahoo! UI example if all of the items are dragged out of the list then how do you drag anything back into the list. There wouldn't be any targets.
The script.aculo.us sortable list example (which doesn't seem to work on my computer) makes the list items (<li>'s) and the list (<ul>) targets. So if the list is empty then the list itself can accept a new item. This seems like too much of a special case and the list items are still targets.
In this example, I tried making only the list itself a target. The list is an instance of a DDTarget subclass called "Basket" and the list items are instances of a DDProxy subclass called "Apple". At all times a Basket instance knows the apples it contains but doesn't need to know their order.
When a dragStart occurs, the code caches the regions of all the baskets that are legal targets for the dragged apple. For each of these baskets, the region of each contained apple is also cached.
When an onDragOver fires, the vertical coordinates of the cached regions are used to determine if the dragged apple should be placed above an apple in the basket or at the bottom of the list. It makes no difference if the basket has apples or not.
I doubt my experiment would be useful in all sortable list applications but it does have an advantage over the list items being targets. In the example, you will see that if you are dragging inside the basket but outside any contained apples then the position of insertion can still be calculated. This would work if the gaps between the individual apples is very large or if the basket is much bigger in length or width than the contained apples.
For me it also just seems right. The basket is the target. The other apples are not targets.
Comments
Have something to write? Comment on this article.
Nige,
I agree about the synchronization issue. I think it is a little unfortunate that dragdrop examples don't even hint at this issue. The dragged items just move at the end of the drag. Fine for a visual game but not so good for a database backed application.
In my example, I insert a "verifying move" place holder that doesn't work in the example but would work as a Ajax loading indicator. When the Ajax response says the move is ok then I remove the place holder and would actually make the DOM move.
Another approach would be to have all the elements move and then have the user click a "submit new order" button. This would be similar to a standard form submission. It could be good in some cases but the user would have to remember to press submit.
The web wasn't designed to avoid the stale browser problem. Inventing ways to keep browsers fresh and in sync when using Ajax gets interesting.
Have something to write? Comment on this article.
Looks good.
I agree with draggables not being targets as you know.
What I am concerned with now is synchronization of server state with complex drag-drop states.
It's all very well moving DOM elements around, but in an application, that's not useful unless you actually know what's being moved to where, and can update an object model somewhere to reflect that.
I attach a property called "data" to my DD objects, and in my pattern where the target is informed about drops and creates the appropriate new objects, the data is transferred across to the new object. The data is used to pass state back to the server.
We're using Java EJB3/Hibernate to persist POJOs so I can use the beans methods and reflection to update properties by name.
I'm trying to develop a technique of updating an object graph as deep as needed based on a POSTed submission of XML rather than a standard uurlencoded list of form fields which are just name/value pairs and must be scanned and updated "manually".
In my DDTreeView, you can get an XML version of it which looks like
The Menu class has a method
List<Menu> getSubmenus();
I have code which will update a top level Menu according to a returned XML document going as deep as necessary through all nested subMenu Lists to make the object tree match the XML. It's coded to know about the Menu object.
This works well for this one case.
What I'm thinking of is a more general purpose XML schema to update Java beans:
Converters will be needed to be specified for cases like above where a Component is dropped into a Menu. A new Menu item will be created which references that Component.
As you can imagine, this will require more framework in the browser to create a response, but this should be generatable from the object model in the server.