首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >CSS 定位和层叠上下文

CSS 定位和层叠上下文

作者头像
Cellinlab
发布2023-05-17 19:18:04
发布2023-05-17 19:18:04
1.5K00
代码可运行
举报
文章被收录于专栏:Cellinlab's BlogCellinlab's Blog
运行总次数:0
代码可运行

position 属性的初始值是 static。如果把它改成其他值,就说元素就被定位了。而如果元素使用了静态定位,那么就说它未被定位。

布局方法是用各种操作来控制文档流的行为。定位则不同:它将元素彻底从文档流中移走。它允许将元素放在屏幕的任意位置。还可以将一个元素放在另一个元素的前面或后面,彼此重叠。

# 固定定位

给一个元素设置 position: fixed 就能将元素放在视口的任意位置。这需要搭配四种属性一起使用:top、right、bottom 和 left。设置这四个值还隐式地定义了元素的宽高。

# 用固定定位创建一个模态框

代码语言:javascript
代码运行次数:0
运行
复制
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    body {
      font-family: Helvetica, Arial, sans-serif;
      min-height: 200vh;
      margin: 0;
    }

    button {
      padding: .5em .7em;
      border: 1px solid #8d8d8d;
      background-color: white;
      font-size: 1em;
    }

    .top-banner {
      padding: 1em 0;
      background-color: #ffd698;
    }

    .top-banner-inner {
      width: 80%;
      max-width: 1000px;
      margin: 0 auto;
    }

    .modal {
      display: none;
    }

    .modal-backdrop {
      position: fixed;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      background-color: rgba(0, 0, 0, 0.5);
    }

    .modal-body {
      position: fixed;
      top: 3em;
      bottom: 3em;
      right: 20%;
      left: 20%;
      padding: 2em 3em;
      background-color: white;
      overflow: auto;
    }

    .modal-close {
      cursor: pointer;
    }
  </style>
</head>
<body>
  <header class="top-banner">
    <div class="top-banner-inner">
      <p>Find out what's going on at Wombat Coffee each
        month. Sign up for our newsletter:
        <button id="open">Sign up</button>
      </p>
    </div>
  </header>
  <div class="modal" id="modal">
    <div class="modal-backdrop"></div>
    <div class="modal-body">
      <button class="modal-close" id="close">Close</button>
      <h2>Wombat Newsletter</h2>
      <p>Sign up for our monthly newsletter. No spam.
         We promise!</p>
      <form>
        <p>
          <label for="email">Email address:</label>
          <input type="text" name="email"/>
        </p>
        <p><button type="submit">Submit</button></p>
      </form>
    </div>
  </div>
  <script type="text/javascript">
    var modal = document.getElementById('modal');
    var open = document.getElementById('open');
    var close = document.getElementById('close');
    open.addEventListener('click', function(evt) {
      evt.preventDefault();
      modal.style.display = 'block';
    });
    close.addEventListener('click', function(evt) {
      evt.preventDefault();
      modal.style.display = 'none';
    });
  </script>
</body>
</html>

https://codepen.io/cellinlab/pen/oNpMMxQ

# 控制定位元素的大小

定位一个元素时,不要求指定四个方向的值,可以只指定需要的方向值,然后用 width 和/或 height 来决定它的大小,也可以让元素本身来决定大小。

代码语言:javascript
代码运行次数:0
运行
复制
.fixed {
  position: fixed;
  top: 1em;
  right: 1em;
  width: 20%;
}

这段代码会将元素放在距离视口顶部和右边 1em 的位置,宽度为视口宽度的20%。它省略了 bottom 和 height 属性,元素的高度由自身的内容决定。

因为固定元素从文档流中移除了,所以它不再影响页面其他元素的位置。别的元素会跟随正常文档流,就像固定元素不存在一样。也就是说它们通常会在固定元素下面排列,视觉上被遮挡。

# 绝对定位

固定定位让元素相对视口定位,此时视口被称作元素的包含块(containing block)。

绝对定位的行为也是如此,只是它的包含块不一样。绝对定位不是相对视口,而是相对最近的祖先定位元素。跟固定元素一样,属性 top、right、bottom 和 left 决定了元素的边缘在包含块里的位置。

