当position:sticky被触发时的事件

Ale*_*ust 33 html javascript css jquery position

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

它运行良好,远远优于之前的JavaScript替代方案(例子),但据我所知,当触发事件时不会触发任何事件,这意味着当条形图到达页面顶部时我无法执行任何操作,与以前不同解.

stuck当一个元素position: sticky点击页面顶部时,我想添加一个类(例如).有没有办法用JavaScript来听这个?使用jQuery很好.

position: sticky可以在此处找到新使用的演示.

mat*_*ick 26

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

const observer = new IntersectionObserver(callback, {
  rootMargin: '-1px 0px 0px 0px',
  threshold: [1],
});

observer.observe(element);
Run Code Online (Sandbox Code Playgroud)

  • 这个答案不适用于与页面顶部齐平的元素。例如,导航栏。它将始终被视为已激活。 (5认同)
  • 你能举个例子吗?这似乎对我不起作用。 (4认同)
  • 最优雅的答案恕我直言 (3认同)

Sco*_*ard 14

如果有人通过Google来到这里,他们自己的工程师之一就可以使用IntersectionObserver,自定义事件和标记来解决问题:

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

  • 那使用JavaScript.我们需要纯CSS方法. (5认同)
  • 纯 CSS 解决方案会违反 CSS 工作原理的一些基本规则。想象一下,一条规则导致元素被粘住,但随后一些新的“:stuck”选择器更改了 CSS,因此它不再被粘住。你会有一个无限循环。CSS 的架构经过精心设计,可以避免出现此类情况。 (4认同)
  • 这是一个解释不充分的答案,但是,如果用户可以使用仅使用chrome的解决方案,则该链接将很好地回答该问题。 (3认同)
  • @Green 纯 CSS 解决方案会很棒,但还没有。加上 OP 要求提供 JavaScript 解决方案,所以这应该是公认的答案。 (3认同)

vsy*_*ync 8

带有IntersectionObserver的演示(技巧):

// 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)
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
<section>Space</section>
<header>Sticky Header</header>
Run Code Online (Sandbox Code Playgroud)

top值必须为,-1px否则该元素将永远不会与浏览器窗口的顶部相交(因此永远不会触发相交观察器)。

为了解决1px隐藏内容的问题,1px应在边框或粘贴元素的填充处添加额外的空间。

带有老式scroll事件监听器的演示:

  1. 自动检测第一个可滚动父级
  2. 限制滚动事件
  3. 关注点分离的功能组成
  4. 事件回调缓存:(scrollCallback能够根据需要取消绑定)

// 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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
<section>Space</section>
<header>Sticky Header</header>
Run Code Online (Sandbox Code Playgroud)


这是一个使用第一种技术的React组件演示

  • 您可以通过使用“border-top: 1pxolidtransparent”来避免计算函数。 (3认同)
  • 这样做的问题是,如果元素在页面下方的视图之外,也会应用 isSticky。如果您依赖类来显示/隐藏滚动内容,这可能会导致问题和内容闪烁。 (3认同)
  • 好吧,如果你设置 `top: 0` 那么它就永远不会与浏览器窗口的顶部相交。设置“top:-1px”将允许“1px”的交集(尽管“1px”的内容将不可见,因此需要补偿该技巧)。聪明的 (2认同)
  • 我做了一些编辑。我澄清了“技巧”是什么以及为什么需要它。我还向人们澄清了 CSS 的哪些部分是必需的,哪些部分是多余的(为了清楚起见,我正在考虑删除不必要的内容)。最后,将“演示”一词作为链接,可以让人们更轻松地以更直观的方式访问,而不是一直滚动到答案末尾来访问实时演示。所有这些都使得答案对人们更有用。 (2认同)
  • 解决如下:`e.target.classList.toggle('isSticky', e.boundingClientRect.top &lt; 0)` (2认同)

Wil*_*ler 5

目前没有原生解决方案.请参阅定位位置:当前处于"卡住"状态的粘性元素.但是,我有一个CoffeeScript解决方案,可以与本机position: sticky和polyfill一起使用,实现粘性行为.

将"粘性"类添加到要粘贴的元素:

.sticky {
  position: -webkit-sticky;
  position: -moz-sticky;
  position: -ms-sticky;
  position: -o-sticky;
  position: sticky;
  top: 0px;
  z-index: 1;
}
Run Code Online (Sandbox Code Playgroud)

CoffeeScript用于监控"粘性"元素位置,并在处于"粘性"状态时添加"卡住"类:

$ -> new StickyMonitor

class StickyMonitor

  SCROLL_ACTION_DELAY: 50

  constructor: ->
    $(window).scroll @scroll_handler if $('.sticky').length > 0

  scroll_handler: =>
    @scroll_timer ||= setTimeout(@scroll_handler_throttled, @SCROLL_ACTION_DELAY)

  scroll_handler_throttled: =>
    @scroll_timer = null
    @toggle_stuck_state_for_sticky_elements()

  toggle_stuck_state_for_sticky_elements: =>
    $('.sticky').each ->
      $(this).toggleClass('stuck', this.getBoundingClientRect().top - parseInt($(this).css('top')) <= 1)
Run Code Online (Sandbox Code Playgroud)

注意:此代码仅适用于垂直粘性位置.


Tur*_*adg 2

添加 Chrome 后position: sticky,发现它还不够准备,并被降级为 --enable-experimental-webkit-features 标志。保罗·爱尔兰 (Paul Irish)在二月份表示,“atm 功能处于一种奇怪的边缘状态”。

我一直在使用Polyfill,直到它变得令人头疼。它工作得很好,但也有一些极端情况,比如 CORS 问题,并且它会通过对所有 CSS 链接执行 XHR 请求并重新解析浏览器忽略的“position:sticky”声明来减慢页面加载速度。

现在我使用ScrollToFixed,我比StickyJS更喜欢它,因为它不会用包装器弄乱我的布局。