1. 程式人生 > >不要告訴我你懂margin

不要告訴我你懂margin

轉自地址: http://www.hicss.net/do-not-tell-me-you-understand-margin/

你真的瞭解margin嗎?你知道margin有什麼特性嗎?你知道什麼是垂直外邊距合併?margin在塊元素、內聯元素中的區別?什麼時候該用padding而不是margin?你知道負margin嗎?你知道負margin在實際工作中的用途嗎?常見的瀏覽器下margin出現的bug有哪些?……

寫css,你少不了與margin打交道,而對於這個平時我們最常用的css屬性我們並非十分了解。介於此我打算寫下這篇文章,一來是自己工作中的總結,也是對自己知識的一次梳理。

Margin是什麼

CSS 邊距屬性定義元素周圍的空間。通過使用單獨的屬性,可以對上、右、下、左的外邊距進行設定。也可以使用簡寫的外邊距屬性同時改變所有的外邊距。——W3School

邊界,元素周圍生成額外的空白區。“空白區”通常是指其他元素不能出現且父元素背景可見的區域。——CSS權威指南

我比較喜歡使用“外邊距”這個詞來解釋margin(同理padding可以稱之為“內邊距”,但是我又恰恰喜歡稱呼padding為“補白”或者“留白”),我們可以很清楚的瞭解到margin的最基本用途就是控制元素周圍空間的間隔,從視覺角度上達到相互隔開的目的。

Margin的特性

margin始終是透明的。

margin通過使用單獨的屬性,可以對上、右、下、左的外邊距進行設定。即:margin-top、margin-right、margin-bottom、margin-left。

外邊距的 margin-width 的值型別有:auto | length | percentage

也可以使用簡寫的外邊距屬性同時改變所有的外邊距:margin: top right bottom left;(eg: margin:10px 20px 30px 40px) 記憶方式是元素周圍正上方順時針“上右下左”記憶。

並且規範還提供了省略的數值寫法,基本如下:

1、如果margin只有一個值,表示上右下左的margin同為這個值。例如:margin:10px; 就等於 margin:10px 10px 10px 10px;

2、如果 margin 只有兩個值,第一個值表示上下margin值,第二個值為左右margin的值。例如:margin:10px 20px; 就等於 margin:10px 20px 10px 20px;

 

3、如果margin有三個值,第一個值表示上margin值,第二個值表示左右margin的值,第三個值表示下margin的值。例如:margin:10px 20px 30px; 就等於 margin:10px 20px 30px 20px;

4、如果margin有四個值,那這四個值分別對應上右下左這四個margin值。例如:margin:10px 20px 30px 40px;

在實際應用中,個人不推薦使用三個值的margin,一是容易記錯,二是不容易日後修改,一開始如果寫成margin:10px 20px 30px;日後需求改動為上10px,右30px,下30px,左20px,你不得不還是得把這個margin拆開為margin:10px 30px 30px 20px;費力且不討好,不如一開始就老老實實的寫成margin:10px 20px 30px 20px;來的實在,不要為了現在節省倆個位元組而讓日後再次開發的成本上升。

垂直外邊距合併問題

別被上面這個名詞給嚇倒了,簡單地說,外邊距合併指的是,當兩個垂直外邊距相遇時,它們將形成一個外邊距。合併後的外邊距的高度等於兩個發生合併的外邊距的高度中的較大者。你可以檢視W3Shool CSS外邊距合併瞭解這個基本知識。

實際工作中,垂直外邊距合併問題常見於第一個子元素的margin-top會頂開父元素與父元素相鄰元素的間距,而且只在標準瀏覽器下(FirfFox、Chrome、Opera、Sarfi)產生問題,IE下反而表現良好。例子可以檢視下面程式碼(IE下表現“正常”,標準瀏覽器下查看出現“bug”):

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

<title>垂直外邊距合併</title>

<style>

