开发者

jQuery scroll calculations inaccurate at high-speed scrolling

I've run into something very odd. The likelihood that it's an error in my code is high, but I can't figure out why it's happening.

First, here's an example page: http://designintellection.com/quotes/2011/jan/13/design-thinking-visual/

If you notice the two nav tabs on the right of the page, when you scroll down they shrink in size and then become position:fixed as you scroll down the rest of the page. As you scroll back up they grow to their original size and go back to non-fixed positioning. (If you're wondering why I'm shrinking them I'm eventually going to replace the text with an icon.)

I've posted the code (JS & CSS) to accomplish this effect below. I know it can be written better, but the main thing I'm trying to discern is if Javascript is able to capture every specific scroll position. I say that because if you scroll slowly then the tabs behave as they're supposed to, you can go up and down as much as you like. However if you scroll at a normal to fast speed then the size, margins and positioning fall apart and you get very strange behavior.

On some level I feel like I'm taking crazy pills, usually I would try to look for bugs, etc. but it seems so strange to me that it works when you scroll slowly and doesn't when you scroll fast.

jQuery:

    $(function() {
    $(window).scroll(function() {
        var scroll = $(window).scrollTop();

        if (scroll > 80) {
            $("#the-portfolio-link").css({"width":"2px", "margin-top":"0"});
            $("#the-portfolio-link").addClass("page-link-portfolio-stay");
            $("#the-services-link").css({"margin-top":"108px"});
        }

        if ((scroll >= 22) && (scroll < 80)) {
            $("#the-portfolio-link").css({"width":"60px", "margin-top":"65px"});
            $("#the-portfolio-link").removeClass("page-link-portfolio-stay");

            var new_width = 60 + (22 - scroll);
            var existing_margin = 65;
            var new_margin_services = 65 - (60 - new_width);

            if (existing_margin < 65) {
                var new_margin = 65 - (67 - new_width);
                var margin_top = new_margin;
            }
            else {
                var margin_top = 65;
            }

            $("#the-portfolio-link").css({"width":new_width+"px", "margin-top":margin_top+"px"});
            $("#the-services-link").css({"margin-top":new_margin_services+"px"});开发者_开发问答
        }

        if ((scroll > 80) && (scroll <= 138)) {
            $("#the-services-link").css({"width":"60px", "margin-top":"108px"});
            $("#the-services-link").removeClass("page-link-services-stay");

            var new_services_width = 60 + (80 - scroll);
            var existing_services_margin = $("#the-services-link").css("margin-top").replace("px","");

            var margin_services_top = 108 + (60 - new_services_width);

            $("#the-services-link").css({"width":new_services_width+"px", "margin-top":margin_services_top+"px"});
        }

        if (scroll > 138) {
            $("#the-services-link").css({"width":"2px", "margin-top":"0"});
            $("#the-services-link").addClass("page-link-services-stay");
        }
    });
});

CSS (relevant code only):

.main-nav { position:absolute; left:1102px; top:55px; }
.main-nav-link { display:block; width:60px; height:22px; margin:65px 0 0 36px; }

.page-link-portfolio-stay { width:2px; margin-top:0; position:fixed; left:1102px; top:40px; overflow:hidden; }
.page-link-services-stay { width:2px; margin-top:0; position:fixed; left:1102px; top:83px; overflow:hidden; }

Any ideas, etc. are much appreciated.

Thanks! -David


`I'd guess that you're getting events faster than your callback can deal with them, that might be causing multiple calls to your callback to be running at once and maybe they're fighting each other.

Try a simple guard like this:

var busy = false;
$(window).scroll(function() {
    if(busy)
        return;
    busy = true;
    var scroll = $(window).scrollTop();

    //...

    if (scroll > 138) {
        $("#the-services-link").css({"width":"2px", "margin-top":"0"});
        $("#the-services-link").addClass("page-link-services-stay");
    }
    busy = false;
});

and see what happens. If that fixes it then at least you'll know what the problem was. This guard hack isn't a solution of course, it is just a quick and easy debugging aid to help you see what the problem is. A real solution would probably be to manage the event queue yourself with .queue and .dequeue.

You could also create all your jQuery objects outside the callback, queue up a bunch of CSS and class changes inside the callback, and then apply all the changes in one block at the end. That wouldn't solve your problem but it would reduce the amount of conflict and contention.

BTW, this piece of code doesn't do anything useful at all:

    if(existing_margin < 65) {
        var new_margin = 65 - (67 - new_width); 
        var margin_top = new_margin;  
    }
    else {
        var margin_top = 65;  
    }

You're defining new new_margin and margin_top variables inside the branches and then throwing them away.


I rearranged the code and found a workable solution:

jQuery:

jQuery.fn.main_nav_behavior = function(nav_id, class_apply, orig_margin_top, nav_order, nav_order_magnitude) {
    $(window).scroll(function() {
        var scroll = $(window).scrollTop();

        if ((scroll >= 22) && (scroll <= 80)) {
            var new_width = 60 + (22 - scroll);
            var existing_margin = $(nav_id).css("margin-top").replace("px","");

            if ((existing_margin < orig_margin_top) && ($(nav_id).hasClass(class_apply))) {
                var new_margin = (orig_margin_top - nav_order_magnitude) - ((orig_margin_top + 2) - (new_width*nav_order));
                var margin_top = new_margin;
            }
            else {
                if (nav_order_magnitude == 0) {
                    var margin_top = orig_margin_top;
                }
                else {
                    var new_margin = orig_margin_top - ((60 - new_width)*(nav_order-1));
                    var margin_top = new_margin;
                }
            }

            $(nav_id).css({"width":new_width+"px", "margin-top":margin_top+"px"});
        }
        else if (scroll > 80) {
            $(nav_id).css({"width":"2px", "margin-top":"0"});
            $(nav_id).addClass(class_apply);
        }
        else {
            $(nav_id).css({"width":"60px", "margin-top":orig_margin_top+"px"});
            $(nav_id).removeClass(class_apply);
        }
    });
}

$(function() {
    $(".main-nav").main_nav_behavior("#the-portfolio-link", "page-link-portfolio-stay", 65, 1, 0);
    $(".main-nav").main_nav_behavior("#the-services-link", "page-link-services-stay", 166, 2, 2);
    $(".main-nav").main_nav_behavior("#the-blog-link", "page-link-blog-stay", 267, 3, 4);
});

CSS:

.main-nav-link { display:block; width:60px; height:22px; margin:65px 0 0 36px; padding:7px 20px 5px 20px; position:absolute; overflow:hidden; }

.page-link-portfolio { margin-top:65px; }
.page-link-services { margin-top:166px; }
.page-link-blog { margin-top:267px; }

.page-link-portfolio-stay { width:2px; margin-top:0; position:fixed; left:1102px; top:40px; overflow:hidden; }
.page-link-services-stay { width:2px; margin-top:0; position:fixed; left:1102px; top:83px; overflow:hidden; }
.page-link-blog-stay { width:2px; margin-top:0; position:fixed; left:1102px; top:126px; overflow:hidden; }

HTML:

<nav class="main-nav">
  <ul class="main-nav-list">
    <li class="main-nav-item"><a id="the-portfolio-link" class="page-link-portfolio main-nav-link" href="/portfolio/v6/">Portfolio</a></li>
    <li class="main-nav-item"><a id="the-services-link" class="page-link-services main-nav-link" href="/services/v6/">Services</a></li>
    <li class="main-nav-item"><a id="the-blog-link" class="page-link-blog main-nav-link" href="/services/v6/">Blog</a></li>
  </ul>
</nav>

The events seem to be happening faster than the callbacks can handle (see mu is too short's answer) but it always seems to settle on the correct spot.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