margin系列之布局篇

2018-02-24 15:22 更新

margin系列之布局篇

原作者:doyoe
原文鏈接:http://blog.doyoe.com/2013/12/31/css/margin%E7%B3%BB%E5%88%97%E4%B9%8B%E5%B8%83%E5%B1%80%E7%AF%87/

前端工程師對(duì)CSS的基本訴求

布局能力或許是Web前端工程師對(duì)CSS的最基本的訴求,當(dāng)開(kāi)始進(jìn)入到這個(gè)崗位,就避免不了要和CSS打交道,而和CSS交往,布局當(dāng)然是不可或缺的。

很遺憾的是,CSS2.1之前都沒(méi)有出現(xiàn)真正意義上的布局屬性,直至現(xiàn)如今的CSS3,才開(kāi)始出現(xiàn)了一些,如:flex, grid 等,不過(guò)其兼容性及國(guó)內(nèi)瀏覽器的使用情況,真令人捉急。

不過(guò),有需求就會(huì)有變通,對(duì)于達(dá)成布局目的,已衍生出各式各樣的方法,如:float, inline-block, table, absolute 等等。

margin的布局之道

其實(shí),這個(gè)話題有點(diǎn)脫離 margin 的能力范圍,因?yàn)閱渭兊?margin 并無(wú)法完成復(fù)雜布局,它更多做的是輔助,但卻又難以替代。

經(jīng)典左右結(jié)構(gòu)

兩欄結(jié)構(gòu)應(yīng)該是最常見(jiàn)和經(jīng)典的網(wǎng)頁(yè)呈現(xiàn)之一吧?如下 圖一

classsic layout(圖一)

相信對(duì)于這樣一個(gè)網(wǎng)頁(yè)呈現(xiàn),你不會(huì)陌生。那么你有多少種方案可以達(dá)成該布局?我想,4至5種應(yīng)該是保守估計(jì)吧?
這次,我們只看 margin 是如何做的。

absolute + margin 方式

HTML

<header id="hd">頭部</header>
<div id="bd">
    <aside id="aside">側(cè)邊欄固定寬度</aside>
    <div id="main">主內(nèi)容欄自適應(yīng)寬度</div>
</div>
<footer id="ft">底部</footer>

CSS

#aside{
    position:absolute;
    top:0;
    left:0;
    width:200px;
}
#main{
    margin-left:210px;
}

如上關(guān)鍵代碼,我們即可實(shí)現(xiàn) 圖一 布局,該布局有一個(gè)特點(diǎn)就是,#main 可以自適應(yīng)可用空間。

假定 HTML 是給定的,我們來(lái)解讀一下 CSS 代碼:
我們知道塊級(jí)元素的特性之一是換新行,也就是說(shuō),如果想讓 #main#aside 在同行顯示,我們要么改變其顯示屬性為 inline-level(即之前說(shuō)的inline-block布局方式),要么改變其流方式(absolute, float, flex and etc…)。

如上述代碼,我們使用了 absolute,即讓 ‘#aside’ 脫離常規(guī)流,通過(guò)絕對(duì)定位到想要的位置。

主內(nèi)容欄自適應(yīng)寬度

同時(shí)你會(huì)發(fā)現(xiàn),我們并有改變 #main 的顯示屬性或者流方式,也就是說(shuō)其仍然具備塊級(jí)元素的特性,所以它會(huì)自動(dòng)適應(yīng)剩余寬度,即我們常說(shuō)的自適應(yīng)寬度。

我們并不希望 #main 區(qū)域會(huì)包含 #aside 在內(nèi),于是利用 margin 給 ‘#aside’ 預(yù)留出足夠其顯示的空間,即可達(dá)成我們所要的布局。

可能你會(huì)問(wèn)為什么是 margin-left:210px 而不是 200px,實(shí)際確實(shí)應(yīng)該是 200px,多出來(lái)的 10px 只是為了創(chuàng)建一個(gè)列間隙,與布局實(shí)現(xiàn)無(wú)關(guān)。
來(lái)看看具體的實(shí)現(xiàn) DEMO1: margin+absolute布局:左欄固定主內(nèi)容自適應(yīng)
就這樣,是不是很簡(jiǎn)單?其實(shí)它還有亮點(diǎn),那就是:

任意調(diào)整列順序

在不修改 HTML 的情況下,只需簡(jiǎn)單的修改 CSS,我們即可讓左右兩欄的順序調(diào)換,來(lái)看代碼:

CSS

#aside{
    position:absolute;
    top:0;
    right:0;
    width:200px;
}
#main{
    margin-right:210px;
}

其實(shí)現(xiàn)原理沒(méi)變,同樣看看 DEMO2: margin+absolute布局:右欄固定主內(nèi)容自適應(yīng)

