如何在没有 JavaScript 的情况下使元素相对于窗口具有粘性(如果可能)?

Jos*_*e A 5 html javascript css

我一直爱着position: sticky。它无需求助于 JavaScript 即可解决大部分(如果不是全部)问题。但是,我已经碰壁了。我需要制作一个嵌套在几个元素内的元素<div>以使其具有粘性。我们知道是和position: sticky的混合,因此它将锚定到它的第一个父级。position: relativeposition: fixed

来自MDN

元素按照文档的正常流程定位,然后相对于其最近的滚动祖先和包含块(最近的块级祖先)进行偏移

在这种情况下,我想让标题相对于窗口而不是容器具有粘性。HTML 使我很难在嵌套之外重组它<div>

如果没有 JavaScript,这可能吗?

这是代码:

<div class="attendance">
<!-- Here's the header I want to make sticky to the window, and not to div.attendance-->
    <header class="text-center sticky">Monday 11/22/2019</header>
<!-- Header above -->
    <div class="date-config">
        <div class="form-group">
            <input type="checkbox" id="workable" /> No Work<br />
        </div>

        <div class="form-group">
            <label for="notes">Notes:</label>
            <textarea id="notes" class="form-control"></textarea>
        </div>
        <label for="markall">Mark all as>
        <select id="markall" class="form-control">
            <option></option>
            <option>Absent</option>
            <option>Present</option>
        </select>
    </div>

    <div class="student-attendance">
      Hello :)   
    </div>

</div>
Run Code Online (Sandbox Code Playgroud)

有任何想法吗?

PS:我找到了这个,但它使用JavaScript。

编辑: 这是一个糟糕但有效的示例(注意!它是西班牙语 - 查找日期!它们不会粘在窗口上!)。

Jos*_*e A 3

好的!首先,我想道歉,因为如果不渲染 HTML 就无法回答这个问题。幸运的是,我已经找到了解决方案。

TL;DR在这种情况下,不,您需要 JavaScript。您需要translateY在元素中实现转换才能实现此目的。我不知道问题是否在于父元素具有转换属性,并且它导致了此错误,或者还有其他原因导致了该问题。

解释

我目前正在使用一个名为tiny slider的轮播 JS 库。我显示表单元素而不是图像(构建响应式表格;当我尝试使用 CSS 网格时遇到问题)。到目前为止,一切都很好。sticky当我想设置日期标题时,问题就开始了。

我采用了现代的设置方法position:sticky,但这不起作用。这些元件会堵塞在某个位置,并且不会移动或粘住。我开始在线研究(最终提出了同样的问题)以及 HTML 本身。我确实发现有很多parent<div>是由tiny-slider创建的。我的理论是它与其中一位父母产生了依恋。

position:fixed因此,我决定尝试一下结合事件的老策略scroll。但是,这没有用。回到网上并 Google-Fuing 一下,似乎有一个老错误[1] [2] [3],每当将翻译应用于其中一个父项时,就会创建一个根外容器,并且position:fixed不会创建该错误。按预期工作。

我有一种预感,这可能是粘性不起作用的原因之一,但根据这个答案,似乎并非如此。

我思考了一段时间,最后决定使用transformCSS 属性translateY。我在浏览器中做了一个小实验,成功了!

因此,我最终实现了scrolleventListener 并侦听标头的父级位置,并应用 getBoundingClientRect() 来获取偏移量。如果我将它应用到元素本身,它就会给我通过 CSS 应用的翻译位置。

我怀疑这可能是移动浏览器的性能瓶颈。因此,我检查了变换函数是否在 a 内部被调用requestAnimationFrame,并且它已经应用了will-changeCSS样式表中的属性。

我在 Google Chrome 中以 4 倍 CPU 减速的方式运行代码,并获得了良好的结果

这是我得到的结果函数(其中 elemsToFixed 是所有<header>元素,阈值是顶部偏移量,因此它不会与导航栏冲突):

export function fixedHeaderScroll(elemsToFixed: HTMLHeadingElement[], threshold: number) {
  if (!elemsToFixed || elemsToFixed.length === 0) {
    console.error("elemsToFixed can't be null or empty");
    return;
  }
  console.log('Total elems', elemsToFixed.length);
  // We assume that all of the elements are on the same height.
  const firstEl = elemsToFixed[0];
  let propSet = false;
  window.addEventListener('scroll', (e) => {
    window.requestAnimationFrame(() => {
      const top = firstEl.parentElement!.getBoundingClientRect().top;
      if (top > threshold) {
        if (!propSet) return;
        propSet = false;
        setElemsFixed(elemsToFixed, top, threshold, false);
        return;
      }
      propSet = true;
      setElemsFixed(elemsToFixed, top, threshold);
    });
  });
}

function setElemsFixed(elemsToFixed: HTMLHeadingElement[], top: number,
                       threshold: number, setFixed = true) {
  console.log('SetElemsFixed is', setFixed);
  elemsToFixed.forEach((elem) => {
    if (!setFixed) {
      elem.removeAttribute('style');
      return;
    }

    elem.style.transform = `translateY(${(top * -1)}px)`;
  });
}
Run Code Online (Sandbox Code Playgroud)

下图显示 CPU 减慢了 4 倍,并且样式(具有 26 个元素)的计算约为 29.4 毫秒(这太棒了!)。在 Windows 和 i7 4700MQ 处理器上使用 Chrome 70。

chrome 分析速度减慢 4 倍