Tuesday, January 18, 2011

Delegation Drag

Its been some time since I have written something and its simply because there is not much to write about. But now I have something :P

The application that I'm working on currently use a lot of drag and drop features for many objects. The number of these objects in the UI varies from user to user. The code has been written to create a DragDrop object for each of these UI elements. Therefore if you were supposed to have many elements, the page would stall thus making actions damn slow. I was told to improve performance by changing this to a delegation model.

The delegation model would find the event attachments done to the main container and not the individual elements. The event would bubble up to the container which would be handled by the handler written for this. The container would be specified as well as the css selector for the elements which would have the drag and drop feature.

To make this work was pretty easy with YUI3. A delegation class was present which gave me the initial stuff to get started. But then came problems.

The application would have different drop targets at different points of time. An ajax action would change the UI which would then have different drop targets. Now if you were using delegation, you need to specify the drop objects in the constructor. I did not find a way where I could add/remove drop targets dynamically. This is when I saw a post from an author who did a work around for this. He had created the drop targets inside the mouseover action of the drag, thus scoping it :). I put these drop objects inside a scoped variable so that when the UI changed, I reset this variable.


YUI().use('dd-delegate', 'dd-constrain', 'dd-proxy', 'dd-drop', function(Y) {

var del = new Y.DD.Delegate({
container: '#container1',
nodes: '.appt',
target: true,
dragConfig: { groups: ['default']}
});

del.dd.plug(Y.Plugin.DDConstrained, {
constrain2node: '#container1'
});

del.dd.plug(Y.Plugin.DDProxy, {
moveOnEnd: false,
cloneNode: false
});

del.on('drag:mouseDown', function(e){
if(_self.createdDrops.length ==0){
var drops = Y.all('td.day');
Y.each(drops, function(v, k) {
var tar = new Y.DD.Drop({
node: v,
groups: ['default']
});
_self.createdDrops.push(tar);
});
}
});

del.on('drag:start', function(e) {
_self.delDragStart(e, Y);
});

del.on('drag:end', function(e) {
_self.delDragStop(e);
});

del.on('drag:drophit', function(e) {
_self.delDragHit(e,Y);
});

del.on('drag:over', function(e){
_self.delDragOver(e);
});

del.on('drag:exit', function(e){
_self.delDragExit(e);
});

_self.del = del;
});


I also required there be different drag objects. Those which went to specific drop targets. I started specifying the 'container' parameter instead of the 'cont' parameter and that solved some issues. To have another set of delegates, I just took the above code and changed the 'container' parameter to get the correct set of draggables. I also change the groups parameter bit that did not seem to make a difference ...

References
http://yuilibrary.com/forum/viewtopic.php?p=17845
http://developer.yahoo.com/yui/3/dd/
http://developer.yahoo.com/yui/3/examples/dd/