題目以下圖,一個很簡單也很經常使用的佈局:php
左右排布的列表,左側是標籤信息,右側是描述信息。css
在微信粉絲羣裏面發出題目後,總共收到了將近50人的回答,完整回答列表能夠參見此issues:github.com/zhangxinxu/…html
也歡迎關注這個github項目。前端
今天上午在B站進行了答疑直播,本文是整理後的文字版。css3
很多回答出現了下面的CSS:git
* { padding: 0; margin: 0; }複製代碼
這是一直偷懶的CSS reset寫法,我我的是不推薦這種寫法的,一來帶來比較多的資源開銷,並且這些開銷徹底是沒有必要的,有種爲了幾棵樹砍掉整個森林的感受,在全部的HTML標籤中,默認有padding值的屈指可數,徹底沒有必要使用通配符進行重置;二來,對於有些元素,默認的margin
屬性是有用的,比方說表單元素中的單選框和複選框,其默認margin值能夠和後面文字保持合理的間距,若是使用通配符重置,就會擠成一坨,閱讀體驗並很差。github
有人在實現的時候,容器寫死了高度,實際開發的時候咱們不能保證需求會不會變更,比方說增長一個條目的數據。若是咱們的容器高度是定死的,那必然增長新條目的時候會出現佈局上的bug,會下降容錯性和可維護性,所以,避免定高。面試
可能有將近1/3的人,在實現這種佈局的時候,都是dt
和dd
寬度各佔50%,不管是左右浮動,flex
佈局或者使用inline-block
排列。瀏覽器
示意以下:安全
dt, dd{
width: 50%;
}
dt{
float: left;
}
dd{
float: right;
text-align: right;
}複製代碼
若是是應付這條題目呢,能夠說是勉強及格,可是若是放在實際開發中,則侷限性就會比較大。因爲dd
標籤中的內容是動態的,可能就會出現長段的相似於備註這樣的選項,此時dd
50%寬度就會有問題,在在移動端,咱們顯示的寬度可能就只有300多像素,因而,佈局可能就會表現成這樣:
並且根據個人測試,通常容器的寬度小於290像素,時間也會掉下來,由於,50%並非一個實際開發中的最佳取值,能夠調整下。比較好的作法是,左側安全寬度,右側自動分配剩餘空間。
這是一個使用dd標籤進行絕對定位的案例:
使用dd標籤一個一個絕對定位,最大的問題在於,維護性太糟糕了,若是要新增一個條目必然要從新再寫一段CSS,若是這裏有20行數據,難道還要再增長20多個語句?
實際上,dd標籤絕對定位是可使用的,可是,沒有必要使用top進行一個一個定位,以下修改:
其餘那些洋洋灑灑的CSS都是多餘的,就下面這幾行足夠了,兼容性很是OK。
dl {
border: 1px solid #000;
position: relative;
}
dd {
position: absolute;
right: 0;
margin: -1.2em 0 0 0;
}複製代碼
爲何有用,這裏不展開,《CSS世界》這本書的絕對定位部分有解答,有興趣能夠買一本看看。
relative
定位有時候很是管用,只是這裏不太合適:
dd {
position: relative;
top: -20px;
}複製代碼
最大的問題在於relative
元素定位的時候本來佔據的空間他是依然保留的,因而會留下一片空白區域。這裏,咱們能夠把top
改爲margin-top
效果就能夠了。
還有的實現是直接左右浮動,沒有定寬:
dt{
float: left;
clear: left;
}
dd{
float: right;
clear: right;
}複製代碼
這個方法要比左右各50%寬度要好一些,適用的場景要更廣一些。
這裏的clear:right
是多餘的,沒有任何做用。更好的寫法是這樣:
dt{
float: left;
clear: both;
}
dd{
float: right;
}複製代碼
可是,若是把極端狀況考慮在內,單純的左右浮動啊,容易產生錯位的問題,例如,咱們右側的描述內容較多的時候:
這就引出了下面一個比較重要的點,一個好的佈局實現,應該要能應付各類極端場景。
對於文本內容而言,所能出現的極端場景,包括下面三種:
若是本文的小測題是一道面試題的話,最終對候選人的評價最加分的不是用了什麼新技術,也不是用了什麼稀奇古怪的奇巧淫技,而是可否預知到可能遇到的場景並在代碼層面作好容錯處理。這不只能夠體現出足夠的開發經驗,還能體現出全局意識,以及很是重要的基本功。
咱們一個一個來。
首先是連續英文字符。這個簡單,咱們可使用word-break
屬性:
word-break: break-all;複製代碼
其次是沒有文字內容。這個問題其實是開發的鍋,在內容輸出的時候,若是沒有數據,應該範圍「暫無」,或者「-」這樣的缺省信息,可是,多年的經驗告訴我,從內容輸出和呈現上,必定不要相信後臺開發人員,咱們本身必定要留一手,不然出現了佈局問題,報告單是提到前端這裏的。
咱們能夠這麼處理:
dd:empty::before {
content '-';
color: #999;
}複製代碼
這樣,即便dd標籤裏面沒有輸出任何文字,也會有字符佔位,這樣,佈局就很是穩固。
最後是文字內容不少,這樣就要靠佈局策略了。
這個佈局比較好的實現是這樣子的:左側固定寬度,寬度足夠安全,右側自動填滿剩餘空間。
根據我多年的開發經驗,左側的標籤描述雖然也有動態特性,可是內容倒是產品經理決定的,不是用戶輸入的,所以,個數可控,不要擔憂會超過四個字,中文就有這個特性。所以,咱們能夠放心大膽地把左側dt標籤佔據空間設定爲5
。必定要使用
emem
單位,不要使用px
或者rem
,這樣,不管容器的字號大小是多少,左側寬度都不會空間不足,很是彈性,容錯性很強。
而後,剩下的就是讓右側dd標籤內容寬度自動填滿剩餘空間。
方法不少。
這是其中一我的的解答,代碼雖少,倒是很是棒的解答:
dt { position: absolute; }
dd {text-align: right; }複製代碼
可是,若是文字內容不少,則dd標籤裏面內容會和dt發生重疊:
因此,咱們加個5em
大小的左margin
就能夠了。
dt { position: absolute; }
dd {text-align: right; margin-left: 5em; }複製代碼
效果以下截圖:
此方法兼容性很是好,低版本IE瀏覽器也支持,但position:absolute
元素的層疊順便比較高,若是頁面佈局複雜,出現了元素層疊的場景,則此方法須要斟酌下,由於極可能會增長佈局的複雜度(額外的z-index
進行層級控制)。
Flex佈局也能實現咱們想要的效果,代碼以下:
dl {
display: flex;
flex-wrap: wrap;
}
dt {
width: 5em;
}
dd {
width: calc(100% - 5em);
text-align: right;
}複製代碼
效果以下截圖:
關於Flex佈局,若是不瞭解,能夠參見我以前寫的文章:「寫給本身看的display:flex佈局教程」。
Flex佈局的優勢是佈局的呈現傻白甜,很好理解。不足就是會存在些許兼容性問題,在一些老舊的Android手機上。
Grid佈局實現是容錯性最強,語義最佳的方法,其最大的優勢是,左側的標籤描述文字,就是5個漢字,6個漢字,佈局依然堅挺。
代碼以下:
dl {
display: grid;
grid-template-columns: auto 1fr;
grid-column-gap: 1em;
}
dd {
text-align: right;
}複製代碼
效果以下截圖:
關於Grid佈局,若是不瞭解,能夠參見我以前寫的文章:「寫給本身看的display:grid佈局教程」。
若是沒有兼容性方面的限制,則是最佳實現。
有人就用了下面的方法實現,對說明對CSS基礎知識仍是比較瞭解的。
dt {
width: 5em;
float: left;
}
dd {
text-align: right;
overflow: hidden;
}複製代碼
關鍵是這裏的overflow:hidden
,文字內容再多也不會浮動環繞,具體原理能夠見這篇文章「CSS深刻理解流體特性和BFC特性下多欄自適應佈局」。
效果以下截圖:
此方法兼容性很不錯,直到IE7瀏覽器都支持,IE6不支持,若是要兼容IE6,能夠試試加一句_display:inline-block
。
這個方法是我最後補充的沒有人提到的方法,是最最簡單的實現:
dd {
margin: -1.5em 0 0 5em;
text-align: right;
}複製代碼
就結束了。
寬度自適應,字號大小自適應,文字個數自適應。
效果以下截圖:
此方法兼容性最強,上至IE6瀏覽器都支持,代碼最少,各類優勢也都有,也不須要掌握什麼flex佈局,grid佈局,也不須要了解什麼BFC之類概念,就一個簡簡單單的margin
屬性就搞定了,是實際項目開發中的最佳實現。然而,至少在答題的這波人中,卻沒人知道這個方法,什麼緣由呢?
以上五種方法,雖然代碼是不一樣的,可是最終實現的效果倒是如出一轍的。
眼見爲實,您能夠狠狠的點擊這裏:CSS小測1比較好的5種佈局實現demo
demo頁面能夠調整容器的寬度和字號大小,咱們能夠看到全部5個佈局都表現良好,以下GIF錄屏示意:
實現完整代碼以下:
/* 公共部分 */
dl {
line-height: 1.5;
margin: 0; padding: 10px;
border: 1px solid #ccc;
background-color: #fff;
}
dd {
word-break: break-all;
text-align: right;
margin-left: 0;
}
dd:empty::before {
content: '-';
color: #999;
}
/* absolute實現 */
dt {
position: absolute;
}
dd {
margin-left: 5em;
}
/* flex實現 */
dl {
display: flex;
flex-wrap: wrap;
}
dt {
width: 5em;
}
dd {
width: calc(100% - 5em);
}
/* grid實現 */
dl {
display: grid;
grid-template-columns: auto 1fr;
grid-column-gap: 1em;
}
/* float實現 */
dt {
width: 5em;
float: left;
}
dd {
overflow: hidden;
}
/* 流體特性實現 */
dd {
margin: -1.5em 0 0 5em;
}複製代碼
上週新建了一個粉絲羣,週三發小測題目,每週依次是CSS、DOM和JS,週六上午答疑,答疑方式是直播加羣聊。我估計下週就會羣滿,到時候想進來估計也進不來了。想入羣的能夠加我好友:zhangxinxu-job,申請信息「入羣」,最好帶上本身的姓名,方便我備註。
直播地址是:live.bilibili.com/21193211
答疑直播時間爲每週六上午10:00-11:00,有時候睡懶覺可能會延後一點。
入羣和直播都是免費的,若是你以爲有所收穫,想要表示感謝,能夠關注個人公衆號,或者買本個人《CSS世界》就能夠了。
(本文完)