Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript window.scroll delay causes asynchronous redraw

The Goal

So I have a list of <div>s all in a single column layout that have either the class "active" or "inactive". The active class shows a graphic to the right of the item and the inactive class doesn't. I have it setup so that hitting the up or down arrow key moves the "active" class (and the graphic with it) to the previous or next item. It isn't animated, but you can visually see the graphic disappearing and reappearing on the tag above or below.

Now I'd like to have the page scroll down on arrow keypress so that the top edge of the item is always in the same spot. Since the element list is larger than the page window, it's necessary to automatically scroll the browser so that the selected <div> is always in the center of the screen...

The Code

//Paging through items with arrow keys
theWindow.keydown(function (e) {
    var key = e.keyCode,
        oldItem = $('li.active')

    if ((key === 40 && oldItem.next().length) || (key === 38 && oldItem.prev().length)) {
        var theWindowMod = (window.innerHeight / 2) + 43,
            theHTML = $('html'),
            theDetail = $('.detail')

        theHTML.addClass('notransition')

        if (key === 40 && oldItem.next().length) {
            oldItem.removeClass('active').next().addClass('active')
        } else if (key === 38 && oldItem.prev().length) {
            oldItem.removeClass('active').prev().addClass('active')
        }

        var newItem = $('li.active')

        window.scroll(0, newItem.offset().top - theWindowMod)
        e.preventDefault()
        $('.detail-inner.active').fadeOut(0).removeClass('active')
        $('section.active, .tab.active').removeClass('active')
        newItem.find('.tab').add(theDetail).addClass('active')
        theDetail.find('.detail-' + newItem.attr('class').split(' ')[0]).addClass('active').fadeIn(0)

        setTimeout(function () {
            theHTML.removeClass('notransition')
        }, 1)
    }
});

The Problem

The problem is that in all versions of Safari but no other browser, the window.scroll method is just a bit behind the CSS class switching performance wise. What happens is they end up in two different redraw events and it looks like the page is 'glitching' when you scroll down because you can briefly see the graphic to the right of the next element before the browser scrolls down.

The Live Demo

You can view it live here:

http://hashtag.ly/#minecraft

Use the arrow keys to page through items. Notice the jump. How should I go about resolving this?

like image 781
alt Avatar asked Nov 04 '22 09:11

alt


2 Answers

I think a solution that avoids the issue is your best bet.

From a UX perspective, when I'm browsing a site, I don't like it when the site usurps control of the scroll position.

Also, for people with tall-ish browsers (like me) currently there can be a lot of white real-estate between the details and the selected post. (see screenshot)

Details are far from selected post.

My recommendation is to change the design so the details show up next to the selected post and let the user do the scrolling. Controlling the locaiton of the details with CSS, so they're next to the selected post, will put it in the same render-cycle as everything else.

The details being closer to the selected post might look something like this:

Details closer to selected post.

Update:

Come to think of it, AOL's email client Alto has the UX that you've implemented. In Alto, the left column does scroll automatically if you browse with the keys. But, you're not actually scrolling, they're adding content into the container element and bringing it into view (I forget what this is called... virtualization?). It looks like they're managing all the scroll-related visuals and behavior, themselves, and are not using the native functionality. So, it's all JS controlled CSS and DOM manipulation, there isn't actually a scrollTo() invocation. This puts it all in the same render cycle.

AOL Alto email

like image 76
tiffon Avatar answered Nov 08 '22 08:11

tiffon


Try using a button instead of the div and use setfocus() when the specific item is activated. The browser will automatically scroll to make the focused button always visible. You can use CSS to make the button look exactly like the div.

like image 43
ATOzTOA Avatar answered Nov 08 '22 08:11

ATOzTOA