主內(nèi)容優(yōu)先顯示

可以更Cool一點(diǎn),你覺(jué)得呢?很多時(shí)候,你也許會(huì)考慮到,不論在何種情況下,總想保證主要的內(nèi)容優(yōu)先于次要的內(nèi)容呈現(xiàn)給用戶,那么,怎么做?

很簡(jiǎn)單,只需要將主要內(nèi)容的HTML排在次要內(nèi)容的HTML之前即可,因?yàn)樗琼樞蚣虞d渲染的。我們可以這樣:

HTML

<header id="hd">頭部</header>
<div id="bd">
    <div id="main">主內(nèi)容欄自適應(yīng)寬度</div>
    <aside id="aside">側(cè)邊欄固定寬度</aside>
</div>
<footer id="ft">底部</footer>

是的,我們只需要將 #main 的HTML挪到 #aside 的HTML前面,令人興奮的是,改變HTML之后,CSS不需要做任何改變。我們來(lái)看 DEMO3: margin+absolute布局:左欄固定主內(nèi)容自適應(yīng),主內(nèi)容有限顯示

當(dāng)然,調(diào)正列順序的 DEMO4: margin+absolute布局:右欄固定主內(nèi)容自適應(yīng),主內(nèi)容有限顯示 也同樣簡(jiǎn)單,我們只需要寫(xiě)HTML時(shí)注意一下即可。

致命缺陷

列舉了 absolute+margin 布局的很多優(yōu)點(diǎn),但只說(shuō)一個(gè)問(wèn)題,就足以讓你在是否選用這種方式時(shí)深思熟慮,是什么呢?

我們知道 absolute 是定位流,脫離正常排版,也就是說(shuō)絕對(duì)定位元素不影響其上下文的排版方式,你意識(shí)到我想說(shuō)什么了么?

OK,用代碼來(lái)演示:

HTML

<header id="hd">頭部</header>
<div id="bd">
    <div id="main">主內(nèi)容欄自適應(yīng)寬度</div>
    <aside id="aside">側(cè)邊欄固定寬度,我的內(nèi)容可能比主內(nèi)容多,高度比主內(nèi)容欄高</aside>
</div>
<footer id="ft">底部</footer>

看完代碼,估計(jì)你猜到了。是的,#aside 無(wú)法撐開(kāi)父元素的高度,它將會(huì)溢出父元素區(qū)域,結(jié)果如下圖:
classsic layout(圖二)

來(lái)看看這缺陷所導(dǎo)致的情況 DEMO5: margin+absolute布局的致命缺陷

此時(shí)假設(shè)你設(shè)置父元素 overflow:hidden 那么溢出部分將會(huì)被裁減,同樣不符合布局意圖,無(wú)法可破。所以在內(nèi)容量不可控的場(chǎng)景,不推薦使用這種方式。

float + margin 方式

absolute + margin 方式一樣,float + margin 方式一樣是經(jīng)典的利用來(lái)布局的方案,并且被更廣泛使用。我們?nèi)匀灰?圖一 為例,來(lái)看代碼:

HTML

<header id="hd">頭部</header>
<div id="bd">
    <aside id="aside">側(cè)邊欄固定寬度</aside>
    <div id="main">主內(nèi)容欄自適應(yīng)寬度</div>
</div>
<footer id="ft">底部</footer>

CSS

#aside{
    float:left;
    width:200px;
}
#main{
    margin-left:210px;
}

如上述代碼,我們使用了 float,即從圖文環(huán)繞形態(tài)演變而來(lái)。當(dāng) #aside 定義了 float,那么緊隨其后的元素將會(huì)環(huán)繞在其周?chē)2贿^(guò)環(huán)繞并不是我們想要的結(jié)果,我們想要的是 ‘#main’ 也自成封閉矩形,所以利用 margin 留出足夠 #aside 顯示的空間,中斷環(huán)繞即可。

當(dāng)然,此時(shí) #main 也是自適應(yīng)寬度的,來(lái)看具體實(shí)例 DEMO6: margin+float布局:左欄固定主內(nèi)容自適應(yīng)

它是否也具備可任意調(diào)整列順序的特點(diǎn)?何不一試?

CSS

#aside{
    float:right;
    width:200px;
}
#main{
    margin-right:210px;
}

看過(guò) DEMO7: margin+float布局:右欄固定主內(nèi)容自適應(yīng),你會(huì)發(fā)現(xiàn),是的,這種方式也支持任意調(diào)整列順序,很棒。

從這種趨勢(shì)看來(lái),貌似 float + margin 的方式會(huì)成為黑馬,不過(guò)遺憾的告訴你,這種方式無(wú)法支持主內(nèi)容優(yōu)先顯示。但我們有更Cool的解決方案。

