More than one draggable scroll container in jQuery UI

Published Thursday, March 12, 2009 in Development, jQuery

jQuery UI's draggable plug-in has a scroll-setting which, if set to true, automatically scrolls the container of a draggable object.

The plug-in looks for the first parent-element with overflow set to either auto or scroll of the draggable element if the scroll-option is set to true.

But if you have one area with elements that you can drag onto another area you might want both the draggable and the droppable area to scroll when dragging occurrs.

Thus, I've made a few small changes to the scroll-function of the draggable plug-in which allows for the scroll-option to be a comma-separated list of CSS-selectors which will act as scroll container(s).

What I basically had to do was just wrap the whole functionality in a loop and store more than one overflow-object in an array.

The stuff you want to change to allow this starts with:

$.ui.plugin.add("draggable", "scroll", {

And you want to change it into (I've commented by changes)

$.ui.plugin.add("draggable", "scroll", {
    start: function(e, ui) {
        var o = ui.options;
        var i = $(this).data("draggable");
        var j = 0; // Added this for looping

        o.scrollSensitivity    = o.scrollSensitivity || 20;
        o.scrollSpeed        = o.scrollSpeed || 20;
        i.overflows            = []; // Added this to hold all the overflow elements

        // If the scroll-option is a string
        if (typeof(o.scroll) == 'string') {
            // Break out all the selectors and store the elements in the overflows-array
            var selectors = o.scroll.split(',');

            for (j = 0; selectors[j]; j++) {
                i.overflows.push({
                    overflowY: $(selectors[j]), 
                    overflowX: $(selectors[j])
                });
            }
        } else {
            // This is the same as before except I put it all in the first element of the overflows-array
            i.overflows[0] = {
                overflowY: function(el) {
                    do { if(/auto|scroll/.test(el.css('overflow')) || (/auto|scroll/).test(el.css('overflow-y'))) return el; el = el.parent(); } while (el[0].parentNode);
                    return $(document);
                }(this), 
                overflowX: function(el) {
                    do { if(/auto|scroll/.test(el.css('overflow')) || (/auto|scroll/).test(el.css('overflow-x'))) return el; el = el.parent(); } while (el[0].parentNode);
                    return $(document);
                }(this)
            };
        }

        // Added the loop so that this happens to all overflow-elements
        for (j = 0; i.overflows[j]; j++) {        
            if(i.overflows[j].overflowY[0] != document && i.overflows[j].overflowY[0].tagName != 'HTML') i.overflows[j].overflowYOffset = i.overflows[j].overflowY.offset();
            if(i.overflows[j].overflowX[0] != document && i.overflows[j].overflowX[0].tagName != 'HTML') i.overflows[j].overflowXOffset = i.overflows[j].overflowX.offset();
        }
        
    },
    drag: function(e, ui) {
        var o = ui.options;
        var i = $(this).data("draggable");
        var j = 0; // Added this for looping

        // Added this for-loop as well as put everything in the overflows-array
        for (j = 0; i.overflows[j]; j++) {        
            if(i.overflows[j].overflowY[0] != document && i.overflows[j].overflowY[0].tagName != 'HTML') {
                if((i.overflows[j].overflowYOffset.top + i.overflows[j].overflowY[0].offsetHeight) - e.pageY < o.scrollSensitivity)
                    i.overflows[j].overflowY[0].scrollTop = i.overflows[j].overflowY[0].scrollTop + o.scrollSpeed;
                if(e.pageY - i.overflows[j].overflowYOffset.top < o.scrollSensitivity)
                    i.overflows[j].overflowY[0].scrollTop = i.overflows[j].overflowY[0].scrollTop - o.scrollSpeed;
            } else {
                if(e.pageY - $(document).scrollTop() < o.scrollSensitivity)
                    $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
                if($(window).height() - (e.pageY - $(document).scrollTop()) < o.scrollSensitivity)
                    $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
            }

            if(i.overflows[j].overflowX[0] != document && i.overflows[j].overflowX[0].tagName != 'HTML') {
                if((i.overflows[j].overflowXOffset.left + i.overflows[j].overflowX[0].offsetWidth) - e.pageX < o.scrollSensitivity)
                    i.overflows[j].overflowX[0].scrollLeft = i.overflows[j].overflowX[0].scrollLeft + o.scrollSpeed;
                if(e.pageX - i.overflows[j].overflowXOffset.left < o.scrollSensitivity)
                    i.overflows[j].overflowX[0].scrollLeft = i.overflows[j].overflowX[0].scrollLeft - o.scrollSpeed;
            } else {
                if(e.pageX - $(document).scrollLeft() < o.scrollSensitivity)
                    $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
                if($(window).width() - (e.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
                    $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
            }
        }
    }
});

So to use more than one scroll container simply specify the scroll options like so:

$('li').draggable({scroll: '#drop-area, #drag-area', refreshPositions: true});

I'm also setting refreshPositions to true so that the droppable's positions are recalculated when user drags (you can try it without to see the problem otherwise).

jQuery.com has this to say about refreshPositions: "Caution: This solves issues on highly dynamic pages, but dramatically decreases performance." so use with care.

Note that this is for version 1.5.3, not sure if this is fixed or if it looks even remotely similar in latest version.


Rate It


Bookmark It

  • del.icio.us
  • Digg
  • Furl
  • Google
  • Technorati
  • Ma.gnolia
  • BlinkList
  • Blogmarks
  • Rojo
  • StumbleUpon


Post It

From June 02 to April 23

  1. Mogrify is what you're looking for if you want to convert multiple images to multiple other images in ImageMagick
  2. Tommorrow, finally, the inFamous demo will be friggin availble on PSN!! Suweeeeeeeeet
  3. Fuck canvas is cool, I've started playing around with old 3D-shit again :)

September 2010

S M T W T F S
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30


Recent Comments

  1. Vlad on A follow-up to "An alternative to div overlays":
    I'll try this.....
  2. Steve on jQuery Live Ajax Search Plug-in:
    This is a great script but I've got...
  3. Frank on jQuery Live Ajax Search Plug-in:
    Hey, sounds like a great plugin! I ...
  4. Andreas on jQuery Drag to Select Plug-in:
    @Paul - I didn't take into account ...

Style Switcher

The style switcher allows you to change the look and feel of exscale.se.
Only CSS and JavaScript are changed. The XHTML stays the same.

For more information about the styles, check the styles page.


Categories


Random Quote

conwincdence, i think not. - christus11


Random Images




Answer This!

Do you find the "scroll-pagination" annoying? (If you don't know what it is, scroll to the bottom of the first-page)


Blog Roll