# 让Close按钮绝对定位

代码语言:javascript
代码运行次数:0
运行
复制
 <html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    body {
      font-family: Helvetica, Arial, sans-serif;
      min-height: 200vh;
      margin: 0;
    }

    button {
      padding: .5em .7em;
      border: 1px solid #8d8d8d;
      background-color: white;
      font-size: 1em;
    }

    .top-banner {
      padding: 1em 0;
      background-color: #ffd698;
    }

    .top-banner-inner {
      width: 80%;
      max-width: 1000px;
      margin: 0 auto;
    }

    .modal {
      display: none;
    }

    .modal-backdrop {
      position: fixed;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      background-color: rgba(0, 0, 0, 0.5);
    }

    .modal-body {
      position: fixed;
      top: 3em;
      bottom: 3em;
      right: 20%;
      left: 20%;
      padding: 2em 3em;
      background-color: white;
      overflow: auto;
    }

    .modal-close {
      position: absolute;
      top: 0.3em;
      right: 0.3em;
      padding: 0.3em;
      cursor: pointer;
    }
  </style>
</head>
<body>
  <header class="top-banner">
    <div class="top-banner-inner">
      <p>Find out what's going on at Wombat Coffee each
        month. Sign up for our newsletter:
        <button id="open">Sign up</button>
      </p>
    </div>
  </header>
  <div class="modal" id="modal">
    <div class="modal-backdrop"></div>
    <div class="modal-body">
      <button class="modal-close" id="close">Close</button>
      <h2>Wombat Newsletter</h2>
      <p>Sign up for our monthly newsletter. No spam.
         We promise!</p>
      <form>
        <p>
          <label for="email">Email address:</label>
          <input type="text" name="email"/>
        </p>
        <p><button type="submit">Submit</button></p>
      </form>
    </div>
  </div>
  <script type="text/javascript">
    var modal = document.getElementById('modal');
    var open = document.getElementById('open');
    var close = document.getElementById('close');
    open.addEventListener('click', function(evt) {
      evt.preventDefault();
      modal.style.display = 'block';
    });
    close.addEventListener('click', function(evt) {
      evt.preventDefault();
      modal.style.display = 'none';
    });
  </script>
</body>
</html>

https://codepen.io/cellinlab/pen/BaJPPWo

通常情况下,包含块是元素的父元素。如果父元素未被定位,那么浏览器会沿着 DOM 树往上找它的祖父、曾祖父,直到找到一个定位元素,用它作为包含块。

如果祖先元素都没有定位,那么绝对定位的元素会基于初始包含块(initial containing block)来定位。初始包含块跟视口一样大,固定在网页的顶部。

# 定位伪元素

text-indent 属性将文字推到右边,溢出元素。它的确切值不重要,只要大于按钮宽度即可。由于text-indent 是继承属性,需要在伪类元素选择器上设为 0,因此 x 便不会缩进。

代码语言:javascript
代码运行次数:0
运行
复制
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    body {
      font-family: Helvetica, Arial, sans-serif;
      min-height: 200vh;
      margin: 0;
    }

    button {
      padding: .5em .7em;
      border: 1px solid #8d8d8d;
      background-color: white;
      font-size: 1em;
    }

    .top-banner {
      padding: 1em 0;
      background-color: #ffd698;
    }

    .top-banner-inner {
      width: 80%;
      max-width: 1000px;
      margin: 0 auto;
    }

    .modal {
      display: none;
    }

    .modal-backdrop {
      position: fixed;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      background-color: rgba(0, 0, 0, 0.5);
    }

    .modal-body {
      position: fixed;
      top: 3em;
      bottom: 3em;
      right: 20%;
      left: 20%;
      padding: 2em 3em;
      background-color: white;
      overflow: auto;
    }

    .modal-close {
      position: absolute;
      top: 0.3em;
      right: 0.3em;
      padding: 0.3em;
      cursor: pointer;
      font-size: 2em;
      height: 1em;
      width: 1em;
      text-indent: 10em; /* 让元素里的文字溢出并隐藏 */
      overflow: hidden;
      border: 0;
    }
    .modal-close::after {
      position: absolute;
      line-height: 0.5;
      top: 0.2em;
      left: 0.1em;
      text-indent: 0;
      content: '\00D7';
    }
  </style>
