2019 年 1 月 29 日,Chrome72 正式版(72.0.3626.81)發布,本次發布帶來了一個改變,且沒有在更新日誌中提及,該改變導致某些網站發生了布局錯亂。該改變主要針對的是嵌套的flex布局,下面我們一起看下是怎麼回事。
問題首先,我們有一個嵌套的 flex 布局,代碼如下:
<style>
div {
box-sizing: border-box;
}
.flex {
display: flex;
flex-direction: column;
}
.area {
padding: 10px;
height: 300px;
width: 300px;
background-color: #3fb9ab;
color: #fff;
}
.item {
padding: 10px;
flex: 1;
background-color: #158c7e;
}
.nest-item {
flex: 1;
overflow: auto;
background-color: #046b5f;
}
.content {
padding: 10px;
height: 600px;
}
</style>
<div class="area flex">
area
<div class="item flex">
item
<div class="nest-item">
<div class="content">content</div>
</div>
</div>
</div>
希望實現這樣的效果:父容器 area 有一個指定的高度,且它是一個 flex 彈性盒子,它內部有一個子元素 item,使用 flex:1 指定了佔滿剩餘空間,且 item 也是一個 flex 彈性盒子,它內部還有一個同樣佔滿剩餘空間的嵌套子元素 nest-item,通過設置 overflow:auto 讓它的內容超出後顯示滾動條。效果如下:
這樣布局的想法很簡單,即通過設置彈性盒子子元素的擴展比率,能得到一個自動佔滿剩餘空間高度的容器,再在這個容器中放需要顯示的內容,在某些情況下,這確實是一個比較不錯的主意,在 Chrome72 之前都是可以正常顯示的。但是 Chrome72.0.3626.81中顯示如下:
追溯為什麼會出現這樣的問題呢?我們看一下規範( https://drafts.csswg.org/css-flexbox/#min-size-auto )flex 彈性盒子主軸上子元素的最小大小是內容的大小(視主軸方向為寬或高)。
那麼我們再看一下上面的例子,area 的主軸是縱向的,子元素 item 的最小高度即是內容的高度,而 nest-item 被 content 撐開,content 有一個高度(600px,超出了容器的高度),那麼 item 的最小高度也就超過了 600px。這樣一來,一層層都是被內容撐開,也就沒有出現滾動條了,這樣似乎是符合規範預期的。
在 chromium 的 issue 反饋中,有人提到了這個問題( https://bugs.chromium.org/p/chromium/issues/detail?id=927066 ),根據回復,這正是官方為了讓 Chrome 更加符合規範行為而做的調整。也就是說,Chrome72 之前的版本,這算是一個沒有按照規範行為而出現的 bug。新的調整,其實就是讓 flex 彈性盒子的子元素最小高度的默認行為應用 min-height:min-content ,就像官方回覆中提到的那樣,讓子元素作為 flex 彈性盒子時卻和普通盒子處理方式不同是會讓人困惑的。
解決方法既然知道了原因,那麼如果我們還想使用這樣的布局方式,該怎麼做呢?
對的,我們給 item 指定一個最小高度,讓它不使用默認的行為(即內容的高度),一般我們指定最小高度為0 min-height:0。給 item 加上這個樣式後,我們再看一下效果:
嗯,已經符合我們的預期了。為了驗證規範中提到的對主軸方向的行為,我們修改一下代碼,將主軸設置為水平方向試試,代碼如下:
<style>
div {
box-sizing: border-box;
}
.flex {
display: flex;
flex-direction: row;
}
.area {
padding: 10px;
height: 300px;
width: 300px;
background-color: #3fb9ab;
color: #fff;
}
.item {
padding: 10px;
flex: 1;
background-color: #158c7e;
}
.nest-item {
flex: 1;
overflow: auto;
background-color: #046b5f;
}
.content {
padding: 10px;
width: 600px;
}
</style>
<div class="area flex">
area
<div class="item flex">
item
<div class="nest-item">
<div class="content">content</div>
</div>
</div>
</div>
效果如下:
看來主軸為水平方向時,是符合規範預期行為的(Chrome72 及以前的版本都符合),那麼我們給 item 加上一句樣式 min-width:0 ,效果如下:
嗯,是符合我們預期的。
結語好了,現在你已經知道是怎麼一回事了,可是等等,你說你升級到Chrome72沒有發現我說的問題?
那是因為官方注意到這個修改會影響到一些網站的正常顯示,因此在 2019 年 2 月 6 日(正是春節假期間)發布的 Chrome72.0.3626.96 中,將這個問題還原回以前的行為了( https://chromium.googlesource.com/chromium/src/+/032ef9666487db1d04b656a095b041de8c6d2b63 )。
官方的意思是為了避免這個修改給某些網站帶來的不好的影響,因此預留時間給大家修改,等到Chrome73將會發布這一改變。所以為了未來更好的瀏覽體驗,檢查一下你的頁面吧!