我腦中飄來飄去的css魔幻屬性

地鐵上的小前端發表於2017-12-14

最近看到一篇20 個CSS高階技巧彙總的彙總,感觸很深,不過我想,與技巧相比,有些常見css佈局難題,有時候更加讓我們的日常開發變得躊躇沮喪吧。 在寫這一篇文章之前,自己還寫過一篇:我所不注意的那些CSS冷知識,但卻阻止了我做專案的速度,如果你看了,我相信你也會受益的。

為什麼此處li標籤內的p元素看起來獨自撐開了一行

這是我在segmentfault上看到的一個問題,以前自己遇到過,所以就很熱情洋溢的去回答了一下,難道遇到個自己會的,示例程式碼是這樣的:
CSS:

li{
    display: inline-block;
    text-align: center;
}
.left,.center,.right{
    width:300px;
    height:300px;
}
.left{
    background-color: #999;
}
.center{
    background-color: #ccc;
}
.right{
    background-color: #eee;
}  
HTML:
<ul>
    <li class="left">
        <p style="display: inline-block;">1</p>  
    </li>
    <li class="center"></li>
    <li class="right"></li>
</ul>  
複製程式碼

測試圖片
大概就是這樣子,其實文和圖有點不對應,程式碼中第一個模組他只寫了一個“1”,我為了現象更加明顯,且好說明為什麼,就打了一大段文字,現在我們來說說為什麼。先來一張圖,看懂vertical-align的幾個屬性,順便帶上圖片出處,文章講得還可以,理解這張圖片,後面就好理解了。

vertical-align屬性

inline-block的vertical-align 屬性預設是baseline對齊(深入理解的送福利),也就是英文文字小寫字母a,b,c這類字母底部的那條線,因為這些是外國人發明的,所以以英文字母才有針對性。inline-block擁有vertical-align屬性,其預設是基線對齊的,所以這三個inline-box需要基線對齊,而其基準線就是正常流中最後一個line box的基線,如果這個元素是空的,沒有內容,那麼這個基線就是最後這個元素的margin-bottom線;如果這個元素不為空,那麼這個元素的基線就是元素裡面內容最後一行文字的基線;所以我們一個一個來套,發現這三個li元素在一行,第一個有文字,其基線為文字底部;最後一個沒有文字,其基線為margin-bottom線,考試要考,劃重點,可以自己為元素設定margin-bottom試試,這就會造成第一個和二,三個錯行的感覺,其實他兩是為了基線對齊,所以多敲幾十個文字就能明顯看出其差別。所以最簡單的解決方案就是為li新增vertical-align: 屬性不為baseline,氣不氣,改變其縱向的對齊方式的預設屬性;為啥非弄個折騰人勒。關於vertical-align,如果還想做這方面的深入瞭解,可以看看張大俠的分析

img圖片撐不滿整個div,有空隙

直接上圖更直觀(箭頭所指):

撐不滿的div
相關css和html:

<style>
    body,div{margin: 0;padding: 0;}
    .test{
        background-color: yellowgreen;
    }
    img{
        width:260px;
        height:260px;
    }
</style>
<body>
<div class="test">
    <img width="130" height="130" src="https://user-gold-cdn.xitu.io/2017/12/10/160409cc0f090c6f">
</div>
複製程式碼
其實這個問題,如果你單單這樣看,和我一樣涉世未深的話,是一眼看不出答案的,但是如果你在圖片後面多敲兩個文字,你就會發現,和上個問題,這又是一個有關於vertical-align屬性相關的問題。
<div class="test">
    <img width="130" height="130" src="https://user-gold-cdn.xitu.io/2017/12/10/160409cc0f090c6f"><span>abcd看文字</span>
</div>  
複製程式碼

讓人恍然大悟的效果圖:

恍然大悟的效果圖

