Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Disabling jQuery drag when scrolling contents of draggable item

I don't normally make this sort of question / answer, but figured I'd do so, since I've seen this question asked 20+ times, and not a single answer actually works. In short, the problem is that if you have scrollable content (overflow: auto; anywhere inside of a draggable jQuery item, when you click and drag the scrollbar's the parent draggable container drags along with it. So, I spent some time working up a solution.

Here's an example of some html that would present this problem:

<div class="draggable" style="width:100px;height:100px;">
  <div id="content" style="width:80px;height:80px;overflow:auto;">
    Nullam quis risus eget urna mollis ornare vel eu leo. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam id dolor id nibh ultricies vehicula ut id elit.
  </div>
</div>

The typical way you initialize an element to be draggable, is something like this:

$(".draggable").draggable()
like image 360
PriorityMark Avatar asked Jan 26 '12 03:01

PriorityMark


3 Answers

I suggest a solution based on a mix of [1] and the solution provided by PriorityMark. This solution works more reliably and I think it's a little more efficient as well.

$(".draggable").draggable({
    start: function(event) {
        var content = $(this).children('.content');

        // if we're scrolling, don't start and cancel drag
        if (event.originalEvent.pageX-content.offset().left > content.innerWidth())
        {
            console.log('should-cancel');
            $(this).trigger("mouseup");
            return false;
        }
    }
});

For this to work, I slightly adjusted the example DOM (but this shouldn't be that big a problem):

<div class="draggable" style="width:100px;height:100px;overflow:auto;">
  <div class="content" style="width:80px;height:80px;">
    Nullam quis risus eget urna mollis ornare vel eu leo. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam id dolor id nibh ultricies vehicula ut id elit.
  </div>
</div>​

Please note that here the draggable div has the overflow, not the content div. For the solution it doesn't really matter, but I didn't want to have to add an extra div-level, since it's not strictly necessary to make the point.

Here is the jsfiddle.

[1] - listen for mouse events … except a div's overflow:scroll scrollbar?

like image 63
eleotlecram Avatar answered Nov 12 '22 23:11

eleotlecram


The solution is to bind to the scroll event on the elements your initializing, and any of that elements children. Then, when any of the children invoke a scroll command, find all of the draggable parents of that element, and set a scrolled data element on that element.

Under jQuery UI's current version (1.8.16), the start event always kicks off when you mouseup on the scrollbar, and progates up the tree, so this solution works pretty well in my testing.

$(".draggable").draggable({
    start: function() {
        // if we're scrolling, don't start and cancel drag
        if ($(this).data("scrolled")) {
            $(this).data("scrolled", false).trigger("mouseup");
            return false;
        }
    }
}).find("*").andSelf().scroll(function() {               
    // bind to the scroll event on current elements, and all children.
    //  we have to bind to all of them, because scroll doesn't propagate.

    //set a scrolled data variable for any parents that are draggable, so they don't drag on scroll.
    $(this).parents(".ui-draggable").data("scrolled", true);

});

For your viewing / dabbling pleasures, I've included a jsfiddle of the issue as well.

like image 45
PriorityMark Avatar answered Nov 12 '22 23:11

PriorityMark


I was searching for this problem and found a smaller solution which works perfectly for me and I want to share it. My solution is, to stop propagate the "mousedown" event on the child/content. No mousedown means no dragging ;)

$(".draggable").draggable();
$("#content").mousedown(function(event) {
    event.stopPropagation();
});
like image 1
Robert Avatar answered Nov 12 '22 22:11

Robert