My Secret Life as a Spaghetti Coder
home | about | contact | privacy statement | getting started with cfrails
I was having trouble with dragging and dropping elements using Scriptaculous's dragdrop.js. Apparently, I'm not the only one. The problem was:
I have a div with overflow = auto, so when there is more content than the size of the div, scrollbars appear. My draggables and droppables are all elements inside of that div. Everything works fine when the scrollbar is scrolled all the way to the top, but when you scroll it down any amount, the draggables fail.
There are plenty of potential solutions out there, but none of them worked for me. I would get the scrolling working, and then the draggable would move away from the cursor. I'd get it in sync with the mouse cursor and the scrolling would get crazy again. I'd fix that and then no matter where I dropped it, if the div had been scrolled, dropping would fail.

Here's how I fixed the problem:
  1. In the Draggable#updateDrag function (~ line 356), on the first line, I changed the value of the pointer argument to take into account how much the container had scrolled:

    pointer = new Array(pointer[0] + this.options.scroll.scrollLeft, pointer[1] + this.options.scroll.scrollTop);

    At least one of the solutions I recall seeing mentioned this.

  2. In the same function, I changed the first two elements in the p array before the last two elements get pushed onto it:

    p = new Array(p[0] + this.options.scroll.scrollLeft, p[1] + this.options.scroll.scrollTop);

    This also just takes into account how far the container has been scrolled.

  3. To ensure my droppables were able to receive the draggables given the adjusted coordinates, we need to adjust the scroll position just as we did above. First, I adjusted the code in my webpage that produces the droppables and added a scroll parameter that should be the name of the element that scrolls (the same parameter the draggable accepts):

    Droppables.add('slot_1_7_2', { scroll: 'weeklycalendar' });

    Since Droppable elements don't generally take a scroll option, we'll need to modify that code in Scriptaculous's dragdrop.js file as well. In the Droppable#fire function (~ line 109) add the folling lines under Position.prepare();:

    var point = [Event.pointerX(event), Event.pointerY(event)];
    if(this.last_active.scroll){
      point[0] += $(this.last_active.scroll).scrollLeft;
      point[1] += $(this.last_active.scroll).scrollTop;
    }

    Finally, just underneath that where it calls this.isAffected, change the first parameter from [Event.pointerX(event), Event.pointerY(event)] to use the variable we created above, pointer.
That should be it. If you've tried the above and still get problems, feel free to leave a comment below, or contact me and I'll do my best to help out.

I haven't submitted a patch because I didn't check to see that this was a general solution. It seems like it should be, but without testing it outside my intended usage, I don't think it'd be accepted anyway. Quite frankly, I'm not thrilled about adding a new option to droppables, but it seemed like the simplest route to fix my problem at the time.

Yes, I tried setting includeScrollOffsets to true and using Position#withinIncludingScrolloffsets in Prototype, and that failed for me too.

Hey! Why don't you make your life easier and subscribe to the full post or short blurb RSS feed? I'm so confident you'll love my smelly pasta plate wisdom that I'm offering a no-strings-attached, lifetime money back guarantee!


Comments
Leave a comment

Hi Sam...Could you post the code modified?? Because I got an error and maybe I'm doing something wrong.

Thanks

Posted by Jorge Alvarez on Apr 08, 2010 at 01:38 PM UTC - 5 hrs

Yes, I'll post it when I've got it handy - give me until tomorrow for that.

Posted by Sammy Larbi on Apr 08, 2010 at 02:32 PM UTC - 5 hrs

ok... no problem!! Thanks!

Posted by Jorge Alvarez on Apr 08, 2010 at 02:36 PM UTC - 5 hrs

Jorge,

The modified dragdrop.js file is here: http://www.codeodor.com/code/dragdrop.js

Posted by Sammy Larbi on Apr 09, 2010 at 04:58 PM UTC - 5 hrs

Let me know how it goes!

Posted by Sammy Larbi on Apr 09, 2010 at 05:02 PM UTC - 5 hrs

