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

时间:2011-09-12 18:15:00

标签: jquery scroll

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

在尝试更新之前检查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");
    }
});

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

5 个答案:

答案 0 :(得分:58)

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

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

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");
    }
}

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


这是一个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);

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

您的代码将按照以下方式实现:

$(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");
    }
});

答案 1 :(得分:11)

我发现这种方法对$(window).scroll()

更有效
var userScrolled = false;

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

setInterval(function() {
  if (userScrolled) {

    //Do stuff


    userScrolled = false;
  }
}, 50);

在此主题上查看John Resig's post

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

答案 2 :(得分:2)

让您的功能更有效率。

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

$(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");
        }
    }
});

答案 3 :(得分:0)

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

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;
    }   
});

答案 4 :(得分:0)

以下答案不使用jQuery,但是使用现代浏览器功能可以达到相同的结果。 OP还询问是否有其他更有效的方法。与使用jQuery甚至使用onScroll事件侦听器的普通JavaScript解决方案相比,以下方法的性能要高得多。

我们将结合使用2件很棒的东西:

  • position: sticky' CSS属性
  • IntersectionObserver API

要获得header卡住的效果,我们可以使用position: sticky; top: 0px; CSS属性的组合。这将允许元素随页面滚动并在到达页面顶部时卡住(就像fixed一样。)

要更改停留元素或任何其他元素的样式,我们可以使用IntersectionObserver API-允许我们观察2个元素的交集处的变化。

为达到我们想要的效果,我们将添加一个sentinel元素,当header元素到达顶部时,该元素将用作指示符。换句话说:

  • 当前哨不与视口相交时,我们的header位于顶部
  • 前哨与视口相交时,我们的header不在顶部

有了这两个条件,我们可以对header或其他元素应用任何必要的样式。

const sentinelEl = document.getElementById('sentinel')
const headerEl = document.getElementById('header')
const stuckClass = "stuck"

const handler = (entries) => {
  if (headerEl) {
    if (!entries[0].isIntersecting) {
      headerEl.classList.add(stuckClass)
    } else {
      headerEl.classList.remove(stuckClass)
    }
  }
}

const observer = new window.IntersectionObserver(handler)
observer.observe(sentinelEl)
html,
body {
  font-family: Arial;
  padding: 0;
  margin: 0;
}

.topContent,
.pageContent,
#header {
  padding: 10px;
}

#header {
  position: sticky;
  top: 0px;
  transition: all 0.2s linear;
}

#header.stuck {
  background-color: red;
  color: white;
  
}

.topContent {
  height: 50px;
  background-color: gray;
}

.pageContent {
  height: 600px;
  background-color: lightgray;
}
<div class="topContent">
  Content above our sticky element
</div>
<div id="sentinel"></div>
<header id="header">
  Sticky header
</header>
<div class="pageContent">
  The rest of the page
</div>