</head>
<body>
  <header class="top-banner">
    <div class="top-banner-inner">
      <p>Find out what's going on at Wombat Coffee each
        month. Sign up for our newsletter:
        <button id="open">Sign up</button>
      </p>
    </div>
  </header>
  <div class="modal" id="modal">
    <div class="modal-backdrop"></div>
    <div class="modal-body">
      <button class="modal-close" id="close">Close</button>
      <h2>Wombat Newsletter</h2>
      <p>Sign up for our monthly newsletter. No spam.
         We promise!</p>
      <form>
        <p>
          <label for="email">Email address:</label>
          <input type="text" name="email"/>
        </p>
        <p><button type="submit">Submit</button></p>
      </form>
    </div>
  </div>
  <script type="text/javascript">
    var modal = document.getElementById('modal');
    var open = document.getElementById('open');
    var close = document.getElementById('close');
    open.addEventListener('click', function(evt) {
      evt.preventDefault();
      modal.style.display = 'block';
    });
    close.addEventListener('click', function(evt) {
      evt.preventDefault();
      modal.style.display = 'none';
    });
  </script>
</body>
</html>

https://codepen.io/cellinlab/pen/NWXBBvR

伪类元素现在是绝对定位。因为它表现得像按钮的子元素一样,所以定位的按钮就成为其伪元素的包含块。设置一个较小的 line-height 让伪元素不要太高,用 top 和 left 属性让它在按钮中间定位。

绝对定位是定位类型里的重量级选手。它经常跟 JavaScript 配合,用于弹出菜单、工具提示以及消息盒子。

# 相对定位

当第一次给元素加上 position: relative 的时候,通常看不到页面上有任何视觉改变。相对定位的元素以及它周围的所有元素,都还保持着原来的位置。如果加上 top、right、bottom 和 left 属性,元素就会从原来的位置移走,但是不会改变它周围任何元素的位置。

如图,四个 inline-block 元素,给第二个元素加上三个额外的属性:position: relative、top: 1em、left: 2em,将其从初始位置移走,但是其他元素没有受到影响。它们还是围绕着被移走元素的初始位置,跟随着正常的文档流。

跟固定或者绝对定位不一样,不能用 top、right、bottom 和 left 改变相对定位元素的大小。这些值只能让元素在上、下、左、右方向移动。可以用 top 或者 bottom,但它们不能一起用(bottom 会被忽略)。同理,可以用 left 或 right,但它们也不能一起用(right 会被忽略)。

有时可以用这些属性调整相对元素的位置,把它挤到某个位置,但这只是相对定位的一个冷门用法。更常见的用法是使用 position: relative 给它里面的绝对定位元素创建一个包含块。

# 创建一个下拉菜单

代码语言:javascript
代码运行次数:0
运行
复制
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    body {
      font-family: Helvetica, Arial, sans-serif;
      min-height: 200vh;
      margin: 0;
    }

    button {
      padding: .5em .7em;
      border: 1px solid #8d8d8d;
      background-color: white;
      font-size: 1em;
    }

    .top-banner {
      padding: 1em 0;
      background-color: #ffd698;
    }

    .top-banner-inner {
      width: 80%;
      max-width: 1000px;
      margin: 0 auto;
    }

    .modal {
      display: none;
    }

    .modal-backdrop {
      position: fixed;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      background-color: rgba(0, 0, 0, 0.5);
    }

    .modal-body {
      position: fixed;
      top: 3em;
      bottom: 3em;
      right: 20%;
      left: 20%;
      padding: 2em 3em;
      background-color: white;
      overflow: auto;
    }

    .modal-close {
      position: absolute;
      top: 0.3em;
      right: 0.3em;
      padding: 0.3em;
      cursor: pointer;
      font-size: 2em;
      height: 1em;
      width: 1em;
      text-indent: 10em; /* 让元素里的文字溢出并隐藏 */
      overflow: hidden;
      border: 0;
    }
    .modal-close::after {
      position: absolute;
      line-height: 0.5;
      top: 0.2em;
      left: 0.1em;
      text-indent: 0;
      content: '\00D7';
    }
    .container {
      width: 80%;
      max-width: 1000px;
      margin: 1em auto;
    }
    .dropdown {
      display: inline-block;
      position: relative;
    }
    .dropdown-label {
      padding: .5em 1.5em;
      border: 1px solid #ccc;
      background-color: #eee;
    }
    .dropdown-menu {
      display: none;
      position: absolute;
      left: 0;
      top: 2.1em;
      min-width: 100%;
      background-color: #eee;
    }
    .dropdown:hover .dropdown-menu {
      display: block;
    }
    .submenu {
      padding-left: 0;
      margin: 0;
      list-style-type: none;
      border: 1px solid #999;
    }
    .submenu > li > a {
      display: block;
      padding: .5em 1.5em;
      background-color: #eee;
      color: #369;
      text-decoration: none;
    }
    .submenu > li > a:hover {
      background-color: #fff;
    }
  </style>