這下你應該就懂了,下面的空隙的距離實際上等與1個line-height的底邊與baseline之間的間距。仔細觀察,圖片的底邊是和a的下邊緣是在一條水平線上的,而不是和‘看’字下邊緣一條水平線上的。所以為什麼上面說這又是一個和vertical-align屬性相關的問題。先說解決方案 針對於父元素div:

  • 設定行高足夠小,比如.test{line-height:0},至於這麼小嗎,其實高度小於top線和baseline線之間的距離的距離就行了,至於到底多小,這和font-size是相關的,其目的就是沒有多餘的高度拿來給baseline下面的空間用(個人理解);
  • 上面說了設定line-height最小和font-size相關,所以,還有的方法,就是直接設定字型大小為0,.test{font-size:0;},道理你應該懂;

針對於圖片div:

  • 上面說了這是一個和vertical-align屬性相關的問題,所以設定vertical-align屬性不為baseline也可以解決,比如img{vertical-align:top;},當然也可以是數字,比如img{vertical-align:-10px;},這個數值絕對不是正值,其數值應該是大於bottom線和line-height的底邊距離的;
  • 最後一種,就是vertical-align是一個對塊狀元素無效的屬性,僅針對於內聯元素有效的,當然inline-block也有效.所以img{display:block;}也可以解決問題。 也許到這裡,你和我一樣,有疑惑,為什麼vertical-align是一個對塊狀元素無效的屬性,設定img為塊級元素,其和div就可以完美在一起,而一個內聯元素放在塊狀元素裡,就非得有隔閡。開始,我也是有這個疑問的,個人理解就是塊狀元素裡面裝了一個內聯元素,如果塊狀沒有顯示的設定高度,其高度是由裡面的最高的lineboxes組成的,這個div其實就是有兩個lineboxes組成,圖片linebox和,其實還有一個linebox就是div自身的innerText(''),這不過這裡內容為空,如果你把span去掉,你就更能理解這個隱身的linebox,所以就像是兩個內聯元素在一起,需要baseline對齊。所以網上有人說設定img{font-size:0;},是非常錯誤的,img元素很特殊,他不但是內聯元素,他還是一個置換元素(下面會講),它的高度不是文字內容撐開的,是其置換的圖片高度撐開的,所以設定font-size是無效的。

浮動不按想要的方式浮

浮動常出現的形式
像上圖那樣的形式,盒子由導航欄和右側一個搜尋框或者登入名什麼的一起組成,這也是我們常用浮動的方式來解決這樣的佈局。
說浮動前,先說三點概念:
1.浮動最初出現的意義是為了解決文字環繞圖片這種在雜誌報紙中常會出現的佈局樣式; (看下圖) 2.浮動與絕對定位能實現相同的效果,但的區別是,浮動未脫離正常文件流,但絕對定位脫離了正常文件流;
3.浮動能帶來靈活的佈局,但同時也帶來了父元素高度塌陷的缺點(看下圖),所以清除浮動是使用浮動前的必修課,後面會說到;
文字環繞
高度塌陷帶來的佈局混亂
現在看一下高度塌陷相關的程式碼:

<div class="test">
<img width="130" height="130" src="https://user-gold-cdn.xitu.io/2017/12/10/160409cc0f090c6f">
1.浮動最初出現的意義是為了解決文字環繞圖片這種在雜誌報紙中常會出現的佈局樣式;<br>
2.浮動與絕對定位能實現相同的效果,但的區別是,浮動未脫離正常文件流,但絕對定位脫離了正常文件流;<br>
3.浮動能帶來靈活的佈局,但同時也帶來了父元素高度塌陷的缺點,所以清除浮動是使用浮動前的必修課,後面會說到;<br>
<br>
</div>
<div class="blank"></div>
<div>
<div class="box">
    <span class="dot"></span>
    我是下面一個div的文字。
</div>
    <div class="blank"></div>
<div class="box">
    <span class="dot"></span>
    我是再下面一個div的文字。。
</div>
    <input  width="260" value="輸入一段文字"/>
</div>
  
