如果您将“ bottom:0”指定为“ sticky”,为什么它会做一些与规范不同的事情?

dam*_*ymq 8 css css-position sticky

当我阅读有关MDN position属性的文章时,这是一个问题。我认为此处sticky描述的行为与实际行为之间存在明显的差异。


根据MDN,固定位置元素将被视为相对位置元素,直到超过指定的阈值为止;当超过阈值时,它们将被视为固定位置元素,直到达到父元素的边界为止(Link)。

粘性定位可以看作是相对定位和固定定位的混合体。粘性定位的元素将被视为相对定位,直到其超过指定的阈值为止,在该点处将其视为固定的,直到其到达其父对象的边界为止。例如...

#one { position: sticky; top: 10px; } 
Run Code Online (Sandbox Code Playgroud)

...将相对位置为id的元素定位到视口滚动之前,以使元素距离顶部小于10个像素。超过该阈值,该元素将从顶部固定为10个像素。

因此,我创建了以下代码并确认了操作。

#one { position: sticky; top: 10px; } 
Run Code Online (Sandbox Code Playgroud)
body {
  margin: 0;
}

.container {
  display: flex;
  flex-direction: column;
}

.container>* {
  width: 100%;
}

header {
  background: #ffa;
  height: 130vh;
}

main {
  background: #faf;
  height: 210vh;
}

footer {
  background: #faa;
  height: 8vh;
  position: sticky;
  bottom: 0;
}

.footer {
  background: #aff;
  height: 100vh;
}
Run Code Online (Sandbox Code Playgroud)

根据MDN文章,此代码“是相对放置元素,直到通过滚动视口使元素的位置离视口底部小于0px为止,并且当它离底部大于0px时成为固定的放置元素“ 我刚在想。

但是,结果是“滚动到固定位置元素,直到通过滚动视口使元素的位置从视口的下端开始小于0px为止,并且当从下视点大于0px时成为相对排列的元素结束”。


为什么指定bottom:0结果与MDN中显示的行为相反?

top: 0指定时,相对位置将在元素未到达bottom: 0视口时应用,而在到达时将应用固定位置。如果bottom: 0指定,则相反。相对位置在元素未到达bottom: 0视口时应用,而固定位置在元素到达视口时应用

我读了CSS3,但是它的机制很难读

Tem*_*fif 12

根据 MDN,固定位置元素被视为相对位置元素,直到超过指定的阈值

这完全是语言问题,因为上面的句子并不意味着元素一定会开始position:relative 然后变得固定。它说直到超过指定的阈值。那么,如果最初我们超过了指定的阈值怎么办?这实际上是你的例子的情况。

换句话说,position:sticky有两种状态。

  1. 它被视为亲戚
  2. 当超过指定的阈值时,它被视为固定

哪个是第一个取决于您的 HTML 结构。

下面是一个基本的例子来说明:

body {
  height:150vh;
  margin:0;
  display:flex;
  flex-direction:column;
  border:2px solid;
  margin:50px;
}

.b {
  margin-top:auto;
  position:sticky;
  bottom:0;
}

.a {
  position:sticky;
  top:0;
}
Run Code Online (Sandbox Code Playgroud)
<div class="a"> 
  I will start relative then I will be fixed
</div>
<div class="b"> 
I will start fixed then I will be relative
</div>
Run Code Online (Sandbox Code Playgroud)

你也可以混合使用。我们开始固定,变得相对,然后再次固定:

body {
  height:250vh;
  margin:0;
  display:flex;
  flex-direction:column;
  border:2px solid;
  margin:50px;
}
body:before,
body:after {
  content:"";
  flex:1;
}

.a {
  position:sticky;
  top:0;
  bottom:0;
}
Run Code Online (Sandbox Code Playgroud)
<div class="a"> 
  I will start fixed then relative then fixed
</div>
Run Code Online (Sandbox Code Playgroud)

正如你在上面的例子中看到的,两个状态都是独立的。如果 的条件position:fixed为真,那么我们有position:fixed,如果不是,那么它是相对的。