</head>
<body>
  <header class="top-banner">
    <div class="top-banner-inner">
      <p>Find out what's going on at Wombat Coffee each
        month. Sign up for our newsletter:
        <button id="open">Sign up</button>
      </p>
    </div>
  </header>
  <div class="modal" id="modal">
    <div class="modal-backdrop"></div>
    <div class="modal-body">
      <button class="modal-close" id="close">Close</button>
      <h2>Wombat Newsletter</h2>
      <p>Sign up for our monthly newsletter. No spam.
         We promise!</p>
      <form>
        <p>
          <label for="email">Email address:</label>
          <input type="text" name="email"/>
        </p>
        <p><button type="submit">Submit</button></p>
      </form>
    </div>
  </div>
  <div class="container">
    <nav>
      <div class="dropdown">
        <div class="dropdown-label">Main Menu</div>
        <div class="dropdown-menu">
          <ul class="submenu">
            <li><a href="/">Home</a></li>
            <li><a href="/coffees">Coffees</a></li>
            <li><a href="/brewers">Brewers</a></li>
            <li><a href="/specials">Specials</a></li>
            <li><a href="/about">About us</a></li>
          </ul>
        </div>
      </div>
    </nav>

    <h1>Wombat Coffee Roasters</h1>
  </div>
  <script type="text/javascript">
    var modal = document.getElementById('modal');
    var open = document.getElementById('open');
    var close = document.getElementById('close');
    open.addEventListener('click', function(evt) {
      evt.preventDefault();
      modal.style.display = 'block';
    });
    close.addEventListener('click', function(evt) {
      evt.preventDefault();
      modal.style.display = 'none';
    });
  </script>
</body>
</html>

https://codepen.io/cellinlab/pen/OJzwoJp

# 创建一个 CSS 三角形

