当元素在 Chrome 中更改不透明度时,滚动条为 disappearing/reappearing

Scrollbar is disappearing/reappearing when element changes opacity in Chrome

我有一个名为 img-with-overlaydiv,它包含一个静态定位图像和一个绝对定位叠加层。当 img-with-overlay 悬停在上方时,叠加层的不透明度从 0 变为 1。这样做的一个意外副作用是,在 Chrome 和基于 Chromium 的浏览器中,当覆盖层不透明时,垂直滚动条会消失。它不会出现在 Firefox 中。

你问为什么滚动条在那里?你可以看到有一个 container 和一个非常具体的 widthheight。即使这些尺寸改变了半个像素,也不会发生意想不到的副作用。我正在使用此容器来表示我网页的 body,尽管我已将其缩小以适合片段。虽然大多数屏幕尺寸不会出现问题,但我仍然想防止它过度发生。

$(document).ready(function () {
  $('.img-with-overlay').hover(function () {
    $('.overlay').css('opacity',1);
  }, function () {
    $('.overlay').css('opacity',0);
  })
});
.container {
  width: 600px;
  height: 248.5px;
  overflow: auto;
  overflow-x: hidden;
}

.img-with-overlay {
  position: relative;
  overflow: hidden;
  width: 100%;
}

img {
  display: block;
  width: 100%;
}

.overlay {
  position: absolute;
  top: 0;
  opacity: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
  <div class="img-with-overlay">
    <img src="https://via.placeholder.com/468x200?text=Hover">
    <div class="overlay">This is an overlay</div>
  </div>
</div>

为什么覆盖层的不透明度改变时滚动条disappearing/reappearing?

我注意到的一些有趣的事情:

发生的事情很简单,但并不那么明显。

背景

当您设置图像 width: 100% 时,系统会自动计算图像高度以保持原始比例。容器比图像高一点,因为它有 overflow-y: auto,所以出现垂直滚动。但是现在奇迹发生了:垂直滚动条减少了容器的内部宽度,所以图像宽度相应地减少,它的高度也相应地减少以保持比例。但是现在图像比容器高一点点,所以不再需要垂直滚动条。但如果滚动条消失,图像将再次展开,开始无限循环,一切都在闪烁。为防止这种竞争情况,浏览器选择始终显示滚动条,但由于图像适合容器,因此无需向下滚动,因此滚动条被禁用。

这解释了为什么稍微改变一下尺寸就可以解决问题:如果您使容器稍微窄一点或高一点,图像总是适合,而不需要滚动条;如果您使容器更宽或更短,图像将永远无法容纳并且始终显示滚动条。

不透明度问题

现在考虑以下问题。您的图片同时包含 display: blockwidth: 100%。这两个属性影响相同的 属性(宽度),但在布局工作流程中以不同的方式和不同的时间影响。这足以启动竞争条件,该行为未定义,浏览器会尝试以自己的方式解决。您可以通过打开检查器并切换 img 的 display 属性来验证它:开始时您有一个禁用的滚动条,然后当您关闭该属性时滚动条被启用,然后当您重新启用该属性时滚动条完全消失。什么?!我们又回到了初始状态,结果却不一样?! :我的天啊: 大多数情况下,这种竞争条件没有影响,但在这种情况下,由于上一节中的描述,它变得很清楚。

现在让我们谈谈不透明度。不透明度小于 1 的元素必须以特定的顺序(从后到前)呈现,并且在呈现背面不透明元素之后。虽然我不知道在 Chrome 和 Firefox 中这是如何实现的,但行为表明在 Chrome 中渲染顺序会以某种方式影响布局工作流程。这解释了为什么 opacity=1 的行为不同于 opacity=0.999。但这只会发生,因为存在没有定义行为的条件,如前所述。

因此解决方案非常简单:删除导致未定义行为的条件。由于 display: block 不影响图像大小,而只影响 space 元素占用,因此要做的事情很明显: 删除 display: block 并仅保留 [= img 元素上的 10=].

如果出于某种原因您不想删除 display: block,您可以通过在容器上编码 overflow-y: scroll 强制滚动条始终可见(同时删除溢出属性) .这样您就不会删除导致未定义行为的条件,但会删除滚动条上的竞争条件。