先记住这个答案
视觉上两者都把超出盒子的内容裁掉,关键区别在滚动。overflow: hidden 的元素依然是滚动容器,只是不显示滚动条,JS 设置 scrollTop、调用 scrollIntoView、聚焦内部的输入框,都能让它滚动。overflow: clip 是纯裁剪,元素不是滚动容器,程序也滚不动它。由此还带出两个差异:hidden 会建立 BFC,会成为后代 position: sticky 的吸附参照;clip 两样都不做。
hidden可编程滚动,clip不可clip不接受scrollTop滚动hidden会建立 BFC,clip不会clip不建立滚动容器
用一段 JS 看出区别
两个一样高的盒子,内容都比盒子高,分别用 hidden 和 clip,然后用 JS 去滚它们。
scrollTop 的结果JavaScript// .a { height: 100px; overflow: hidden }
// .b { height: 100px; overflow: clip }
// 两个盒子里都放了 300px 高的内容
const a = document.querySelector('.a')
const b = document.querySelector('.b')
a.scrollTop = 80
b.scrollTop = 80
console.log(a.scrollTop) // 80,内容向上移了 80px
console.log(b.scrollTop) // 0,设置被忽略,内容纹丝不动hidden 只是把滚动条藏了起来,滚动能力还在,所以 scrollTop 能改。clip 的元素没有滚动位置这个概念,scrollTop 永远是 0。
这个差异在项目里经常以 bug 的形式出现。比如一个 overflow: hidden 的轮播容器里有输入框,用户按 Tab 聚焦到被裁掉的那一张卡片里的输入框,浏览器为了让它可见,会自动滚动这个容器,整个轮播就错位了。锚点跳转、scrollIntoView 也会触发同样的效果。用 clip 就不存在这个问题。
为什么 hidden 会影响布局,clip 不会
一个元素变成滚动容器,就必须把自己的内容「关」在里面,所以 overflow 取 hidden、auto、scroll 时都会建立 BFC(块级格式化上下文,里面的布局不和外面互相影响)。clip 不是滚动容器,没有这层副作用。
- 两个布局副作用
- 第一是 BFC。
overflow: hidden常被拿来清除浮动、阻止父子外边距塌陷,靠的就是建立 BFC。把它换成clip,浮动不会被包住,外边距照样塌陷。所以替换之前要确认原来的hidden是不是在承担这个作用,想要 BFC 又不想要滚动容器,可以用display: flow-root。 第二是 sticky。position: sticky的元素吸附在离它最近的滚动容器上。祖先里有一个overflow: hidden,sticky 就会以它为参照,而它本身不滚动,结果就是 sticky 看起来完全失效。把这个祖先改成overflow: clip,sticky 会继续往上找到真正滚动的页面或容器,吸顶效果就回来了。
clip 独有的两个能力
clip 除了「不滚动」之外,还有两个 hidden 做不到的用法。
- 只裁一个方向
overflow-x和overflow-y分开写时,如果一个是hidden、另一个是visible,浏览器会把visible自动算成auto,因为滚动容器不可能只在一个方向上关住内容。结果本来想横向裁剪、纵向放开,却多出一个纵向滚动条。overflow-x: clip配overflow-y: visible就没有这个转换,真的只裁左右。- 往外放宽一点再裁
overflow-clip-margin: 8px表示内容可以溢出盒子 8 像素再被裁掉,适合给卡片里的阴影、聚焦外框留出位置。这个属性只在overflow: clip上生效,对hidden没用。
容易答错的地方
- 以为
hidden就是不能滚动 hidden禁止的只是用户通过滚动条和滚轮去滚,JS、聚焦、锚点都能让它滚。需要真正「冻结」内容位置时,要么用clip,要么自己监听scroll事件把scrollTop复位。- 把所有
hidden无脑换成clip clip不建立 BFC。原来依赖overflow: hidden清浮动、防外边距塌陷的地方,换完布局就会变。替换前先看这个hidden是为了裁剪还是为了 BFC。- 以为
clip能防止body滚动 - 在弹窗打开时给
body设overflow: hidden是锁定页面滚动的常见做法,它依赖的是视口的滚动行为,写在html或body上的overflow会传给视口。这时写clip没有额外好处,锁滚动还是用hidden。
面试官还会怎么问?
除了 overflow,还有哪些方式能裁剪内容?
clip-path 可以按任意形状裁剪,也不产生滚动容器。contain: paint 会让后代不画到元素自身的盒子之外,同时建立新的层叠上下文、BFC 和包含块。几种方式的区别在于副作用,选的时候看需不需要 BFC、需不需要限制绘制范围。
overflow: clip 的兼容性怎么样?
现在主流浏览器都支持,但较老的 Safari 不认识这个值,会把整条声明丢掉,内容直接溢出。稳妥的写法是先写一行 overflow: hidden 再写一行 overflow: clip,不支持的浏览器退回 hidden。
overflow: hidden 的容器,怎么判断内容有没有被裁掉?
比较 scrollHeight 和 clientHeight,前者大于后者就说明内容溢出了,横向同理比较 scrollWidth 和 clientWidth。这个判断对 clip 同样适用,因为 scrollHeight 反映的是内容总高度。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。