jQuery draggable shows helper in wrong place after page scrolled

Alex picture Alex · Apr 26, 2011 · Viewed 69.1k times · Source

I'm using jQuery draggable and droppable for a work-planning system I'm developing. Users drag jobs to a different day or user, and then data is updated using an ajax call.

Everything works fine, except when I scroll down the main page (Jobs appear on a large week planner that exceeds the bottom of my browser window). If I try and drag a draggable element here, the element appears above my mouse cursor the same amount of pixels as I've scrolled down.. The hover state still works fine and the functionality is bang on but it doesn't look right.

I'm using jQuery 1.6.0 and jQuery UI 1.8.12.

I'm sure there's a offset function I need to add but I don't know where to apply it, or if there's a better way. Here's my .draggable() initialisation code:

$('.job').draggable({
  zIndex: 20,
  revert: 'invalid',
  helper: 'original',
  distance: 30,
  refreshPositions: true,
});

Any idea what I can do to fix this?

Answer

DarthJDG picture DarthJDG · Apr 26, 2011

This might be a related bug report, it's been around for quite a while: http://bugs.jqueryui.com/ticket/3740

It seems to happen on every browser I tested (Chrome, FF4, IE9). There are a few ways you can work around this issue:

1. Use position:absolute; in your css. Absolutely positioned elements don't seem to be affected.

2. Make sure the parent element (event if it's the body) has overflow:auto; set. My test showed that this solution fixes the position, but it disables the autoscroll functionality. You can still scroll using the mousewheel or the arrow keys.

3. Apply the fix suggested in the above bug report manually and test thouroughly if it causes other problems.

4. Wait for an official fix. It's scheduled to jQuery UI 1.9, although it has been postponed a few times in the past.

5. If you're confident that it happens on every browser, you can put these hacks into the affected draggables' events to correct the calculations. It's a lot of different browsers to test though, so it should only be used as a last resort:

$('.drag').draggable({
   scroll:true,
   start: function(){
      $(this).data("startingScrollTop",$(this).parent().scrollTop());
   },
   drag: function(event,ui){
      var st = parseInt($(this).data("startingScrollTop"));
      ui.position.top -= $(this).parent().scrollTop() - st;
   }
});