float + 負(fù)margin 方式

接下來(lái)我要說(shuō)的大家可能都猜到了,對(duì),經(jīng)典的圣杯布局。至于圣杯的名字由來(lái),大家可以自行Google,這里不做贅述。

恩,HTML當(dāng)然是使用主內(nèi)容優(yōu)先顯示的那種:

HTML

<header id="hd">頭部</header>
<div id="bd">
    <div id="main">主內(nèi)容欄自適應(yīng)寬度</div>
    <aside id="aside">側(cè)邊欄固定寬度</aside>
</div>
<footer id="ft">底部</footer>

CSS

#bd{
    padding-left:210px;
}
#aside{
    float:left;
    position:relative;
    left:-210px;
    width:200px;
    margin-left:-100%;
}
#main{
    float:left;
    width:100%;
}

如上代碼,既是圣杯布局的核心Code,如果你看懂了,你會(huì)發(fā)現(xiàn),這其實(shí)很簡(jiǎn)單,不是么?

簡(jiǎn)單解釋一下上面的CSS Code,首先我們是在做一個(gè)左側(cè)固定寬度,右側(cè)自適應(yīng)寬度的布局。我們說(shuō)過(guò)要讓塊級(jí)元素在同行顯示的條件:改變顯示方式,改變流方式,這里我們選擇了使用 float 來(lái)將 #main#aside 變成浮動(dòng)流。

OK,這時(shí)我們具備 #main#aside 能在同行顯示的前置條件。我們知道,浮動(dòng)元素其寬度如果沒(méi)有顯式定義,則由其內(nèi)容決定。正好,#aside 是定寬的,所以顯示給它定義 width:200px,但此時(shí) #main 該怎么辦?不設(shè)置 width 不對(duì),因?yàn)閷挾葘⒈粌?nèi)容左右,設(shè)置 width:100% 也不對(duì),因?yàn)檫@樣的話,就沒(méi)有 #aside 的立足之地了,正確的應(yīng)該是 width: calc(100% - 200px),不是么?可惜,這是新特性,只好作罷。

變通?是的,有的時(shí)候稍微換個(gè)思路,你會(huì)覺(jué)得豁然開(kāi)朗。

#main 不是要自適應(yīng)嗎?那就給它個(gè) 100%,怎么做?我們?cè)诎瑝K #bd 中就將 #aside 的寬度刨除,寬度全部都給 #main。恩,我們只需要這樣 #bd{padding-left:210px;} (10px仍然是用來(lái)做間隙的),這時(shí) #main 就可以設(shè)置 width:100% 了,由于 #bd 設(shè)置了 padding,所以已在左邊預(yù)留出了一塊寬 210px 的區(qū)域。此時(shí)的問(wèn)題在于如果將 #aside 挪到這個(gè)地方,你想對(duì)了,我們是在聊 負(fù)margin 布局,自然需要利用上。

#aside{margin-left:-100%;} 這樣可以了嗎?很明顯,這樣還不行,此時(shí) #aside#main 的起始位置將會(huì)重合,因?yàn)?#asidemargin-left 計(jì)算值是相對(duì)包含塊來(lái)計(jì)算的,而此時(shí)包含塊的寬度等于 #main 的寬度。

如何讓 #aside 再向左偏移 210px?顯然 margin 是不行了,因?yàn)槲覀円呀?jīng)用掉它了。如果你看過(guò)之前的文章的話,你可能還記得,有一篇文章講 margin系列之與相對(duì)偏移的異同。恩,是的,這時(shí)我們可以借助相對(duì)偏移。

向左偏移 210px 是件很簡(jiǎn)單的事:#aside{position:relative;left:-210px;}

至此,你的布局OK了,這就是圣杯的實(shí)現(xiàn)方式。來(lái)看已實(shí)現(xiàn)好的示例 DEMO8: 圣杯:左欄固定主內(nèi)容自適應(yīng)

當(dāng)然,圣杯布局必須可以任意調(diào)整列順序,要不,怎么能說(shuō)是更Cool些的方案呢?

CSS

#bd{
    padding-right:210px;
}
#aside{
    float:left;
    position:relative;
    right:-210px;
    width:200px;
    margin-left:-200px;
}
#main{
    float:left;
    width:100%;
}

這個(gè)就直接看示例好了,不再一一解釋代碼 DEMO9: 圣杯:右欄固定主內(nèi)容自適應(yīng)

所以圣杯布局具備前兩種方式共同的優(yōu)點(diǎn),同時(shí)沒(méi)有他們的不足,但圣杯本身也有一些問(wèn)題,在IE6/7下報(bào)廢,不過(guò)不用慌,因?yàn)樗杀恍迯?fù)。

你想到方法了嗎?

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)