更有效的方法来处理jquery中的$(window).scroll函数?

时间:2022-11-29 20:15:51

In the code below, I'm checking to see if the window is being scrolled past a certain point and if it is, change an element to use fixed position so that it doesn't scroll off the top of the page. The only problem is that is seems to be HIGHLY client-side-memory intensive (and really bogs down the scrolling speed) because at every single scroll pixel I am updating the style attributes over and over on the element.

在下面的代码中,我正在检查窗口是否正在滚动超过某个点,如果是,请更改元素以使用固定位置,以便它不会滚动到页面顶部。唯一的问题是,似乎是高度客户端内存密集型(并且确实使滚动速度陷入困境),因为在每个滚动像素上,我都在元素上反复更新样式属性。

Would checking if the attr is already there before attempting to update it make a significant difference? Is there a completely different and more efficient practice to get the same result?

在尝试更新之前检查attr是否已经存在会产生重大影响吗?是否存在完全不同且更有效的实践以获得相同的结果?

$(window).scroll(function () {
    var headerBottom = 165;
    var fcHeight = $("#pnlMainNavContainer").height();

    var ScrollTop = $(window).scrollTop();
    if (ScrollTop > headerBottom) {
        $("#HeaderContentBuffer").attr("style", "margin-top:" + (fcHeight) + "px;");
        $("#AddFieldsContainer").attr("style", "position:fixed;width:320px;top:70px;left:41px;");
    } else {
        $("#HeaderContentBuffer").attr("style", "margin-top: 0px;");
        $("#AddFieldsContainer").removeAttr("style");
    }
});

As I'm typing this, I notice that *.com using the same type of functionality with their yellow "Similar Questions" and "Help" menus on the right hand side of this page. I wonder how they do it.

当我输入此内容时,我注意到*.com使用相同类型的功能,并在本页右侧使用黄色的“类似问题”和“帮助”菜单。我想知道他们是怎么做到的。

4 个解决方案

#1


53  

One technique you can use is to set a timer on the scroll event and only do the main work when the scroll position hasn't changed for a short period of time. I use that technique on resize events which have the same issue. You can experiment with what timeout value seems to work right. A shorter time updates with shorter pauses in scrolling and thus may run more often during the scroll, a longer time requires the user to actually pause all motion for a meaningful time. You will have to experiment with what timeout value works best for your purposes and it would be best to test on a relatively slow computer since that's where the issue of scroll lag would be most pronounced.

您可以使用的一种技术是在滚动事件上设置计时器,并且仅在滚动位置在短时间内没有更改时才执行主要工作。我在具有相同问题的resize事件上使用该技术。您可以尝试使用什么超时值正常工作。较短的时间更新,滚动时间较短,因此在滚动期间可能会更频繁地运行,较长的时间需要用户实际暂停所有运动一段有意义的时间。您将不得不尝试使用哪种超时值最适合您的目的,最好在相对较慢的计算机上进行测试,因为滚动延迟的问题最为明显。

Here's the general idea how this could be implemented:

以下是如何实现这一点的一般概念:

var scrollTimer = null;
$(window).scroll(function () {
    if (scrollTimer) {
        clearTimeout(scrollTimer);   // clear any previous pending timer
    }
    scrollTimer = setTimeout(handleScroll, 500);   // set new timer
});

function handleScroll() {
    scrollTimer = null;
    var headerBottom = 165;
    var fcHeight = $("#pnlMainNavContainer").height();

    var ScrollTop = $(window).scrollTop();
    if (ScrollTop > headerBottom) {
        $("#HeaderContentBuffer").attr("style", "margin-top:" + (fcHeight) + "px;");
        $("#AddFieldsContainer").attr("style", "position:fixed;width:320px;top:70px;left:41px;");
    } else {
        $("#HeaderContentBuffer").attr("style", "margin-top: 0px;");
        $("#AddFieldsContainer").removeAttr("style");
    }
}

You may also be able to speed up your scroll function by caching some of the selectors when the scrolling first starts so they don't have to be recalculated each time. This is one place where the extra overhead of creating a jQuery object each time might not be helping you.

当滚动首次启动时,您也可以通过缓存某些选择器来加快滚动功能,这样就不必每次都重新计算它们。这是一个每次创建jQuery对象的额外开销可能无法帮助您的地方。


Here's a jQuery add-on method that handles the scrolling timer for you:

这是一个jQuery附加组件方法,为您处理滚动计时器:

(function($) {
    var uniqueCntr = 0;
    $.fn.scrolled = function (waitTime, fn) {
        if (typeof waitTime === "function") {
            fn = waitTime;
            waitTime = 500;
        }
        var tag = "scrollTimer" + uniqueCntr++;
        this.scroll(function () {
            var self = $(this);
            var timer = self.data(tag);
            if (timer) {
                clearTimeout(timer);
            }
            timer = setTimeout(function () {
                self.removeData(tag);
                fn.call(self[0]);
            }, waitTime);
            self.data(tag, timer);
        });
    }
})(jQuery);

