javascript - 位置 :sticky is triggered 时检测的事件

标签 javascript jquery html css position

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

它运行良好,远远优于以前的 JavaScript 替代方案 ( example ),但据我所知,当它被触发时没有事件被触发,这意味着当栏到达页面顶部时我无法做任何事情,与之前的解决方案不同。

我想在带有 position: sticky 的元素到达页面顶部时添加一个类(例如 stuck)。有没有办法用 JavaScript 来监听这个?使用 jQuery 很好。

最佳答案

DemoIntersectionObserver (使用技巧):

// 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)
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;
}
<section>Space</section>
<header>Sticky Header</header>

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

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

💡 或者,如果您希望保持 CSS 不变 (top:0),则可以在交点观察者级别应用“校正”,方法是添加设置 rootMargin: '-1px 0px 0px 0px'(如 @mattrick 在他的回答中所示)

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

  1. auto-detecting first scrollable parent
  2. Throttling the scroll event
  3. 关注点分离的功能组合
  4. 事件回调缓存:scrollCallback(以便能够在需要时解除绑定(bind))

// 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);
        }
    }
}
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;
}
<section>Space</section>
<header>Sticky Header</header>


这是一个React component demo它使用第一种技术

关于javascript - 位置 :sticky is triggered 时检测的事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16302483/

相关文章:

javascript - 编写 JQuery/Javascript 淡入淡出函数

JQuery 如何选择 ul 中没有特定类的所有 li 项

html - 如何将第一个表格列设置为粗体(CSS)

iphone - 如何在 Objective-C 中解析 HTML 响应?

html - 当我使用 json 数据时,Google Chart 是空白的

javascript - 动态脚本加载同步

php - 使用 json_encode 将数组从 PHP 传递到 JavaScript 显示空值

使用 String 的 DefineProperty 的 Javascript getter 返回 null

javascript - 复选框列表末尾的 jQuery 验证插件错误消息

javascript - 单击按钮时隐藏元素时出现问题