CSS 的 margin 很困難
原文本來是有圖的,但我懶得把圖拖過來漢化了,欲配圖看文者請移師到原文博客內參詳。
toggle()及變數、calc、:match 之類,直至現在都沒有的 CSS 屬性、選擇器被提出・實行起來,CSS3、4都變得有趣起來了。
在老舊的 Android 瀏覽器以外,沒有 prefix 下使用 border-radius
、 box-shadow
等等的情況也逐漸增多了。
可能現在的最新技術在將來會變成必要的功能也說不定,不過基本功也是很重要的。
像是 float
、 position
之類,折騰 CSS 時總是會有幾個位置會瓶頸,今次挑了個我認為是當中最難的 margin 來寫。
不時會聽到「margin 的 bug 挺多的」。
不過隨著深入了解,也有可能把正常動作跟 bug 互相混淆的情況發生。
如果能正確理解 margin 的話,就能築起一個具效率性的結構。雖然敘述的只是很基本的內容,如果能讀一下的話我會很榮幸的。 ## 盒子模式 (box model) ##
在說明 margin 之前,首先有需要先認識盒子模式。
當運用 CSS 時,下圖也必須要理解一下。
在 CSS 指定 margin 之後,上面圖解裡有寫 margin 的部份,就可以輸入相應的值。
margin 的種類
margin 有著多種類的屬性,同時也可省略。
個別指定
-
margin-top
-
margin-bottom
-
margin-left
-
margin-right
省略
-
margin: 0;
(上下左右) -
margin: 0 0;
(上下)(左右) -
margin: 0 0 0;
(上)(左右)(下) -
margin: 0 0 0 0;
(上)(右)(下)(左)
邏輯屬性
-
margin-start
-
margin-end
-
margin-before
-
margin-after
margin-top 等等應該已經司空見慣,我想應該會有人是第一次看見 margin-start。
margin-(start|end|before|after) 是要禁止書寫方向旋轉而被提出來的。
margin-top 是從畫面看到的物理方向。相對的,
margin-start 就變成邏輯方向。
當使用這個屬性時,同一 CSS 裡的橫向、直向書寫都能保持平滑的切換。(不過 IE8 等等瀏覽器不能實現。)
margin, auto 和 width 的關係
在 margin 上設值為 auto 的話,正常都會是 0。
不過,如果指定了寬度 (width) 的話,不管 margin 的左右哪一方被設成 auto 也好,都會自動算出指定的數值。
當瀏覽器的寬度是 600px 的情況下
div {
width:100px;
margin-left:auto;
}
在上面的例子得出,因為瀏覽器寬度是 600px,所以 margin-left 會自動變成 500px。
margin 的中央配置
在指定了寬度、margin-left 和 margin-right 指定了 auto 的情況下,左右的 margin 會被等分,盒子就會被配置於中央了。
Negative Margin
把 margin 的數值指定成負值可以移動要素。
在把出了格局 (pattern) 以外的結構再重組時可以幫到很多忙。
例如,想把結構弄成像以下般的圖。
<style>
/* css reset */
ul {
margin: 0;
padding: 0;
list-style: none;
}
/* demo */
#list {
margin: 0 auto;
border: 1px solid #000;
padding: 8px 8px 0;
width: 488px;
}
ul {
margin-right: -8px; /* 在這兒指定 Negative Margin */
/* width: 496px; 新增:如果無視 IE6,7 的話此段可以刪除 */
overflow: hidden;
}
li {
float: left;
margin: 0 8px 8px 0;
width: 116px;
height: 116px;
background-color: #bbb;
}
</style>
<div id="list">
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
使用 Negative Margin 將會擴大結構的寬度。
可是如果不能理解上面每一行源碼的意思,那還是不要多用它了。
margin 與 padding
不管是 margin 還是 padding 也好,都能給予空間。
<style type="text/css">
.margin {
margin-bottom:10px;
}
.padding {
padding-bottom:10px;
}
</style>
<div class="margin">aa</div>
<div class="margin">bb</div>
<div class="padding">aa</div>
<div class="padding">bb</div>
margin 和 padding 都能給予空間。 表面上一樣,實則內裡運作完全不同。
padding 能把背景色、柵格 (grid) 留白,可是 margin 是可以把要素的全部給予間隔。
margin 的相沖
作為 padding 所沒有的功能,margin 卻帶有相沖作用。
這種作用會令 margin 之間產生重疊效果,覺得「margin 真的很難用」的最大理由也許就是因為這個。
有時候在特定的情況下,會產生出相沖不起作用的微妙現象。
因為太過微妙,初學者可能會心想「這不是有 bug 麼?」,而且還是經常性。
可是理解到 margin 的相沖理論,比起指定 margin 的單一方向、或使用 padding 來說,利用 HTML, CSS 來清除的情況還要多。
<style type="text/css">
.margin {
margin:10px 0; /* 上下指定距離 1em */
}
.padding {
padding:10px 0; /* 上下指定距離 1em */
}
</style>
<div class="margin">aa</div>
<div class="margin">bb</div>
<div class="padding">aa</div>
<div class="padding">bb</div>
在 padding 的情況下,div 的間是 10px+10px=20px。
在 margin 相沖作用下,div 之間只有 10px。
要理解相沖現象,把屬性比喻成人的話可能會更容易明白。
-
width
是骨骼 -
height
是身高 -
border
是皮膚 -
padding
是脂肪 -
margin
是心臟的距離
脂肪是一個物理的距離。
假設與心臟的距離是指 margin 的話,那麼 A 先生和心臟的距離是 1。 但是 B 先生不這麼認為,他覺得是 3。 即使 A 先生在進行心臟收縮的運動,但是客觀來說他們兩位的心臟距離實際會是 3。
margin 相沖的數值
數值愈大,便不會被相沖。
<style type="text/css">
.a {
margin:20px 0;
}
.b {
margin:30px 0;
}
</style>
<div class="a">a</div>
<div class="b">b</div>
在上方例子中,原本 div 之間的距離會是 20px + 30px = 50px。但因為相沖作用,較小的數值會被消除。
子元素相沖
即使是子元素的狀態也好,當 margin 重疊時便會發生。
兩方都有設定 margin 屬性時,相沖現象便會發生。
<style type="text/css">
.parent{
margin:10px 0;
}
.child {
margin:30px 0;
}
</style>
<div class="parent">
<div class="child">child</div>
</div>
子元素相沖的另一個例子
<style type="text/css">
.parent {
margin:30px 0;
}
.child {
margin:10px 0 40px;
}
</style>
<div class="parent">
<div class="child">demo</div>
</div>
上面的例子指出, .parent
的 margin-top 屬性的值比較大所以最終它的值會是 30px。
可是 margin-bottom 的值是 .child
的比較大所以最終的值會是 40px。
在相沖現象發生時,數值愈大愈會被優先處理。這點要好好記住。
不會導致相沖現象出現的個案
當父元素被指定了某類特定的 CSS,子元素便不會與之相沖。
- border (被 margin 指定了方向)
- padding (被 margin 指定了方向)
- overflow: hidden, scroll (auto, visible 值不適用)
- position: absolute, fixed (relative, static 值不適用)
還有除了上述的例子外,根據 float 屬性也會有相沖不會發生的情況。
margin 和 float 的關係
margin 和 float 有著緊密的關係。
要純熟的使用 float,一定要對 margin 有所理解。
在指定了 margin 的元素時再指定 float 的情況下
不管是父或子元素,指定了 margin 之上再加入 float 屬性就會令相沖無效。
又像是記載了 width 和 margin: auto; 的關係一樣,當指定 width 和 margin 值時的 auto,會自動算出數值。
可是在這個情況下,如果有指定 float 的話,margin 中的 auto 數值必定歸 0。
指定 margin: auto;
和 float
的例子
<style>
div {
margin:10px auto;
width:300px;
float:left;
}
.child {
margin:10px;
width:100px;
height:100px;
float:left;
}
</style>
<div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>
- 不管是父或子元素,只要是有指定 float 屬性的話,父子元素不會發生相沖現象。
- 指定了 float 屬性以後,
margin: auto;
的值必定歸 0。 - 即使鄰近的元素也指定了 float,也不會構成相沖。
當使用 float 時,該元素的 margin 效果就會變得像 padding 一樣。
float 鄰接指定了 margin 的元素的情況下
在父子元素指定 float 不會導致相沖。
不過當 float 是鄰接著它們的話,margin 和 float 就會發生相沖。
以下是相沖的基本條件。
- float 鄰接著別的元素
- 鄰接著的元素被指定了 margin
- 鄰接著的元素沒被指定 float
會觸發的條件不限於以上所述的例子。
基於不同情況下會引起相沖或不相沖,在這兒就先捨棄其中一部份的說明。
以下是 margin 和 float 相沖的例子。
<style>
img {
float:left;
margin:0 10px;
}
.test {
margin-top:96px;
}
</style>
<img src="dummy.jpg" alt="" width="100" height="100" />
<img src="dummy.jpg" alt="" width="100" height="100" />
<img src="dummy.jpg" alt="" width="100" height="100" />
<div class="test">float 和 margin 的相沖</div>
只有一個元素被指定 margin 和 float 的話是不會引起 margin 的相沖。
可是,在上面的例子中可以看見,margin 和 float 滿足了相沖所需的條件,結果就會像圖中所示的動作一樣。
以及這樣的相沖,跟解除 float 的 clear
屬性也有著關係。
clear 不是用來解除 float 屬性的
經常有人說 clear 屬性是用來「解除 float」的,它其實是指示 float 要不要鄰接其他元素的一個屬性來的。
當中的動作跟剛才的例子非常相似。
clear 屬性的規格
Values other than ‘none’ potentially introduce clearance. Clearance inhibits margin collapsing and acts as spacing above the margin-top of an element. It is used to push the element vertically past the float.
Computing the clearance of an element on which ‘clear’ is set is done by first determining the hypothetical position of the element’s top border edge. This position is where the actual top border edge would have been if the element’s ‘clear’ property had been ‘none’.
Source: 9.5.2 Controlling flow next to floats: the ‘clear’ property - W3C Visual formatting model
clear 會抑制 margin 的相沖,使得元素像接近 float 的底部般,margin-top 的值會採取自動調整般的舉動。
在 clear: none;
的情況下,會作出正常舉動。
以下是 margin, float, clear 的例子。
<style>
img {
float:left;
}
.right {
float:right;
width:80px;
height:240px;
margin-bottom:10px;
}
.clear {
margin-top:3000px;
clear:left;
}
.clear-right {
margin-top:-1000px;
clear:right;
}
</style>
<img src="dummy.jpg" alt="" width="80" height="80" />
<img src="dummy.jpg" alt="" width="80" height="80" />
<img src="dummy.jpg" alt="" width="80" height="80" />
<div class="right"></div>
<div class="clear">clear:left;</div>
<div class="clear-right">clear:right;</div>
.clear
已被指定成 margin-top: 3000px。理應中間會有 3000px 的間隔,但是它緊接著指定了 float: left
的 img 的底部。
這就是 clear 有的規格。
因為指定了 clear: left;
,margin-top 會自動調整,含有 float: left;
的元素便會緊接著前面的。
同時,指定了 float: right;
的 .right 的底部(含 margin),跟 .clear-right 重疊起來了。
這個情況並不是 margin-top 在調整 .right 的頂部至底部。
其實它在調整從 .clear 至 .right 底部的距離。
要說為什麼,雖然那些指定了 float 的隣接元素和 margin 會變成相沖關係,但是像 .clear 般的隣接元素不會跟 margin 發生相沖關係的情況下,那兒才是出發點。
如果要同時使用 clear 和 margin-top 的話,要注意 clear 屬性是會優先處理的。
相沖的優點
雖然是麻煩又複雜的 margin,但當要給予適當間隔的時候……
利用相沖等等技巧,在很多情況下都能寫出整潔的代碼。
來把一個結構以寫成 margin 和 padding 的方式比較一下。
已完成的結構
margin
<style type="text/css">
dl {
margin: 3em 0;
}
dt {
display: inline;
float: left;
}
dd {
margin: 1em 0 3em 120px;
}
p {
margin: 1em 0;
}
</style>
<dl>
<dt>margin Demo 1</dt>
<dd>從 dd 元素中直接輸入文字</dd>
<dt>margin Demo 2</dt>
<dd>
<p>把 p 元素放進 dd 元素裡。</p>
<p>dd 和 p 的 margin 重疊,產生相沖。</p>
</dd>
<dt>margin Demo 3</dt>
<dd>
<img src="dummy.jpg" alt="" width="480" height="32">
<p>即使插入圖像也會空出間隔來。</p>
<p>再往下插入也沒有問題。</p>
<img src="dummy.jpg" alt="" width="480" height="32">
<p>繼續往下插入也沒有問題。</p>
</dd>
</dl>
padding
<style type="text/css">
/* CSS Reset */
dl, dd, p {
margin: 0;
}
dl {
padding: 3em 0;
}
dt {
display: inline;
float: left;
}
dd {
padding: 0 0 3em 120px;
}
.pt {
padding-top: 1em;
}
.pb {
padding-bottom: 1em;
}
.last-child {
padding-bottom: 0;
}
</style>
<dl>
<dt>margin Demo 1</dt>
<dd>從 dd 元素中直接輸入文字</dd>
<dt>margin デモ2</dt>
<dd>
<p class="pb">把 p 元素放進 dd 元素裡。</p>
<p>因為要給予 p 元素間隔,不得不加 class 進去。</p>
</dd>
<dt>margin Demo 3</dt>
<dd class="last-child">
<img src="dummy.jpg" alt="" width="480" height="32">
<p class="pt pb">插入圖像的話會變成 class 祭典。</p>
<p class="pb">打算想加入一個新的 p 元素,不得不加 class 進去。</p>
<img src="dummy.jpg" alt="" width="480" height="232">
<p class="pt">過份增加 class 了。</p>
</dd>
</dl>
這是利用了 margin 的相沖和 padding 來建立結構的比較圖。
使用 padding 的話,於最後的元素要指定 padding-bottom: 0;
,把 class 分得更細了。
像是用 .mt10, .mt20等等的人,多數都是利用 padding 來指定。
相對來說,利用 margin 的相沖技巧,HTML 和 CSS 雙方都能縮短代碼量。
也能保持適當的間隔,特別是對於以常規動作插入但沒指定 CSS 的 img 來說,某程度還更好地進行維護。
上面就是恰當地使用相沖的例子。
於設計上來說,父元素若加入了 border, padding 等等屬性,有機會不能使用相沖。
可是,能理解相沖的話,書寫 HTML 或 CSS 的效率將會有所改變。
瀏覽器的預設 CSS
於瀏覽器預設的 CSS,跟 margin 是有關連的。
像是 h1 或 p 等等元素,在瀏覽器早已有 CSS 加載。
h1 {
margin:0.67em 0;
font-size:2em
}
p {
margin:1em 0;
}
這是會預算到有相沖為前提,所以才會在上下指定 margin。
根據這份 CSS,於「文章」恰當加入 markup 的話,便能保持適當的間隔。
當要用預設 CSS 發揮其作用去構築結構時,相沖這方面的考慮將會變得更重要。
經常都會被 margin 的規格和 bug 給難到。
折騰 margin 必須要有一定經驗和深入理解的程度才會成功。
解決 margin 的 bug 的方法也有很多,用作 CSS 的主要構築之上應該會成為一個很有用的屬性。
已有 0 人发表留言,猛击->> 这里<<-参与讨论
ITeye推荐