我们可以认为浏览器会实现这个伪代码:

on_scroll_event() {
   if(threshold exceeded)
      position <- fixed
   else
      position <- relative
}
Run Code Online (Sandbox Code Playgroud)

为了更准确和完整地了解该机制,您需要考虑 3 个要素。粘性元素(以及 top/bottom/left/right 的值)、粘性元素的包含块和带有滚动框的最近祖先。

  1. 带有滚动框的最近祖先只是具有与可见性不同的溢出的最近祖先,默认情况下它将是视口(正如我在这里解释的:什么是“滚动框”?)。此元素上的滚动将控制粘性行为。
  2. 粘性元素的包含块与相对元素ref的包含块相同

左/上/下/右是相对于滚动框计算的,包含块将定义粘性元素的限制。

下面是一个例子来说明:

on_scroll_event() {
   if(threshold exceeded)
      position <- fixed
   else
      position <- relative
}
Run Code Online (Sandbox Code Playgroud)
body {
 margin:0;
}
.wrapper {
  width:300px;
  height:150px;
  border:2px solid red;
  overflow:auto;
}

.parent {
   height:200%;
   margin:100% 0;
   border:2px solid;
}

.sticky {
  position:sticky;
  display:inline-block;
  margin:auto;
  top:20px;
  background:red;
}
.non-sticky {
  display:inline-block;
  background:blue;
}
Run Code Online (Sandbox Code Playgroud)

最初我们的元素是隐藏的,这是合乎逻辑的,因为它不能超出其包含块(其限制)。一旦我们开始滚动,我们将看到我们的粘性元素和相关元素的行为完全相同。当我们有一个距离20px的粘性元件和我们达到阈值的滚动框的上边缘之间,我们开始具有position:fixed直到我们在底部再次到达包含块的限制(即,我们没有更多的有粘性行为空间)

现在让我们用底部替换顶部

<div class="wrapper"><!-- our scrolling box -->
  <div class="parent"><!-- containing block -->
    <div class="sticky">I am sticky</div>
    <div class="non-sticky">I am the relative position</div>
  </div>
</div>
Run Code Online (Sandbox Code Playgroud)
body {
 margin:0;
}
.wrapper {
  width:300px;
  height:150px;
  border:2px solid red;
  overflow:auto;
}

.parent {
   height:200%;
   margin:100% 0;
   border:2px solid;
}

.sticky {
  position:sticky;
  display:inline-block;
  margin:auto;
  bottom:20px;
  background:red;
}
.non-sticky {
  display:inline-block;
  background:blue;
}
Run Code Online (Sandbox Code Playgroud)

什么都不会发生,因为当20px元素和滚动框的底部边缘之间存在距离时,粘性元素已经接触到包含块的顶部边缘并且它不能出去。

让我们在之前添加一个元素:

<div class="wrapper"><!-- our scrolling box -->
  <div class="parent"><!-- containing block -->
    <div class="sticky">I am sticky</div>
    <div class="non-sticky">I am the relative position</div>
  </div>
</div>
Run Code Online (Sandbox Code Playgroud)
body {
 margin:0;
}
.wrapper {
  width:300px;
  height:150px;
  border:2px solid red;
  overflow:auto;
}

.parent {
   height:200%;
   margin:100% 0;
   border:2px solid;
}

.sticky {
  position:sticky;
  display:inline-block;
  margin:auto;
  bottom:20px;
  background:red;
}
.non-sticky {
  display:inline-block;
  background:blue;
}

.elem {
  height:50px;
  width:100%;
  background:green;
}
Run Code Online (Sandbox Code Playgroud)

现在我们已经创造50px了一个具有粘性行为的空间。让我们添加顶部和底部:

<div class="wrapper"><!-- our scrolling box -->
  <div class="parent"><!-- containing block -->
  <div class="elem">elemen before</div>
    <div class="sticky">I am sticky</div>
    <div class="non-sticky">I am the relative position</div>
  </div>