Working demo: http://jsfiddle.net/jfriend00/KHeZY/

工作演示:http://jsfiddle.net/jfriend00/KHeZY/

Your code would then be implemented like this:

然后您的代码将实现如下:

$(window).scrolled(function() {
    var headerBottom = 165;
    var fcHeight = $("#pnlMainNavContainer").height();

    var ScrollTop = $(window).scrollTop();
    if (ScrollTop > headerBottom) {
        $("#HeaderContentBuffer").attr("style", "margin-top:" + (fcHeight) + "px;");
        $("#AddFieldsContainer").attr("style", "position:fixed;width:320px;top:70px;left:41px;");
    } else {
        $("#HeaderContentBuffer").attr("style", "margin-top: 0px;");
        $("#AddFieldsContainer").removeAttr("style");
    }
});

#2


10  

I have found this method to be much more efficeint for $(window).scroll()

我发现这个方法对$(window).scroll()更有效。

var userScrolled = false;

$(window).scroll(function() {
  userScrolled = true;
});

setInterval(function() {
  if (userScrolled) {

    //Do stuff


    userScrolled = false;
  }
}, 50);

Check out John Resig's post on this topic.

查看John Resig关于此主题的帖子。

An even more performant solution would be to set a a longer interval that detects if you are close to the bottom or top of the page. That way, you wouldn't even have to use $(window).scroll()

更高效的解决方案是设置更长的间隔,以检测您是否接近页面的底部或顶部。这样,你甚至不必使用$(window).scroll()

#3


2  

Making your function a little more efficient.

使您的功能更有效。

Just check if the style attribute is present/absent before removing/adding the styles.

在删除/添加样式之前,只需检查样式属性是否存在。

$(window).scroll(function () {
    var headerBottom = 165;
    var fcHeight = $("#pnlMainNavContainer").height();

    var ScrollTop = $(window).scrollTop();
    if (ScrollTop > headerBottom) {
        if (!$("#AddFieldsContainer").attr("style")) {
            $("#HeaderContentBuffer").attr("style", "margin-top:" + (fcHeight) + "px;");
            $("#AddFieldsContainer").attr("style", "position:fixed;width:320px;top:70px;left:41px;");
        }
    } else {
        if ($("#AddFieldsContainer").attr("style")) {
            $("#HeaderContentBuffer").attr("style", "margin-top: 0px;");
            $("#AddFieldsContainer").removeAttr("style");
        }
    }
});

#4


0  

Set some logic here. You actually need to set atts once on up, and once on down. So:

在这里设置一些逻辑。实际上你需要设置一次up,一次打开。所以:

var checker = true;
$(window).scroll(function () {

    .......

    if (ScrollTop > headerBottom && checker == true) {
        $("#HeaderContentBuffer").attr("style", "margin-top:" + (fcHeight) + "px;");
        $("#AddFieldsContainer").attr("style", "position:fixed;width:320px;top:70px;left:41px;");
        checker == false;
    } else if (ScrollTop < headerBottom && checker == false) {
        $("#HeaderContentBuffer").attr("style", "margin-top: 0px;");
        $("#AddFieldsContainer").removeAttr("style");
        checker == true;
    }   
});

#1


53  

One technique you can use is to set a timer on the scroll event and only do the main work when the scroll position hasn't changed for a short period of time. I use that technique on resize events which have the same issue. You can experiment with what timeout value seems to work right. A shorter time updates with shorter pauses in scrolling and thus may run more often during the scroll, a longer time requires the user to actually pause all motion for a meaningful time. You will have to experiment with what timeout value works best for your purposes and it would be best to test on a relatively slow computer since that's where the issue of scroll lag would be most pronounced.

您可以使用的一种技术是在滚动事件上设置计时器,并且仅在滚动位置在短时间内没有更改时才执行主要工作。我在具有相同问题的resize事件上使用该技术。您可以尝试使用什么超时值正常工作。较短的时间更新,滚动时间较短,因此在滚动期间可能会更频繁地运行,较长的时间需要用户实际暂停所有运动一段有意义的时间。您将不得不尝试使用哪种超时值最适合您的目的,最好在相对较慢的计算机上进行测试,因为滚动延迟的问题最为明显。

Here's the general idea how this could be implemented:

以下是如何实现这一点的一般概念:

var scrollTimer = null;
$(window).scroll(function () {
    if (scrollTimer) {
        clearTimeout(scrollTimer);   // clear any previous pending timer
    }
    scrollTimer = setTimeout(handleScroll, 500);   // set new timer
});