.test {
background-color: yellowgreen;
font-size: 18px;
vertical-align: top;
}
.test span {
    background-color: bisque;
}
.blank {
    line-height: 20px;
    height: 20px;
}
img {
    width: 260px;
    height: 260px;
    float: left;
}
input {
    border: 1px solid red;
    height: 24px;
    margin-left: 30px;
}
.box {
    background: black;
    color: white;
    padding-left: 20px;
    line-height: 10px;
}
.box .dot {
    display: inline-block;
    width: 4px;
    height: 4px;
    background: white;
    vertical-align: bottom;
}
複製程式碼

圖片一中,實現了文字環繞圖片那種想要的效果,並且後面的元素沒有上移錯位,其原因是上面說過的,如果塊狀元素沒有顯示的設定高度,其高度由其元素內的最高的linebox決定,所以第一張圖片div的高度是比img高度高的,因為我重要的事情說了三遍,文字夠多。而第二張圖片,div高度只有144px,因為img是浮動的,他的linebox被浮動屬性破壞了,而文字又不夠多,所以就造成了所謂的高度塌陷,致使最後兩個div陷進了圖片所在的div中,要知道,這種情況在正常塊狀元素佈局中是根本不會出現的。至於解決浮動引起的高度塌陷,我總結了兩條,分別是:

  1. 使用clear:both,常見的什麼clearfix;
  2. 觸發浮動元素父元素的BFC(塊狀格式上下文,為解決盒子與盒子之間,內容不相符影響而生的概念); 清除浮動,相信大家都懂,而觸發bfc。

我說說我常用的幾條,網上講bfc的很多:

  • float屬性不為none的元素

  • position(absolute,fixed)

  • display (table-cell,inline-block,flex等)

  • overflow屬性不為visible 除了上面講的這些,我還遇到過有人問,為什麼我用了浮動,但元素沒有浮在這一行,卻換了行,像下圖這樣

    浮動不按想要的方式浮

     <div>
      <div class="gr">我是導航欄的一些文字</div>
      <div class="fr">我想浮在右邊</div>
     </div>
     .gr{
        background-color: yellowgreen;
        margin:5px;
      }
      .fr{
        float:right;
        background-color: green;
      }
    複製程式碼

上面這種沒按想要的方式浮,是因為塊狀元素會不敢其內容長度有沒有一行的長度,其都會佔據一行的長度,後面的元素會自動換行。解決這個其最簡單的方式就是將fr元素放在gr元素前,為什麼這樣就可以,因為float破壞了div元素的塊狀屬性,但其未撐開父元素的高度,其浮動屬性為right,預設從右側開始佈局,所以後面的div仍按正常的文件流從最左端開始佈局。

有一種行內元素,又叫置換元素

如果你看上面一題程式碼的時足夠細心,你會發現我給img設定了width和height兩個屬性值為130,但由於又在css屬性裡定義了寬高260,但最終表現出的寬高為260。如果css不定義寬高呢?答案是多少,要不你試試,你慢慢試,我還是先公佈答案:130.這裡我們將會說一個css中的一個鮮為人知的術語:置換元素,那什麼又是置換元素呢?

置換元素是指:瀏覽器根據元素的標籤和屬性,來決定元素的具體顯示內容。

例如:瀏覽器根據我腦中飄來飄去的css魔幻屬性標籤的src屬性顯示圖片。input元素根據標籤的type屬性決定顯示輸入框還是按鈕。還有,還有近來很火的canvas。

置換元素有如下共同點:

  1. 置換元素一般內建寬高屬性,因此可以設定其寬高;
  2. 置換元素與一般的行內元素相比,其可以設定margin,padding,height,width等css屬性;

感覺要寫的還有很多,事件根本不夠用,先睡了,未完待續
如果文中有任何不足和錯誤之處,還請及時指正。

本文首發於:closertb.site

版權所有,歡迎保留原文連結進行轉載:)

相關文章