</div>
Run Code Online (Sandbox Code Playgroud)
body {
 margin:0;
}
.wrapper {
  width:300px;
  height:150px;
  border:2px solid red;
  overflow:auto;
}

.parent {
   height:200%;
   margin:100% 0;
   border:2px solid;
}

.sticky {
  position:sticky;
  display:inline-block;
  margin:auto;
  bottom:20px;
  top:20px;
  background:red;
}
.non-sticky {
  display:inline-block;
  background:blue;
}

.elem {
  height:50px;
  width:100%;
  background:green;
}
Run Code Online (Sandbox Code Playgroud)

现在我们有顶部和底部的行为,逻辑可以恢复如下:

on_scroll_event() {
    if( top_sticky!=auto && distance_top_sticky_top_scrolling_box <20px && distance_bottom_sticky_bottom_containing_block >0) {
          position <- fixed
     } else if(bottom_sticky!=auto && distance_bottom_sticky_bottom_scrolling_box <20px && distance_top_sticky_top_containing_block >0) {
        position <- fixed
     } else (same for left) {
        position <- fixed
     } else (same for right) {
        position <- fixed
     } else {
        position <- relative
     }
}
Run Code Online (Sandbox Code Playgroud)


Sal*_*n A 7

规范是难以所以这里明白的是我尝试基于解释他们MDN。先说几个定义:

  • 粘性元素 - 一个元素 position: sticky
  • 包含块 - 粘性元素的父级
  • 流根 - 让我们说它意味着视口

一个粘性元素position: sticky; top: 100px;的位置如下:

  • 按正常流程定位
  • 并且它的上边缘会与流根的上边缘保持至少 100px 的距离
  • 并且它的底边不能低于包含块的底边

以下示例显示了这些规则的运作方式:

body { font: medium sans-serif; text-align: center; }
body::after { content: ""; position: fixed; top: 100px; left: 0; right: 0; border: 1px solid #F00; }
header, footer { height: 75vh; background-color: #EEE; }
.containing-block { border-bottom: 2px solid #FA0; background: #DEF; }
.containing-block::after { content: ""; display: block; height: 100vh; }
.before-sticky { border-bottom: 2px solid #080; padding-top: 50px; }
.after-sticky { border-top: 2px solid #080; padding-bottom: 50px; }
.sticky { position: sticky; top: 100px; padding-top: 20px; padding-bottom: 20px; background-color: #CCC; }
Run Code Online (Sandbox Code Playgroud)
<header>header</header>
<div class="containing-block">
  <div class="before-sticky">content before sticky</div>
  <div class="sticky">top sticky</div>
  <div class="after-sticky">content after sticky</div>
</div>
<footer>footer</footer>
Run Code Online (Sandbox Code Playgroud)

同样,粘性元素position: sticky; bottom: 100px;的定位如下:

  • 按正常流程定位
  • 并且它的底部边缘会与流根底部边缘保持至少 100px 的距离
  • 并且它的顶边不能超过包含块的顶边

body { font: medium sans-serif; text-align: center; }
body::after { content: ""; position: fixed; bottom: 100px; left: 0; right: 0; border: 1px solid #F00; }
header, footer { height: 75vh; background-color: #EEE; }
.containing-block { border-top: 2px solid #FA0; background: #DEF; }
.containing-block::before { content: ""; display: block; height: 100vh; }
.before-sticky { border-bottom: 2px solid #080; padding-top: 50px; }
.after-sticky { border-top: 2px solid #080; padding-bottom: 50px; }
.sticky { position: sticky; bottom: 100px; padding-top: 20px; padding-bottom: 20px; background-color: #CCC; }
Run Code Online (Sandbox Code Playgroud)
<header>header</header>
<div class="containing-block">
  <div class="before-sticky">content before sticky</div>
  <div class="sticky">bottom sticky</div>
  <div class="after-sticky">content after sticky</div>
</div>
<footer>footer</footer>
Run Code Online (Sandbox Code Playgroud)

我希望这是足够简单的解释。