function handleScroll() {
    scrollTimer = null;
    var headerBottom = 165;
    var fcHeight = $("#pnlMainNavContainer").height();

    var ScrollTop = $(window).scrollTop();
    if (ScrollTop > headerBottom) {
        $("#HeaderContentBuffer").attr("style", "margin-top:" + (fcHeight) + "px;");
        $("#AddFieldsContainer").attr("style", "position:fixed;width:320px;top:70px;left:41px;");
    } else {
        $("#HeaderContentBuffer").attr("style", "margin-top: 0px;");
        $("#AddFieldsContainer").removeAttr("style");
    }
}

You may also be able to speed up your scroll function by caching some of the selectors when the scrolling first starts so they don't have to be recalculated each time. This is one place where the extra overhead of creating a jQuery object each time might not be helping you.

当滚动首次启动时,您也可以通过缓存某些选择器来加快滚动功能,这样就不必每次都重新计算它们。这是一个每次创建jQuery对象的额外开销可能无法帮助您的地方。


Here's a jQuery add-on method that handles the scrolling timer for you:

这是一个jQuery附加组件方法,为您处理滚动计时器:

(function($) {
    var uniqueCntr = 0;
    $.fn.scrolled = function (waitTime, fn) {
        if (typeof waitTime === "function") {
            fn = waitTime;
            waitTime = 500;
        }
        var tag = "scrollTimer" + uniqueCntr++;
        this.scroll(function () {
            var self = $(this);
            var timer = self.data(tag);
            if (timer) {
                clearTimeout(timer);
            }
            timer = setTimeout(function () {
                self.removeData(tag);
                fn.call(self[0]);
            }, waitTime);
            self.data(tag, timer);
        });
    }
})(jQuery);

Working demo: http://jsfiddle.net/jfriend00/KHeZY/

工作演示:http://jsfiddle.net/jfriend00/KHeZY/

Your code would then be implemented like this:

然后您的代码将实现如下:

$(window).scrolled(function() {
    var headerBottom = 165;
    var fcHeight = $("#pnlMainNavContainer").height();

    var ScrollTop = $(window).scrollTop();
    if (ScrollTop > headerBottom) {
        $("#HeaderContentBuffer").attr("style", "margin-top:" + (fcHeight) + "px;");
        $("#AddFieldsContainer").attr("style", "position:fixed;width:320px;top:70px;left:41px;");
    } else {
        $("#HeaderContentBuffer").attr("style", "margin-top: 0px;");
        $("#AddFieldsContainer").removeAttr("style");
    }
});

#2


10  

I have found this method to be much more efficeint for $(window).scroll()

我发现这个方法对$(window).scroll()更有效。

var userScrolled = false;

$(window).scroll(function() {
  userScrolled = true;
});

setInterval(function() {
  if (userScrolled) {

    //Do stuff


    userScrolled = false;
  }
}, 50);

Check out John Resig's post on this topic.

查看John Resig关于此主题的帖子。

An even more performant solution would be to set a a longer interval that detects if you are close to the bottom or top of the page. That way, you wouldn't even have to use $(window).scroll()

更高效的解决方案是设置更长的间隔,以检测您是否接近页面的底部或顶部。这样,你甚至不必使用$(window).scroll()

#3


2  

Making your function a little more efficient.

使您的功能更有效。

Just check if the style attribute is present/absent before removing/adding the styles.

在删除/添加样式之前,只需检查样式属性是否存在。

$(window).scroll(function () {
    var headerBottom = 165;
    var fcHeight = $("#pnlMainNavContainer").height();

    var ScrollTop = $(window).scrollTop();
    if (ScrollTop > headerBottom) {
        if (!$("#AddFieldsContainer").attr("style")) {
            $("#HeaderContentBuffer").attr("style", "margin-top:" + (fcHeight) + "px;");
            $("#AddFieldsContainer").attr("style", "position:fixed;width:320px;top:70px;left:41px;");
        }
    } else {
        if ($("#AddFieldsContainer").attr("style")) {
            $("#HeaderContentBuffer").attr("style", "margin-top: 0px;");
            $("#AddFieldsContainer").removeAttr("style");
        }
    }
});

#4


0  

Set some logic here. You actually need to set atts once on up, and once on down. So:

在这里设置一些逻辑。实际上你需要设置一次up,一次打开。所以:

var checker = true;
$(window).scroll(function () {

    .......

    if (ScrollTop > headerBottom && checker == true) {
        $("#HeaderContentBuffer").attr("style", "margin-top:" + (fcHeight) + "px;");
        $("#AddFieldsContainer").attr("style", "position:fixed;width:320px;top:70px;left:41px;");
        checker == false;
    } else if (ScrollTop < headerBottom && checker == false) {
        $("#HeaderContentBuffer").attr("style", "margin-top: 0px;");
        $("#AddFieldsContainer").removeAttr("style");
        checker == true;
    }   
});