Hi Sammy thanks for this post, much appreciated! Unfortunately your solution doesn't quite work for me - after adding your lines the draggable is offset right away by the amount that I'm scrolled down in the container when I start dragging (seems logical as you're changing the value of the pointer param by the scrolling offset).

I even tried it with the dragdrop.js you posted on http://www.codeodor.com/code/dragdrop.js (adding the new Droppable param in my code of course). Any ideas? I can provide a short screencast with the problem if it helps.

Posted by Michael on Apr 23, 2010 at 05:02 AM UTC - 5 hrs

Actually think I just fixed the problem using Daniele's solution from https://prototype.lighthouseapp.com/projects/8887/... (well, most of it, anyway)

Thanks!

Posted by Michael on Apr 23, 2010 at 06:14 AM UTC - 5 hrs

Michael, thanks for sharing what ended up helping. Now that I see that code, I remember looking at isAffected and having a failure dropping, so I'll need to look back and make sure I uploaded the right code, or try to remember if something else ended up fixing it.

Thanks again!

Posted by Sammy Larbi on Apr 23, 2010 at 06:43 AM UTC - 5 hrs

Hi,

Here is what I wrote to have it running under IE 8.0.6 & Firefox 3.6.3:

Make draggable the elements (with border) in the "width:100px;scrollable:auto" container:
function makeDraggable(container,tag) {

if(!container || !tag) { return false; }
$(container).select(tag).each( function(o) {
new Draggable(o,{
starteffect: function(e){makeDragVisible(container,e);},
endeffect: function(e){e.setStyle({'position':'','width':'','cursor':''});},
zindex: 1000
// , revert: ... // the other options
});
});

}

function makeDragVisible(container,element) {

if(!container || !element) { return false; }
var i=$(container).getStyle('width');
i=i.replace('px','');
i=Math.round(i-20)+'px';
element.setStyle({'width':i,'z-index':1000,'position':'absolute','cursor':'move'});
//
$(container).setStyle({});

}

Important notes: (1) the z-index is repeated (2) notice the container loss of style at the end of 'starteffect'. Cursor and width are simply there to keep the drag user friendly.

I hope it helps.

Yours,
Nicolas

Posted by Nicolas on Jun 18, 2010 at 02:12 AM UTC - 5 hrs

Thanks for sharing your work, Nicolas!

Posted by Sammy Larbi on Jun 18, 2010 at 08:26 AM UTC - 5 hrs

After the line:
var pos = Position.cumulativeOffset(this.element);
in initDrag, I've added the following two lines:

pointer[0] += this.options.scroll.scrollLeft;
pointer[1] += this.options.scroll.scrollTop;

This helps the ghostly drag'n'drop box to be displayed in the right place.

Posted by Tristan Rowley on Jul 15, 2010 at 06:06 AM UTC - 5 hrs

@Tristan: Thanks for contributing to helping anyone else who comes across this.

Posted by Sammy Larbi on Jul 15, 2010 at 07:45 AM UTC - 5 hrs

Leave a comment

Leave this field empty
Your Name
Email (not displayed, more info?)
Website

Comment:

Subcribe to this comment thread
Remember my details
Google
Web CodeOdor.com

Me
Picture of me

Topics
.NET (17)
AI/Machine Learning (12)
Answers To 100 Interview Questions (10)
C and C++ (5)
cfrails (22)
ColdFusion (78)
Customer Relations (14)
Databases (2)
DRY (18)
DSLs (11)
Future Tech (4)
Games (4)
Groovy/Grails (8)
Hardware (1)
IDEs (9)
Java (38)
JavaScript (3)
Linux (1)
Lisp (1)
Mac OS (1)
Management (11)
Miscellany (67)
OOAD (35)
Productivity (5)
Programming (130)
Programming Quotables (6)
Rails (19)
Ruby (56)
Save Your Job (39)
scriptaGulous (4)
Software Development Process (21)
TDD (39)
TDDing xorblog (6)
Tools (2)
Web Development (4)
Windows (1)
With (1)
YAGNI (10)

Resources
Agile Manifesto & Principles
Principles Of OOD
ColdFusion
CFUnit
Ruby
Ruby on Rails
JUnit



RSS 2.0: Full Post | Short Blurb
Subscribe by email:

Delivered by FeedBurner