代码语言:javascript
代码运行次数:0
运行
复制
.dropdown-label {
  padding: 0.5em 2em 0.5em 1.5em;
  border: 1px solid #ccc;
  background-color: #eee;
}
.dropdown-label::after {
  content: ' ';
  position: absolute;
  right: 1em;
  top: 1em;
  border: 0.3em solid;
  border-color: black transparent transparent;
}
.dropdown:hover .dropdown-label::after {
  top: 0.7em;
  border-color: transparent transparent black;
}
代码语言:javascript
代码运行次数:0
运行
复制
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    body {
      font-family: Helvetica, Arial, sans-serif;
      min-height: 200vh;
      margin: 0;
    }

    button {
      padding: .5em .7em;
      border: 1px solid #8d8d8d;
      background-color: white;
      font-size: 1em;
    }

    .top-banner {
      padding: 1em 0;
      background-color: #ffd698;
    }

    .top-banner-inner {
      width: 80%;
      max-width: 1000px;
      margin: 0 auto;
    }

    .modal {
      display: none;
    }

    .modal-backdrop {
      position: fixed;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      background-color: rgba(0, 0, 0, 0.5);
    }

    .modal-body {
      position: fixed;
      top: 3em;
      bottom: 3em;
      right: 20%;
      left: 20%;
      padding: 2em 3em;
      background-color: white;
      overflow: auto;
    }

    .modal-close {
      position: absolute;
      top: 0.3em;
      right: 0.3em;
      padding: 0.3em;
      cursor: pointer;
      font-size: 2em;
      height: 1em;
      width: 1em;
      text-indent: 10em; /* 让元素里的文字溢出并隐藏 */
      overflow: hidden;
      border: 0;
    }
    .modal-close::after {
      position: absolute;
      line-height: 0.5;
      top: 0.2em;
      left: 0.1em;
      text-indent: 0;
      content: '\00D7';
    }
    .container {
      width: 80%;
      max-width: 1000px;
      margin: 1em auto;
    }
    .dropdown {
      display: inline-block;
      position: relative;
    }
    .dropdown-label {
      padding: 0.5em 2em 0.5em 1.5em;
      border: 1px solid #ccc;
      background-color: #eee;
    }
    .dropdown-label::after {
      content: ' ';
      position: absolute;
      right: 1em;
      top: 1em;
      border: 0.3em solid;
      border-color: black transparent transparent;
    }
    .dropdown:hover .dropdown-label::after {
      top: 0.7em;
      border-color: transparent transparent black;
    }
    .dropdown-menu {
      display: none;
      position: absolute;
      left: 0;
      top: 2.1em;
      min-width: 100%;
      background-color: #eee;
    }
    .dropdown:hover .dropdown-menu {
      display: block;
    }
    .submenu {
      padding-left: 0;
      margin: 0;
      list-style-type: none;
      border: 1px solid #999;
    }
    .submenu > li > a {
      display: block;
      padding: .5em 1.5em;
      background-color: #eee;
      color: #369;
      text-decoration: none;
    }
    .submenu > li > a:hover {
      background-color: #fff;
    }
  </style>
</head>
<body>
  <header class="top-banner">
    <div class="top-banner-inner">
      <p>Find out what's going on at Wombat Coffee each
        month. Sign up for our newsletter:
        <button id="open">Sign up</button>
      </p>
    </div>
  </header>
  <div class="modal" id="modal">
    <div class="modal-backdrop"></div>
    <div class="modal-body">
      <button class="modal-close" id="close">Close</button>
      <h2>Wombat Newsletter</h2>
      <p>Sign up for our monthly newsletter. No spam.
         We promise!</p>
      <form>
        <p>
          <label for="email">Email address:</label>
          <input type="text" name="email"/>
        </p>
        <p><button type="submit">Submit</button></p>
      </form>
    </div>
  </div>
  <div class="container">
    <nav>
      <div class="dropdown">
        <div class="dropdown-label">Main Menu</div>
        <div class="dropdown-menu">
          <ul class="submenu">
            <li><a href="/">Home</a></li>
            <li><a href="/coffees">Coffees</a></li>
            <li><a href="/brewers">Brewers</a></li>
            <li><a href="/specials">Specials</a></li>
            <li><a href="/about">About us</a></li>
          </ul>
        </div>
      </div>
    </nav>

    <h1>Wombat Coffee Roasters</h1>
  </div>
  <script type="text/javascript">
    var modal = document.getElementById('modal');
    var open = document.getElementById('open');
    var close = document.getElementById('close');
    open.addEventListener('click', function(evt) {
      evt.preventDefault();
      modal.style.display = 'block';
    });
    close.addEventListener('click', function(evt) {
      evt.preventDefault();
      modal.style.display = 'none';
    });
  </script>
</body>
</html>

https://codepen.io/cellinlab/pen/ZEvjMLV

# 层叠上下文和 z-index

在同一页面定位多个元素时,可能会遇到两个不同定位的元素重叠的现象。

# 理解渲染过程和层叠顺序

浏览器将 HTML 解析为 DOM 的同时还创建了另一个树形结构,叫作渲染树(render tree)。它代表了每个元素的视觉样式和位置。同时还决定浏览器绘制元素的顺序。顺序很重要,因为如果元素刚好重叠,后绘制的元素就会出现在先绘制的元素前面。

通常情况下(使用定位之前),元素在 HTML 里出现的顺序决定了绘制的顺序。

