::before 子边框后面的伪元素

::before pseudo-element behind child border

我有一个 h1 标签,我正在尝试为其添加一个很酷的动画,其中边框将从左上角和右下角的点 'grow'。

我通过将我的 h1 包装在两个 div 中来做到这一点,每个 div 都有一个 ::before::after 伪元素.

当鼠标悬停在根 div 上时,这些 div 的伪元素将 'shrink',显示下面的边框。

问题是,我的根 div 的 ::before 伪元素 边框后面,因此边框会在鼠标悬停时立即显示.设置 ::before::afterz-index 修复它;但是,我不想这样做——这是我为复制/粘贴而制作的一段代码。没有设置 z-index,所以我很困惑为什么会这样。

以前,除了动画需要的东西外,我没有 divs,而且效果很好——但这与它有什么关系?

这似乎是问题所在:::before pseudo-element stacking order issue

但是,没有关于如何修复它的建议答案。当然,我可以添加第三个 div,但我想保留它作为最后的手段。

Fiddle 期:https://jsfiddle.net/zppqgn6s/

看到它用z-index修复了;要查看它应该是什么样子,请取消注释第 34 行。

pseudo-element的边框低于child的原因:

关于为什么 pseudo-element (:before) 的背景落后于 child (h1) 的原始问题的答案可以在 BoltClock 的答案中找到 (您已在问题中链接)。 :before pseudo-element 实际上插入在根 div 的内容之前(包括 h1)。

下面是演示中使用的元素的一般结构:

#anim              /* This is the first element inside root and is positioned (relative) */
    #anim:before   /* This is positioned absolutely with respect to the parent */
    div            /* This element is positioned relatively */
        div:before /* This element is positioned absolutely with respect to the div */
        h1         /* This element doesn't have any positioning */
        div:after  /* This element is positioned absolutely with respect to the div */
    #anim:after    /* This is positioned absolutely with respect to the parent */

现在 based on the specs for the visual rendering layers,下面是发生的情况:

#anim              /* Parent element and its background, border gets painted first (Layer 0) */
    #anim:before   /* Positioned descendant, creates stacking context nested within parent (Layer 0.1)*/
    div            /* Positioned descendant of #anim, second child in flow (Layer 0.2) */
        div:before /* Positioned descendant of div, first child in flow (Layer 0.2.2) */
        h1         /* Non positioned, as per Point 3 gets positioned lowest (Layer 0.2.1) */
        div:after  /* Positioned descendant of div, second such child in flow (Layer 0.2.3) */
    #anim:after    /* Positioned descendant of #anim, third child in flow (Layer 0.3) */

根据层数可以看出(在行内注释中提供),h1 元素位于 #anim:before 上方(但在产生边框收缩效果的所有其他三个元素下方) .

解法:

唯一的解决办法是在 :before 元素之后绘制 child (h1)。这可以通过执行以下任一操作来实现(但它们都需要设置 z-index):

  • 使用 z-index: -1h1 设置为 position: relative(使其落后于 #anim:before
  • z-index: 1(或更高)设置为 #anim:before 元素(使其高于 h1

备用 Solutions/Approaches:

实际上,您不需要此特定动画的所有额外元素(从 top-left 和 bottom-right 交汇的边界)。它们可以使用单个 h1 元素本身来实现,我正在 post 这个答案来说明其中两种方法。虽然你没有要求其他方法,但我喜欢这种效果,它似乎是 post 这个答案的好地方。

通过使用 linear-gradient 背景图片:

在这种方法中,我们为边框的每一侧创建一个渐变(实际上只是一种纯色,因为它不会改变颜色),适当地放置它们,然后将大小从 0% 过渡到100%。对于顶部和底部边框,X-axis 中的大小应在 hover 上从 0% 更改为 100%,而对于左右边框,Y-axis 中的大小应从 0% 更改为 100%.

h1 {
  position: relative;
  display: inline-block;
  padding: 4px;
  background: linear-gradient(to right, #000, #000), linear-gradient(to right, #000, #000), linear-gradient(to bottom, #000, #000), linear-gradient(to bottom, #000, #000);
  background-position: 0% 0%, 100% 100%, 0% 0%, 100% 100%;
  background-size: 0% 2px, 0% 2px, 2px 0%, 2px 0%;  /* 2px is border thickness */
  background-repeat: no-repeat;
  transition: all 1s;
}
h1:hover {
  background-size: 100% 2px, 100% 2px, 2px 100%, 2px 100%;  /* 2px is border thickness */
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<h1>Hover me</h1>
<br>
<h1>How about me?<br>I have dynamic height!</h1>

<div id="wrap">
  <h1>Look at me, I am responsive!!!</h1>
</div>


通过使用pseudo-elements:

这也可以使用 pseudo-elements 通过在 hover 上转换它们的高度和宽度来完成。您已经走上了正确的道路,但不需要额外的元素。

h1 {
  position: relative;
  display: inline-block;
  padding: 4px;
}
h1:after,
h1:before {
  position: absolute;
  content: '';
  height: 0%;
  width: 0%;
  transition: width 1s, height 1s, border .01s 1s;  /* border has a delay because it should become invisible only after height and width become 0 */
}
h1:before {
  left: 0;
  top: 0;
  border-top: 2px solid transparent;
  border-left: 2px solid transparent;
}
h1:hover:before {
  border-top: 2px solid black;
  border-left: 2px solid black;
}
h1:after {
  bottom: 0;
  right: 0;
  border-right: 2px solid transparent;
  border-bottom: 2px solid transparent;
}
h1:hover:after {
  border-right: 2px solid black;
  border-bottom: 2px solid black;
}
h1:hover:before,
h1:hover:after {
  height: calc(100% - 2px);
  width: calc(100% - 2px);
  transition: width 1s, height 1s, border .01s;  /* border has a shorter duration because it immediately needs to change colors */
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<h1>Hover me</h1>
<br>
<h1>How about me?<br>I have dynamic height!</h1>

<div id="wrap">
  <h1>Look at me, I am responsive!!!</h1>
</div>

即使在周围添加了额外的 div 元素(从代码片段中可以看出),这两种方法都有效。