摘錄自 "《CSS核心技術詳解》" 1.1 CSS中你可能會疑問的幾個問題 1.1.1 在CSS中為什麼要有層疊 在CSS中可能會有多個樣式表同時影響同一個元素的某個屬性,設計這個功能的主要原因有兩個,解決模塊化和作者、用戶、用戶代理樣式衝突。 模塊化 一個頁面中的樣式可以拆分成多個樣式表,代碼如下 ...
摘錄自《CSS核心技術詳解》
1.1 CSS中你可能會疑問的幾個問題
1.1.1 在CSS中為什麼要有層疊
在CSS中可能會有多個樣式表同時影響同一個元素的某個屬性,設計這個功能的主要原因有兩個,解決模塊化和作者、用戶、用戶代理樣式衝突。
- 模塊化
一個頁面中的樣式可以拆分成多個樣式表,代碼如下。
@import url(style/base.css);
@import url(style/layer.css);
但這種方式也會隨之產生一個問題,即如果對某個元素的同一個屬性設置樣式,到底應用誰的呢?
- 作者/用戶/用戶代理
當作者(寫代碼的人)和用戶(瀏覽頁面的人),以及用戶代理(一般指瀏覽器)都能更改樣式表時,也會產生同樣的問題:究竟用誰設置的樣式,因此CSS層疊機制就顯得格外重要。
1.1.2 為什麼“@import”指令需要寫在樣式表的開頭
代碼如下。
@import url(style/layer.css);
body{
background-color:red;
}
“@import”指令之所以需要寫在樣式表的開頭,是因為這樣可以使後面的樣式能更好地層疊導入進來的樣式。
1.1.3 當CSS值為0時為什麼可以省略單位
因為當CSS值為0時,任何單位的結果都是一樣的,就像數學中的0乘以任何數都得0。
1.1.4 margin垂直外邊距摺疊的意義是什麼
margin垂直外邊距摺疊的特性主要來自傳統排版,舉個例子,代碼如下。
<style>
body,ul,li{
margin:0;
padding:0;
}
ul{
list-style:none;
}
ul>li{
margin:20px 0;
}
</style>
<ul>
<li>1111111111</li>
<li>2222222222</li>
<li>3333333333</li>
</ul>
效果如圖 1.1
從圖1.1中可以看到3行數字的垂直外邊距都是一樣的。如果沒有這個特性,第一行數字與下麵兩行數字的外邊距就不一樣了,因為我們給每個li都設置了一個上下外邊距,假如沒有外邊距摺疊,那麼第1個li的下邊距加上第2個li的上邊距,就是兩倍的間距了,但是第一個li上面沒有其他元素,所以它只有一個上邊距,最終導致的結果就是,第1個li和後面的幾個li的外邊距不一樣,這顯然不是我們所希望的。而margin外邊距摺疊功能正是要在這種情況下,讓格式可以好看一點。
1.1.1 CSS層疊規則
在介紹CSS層疊規則之前首先舉個例子,代碼如下。
<style>
.box{
color:red;
font-size:18px;
}
</style>
<div class="box">
<a href="#">層疊</a>
</div>
結果如圖1.2所示:
按理說顏色是可以繼承的,那麼為什麼a標簽的顏色沒有變成紅色呢?審查一下元素,如圖1.3所示。
從圖1.3中可以看到繼承的顏色被劃掉了,出現這個問題的原因是瀏覽器對a標簽設置了預設樣式,將繼承的樣式層疊了,因為繼承的樣式權重最小。下麵介紹CSS關於層疊規則是怎麼計算的。
在CSS中一個樣式可能會來自不同的地方,分別是作者,用戶以及用戶代理。那麼問題來了,如果在這幾份樣式中,他們對同一個元素的同一個屬性做了不同的操作,那麼用戶代理應該如何處理這段CSS呢?舉個例子,代碼如下。
/* 作者 */
.box{
color:red;
}
/* 用戶代理 */
.box{
color:green;
}
/* 用戶 */
.box{
color:pink;
}
可以看到用戶代理以及用戶的代碼和作者寫的樣式起衝突了,而CSS的層疊規則就是為瞭解決這些問題的,以下是一些CSS層疊規則。
在層疊中每個樣式規則都有一個權重值,當其中幾條規則同時生效時,權重值最大的規則優先。一般來說作者指定的樣式權重值高於用戶樣式權重值,用戶樣式權重值高於客戶端(用戶代理)權重值。
在層疊順序中,以下權重值從小到大。
- 用戶代理樣式
- 用戶一般樣式
- 作者一般樣式
- 作者重要樣式(!important)
- 用戶重要樣式(!important)
- 如果是兩個樣式來自同一個地方,如都來自作者,並且它們的樣式聲明同樣重要,則根據特異度來計算,特異度高的會覆蓋特異度低的
- 如果特異度也相同,則越往後的樣式優先順序越高
!important聲明規則
!important聲明的樣式比一般聲明優先順序高,並且用戶設置的!important比作者設置的!important優先順序高。這樣做的原因是為了方便用戶實現一些特殊的要求,例如頁面字體大小的調整等。
下麵舉一個!important規則的例子,代碼如下。
<style>
.box{
color:red !important;
}
.box{
color:green;
}
</style>
<div class="box">!important</div>
在正常情況下,後一個“color:green”會層疊前一個“color:red”,但這裡我們給“color:red”設置了!important規則,所以前一個優先順序高。
選擇器特異度的計算
- 如果一個聲明出現在元素的style屬性中,則將a計為1
- b等於選擇器中所有id選擇器加起來的數量和
- c等於選擇器中所有class選擇器和屬性選擇器,以及偽類選擇器加起來的數量和
- d等於選擇器中所有標簽選擇器和偽元素選擇器加起來的數量和
將a、b、c、d這4個數字連接起來(a-b-c-d)就組成了選擇器的特異度。一段特異度的計算,如下所示。
.box{} /* a=0 b=0 c=1 d=0 特異度 = 0,0,1,0 */
.box div{} /* a=0 b=0 c=1 d=1 特異度 = 0,0,1,1 */
#nav li{} /* a=0 b=1 c=0 d=1 特異度 = 0,1,0,1 */
p:first-line{} /* a=0 b=0 c=0 d=2 特異度 = 0,0,0,2 */
style="" /* a=1 b=0 c=0 d=0 特異度 = 1,0,0,0 */
它們的比較順序是先比較a,如果a的值都相同,那麼接著比較b、c、d的值,誰的數大則優先順序就越高。
在使用CSS選擇器時,你需要註意以下兩點。
- 繼承的優先順序最低,沒有特異度;
- 結合符(如+、>等)及通用選擇符(*)特異度為0。
因此,可以知道之前a標簽color屬性為什麼沒有被應用了,因為繼承的優先順序最低。
1.1.6 CSS的命名
在代碼復用時,也許你寫過類似以下這樣的代碼,代碼如下
size-10{
font-size:10px;
}
雖然這段代碼看起來沒什麼問題,但如果考慮到可維護性,那就有很大問題了。假如有一天你不想用10px,想改成12px,也許你會想再加一個class就行了,修改後的代碼如下
size-10{
font-size:10px;
}
size-12{
font-size:12px;
}
但那些頁面中原本用的size-10的class都得修改成size-12,所以不建議這麼修改代碼。筆者建議用語義的方式命名,代碼如下
.size-small{
font-size:12px;
}
這樣寫代碼的好處是當需要調整字體大小時,只需修改一處,而不必修改添加到元素上的class。也就是說不要按照顯示的效果命名,而是根據這個class的用意命名。
1.2 CSS的一些技巧
1.2.1 使用pointer-events控制滑鼠事件
可以用CSS中的pointer-events來控制元素什麼時候響應滑鼠事件,比較常用的一個場景是獲取驗證碼,如圖1.4所示。
圖1.4 獲取驗證碼
當用戶單擊“獲取驗證碼”按鈕後,需要等待60秒才能再次單擊“重發驗證碼”按鈕,在這種情況下,就可以嘗試用pointer-events實現禁用滑鼠單擊事件。在pointer-events屬性中有一個none值,將pointer-events的值設置成none就不會響應滑鼠事件了。舉一個獲取驗證碼的例子,代碼如下。
<style>
a{
color:red;
}
.disable{
pointer-events:none;
color:#666;
}
</style>
<a href="javascript:;" id="btn">發送驗證碼</a>
<script>
var oBtn = document.getElementById('btn');
oBtn.onclick = function(){
oBtn.classList.add('disable');
setTimeout(function(){
oBtn.classList.remove('disable');
},3000)
};
</script>
如果看不懂這段代碼也沒關係,將這段代碼複製下來即可。這段代碼的意義就是定義了一個滑鼠事件禁用的class,單擊“發送驗證碼”按鈕後加上剛剛定義的.disable,3秒以後再將這個class去掉。預設情況下的按鈕,如圖1.5所示
圖1.5 預設情況下
單擊此按鈕後,在3秒內不會再次響應單擊事件。
pointer-events除了可以實現此功能之外,還有很多用處,比如實現a標簽禁止頁面跳轉,提高網頁性能,用戶在滾動頁面時可能會不小心碰到一些元素上綁定的事件,這些事件就會被觸發,從而浪費資源,但如果在頁面滾動時給body加上pointer-events:none;屬性,那麼就避免了這個問題。
pointer-events還有一個妙用,比如將一個遮罩層元素的屬性設置為pointer-events:none;,這樣就可以單擊到遮罩層後面的內容,如圖1.6所示。
圖1.6 運用了pointer-events以後
如圖1.6所示可以看到選中了遮罩層後面的內容,但需要註意的是,pointer-events:none只是用來禁用滑鼠的事件,通過其他方式綁定的事件還是會觸發的,比如鍵盤事件等。另外,如果將一個元素的子元素pointer-events設置成其他值,如auto,那麼當單擊子元素時,還是會通過事件冒泡的形式觸發父元素的事件。
1.2.2 玩轉CSS選擇器
1. 當父元素只有一個子元素時會被選中,代碼如下
<style>
div:first-of-type:last-of-type{
color:red;
}
</style>
<div>123</div>
當只有一個div元素時,效果如圖1.7所示。當有多個div時不會被選中,效果如圖1.8所示。
圖1.7 當只有一個div時
圖1.8 當有多個div時
當然更簡單的方法是直接用CSS3中的結構性偽類選擇器,當父元素只有一個子元素時會被選中,如下:
:only-child
不妨去試試。
2.當父元素有多個子元素時,選中第1個
<style>
div:not(:last-of-type):first-of-type{
color:red;
}
</style>
<div>11111</div>
只有一個子元素時,不會被選中,效果如圖1.9所示。當有多個子元素時,它會選中第一個,效果如圖1.10所示。
圖1.9 只有一個子元素時
圖1.10 當有多個子元素時
當然,如果有多個子元素時,也可以選擇其中任意一個子元素,但最後一個是選中不了的,因為我們已經用“:not”否定了最後一個元素。如果想要擺脫這種限制,可以使用下麵這種方案,代碼如下。
:not(:only-child)
以有多個子元素時選中最後一個子元素為例,代碼如下
<style>
div:not(:only-child):last-of-type{
color:red;
}
</style>
<div>11111</div>
<div>22222</div>
<div>33333</div>
當一個父元素有多個子元素時,最後一個元素會被選中,效果如圖所示。
案例
合理使用這些選擇器可以做很多事情,比如當只有一個子元素時,可以讓它居中顯示,如果有多個子元素時,可以讓它水平排列,代碼如下
<style>
.box div{
width:100px;
height:100px;
border:1px solid red;
margin:0 auto;
}
.box div:not(:only-child){
float:left;
margin-left:20px;
}
</style>
<div class="box">
<div></div>
</div>
當只有一個子元素時,這個div就會被居中顯示,如下圖1.12
圖1.12 當只有一個子元素時,這個div就會被居中顯示
當有多個子元素時,效果如圖所示
1.2.3利用padding實現元素等比例縮放
padding和margin有一個很奇怪的特點,它們的上下外邊距的百分比是根據父元素的寬度來計算的。舉個例子,代碼如下。
<style>
.box{
width:100px;
height:10px;
}
.box div{
width:100%;
padding-bottom:100%;
background-color:red;
}
</style>
<div class="box">
<div></div>
</div>
效果如圖1.14
圖1.14 padding、margin上下外邊距的百分比
在此例子中可以看到 div 的寬度和高度都是 100px。如果根據父元素的高度來計算,那麼div 的高度最終應該是 10px,而不是 100px,因此,若需要實現一個等比例的元素,就可以利用這個特性,但如果使用這種方式,還需要解決另外一個問題,就是如果直接在子元素div中寫入內容,那麼高度會被“撐開”,那就不是等比例了。代碼如下。
<div class="box">
<div>padding-bottom</div>
</div>
若在div中加入一段文字,那麼高度就不再是等比例了,效果如圖1.15所示。
圖1.15 在div中加入一段文字後的高度
但是可以將代碼進行修改,修改後的代碼如下。
<style>
.box{
width:30%;
height:10px;
}
.box div{
position:relative;
overflow:hidden;
background-color:red;
}
.box div::after{
content:'';
display:block;
padding-top:100%;
}
.box div span{
position:absolute;
left:0;
top:0;
}
</style>
<div class="box">
<div>
<span>圖片</span>
</div>
</div>
利用偽元素的padding-top來“撐開”父元素的高,內容通過絕對定位來使用,因為絕對定位的元素是不占位置的,這樣一個等比例寬高縮放就完成了。有時這種特性很有用,比如針對下麵這個需求,如圖1.16所示。
現在需要將圖片等比例縮放,也就是寬和高一樣,但圖片的寬度是自適應屏幕大小的,img標簽在只寫寬度不寫高度的情況下,高度會自適應寬度。圖片沒有載入出來之前的情況,如圖1.17所示。
圖1.16 需求
這個需求是這樣的,圖片等比例縮放,也就是寬和高得一樣,但問題是圖片的寬度是自適應屏幕大小的,原本很簡單因為img標簽在只寫寬度不寫高度的情況下,高度會自適應寬度,但問題不在這,而是如果圖片在沒有載入出來的情況下,會是這樣的,如圖1.17
圖1.17 在圖片沒有載入出來時
從圖1.17可以看到在圖片沒有載入出來之前高度就沒有了,這時利用CSS屬性paddding-top就可以解決這個問題,代碼如下
.photo a{
position:relative;
float:left;
width: calc(33.33% - 1.6rem);
margin:1.2rem 0 0 1.2rem;
outline:1px solid #dedede;
}
.photo a::before{
content:'';
display:block;
padding-top:100%;
}
.photo a img{
position:absolute;
left:0;
top:0;
width:100%;
height:100%;
}
使用一個偽元素將高度“撐起來”,而圖片通過定位來做。還有一種更簡單的做法,就是直接給a標簽設置高度,單位使用vw。vw單位是相對於視口(屏幕)寬度的,代碼如下。
.photo a{
float:left;
width: calc(33.33% - 1.6rem);
height: calc(33.33vw - 1.6rem);
margin:1.2rem 0 0 1.2rem;
outline:1px solid #dedede;
}
.photo a img{
display:block;
width:100%;
height:100%;
}
寬度怎麼設置,高度就怎麼設置,就是把百分比換成vw。但是只在自適應方面才能這樣用,如果是固定的寬、高,直接設置成一樣的就行了,雖然vw可以實現,但相容性還不是很好。
1.2.4 calc函數
在CSS中,如果需要用計算的功能,那麼calc函數將非常有用。calc函數允許進行任何長度值的計算,運算符可以是加(+)、減(-)、乘(*)、除(/)等。但需要註意的是,運算符前後都需要保留一個空格,雖然在某些特殊情況下可能不需要,但最好都加上,下麵來介紹一些calc函數的使用場景。
場景一:
如圖1.18所示,圖中的內容一旦超過了浮動元素的高,那麼這些文本就會與圖片左對齊,這種效果並不是我們想要的。我們想要的效果,如圖1.19所示。
圖1.18 實際不理想的效果
圖1.19 預期理想的效果
如果知道圖片的寬度,那麼解決這個問題也很簡單,給這段文本添加一個左邊距即可,但如果圖片使用的是百分比,那麼就無能為力了,而如果使用calc函數可以很好地解決這個問題,代碼如下。
<style>
.box img{
width:50%;
float:left;
}
.box p{
margin-left:calc(50% + 10px);
}
</style>
<div class="box">
<img src="psb.jpg" alt="">
<p>......</p>
</div>
利用calc函數更改代碼後的效果如圖1.20所示
圖1.20 利用calc函數的效果
場景二:
有時使用百分比會出現一個問題,如圖1.21所示。
圖1.21 使用百分比時可能會出現的問題
其中CSS代碼為
<style>
.box img{
width:25%;
margin:20px;
float:left;
}
</style>
導致這個問題出現的原因是使用了margin值,而代碼中的width:25%並沒有減去這個margin值。因此只需要用calc函數減去margin值就可以了,代碼如下
<style>
.box img{
width:calc(25% - 40px);
margin:20px;
float:left;
}
</style>
最終效果如圖1.22所示
圖1.22 使用calc函數的最終效果圖
場景三:
如果再結合媒體查詢,那麼就很容易實現一個響應式的佈局,代碼如下。
<style>
.box img{
width:calc(100% / 4 - 40px);
margin:20px;
float:left;
}
@media (max-width:600px){
.box img{
width:calc(100% / 2 - 40px);
}
}
</style>
這段代碼表示在屏幕不小於600px時,一行最多可以放4張圖片,如果屏幕小於或等於600px時,一行最多只能放兩張圖片。
1.3 隱藏元素
千萬不要小看“隱藏”這個技能,多瞭解一點,就多一種選擇。如果你是一個新手,就會發現在本節將出現很多你不認識的屬性,它們可能是在CSS 2中就有的屬性,也可能是在CSS 3中出現的新屬性。
1. 通過設置width:0;或height:0;隱藏一個元素
div{width:0;}
或
div{height:0;}
一個物體是由寬和高組成的,那麼至少這個物體得有寬和高,這種方式的缺點是隱藏不了文字。可以將元素的color設置成與背景色一樣的顏色,這樣就看不見了。也可以設置成透明色(transparent),但問題是它們的內容還是存在的,所以需要將文字的大小設置成0,代碼如下
div{font-size:0;}
2. 將元素的opacity:0;設置成0
div{opacity:0;}
元素本身還在,只是看不見而已
div{
opacity:0;
filter:alpha(opacity:0);
}
3. 通過定位將元素移出屏幕範圍
div{
position:absolute;
left:-9999px;
}
元素還在,只是超出了屏幕範圍,看不見了而已。
4. 通過text-indent實現隱藏文字效果
div{text-indent:-999999px;}
給頁面添加LOGO圖片,若還想讓搜索引擎搜索到,則需要添加這段文字,但如果又不想顯示這段文字,就可以使用這個方法。
5. 通過z-index隱藏一個元素
<style>
.box{
position:relative;
}
.box .item{
position:absolute;
left:0;
top:0;
width:100px;
height:100px;
border:1px solid red;
z-index:-1;
}
.box .item:first-of-type{
z-index:1;
}
</style>
<div class="box">
<div class="item">程式員</div>
<div class="item">設計師</div>
</div>
但你會發現文字被“透”上來了,效果如圖
因為預設的背景設置是透明的,並且允許下麵的元素“透”上來,想解決這個問題很簡單,就是給元素添加一個背景,代碼如下。
.box .item{
position:absolute;
left:0;
top:0;
width:100px;
height:100px;
border:1px solid red;
background-color:#fff;
z-index:-1;
}
設置完成後,效果如圖
6. 通過給元素設置overflow來隱藏元素
div{
width:100px;
height:100px;
overflow:hidden;
}
如果元素超出所設置的寬和高,溢出的部分就會被隱藏。如果想讓整個元素隱藏,將元素的寬和高設置成0即可。經常通過這種方式將超出的文字隱藏,代碼如下
<style>
h2{
width:16ch;
overflow:hidden;
white-space:pre;
text-overflow:ellipsis;
}
</style>
<h2>享受一片寧靜的天空。</h2>
當中文超出7個字元以後,文字就會被隱藏,效果如圖
7. 通過visibility將元素設置為不可見
div{visibility:hidden;}
雖然元素不可見,但還占位置。
8. 通過display將元素徹底隱藏
div{display:none;}
元素會被隱藏,並且不占位置。
9. 將元素的背景設置為透明,字體大小設置為0
div{
font-size:0;
background-color:transparent;
}
元素還在,只是看不見。
10. 將元素的max-width或max-height設置為0
div{max-height:0;}
或
div{max-width:0;}
這樣元素的寬度就只能是0了,但是還有文字溢出的問題,如圖1.26所示。
圖1.26 文字溢出
儘管元素寬度是 0,但文字還是被顯示出來了,若想解決這個問題,將文字大小設置成0就可以了,或者使用代碼overflow:hidden;如果你仔細看這個效果,會發現它實際上是一個文字豎排的效果,不過對於英文來說,還得設置一個換行屬性,換行屬性代碼如下
<style>
h2{
max-width:0px;
word-break:break-all;
}
</style>
<h2>享受一片寧靜的天空AAA</h2>
效果如圖1.27
圖1.27 通過設置word-break:break-all;解決英文不換行問題
11. 通過transform的translate函數來隱藏一個元素
div{transform:translate(-99999px);}
和left:-99999px;
原理一樣,把元素移出屏幕可視區。
12. 將元素的縮放設成0
transform:scale(0);
看不見我,看不見我。
13. 讓元素重疊
div{transform:skew(90deg);}
元素重疊了,類似width
等於0。
14. 設置margin負值
div{margin-left:-999999px;}
將元素移出屏幕可視區
15. 將元素裁剪
-webkit-clip-path:polygon(0px 0px,0px 0px,0px 0px,0px 0px);
完,帶上愉快的心情,踏上CSS之旅。