Thursday, February 13, 2014

Retaining Scroll Position With Dialogs

If you set the height of a jQuery UI dialog widget, the content will scroll vertically. This is the expected result — the .ui-dialog-content div sets the overflow CSS property to auto. However, issues take place when you have multiple dialogs, and content that scrolls inside each. Because of the way focusing works, the vertical scroll bar moves back to the top, whenever the dialog loses focus. Ideally, the user would expect the scroll bar to retain it's position, no matter which dialog has the focus.

This fiddle shows you how to do that. I'm not entirely sure what causes the scroll bar to jump back to the top — it might have something to do with the z-index property changing, in conjunction with some other stuff the widget is doing to shuffle the focus around. To work-around this behavior, we need to record the scroll position of internal dialog content as it changes. That way, we can restore it after the dialog loses focus.

This example is creating two dialogs with scrolled content in each. Then we position the first dialog to the left of the page, and the second dialog to the right. Next, we setup our event handlers to manage dialog content scroll positions.

$dialogs.on({

    scroll: function( e ) {
        $( this ).data( "st", $( this ).scrollTop() );
    },

    dialogfocus: function( e, ui ) {
        $( ":ui-dialog" ).not( this ).each(function() {
            var st = $( this ).data( "st" );
            if ( typeof st !== "undefined" ) {
                $( this ).scrollTop( st );
            }
        });
    }

});

The first event is the scroll event — used to track the scrollTop position of scrolled dialog content. The value gets put into the st data item attached to the element. Next is the dialogfocus event. Here is where the scroll fix happens. We look for all dialog widgets that aren't this one. In other words, this dialog just gained focus, so we want to update the scroll position of any other dialogs that have the st data item. If we find one that does, the scrollTop property gets updated with scrollTop().