前言

今天在项目中有一个需求,就是一个可滚动的列表的每一列鼠标悬浮上去就会产生一个浮动框显示其详细内容。简化下情景的代码如下:

<div class="box">
  <div class="item">
  我是列表项
    <div class="susp">我是悬浮框</div>
  </div>
</div>

.box {
  height: 200px;
  border: 1px solid red;
  overflow: auto;
}
.item {
  position: relative;
  height: 150px;
  border-bottom: 1px solid blue;
}
.susp {
  position: absolute;
  left: 0;
  top: 150px;
  height: 100px;
  background-color: #eee;
}

预览

我本来以为的效果是这样子的:
图片描述

实际却是这样的:
图片描述

我的内心是崩溃的,为何绝对定位可以触发容器的滚动效果?以前用css太随意了,根本没考虑过这些问题。

问题分析

万幸我在Stack Overflow找到了真有人提问过这个问题...下面的回答其实解释的也不是很明朗,但是看到了核心的概念containing block,也就是包含块。好吧,让还没看懂解释一脸懵逼的我突然把包含块说清楚也是有点困难...直接搬运W3C中文规范的定义:

  1. 根元素所在的包含块是一个被称为初始包含块的矩形
  2. 对于其它元素,如果该元素的position是'relative'或者'static',包含块由其最近的块容器祖先盒的内容边界形成
  3. 如果元素具有'position: fixed',包含块由连续媒体的视口或者分页媒体的页区建立
  4. 如果元素具有'position: absolute',包含块由最近的'position'为'absolute','relative'或者'fixed'的祖先建立

对照这个定义,我们的目前的情景就是符合第四条,我的悬浮框是absolute,列表项relative就是其包含块,而列表项的包含块就是容器box。理解到这个地步应该差不多可以推测出问题所在,悬浮框的包含块属于容器以内,因此其高度可以触发容器的滚动。虽然我没有在官方规范中找到对应的解释,但是这个理解应该是没有问题的,还请有看官大佬指点更好的分析。

解决方法

问题原因找到了,问题也就迎刃而解,既然是因为悬浮框的包含块在容器内,那么我们就让悬浮框的包含块在其外不就可以了么,就将其包含块默认为初始包含块即可,除非悬浮框的高度超出页面会触发页面的滚动...但悬浮框的设计高度肯定是不可能要超出页面视口的。修改后的代码如下:

<div class="box">
  <div class="item">
  我是列表项
    
  </div>
  <div class="susp">我是悬浮框</div>
</div>

.box {
  height: 200px;
  border: 1px solid red;
  overflow: auto;
}
.item {
  /* position: relative; */
  height: 150px;
  border-bottom: 1px solid blue;
}
.susp {
  position: absolute;
  /* left: 0; */
  /* top: 150px; */
  height: 100px;
  background-color: #eee;
}

其实就是注释掉列表项的position: relative,让悬浮框的包含块指向初始包含块,但是此时注意不能再加定位了,因为你没法算出来的,因此再注释掉lefttop,同时要把悬浮框的div放在列表项div的相邻下面,这样悬浮框是一个BFC,也达到我们想要的位置效果。这个其实和BFC关系不大,但是以前我没总结过,给个参考1参考2有空好好总结一下。

总结

还是那句话,如果一个块的包含块在容器内(包含容器本身),那么其高度就会触发容器的滚动。

参考

文中Stack Overflow问题


Alee
291 声望8 粉丝

既然路走偏了,那就重新开始吧。