代码语言:javascript
代码运行次数:0
运行
复制
<div>one</div>
<div>two</div>
<div>three</div>

定位元素时,这种行为会改变。浏览器会先绘制所有非定位的元素,然后绘制定位元素。默认情况下,所有的定位元素会出现在非定位元素前面。

如图所示,给前两个元素加了 position: relative,它们就绘制到了前面,覆盖了静态定位的第三个元素,尽管元素在 HTML 里的顺序并未改变。

注意,在定位元素里,第二个定位元素还是出现在第一个定位元素前面。定位元素会被放到前面,但是基于源码的层叠关系并没有改变。

通常情况下,模态框要放在网页内容的最后,</body> 关闭标签之前。大多数构建模态框的 JavaScript 库会自动这样做。因为模态框使用固定定位,所以不必关心它的标记出现在哪里,它会一直定位到屏幕中间。

改变固定定位元素的标记位置不会产生不好的影响,但是对相对定位或绝对定位的元素来说,通常无法用改变标记位置的方法解决层叠问题。相对定位依赖于文档流,绝对定位元素依赖于它的定位祖先节点。这时候需要用 z-index 属性来控制它们的层叠行为。

# 用 z-index 控制层叠顺序

z-index属性的值可以是任意整数(正负都行)。z 表示的是笛卡儿 x-y-z 坐标系里的深度方向。拥有较高 z-index 的元素出现在拥有较低 z-index 的元素前面。拥有负数 z-index 的元素出现在静态元素后面。

使用 z-index 是解决网页层叠问题的第二个方法。该方法不要求修改 HTML 的结构。

z-index 的行为很好理解,但是使用它时要注意两个小陷阱。第一,z-index 只在定位元素上生效,不能用它控制静态元素。第二,给一个定位元素加上 z-index 可以创建层叠上下文。

# 理解层叠上下文

一个层叠上下文包含一个元素或者由浏览器一起绘制的一组元素。其中一个元素会作为层叠上下文的根,比如给一个定位元素加上 z-index 的时候,它就变成了一个新的层叠上下文的根。所有后代元素就是这个层叠上下文的一部分。

不要将层叠上下文跟 BFC 弄混了,它们是两个独立的概念,尽管不一定互斥。层叠上下文负责决定哪些元素出现在另一些元素前面,而 BFC 负责处理文档流,以及元素是否会重叠。

实际上将层叠上下文里的所有元素一起绘制会造成严重的后果:层叠上下文之外的元素无法叠放在层叠上下文内的两个元素之间。换句话说,如果一个元素叠放在一个层叠上下文前面,那么层叠上下文里没有元素可以被拉到该元素前面。同理,如果一个元素被放在层叠上下文后面,层叠上下文里没有元素能出现在该元素后面。

叠放在第二个盒子后面的第一个盒子是一个层叠上下文的根。因此,虽然它的z-index值很高,但是它内部的绝对定位元素不会跑到第二个盒子前面。

代码语言:javascript
代码运行次数:0
运行
复制
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    body {
      margin: 40px;
    }
    .box {
      display: inline-block;
      width: 200px;
      line-height: 200px;
      text-align: center;
      border: 2px solid black;
      background-color: #ea5;
      margin-left: -60px;
      vertical-align: top;
    }
    .one {
      margin-left: ;
    }
    .two {
      margin-top: 30px;
    }
    .three {
      margin-top: 60px;
    }
    .positioned {
      position: relative;
      background-color: #5ae;
      z-index: ;
    }
    .absolute {
      position: absolute;
      top: 1em;
      right: 1em;
      height: 2em;
      background-color: #fff;
      border: 2px dashed #000;
      z-index: ;
      line-height: initial;
      padding: 1em;
    }
  </style>
</head>
<body>
  <div class="box one positioned">
    one
    <div class="absolute">nested</div>
  </div>
  <div class="box two positioned">
    two
  </div>
  <div class="box three">
    three
  </div>
</body>
</html>

https://codepen.io/cellinlab/pen/MWrBPaW

