rfadams rfadams - 6 months ago 148
jQuery Question

jQuery Sortable List - scroll bar jumps up when sorting

I created a demo: http://pastebin.me/584b9a86d715c9ba85b7bebf0375e237

When the scroll bar is at the bottom and you drag items to sort them, it causes the scroll bar to jump up. It seems to do this in FF, Safari, Chrome, and IE (at least IE8).

In Firefox on my Mac, it does the jump up when the scroll bar is at the bottom, but also adds a nice flash to the whole list. It just flashes the whole list right as it scrolls it up. I believe that if I figure out what is causing the scroll up (and if I can stop it), the flashing will stop as well.

I don't like it jumping up like that b/c I find it jarring and confusing. I know this is a bit of a corner case, but I'd like to fix it if I could.

So, is there any way to prevent it from scrolling up? Alternately, what is causing it to scroll up?

Thanks

Answer

The problem is very simple.

First let's set up the scenario. You have a list of sortable elements, your page is higher than your screen so it has a scrollbar, your page is at the very bottom, the scroll bar all the way down.

The issue, you go to drag an element which causes jQuery to remove it from the dom then add in a placeholder. Let’s slow it down, it removes the element first, now the list is shorter causing the page to be shorter, causing the scrollbar to be shorter. Then it adds the placeholder, now the page is longer, now the srollbar is longer (but the window doesnt scroll back down, so it appears the scroll bar jumped up).

Best way I found to counteract this would be to ensure the sortable list has a static height, so removing an element doesn't cause it to go shorter. One method of doing this would be

create:function(){
    jQuery(this).height(jQuery(this).height());
}

The above will be called upon the creation of the sortable list, staticly setting its height to it's current height.

If you have pictures in one of the sortable elements, the above code will not work unless you pre-define the dimensions in the tag. Reason is that when the height() is called and set, it's calculating before the image is filled out. No dimension, then the image accounts for no space. Once the image loads, then will it take up space.

Here is a way around having to define dimensions. Simply resize the list height every time an image within is detected to be loaded.

create:function(){
    var list=this;
    jQuery(list).find('img').load(function(){
        jQuery(list).height(jQuery(list).height());
    });
}

Final version:

jQuery(document).ready(function(){  
    jQuery("div#todoList").sortable({
        handle:'img.moveHandle',
        opacity: 0.6,
        cursor: 'move',
        tolerance: 'pointer',
        forcePlaceholderSize: true,
        update:function(){
            jQuery("body").css('cursor','progress');
            var order = jQuery(this).sortable('serialize');
            jQuery.post("/ajax.php?do=sort&"+order,function(data){jQuery("body").css('cursor','default');});
        },
        create:function(){
            var list=this;
            resize=function(){
                jQuery(list).css("height","auto");
                jQuery(list).height(jQuery(list).height());
            };
            jQuery(list).height(jQuery(list).height());
            jQuery(list).find('img').load(resize).error(resize);
        }
    });
});
Comments