.top{width:160px; height:50px; background:#ccf;}

.middle{width:160px; background:#cfc;}

.middle .firstChild{margin-top:20px;}

</style>

</head>

 

<body>

<div class="top"></div>

<div class="middle">

  <div class="firstChild">我其實只是想和我的父元素隔開點距離。</div>

  <div class="secondChild"></div>

</div>

</body>

</html>

如果按照CSS規範,IE的“良好表現”其實是一個錯誤的表現,因為IE的hasLayout渲染導致了這個“表現良好”的外觀。而其他標準瀏覽器則會表現出“有問題”的外觀。好了,如果你讀過了上面W3Shcool的CSS外邊距合併的文章後,就很容易討論這個問題了。這個問題發生的原因是根據規範,一個盒子如果沒有上補白(padding-top)和上邊框(border-top),那麼這個盒子的上邊距會和其內部文件流中的第一個子元素的上邊距重疊

再說了白點就是:父元素的第一個子元素的上邊距margin-top如果碰不到有效的border或者padding.就會不斷一層一層的找自己“領導”(父元素,祖先元素)的麻煩。只要給領導設定個有效的 border或者padding就可以有效的管制這個目無領導的margin防止它越級,假傳聖旨,把自己的margin當領導的margin執行。
對於垂直外邊距合併的解決方案上面已經解釋了,為父元素例子中的middle元素增加一個border-top或者padding-top即可解決這個問題。

一般說來這個問題解釋到這裡,大多數文章就不會再深入下去了,但作為一名實戰開發者,最求的是知其然知其所以然,原本使用margin-top就是為了與父元素隔開距離,而按照你這麼一個解法,其實是一種“修復”,為了“彌補修復”這個父子垂直外邊距合併這個CSS規範“Bug”,而強制在父元素上使用border-top和padding-top,不舒服,也不容易記住,下次再發生這樣的情況還是會忘記這條準則,而且在頁面設計稿裡如果不需要border-top加個上邊框,這麼一加反而畫蛇添足,為以後修改留下隱患。

為什麼一定要用border-top,padding-top去為了這麼一個所謂的標準規範而多寫這麼一行程式碼呢?答案你可以參考另外一篇文章用Margin還是用Padding裡找到答案。

用Margin還是用Padding

何時應當使用margin:
需要在border外側新增空白時。
空白處不需要背景(色)時。
上下相連的兩個盒子之間的空白,需要相互抵消時。如15px + 20px的margin,將得到20px的空白。

何時應當時用padding:
需要在border內測新增空白時。
空白處需要背景(色)時。
上下相連的兩個盒子之間的空白,希望等於兩者之和時。如15px + 20px的padding,將得到35px的空白。

個人認為:margin是用來隔開元素與元素的間距;padding是用來隔開元素與內容的間隔。margin用於佈局分開元素使元素與元素互不相干;padding用於元素與內容之間的間隔,讓內容(文字)與(包裹)元素之間有一段“呼吸距離”。

這裡我截取了部分另外一篇文章的內容,詳細內容請見用Margin還是用Padding

margin在塊元素、內聯元素中的區別

HTML(這裡說的是html標準,而不是xhtml)裡分兩種基本元素,即block和inline。顧名思義,block元素就是以”塊”表現的元素(block-like elements),inline元素即是以”行”表現的元素(character level elements and text strings)。二者表現的主要差別在於,在頁面文件中block元素另起一行開始,並獨佔一行。inline元素則同其他inline元素共處一行。

block元素(塊元素)大致有:P|H1|H2|H3|H4|H5|H6|UL|OL|PRE| DL | DIV | NOSCRIPT | BLOCKQUOTE | FORM | HR | TABLE | FIELDSET | ADDRESS(隨著html5標準的推進,一些元素將被廢除,而一些新的元素將被引入)注意的是並非所有的block元素的預設display屬性都是block,像table這種display:table的元素也是block元素。

inline元素(內聯元素)大致有:#PCDATA(即文字)| TT | I | B | BIG | SMALL|EM | STRONG | DFN | CODE |SAMP | KBD | VAR | CITE | ABBR | ACRONYM|A | IMG | OBJECT | BR | SCRIPT | MAP | Q | SUB | SUP | SPAN | BDO|INPUT | SELECT | TEXTAREA | LABEL | BUTTON

其中有類特殊的元素:如img|input|select|textarea|button|label等,他們被稱為可置換元素(Replaced element)。他們區別一般inline元素(相對而言,稱non-replaced element)是:這些元素擁有內在尺寸(intrinsic dimensions),他們可以設定width/height屬性。他們的性質同設定了display:inline-block的元素一致。

或許有朋友對非置換元素(non-replaced element)有點疑惑,稍微幫助大家理解一下。非置換元素,W3C 中沒有給出明確的定義,但我們從字面可以理解到,非置換元素對應著置換元素(replaced element),也就是說我們搞懂了置換元素的含義,就懂了非置換元素。置換元素,W3C中給出了定義:

“An element that is outside the scope of the CSS formatter, such as an image, embedded document, or applet”

從定義中我們可以理解到,置換元素(replaced element)主要是指 img, input, textarea, select, object 等這類預設就有 CSS 格式化外表範圍的元素。進而可知,非置換元素(non-replaced element)就是除了 img, input, textarea, select, object 等置換元素以外的元素。

margin在塊級元素下,他的效能可以完全體現,上下左右任你設定。且記住塊級元素的margin的參照基準是前一個元素即相對於自身之前的元素有margin距離。如果元素是第一個元素,則就是相對於父元素的margin距離(但第一個元素相對於父元素margin-top而父元素又沒有設定padding-top/border-top的話要需要印證上面的垂直外邊距合併的知識)

margin也能用於內聯元素,這是規範所允許的,但是margin-top和margin-bottom對內聯元素(對行)的高度沒有影響,並且由於邊界效果(margin效果)是透明的,他也沒有任何的視覺影響。

這是因為邊界應用於內聯元素時不改變元素的行高度,如果你要改變內聯元素的行高即類似文字的行間距,那麼你只能使用這三個屬性:line-height,fong-size,vertical-align。請記住,這個影響內聯元素高度的是line-height而不是height,因為內聯元素是一行行的,定一個height的話,那這到底是整段inline元素的高呢?還是inline元素一行的高呢?這都說不準,所以統一都給每行定一個高,只能是line-height了。

margin-top/margin-bottom對內聯元素沒有多大實際效果,不過margin-left/margin-right還是能夠對內聯元素產生影響的。應用margin:10px 20px 30px 40px;,左邊這個css如果寫在inline元素上,他的效果大致是,上下無效果,左邊離他相鄰元素或者文字距離為40px,右邊離他相鄰元素或者文字距離為20px。你可以自行嘗試一番。

最後在內聯元素中還有上文我們提到的非可置換inline元素(non-replaced element),這些個元素img|input|select|textarea|button|label雖然是內聯元素,但margin依舊可以影響到他的上下左右!

總結下來margin 屬性可以應用於幾乎所有的元素,除了表格顯示型別(不包括 table-caption, table and inline-table)的元素,而且垂直外邊距對非置換內聯元素(non-replaced inline element)不起作用。

負margin技術及其應用

在margin所有的實際應用中,負margin技術是我學習css路上最重要一課之一,許多高階應用和頁面上的疑難雜症都可以用負margin技術來實現。margin技術是那麼的有用,限於篇幅我又不想草草了事,所以我決定專門為他寫一篇文章,詳細的說明他的效果、原理、及其應用。在此之前你可以先閱讀懌飛寫的由淺入深漫談margin屬性這篇文章,大致瞭解“margin參考線”的概念,之後再來檢視負margin技術及其應用這篇文章。

常見的瀏覽器下margin出現的bug

林林總總寫了那麼多,最後總結一些瀏覽器中常見的margin Bug吧,以後遇到margin下的佈局問題可以檢視這裡找到解決的方案,如果你還發現其他關於瀏覽器下margin的Bug你可以發表留言,核對採納後我會及時新增進去,感謝你的分享:

IE6中雙邊距Bug:
發生場合:當給父元素內第一個浮動元素設定margin-left(元素float:left)或margin-right(元素float:right)時margin加倍。
解決方法:是給浮動元素加上display:inline;CSS屬性;或者用padding-left代替margin-left。
原理分析:塊級物件預設的display屬性值是block,當設定了浮動的同時,還設定了它的外邊距就會出現這種情況。也許你會問:“為什麼之後的物件和第一個物件之間就不存在雙倍邊距的Bug”?因為浮動都有其相對應的物件,只有相對於其父物件的浮動物件才會出現這樣的問題。第一個物件是相對父物件的,而之後物件是相對第一個物件的,所以之後物件在設定後不會出現問題。為什麼display:inline可以解決這個雙邊距bug,首先是inline元素或inline-block元素是不存在雙邊距問題的。然後,float:left等浮動屬性可以讓inline元素haslayout,會讓inline元素表現得跟inline-block元素的特性一樣,支援高寬,垂直margin和padding等,所以div class的所有樣式可以用在這個display inline的元素上。

IE6中浮動元素3px間隔Bug:
發生場合:發生在一個元素浮動,然後一個不浮動的元素自然上浮與之靠近會出現的3px的bug。
解決方法:右邊元素也一起浮動;或者為右邊元素新增IE6 Hack _margin-left:-3px;從而消除3px間距。
原理分析:IE6瀏覽器缺陷Bug。

IE6/7負margin隱藏Bug:
發生場合:當給一個有hasLayout的父元素內的非hasLayout元素設定負margin時,超出父元素部分不可見。
解決方法:去掉父元素的hasLayout;或者賦hasLayout給子元素,並新增position:relative;
原理分析:IE6/7獨有的hasLayout產生問題。

IE6/7下ul/ol標記消失bug:
發生場合:當ul/ol觸發了haslayout並且是在ul/ol上寫margin-left,前面預設的ul/ol標記會消失。
解決方法:給li設定margin-left,而不是給ul/ol設定margin-left。
原理分析:IE6/7瀏覽器Bug

IE6/7下margin與absolute元素重疊bug:
發生場合:雙欄自適應佈局中,左側元素absolute絕對定位,右側的margin撐開距離定位。在IE6/7下左側應用了absolute屬性的塊級元素與右邊的自適應的文字內容重疊。
解決方法:把左側塊級元素更改為內聯元素,比如把div更換為span。
原理分析:這是由於IE6/IE7瀏覽器將inline水平標籤元素和block水平的標籤元素沒有加以區分一視同仁渲染了。屬於IE6/7瀏覽器渲染Bug。

IE6/7/8下auto margin居中bug:
發生場合:給block元素設定margin auto無法居中
解決方法:出現這種bug的原因通常是沒有Doctype,然後觸發了ie的quirks mode,加上Doctype宣告就可以了。在《打敗IE的葵花寶典》裡給出的方法是給block元素新增一個width能夠解決,但根據本人親測,加with此種方法是無效的,如果沒有Doctype即使給元素新增width也無法讓block元素居中。
原理分析:缺少Doctype宣告。

IE8下input[button | submit] 設定margin:auto無法居中
發生場合:ie8下,如果給像button這樣的標籤(如button input[type="button"] input[type="submit"])設定{ display: block; margin:0 auto; }如果不設定寬度的話無法居中。
解決方法:可以給為input加上寬度
原理分析:IE8瀏覽器Bug。

IE8百分比padding垂直margin bug:
發生場合:當父元素設定了百分比的padding,子元素有垂直的margin的時候,就好像父元素被設定了margin一樣。
解決方法:給父元素加一個overflow:hidden/auto。
原理分析:IE8瀏覽器Bug。