首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >事件以检测何时触发位置:粘滞。

事件以检测何时触发位置:粘滞。
EN

Stack Overflow用户
提问于 2013-04-30 14:34:35
回答 11查看 79.3K关注 0票数 101

我正在使用新的position: sticky (信息)创建一个类似iOS的内容列表。

它工作得很好,比以前的JavaScript alternative (示例)要好得多,但是据我所知,当它被触发时不会触发任何事件,这意味着我不能在条形图到达页面顶部时做任何事情,这与以前的解决方案不同。

我想要添加一个类(例如,当带有** stuck**) 的元素到达页面顶部时,)。有办法用JavaScript听这个吗?jQuery的用法很好.

EN

回答 11

Stack Overflow用户

发布于 2019-09-18 11:17:24

演示IntersectionObserver (使用技巧):

代码语言:javascript
复制
// get the sticky element
const stickyElm = document.querySelector('header')

const observer = new IntersectionObserver( 
  ([e]) => e.target.classList.toggle('isSticky', e.intersectionRatio < 1),
  {threshold: [1]}
);

observer.observe(stickyElm)
代码语言:javascript
复制
body{ height: 200vh; font:20px Arial; }

section{
  background: lightblue;
  padding: 2em 1em;
}

header{
  position: sticky;
  top: -1px;                       /* ➜ the trick */

  padding: 1em;
  padding-top: calc(1em + 1px);    /* ➜ compensate for the trick */

  background: salmon;
  transition: .1s;
}

/* styles for when the header is in sticky mode */
header.isSticky{
  font-size: .8em;
  opacity: .5;
}
代码语言:javascript
复制
<section>Space</section>
<header>Sticky Header</header>

top值需要是-1px,否则元素将永远不会与浏览器窗口顶部相交(因此不会触发交叉观察者)。

为了对抗隐藏内容的1px,应该在粘性元素的边框或填充中添加一个额外的1px空间。

或者,如果您希望保持CSS原样(top:0),则可以通过添加设置rootMargin: '-1px 0px 0px 0px' (如@mattrick在其答复中所示),在交叉口观察者级别应用“更正”。

使用老式的scroll事件侦听器演示:

  1. 自动检测第一个可滚动父级
  2. 控制滚动事件
  3. 关注点的功能组合.分离
  4. 事件回调缓存:scrollCallback (如果需要可以解除绑定)

代码语言:javascript
复制
// get the sticky element
const stickyElm = document.querySelector('header');

// get the first parent element which is scrollable
const stickyElmScrollableParent = getScrollParent(stickyElm);

// save the original offsetTop. when this changes, it means stickiness has begun.
stickyElm._originalOffsetTop = stickyElm.offsetTop;


// compare previous scrollTop to current one
const detectStickiness = (elm, cb) => () => cb & cb(elm.offsetTop != elm._originalOffsetTop)

// Act if sticky or not
const onSticky = isSticky => {
   console.clear()
   console.log(isSticky)
   
   stickyElm.classList.toggle('isSticky', isSticky)
}

// bind a scroll event listener on the scrollable parent (whatever it is)
// in this exmaple I am throttling the "scroll" event for performance reasons.
// I also use functional composition to diffrentiate between the detection function and
// the function which acts uppon the detected information (stickiness)

const scrollCallback = throttle(detectStickiness(stickyElm, onSticky), 100)
stickyElmScrollableParent.addEventListener('scroll', scrollCallback)



// OPTIONAL CODE BELOW ///////////////////

// find-first-scrollable-parent
// Credit: https://stackoverflow.com/a/42543908/104380
function getScrollParent(element, includeHidden) {
    var style = getComputedStyle(element),
        excludeStaticParent = style.position === "absolute",
        overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/;

    if (style.position !== "fixed") 
      for (var parent = element; (parent = parent.parentElement); ){
          style = getComputedStyle(parent);
          if (excludeStaticParent && style.position === "static") 
              continue;
          if (overflowRegex.test(style.overflow + style.overflowY + style.overflowX)) 
            return parent;
      }

    return window
}

// Throttle
// Credit: https://jsfiddle.net/jonathansampson/m7G64
function throttle (callback, limit) {
    var wait = false;                  // Initially, we're not waiting
    return function () {               // We return a throttled function
        if (!wait) {                   // If we're not waiting
            callback.call();           // Execute users function
            wait = true;               // Prevent future invocations
            setTimeout(function () {   // After a period of time
                wait = false;          // And allow future invocations
            }, limit);
        }
    }
}
代码语言:javascript
复制
header{
  position: sticky;
  top: 0;

  /* not important styles */
  background: salmon;
  padding: 1em;
  transition: .1s;
}

header.isSticky{
  /* styles for when the header is in sticky mode */
  font-size: .8em;
  opacity: .5;
}

/* not important styles*/

body{ height: 200vh; font:20px Arial; }

section{
  background: lightblue;
  padding: 2em 1em;
}
代码语言:javascript
复制
<section>Space</section>
<header>Sticky Header</header>

下面是一个React组件演示,它使用了第一种技术

票数 125
EN

Stack Overflow用户

发布于 2020-04-09 06:19:52

我发现了一个与@vsync的答案有点相似的解决方案,但它不需要添加到样式表中所需的"hack“。您可以简单地更改IntersectionObserver的边界,以避免将元素本身移出视图端口之外:

代码语言:javascript
复制
const observer = new IntersectionObserver(callback, {
  rootMargin: '-1px 0px 0px 0px',
  threshold: [1],
});

observer.observe(element);
票数 45
EN

Stack Overflow用户

发布于 2017-10-02 18:00:10

如果有人通过谷歌来到这里,那么他们自己的一名工程师就有了一个使用IntersectionObserver、自定义事件和哨兵的解决方案:

https://developers.google.com/web/updates/2017/09/sticky-headers

票数 26
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16302483

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档