给一个定位元素加上 z-index 是创建层叠上下文最主要的方式,但还有别的属性也能创建,比如小于 1 的 opacity 属性,还有 transform、filter属性。由于这些属性主要会影响元素及其子元素渲染的方式,因此一起绘制父子元素。文档根节点(<html>)也会给整个页面创建一个顶级的层叠上下文。

所有层叠上下文内的元素会按照以下顺序,从后到前叠放:

  1. 层叠上下文的根 z-index 为负的定位元素(及其子元素)
  2. 非定位元素
  3. z-index 为 auto 的定位元素(及其子元素)
  4. z-index 为正的定位元素(及其子元素)
# 用变量记录 z-index

如果不根据组件的优先级定义清晰的层叠顺序,那么一个样式表很容易演变成一场 z-index 大战。如果没有清晰的说明,开发人员在给一个模态框之类的元素添加样式时,为了不被其他元素遮挡,就会设置一个高得离谱的 z-index,比如 999999。这样的事情重复几次后,大家就只能凭感觉给一个新的组件设置 z-index。

如果你使用预处理器,比如 LESS 或 SASS,或者支持的所有浏览器都支持自定义属性,就能很方便地处理这个问题。将所有的 z-index 都定义为变量放到同一个地方。这样就能清晰地看到哪些元素在前哪些元素在后。

代码语言:javascript
代码运行次数:0
运行
复制
--z-loading-indicator: 100;
--z-nav-menu:          200;
--z-dropdown-menu:     300;
--z-modal-backdrop:    400;
--z-modal-body:        410;

如果发现 z-index 没有按照预期表现,就在 DOM 树里往上找到元素的祖先节点,直到发现层叠上下文的根。然后给它设置 z-idnex,将整个层叠上下文向前或者向后放。还要注意多个层叠上下文嵌套的情况。

网页很复杂时,很难判断是哪个层叠上下文导致的问题。因此,在创建层叠上下文的时候就一定要多加小心,没有特殊理由的话不要随意创建,尤其是当一个元素包含了网页很大一部分内容的时候。尽可能将独立的定位元素(比如模态框)放到 DOM 的顶层,结束标签 </body> 之前,这样就没有外部的层叠上下文能束缚它们了。

有些开发人员会忍不住给页面的大量元素使用定位。一定要克制这种冲动。定位用得越多,网页就越复杂,也就越难调试。

如果能够依靠文档流,而不是靠明确指定定位的方式实现布局,那么浏览器会处理好很多边缘情况。记住,定位会将元素拉出文档流。一般来说,只有在需要将元素叠放到别的元素之前时,才应该用定位。

# 粘性定位

粘性定位(sticky positioning),是相对定位和固定定位的结合体:正常情况下,元素会随着页面滚动,当到达屏幕的特定位置时,如果用户继续滚动,它就会“锁定”在这个位置。最常见的用例是侧边栏导航。

