CSS中伪元素的定位上下文问题?

在研究CSS大作《CSS设计指南》(第3版)中,在讲到界面组件中的用CSS为弹出层设计三角形时,对例子代码中伪元素的定位上下文关系产生了很大的困惑


源代码如下:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" /> 
<title>Stylin' with CSS - Figure 6.24 Popup Overlay</title>
<style>
* {margin:0; padding:0; font-family:helvetica, arial, sans-serif;}
figure {
    width:144px; height:153px; /* sizing of image box */
    margin:20px 20px;            /* space between boxes */
    border:1px solid #666;        /* image border */
    position:relative; /* positioning context for popups */
    float:left; /* makes images sit side by side */
    }
img {display:block;}/* remove baseline spacing from under image */
figcaption {
    display:none; /* hides popups */
    position:absolute; /* relative to images */
    left:74%; top:14px; /* positions popup on right side of image */
    width:130px; /* width of popup */
    padding:10px; /* space around popup content */
    background:#f2eaea;
    border:3px solid red; border-radius:6px;
    }
figure:hover figcaption {display:block; z-index:2;} /* displays popup when image is hovered */
figcaption h3 { /* popup content */
    font-size:14px;
    color:#666;
    margin-bottom:6px;
    }
figcaption a { /* popup content */
    display:block;
    text-decoration:none;
    font-size:12px;
    color:#000;
    }
figcaption::after { /* red triangle box*/
    content:""; /* some content required - using empty text string */
    position:absolute; /* relative to popup */
    border:12px solid;
    border-color:transparent red transparent transparent; 
    right:100%; top:17px;
    height:0px; width:0px; /* collapses box to create triangle */ 
    }
</style>
</head>

<body>
    <figure>
        <img src="images/pink_heels.jpg" alt="pink heels" />
        <figcaption>
            <h3>Pink Platforms</h3>
            <a href="#">More info</a>
        </figcaption>
    </figure>
    <figure class="click_me">
        <img src="images/leopard_heels.jpg" alt="leopard heels" />
        <figcaption>
            <h3>Leopard Platforms</h3>
            <a href="#">More info</a>
        </figcaption>
    </figure>
    <figure class="click_me">
        <img src="images/red_heels.jpg" alt="red heels" />
        <figcaption>
            <h3>Red Platforms</h3>
            <a href="#">More info</a>
        </figcaption>
    </figure>
    
    <!-- OK to remove the code between here and </body> -->
    <!-- Please note that the watermarked image/images in this code example is/are provided only for the purpose of displaying the code example in your browser. Licensing rights to this image/images is/are not conveyed to you. If you would like to purchase licensing rights for the images in this book, please visit the following url: --> 
    <div style="clear:both; padding:100px 0 0 0; font-size:.85em; color:#666;">
    <p>To purchase the stock images in this book and other great stock images, go to <a href="http://www.bigstockphoto.com/?refid=PEECNmwgKb">bigstockphoto.com</a></p>
    <p>A code example from <em>Stylin&rsquo; with CSS, Third Edition</em> by Charles Wyke-Smith. Visit <a href="http://www.stylinwithcss.com">stylinwithcss.com</a> for more CSS information and updates.</p>
    </div>
    
</body>
</html>

浏览器渲染后的效果如下:

clipboard.png

文字说明框在悬浮在图片时出现,同时框左边会出现由于边框透明原理而生出的三角形。
自己十分不明白的就是为什么用伪元素生成的三角形会相对position属性为absolute的figcaption(即弹出文字框)作移动,而不是position为relative的祖先元素figure.
定位上下文的知识点中就强调position设定为relative的祖先元素可以成为绝对定位元素定位上下文,而这里明显不符合这种逻辑。
是因为出现了伪类hover的原因吗?
不解!

阅读 2.8k
3 个回答

对于这个问题的答案,自己姑且只能采取经验总结的方式,总结认为伪元素::before和::after创建后position设置为absolute其位置移动属性是相对于设置自己的最近元素的。

参考了MDN文档中对::after和::before的解释,其中::before解释中出现了与此情况类似的代码。

HTML

<ul>
  <li>Buy milk</li>
  <li>Take the dog for a walk</li>
  <li>Exercise</li>
  <li>Write code</li>
  <li>Play music</li>
  <li>Relax</li>
</ul>

CSS

li {
  list-style-type: none;
  position: relative;
  margin: 2px;
  padding: 0.5em 0.5em 0.5em 2em;
  background: lightgrey;
  font-family: sans-serif;
}

li.done {
  background: #CCFF99;
}

li.done::before {
  content: '';
  position: absolute;
  border-color: #009933;
  border-style: solid;
  border-width: 0 0.3em 0.25em 0;
  height: 1em;
  top: 1.3em;
  left: 0.6em;
  margin-top: -1em;
  transform: rotate(45deg);
  width: 0.5em;
}

JavaScript

var list = document.querySelector('ul');
list.addEventListener('click', function(ev) {
  if( ev.target.tagName === 'LI') {
     ev.target.classList.toggle('done'); 
  }
}, false);

运行结果

未点击

点击后

可以看到这里也给before伪元素设置了移动属性top和left,可是——可是它的父元素li是堂堂正正地设置为position:relative的。。。

目前看来也只能总结成自己的踩坑经验了,给定位上下文这个知识点补充一些例外。

因为它是figcaption的伪元素呀

首先当元素的position设置为absolute时,在没有relative的父元素的情况下,这个元素默认的定位上下文是body元素,这是因为他是所有元素的祖先(参考你说的书的3.4.5节)。

我觉得是因为figcaption的position属性设置为absolute,让这个figcaption元素变成一个新的BFC,里面的子元素的祖先就变成了figcaption而不是body,而且在figcaption里面也没有relative元素,所以他的子元素(伪元素)默认的定位上下文就变成了这个figcaption。

推荐问题