代码语言:javascript
代码运行次数:0
运行
复制
<head>
  <style>
    body {
      font-family: Helvetica, Arial, sans-serif;
      min-height: 200vh;
      margin: 0;
    }

    button {
      padding: .5em .7em;
      border: 1px solid #8d8d8d;
      background-color: white;
      font-size: 1em;
    }

    .top-banner {
      padding: 1em 0;
      background-color: #ffd698;
    }

    .top-banner-inner {
      width: 80%;
      max-width: 1000px;
      margin: 0 auto;
    }

    .modal {
      display: none;
    }

    .modal-backdrop {
      position: fixed;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      background-color: rgba(0, 0, 0, 0.5);
      z-index: 1;
    }

    .modal-body {
      position: fixed;
      top: 3em;
      bottom: 3em;
      right: 20%;
      left: 20%;
      padding: 2em 3em;
      background-color: white;
      overflow: auto;
      z-index: 2;
    }

    .modal-close {
      position: absolute;
      top: 0;
      right: 0;
      padding: 0.3em;
      font-size: 2em;
      height: 1.5em;
      width: 1.5em;
      cursor: pointer;
      border: 0;
    }
    .modal-close::before {
      display: block;
      content: '\00D7';
    }

    .sr-only {
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
    }

    .container {
      display: flex;
      width: 80%;
      max-width: 1000px;
      margin: 1em auto;
      min-height: 100vh;
    }

    .dropdown {
      display: inline-block;
      position: relative;
    }

    .dropdown-label {
      padding: 0.5em 2em 0.5em 1.5em;
      border: 1px solid #ccc;
      background-color: #eee;
    }

    .dropdown-label::after {
      content: "";
      position: absolute;
      right: 1em;
      top: 1em;
      border: 0.3em solid;
      border-color: black transparent transparent;
    }
    .dropdown:hover .dropdown-label::after {
      top: 0.7em;
      border-color: transparent transparent black;
    }

    .dropdown-menu {
      display: none;
      position: absolute;
      left: 0;
      top: 2.1em;
      min-width: 100%;
      background-color: #eee;
    }
    .dropdown:hover .dropdown-menu {
      display: block;
    }

    .submenu {
      padding-left: 0;
      margin: 0;
      list-style-type: none;
      border: 1px solid #999;
    }

    .submenu > li + li {
      border-top: 1px solid #999;
    }

    .submenu > li > a {
      display: block;
      padding: .5em 1.5em;
      background-color: #eee;
      color: #369;
      text-decoration: none;
    }

    .submenu > li > a:hover {
      background-color: #fff;
    }

    .col-main {
      flex: 1 80%;
    }

    .col-sidebar {
      flex: 20%;
    }

    .affix {
      position: sticky;
      top: 1em;
    }

  </style>
</head>

<body>
  <header class="top-banner">
    <div class="top-banner-inner">
      <p>Find out what's going on at Wombat Coffee each
        month. Sign up for our newsletter:
        <button id="open">Sign up</button>
      </p>
    </div>
  </header>
  <div class="modal" id="modal">
    <div class="modal-backdrop"></div>
    <div class="modal-body">
      <button class="modal-close" id="close">
        <span class="sr-only">close</span>
      </button>
      <h2>Wombat Newsletter</h2>
      <p>Sign up for our monthly newsletter. No spam.
         We promise!</p>
      <form>
        <p>
          <label for="email">Email address:</label>
          <input type="text" name="email"/>
        </p>
        <p><button type="submit">Submit</button></p>
      </form>
    </div>
  </div>

<div class="container">
  <main class="col-main">
    <nav>
      <div class="dropdown">
        <div class="dropdown-label">Main Menu</div>
        <div class="dropdown-menu">
          <ul class="submenu">
            <li><a href="/">Home</a></li>
            <li><a href="/coffees">Coffees</a></li>
            <li><a href="/brewers">Brewers</a></li>
            <li><a href="/specials">Specials</a></li>
            <li><a href="/about">About us</a></li>
          </ul>
        </div>
      </div>
    </nav>
    <h1>Wombat Coffee Roasters</h1>
  </main>

  <aside class="col-sidebar">
    <div class="affix">
      <ul class="submenu">
        <li><a href="/">Home</a></li>
        <li><a href="/coffees">Coffees</a></li>
        <li><a href="/brewers">Brewers</a></li>
        <li><a href="/specials">Specials</a></li>
        <li><a href="/about">About us</a></li>
      </ul>
    </div>
  </aside>
</div>

  <script type="text/javascript">
  var button = document.getElementById('open');
var close = document.getElementById('close');
var modal = document.getElementById('modal');

button.addEventListener('click', function(event) {
  event.preventDefault();
  modal.style.display = 'block';
});

close.addEventListener('click', function(event) {
  event.preventDefault();
  modal.style.display = 'none';
});
</script>
</body>

https://codepen.io/cellinlab/pen/popZxwv

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022/4/7,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • # 固定定位
    • # 用固定定位创建一个模态框
      • # 控制定位元素的大小
  • # 绝对定位
    • # 让Close按钮绝对定位
    • # 定位伪元素
  • # 相对定位
    • # 创建一个下拉菜单
    • # 创建一个 CSS 三角形
  • # 层叠上下文和 z-index
    • # 理解渲染过程和层叠顺序
    • # 用 z-index 控制层叠顺序
    • # 理解层叠上下文
      • # 用变量记录 z-index
  • # 粘性定位
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档