css - 子容器超过父容器如何垂直居中
PHP中文网
PHP中文网 2017-04-17 11:05:52
0
3
628

子容器小于父容器的情况,我知道如何垂直居中。

但是当子容器的高度超过父容器的时候,我如何让子容器相对于父容器垂直居中?

PS: 补充一句,子容器宽度是百分比,动态的。父容器的宽度是100%。

效果如图, 让红色部分相对于绿色部分垂直居中:

PHP中文网
PHP中文网

认证高级PHP讲师

全部回覆(3)
Ty80

更新:出文章啦,還有表格參考喲http://blog.segmentfault.com/humphry/1190000000381042


相信每一個前端都或多或少總結過居中吧。這裏是居中的最麻煩最不好處理的一個情形:子元素溢出。

我們在解決這個問題之前,先回顧一下,子元素不溢出父元素的時候,我們常見的CSS居中方案。以及為何在子元素溢出的時候,問題變得棘手。

思路

首先我們確定計算流程:

H外 = H内 + 2 * H补 ;

子容器溢不溢出,這個流程都不會變。

我們可以看到,要做到計算,我們必須拿到兩個值,一個是H外,一個是H內。

在到了需要JS計算的時刻,這些值都可以通過CSSOM來取得,而使用jQuery具體的做法如@怡紅公子所說,就不多說了。

而CSS布局的核心在於,在父容器高寬不固定的時候,如何讓瀏覽器幫我們計算?

我們必須保證瀏覽器解析到相應的樣式時,能夠拿到H外和H內。

CSS2.1

子元素負margin方案

這個方案已經濫了。

.outer{
    position: relative;
    overflow: hidden ;
}
.inner{
    position : absolute;
    top: 50%;
    height: 20px;
    margin: -10px;
}

我們不相信瀏覽器,使用手算,將子元素挪去自身高度的50%。這個方案也可以支持子元素高度溢出的情形。

優點:

  • 父元素可以是height:auto,不需要定死父容器的高度
  • 這個方案全瀏覽器支持……如果你 a)不介意 b)下決心要加班處理 IE67下可能導致的一些布局錯亂的話
  • 支持子元素溢出的情形

缺陷:

  • 這個方案需要子容器有一個固定的高,但不能是百分比,子元素若是百分比寬高,margin的百分比是針對父級,因此無效了。
  • 需要人工計算負margin的具體大小
  • 子元素和父元素都需要設置position,這就意味著IE67下麵的數十個友情附贈的美好bug。
  • 由於position:absolute;,子元素對外層高度、寬度塌陷,不能撐寬父容器了。

父容器 line-height = height 單行方案

.outer {
    height: 300px;
    line-height: 300px;
}

如果內容隻有一行文字,很簡單。line-height作用於line-box,把line-box撐到和父容器一樣高,文字節點默認對齊於line-box的baseline。

內部容器,則需要作為行內元素呈遞:

.inner { display: inline-block; }

優點:

  • 沒有涉及relative和absolute定位,布局時相對無痛。
  • 全瀏覽器兼容,當然,考慮到inline-box之間的間隙等問題,需要使用hack來兼容。

缺陷:

  • 父元素必須定高,必須為非百分比單位,否則無法做到line-height等於height
  • 無法覆蓋子元素溢出的情形:.inner高度高於line-height時,會直接把line-box撐高,而line-box排版階段隻有從上往下排的唯一一種可能,失去了居中的效果,變成了頂對齊
  • 如果沒有文字節點,則需要構建一個100%高的hook,或使用:before偽元素。具體方案見CSS小工具,同時inline-box之間的空格間隙需要被考慮在內,見去除inline-block元素間間距的N種方法。
  • line-height是一個可以繼承給子元素的值,在這種方案裏麵,必然會導致line-height被繼承,因此在需要排版子元素時,需要複寫line-height

這個方案是可以改進,以適應溢出情形的,在中間增加一層足夠高(你來定義一個高度,比如10000px)的容器,用負margin方法垂直居中於外層,然後用line-height=height方法讓內部居中於中間層。實現比較複雜,也結合了兩種方案的優點,和……缺點。個人覺得還不如直接使用負margin方案來得爽快些。

子元素margin:auto方案

.outer{
    position: relative;
    height: 100px ;
    overflow: hidden ;
}
.inner{
    margin-top : auto;
    margin-bottom : auto;
    position : absolute;
    top: 0;
    bottom: 0;
    height: 20px;
}

這個的原理寫在CSS2.1中:

‘top’ ‘margin-top’ ‘border-top-width’ ‘padding-top’ ‘height’ ‘padding-bottom’ ‘border-bottom-width’ ‘margin-bottom’ ‘bottom’ = 包含塊的高度

在其他值不是auto的時候,margin-top和margin-bottom是可以根據上式算出的,原理類似於水平居中。看到沒有,這個包含塊高度算式就複現了我們需要的計算過程。

子元素高度溢出父容器時,這個方案依然可行。

優點:

  • 父元素可以是height:auto,不需要定死父容器的高度
  • 無需手算,給出高度,瀏覽器自己搞定其他的內容
  • 完美支持子元素溢出的情形

缺陷:

  • 這個方案需要子容器有一個固定的高(百分比也可行)
  • 子元素和父元素都需要設置position,這就意味著IE67下麵的數十個友情附贈的美好bug。
  • 由於position:absolute;,子元素對外層高度、寬度塌陷,不能撐寬父容器了。
  • 這個方案僅僅支持IE8 。IE6和IE7由於對同時定義top、bottom屬性的樣式解析與 css2.1 不一致,不支持這種定位方式。

這是CSS2.1範疇內適用麵最廣的垂直居中方式。能夠涵蓋溢出的情形。

display:table-cell vertical-align方案

見圖片垂直居中tabel_cell

優點:

  • 無需手算,子元素不給出高度,瀏覽器也能自己搞定其他的內容

缺陷:

  • 父元素就算給定高度,設置overflow,也不會導致溢出隱藏;在子元素溢出的時候,父容器甚至都不能保有自身設置的高度,直接會被撐高
  • display:tabel-cell本身讓很多屬性無效
  • display:tabel-cellIE6和IE7不支持

這個方案不符合樓主的要求,就不多說了。


CSS3

-50% 的 translate方案

見居中百分比寬高的元素

.inner{
    position : absolute;
    top: 50%;
    transform: translate(-50%, -50%);
}

這裏其實是負margin的改版,僅僅是用translate替換了負margin,因為translate是針對容器本身的。

優點

  • 支持子元素溢出的情形
  • 子元素可以不指定高度,也可以相對父級高寬做百分比設置,非常靈活
  • 父容器也可以不指定高度

缺點

  • 兼容性,ie9 (但,從好處來講,其實這個兼容性已經完全不需要考慮IE6~8的相對/絕對布局bug了)
  • 你會讓代碼陷入一個前綴的海洋……
.inner{
    -webkit-transform:translate(-50%, -50%);
    -moz-transform:translate(-50%, -50%);
    -ms-transform:translate(-50%, -50%);
    -o-tranform:translate(-50%, -50%);
    transform:translate(-50%, -50%);
}

background方案

.inner{ 
    background-image : url() ;
    background-size: cover ;
    height: 100% ;
    display: block ;
}

如果是一個圖片,可以用background-size:coverbackground-size:contain來做到簡單的居中。若不需要拉伸,也可以使用background-position-y:center 來做。

為何不把這個方案放在CSS2.1中呢,因為隻有在CSS3中,背景才可以相對容器變化大小,比如等於容器高度:

.inner{ background-size: auto 100% ; }

或者等於容器寬度

.inner{ background-size: 100% auto ; }

優點:

  • 支持背景溢出的情形(廢話)
  • 圖片相對父級的各種情形都可以完美覆蓋
  • 你甚至可以把inner的標簽省掉,直接把背景放到outer之上

缺點:

  • 隻能是圖片
  • 隻能是背景
  • 隻能是IE9 和現代瀏覽器

flexbox方案

很抱歉,我還沒有試驗出flexbox在子元素溢出時也能保持居中的解決方案……最好的結果是子元素被拉伸(= =#)。有人有過實例嗎?


其實在這裏討論的居中方案暗含了一個條件:父容器overflow:hidden,父容器本身就有BFC……其實前端排版中的垂直居中還不止於此,父容器可以被撐高是另外一種情形,不過偏離LZ的問題太遠,這裏不再多說。

巴扎黑

如果超過部分上下正好是一樣的話,直接用line-height: 子容器高度(單行情況)或者display:inline-table;vertical-algin:middle;(對應其它情況)保證在子容器居中就可以保證相對於父容器居中了吧。另外,如果不兼容過時的瀏覽器的話,可以試試CSS3的一些東西,做垂直居中還是非常簡單方便的:http://zh.learnlayout.com/flexbox.html

如果超過部分上下不一樣的話我暫時隻能想到用JavaScript計算差值然後給margin的方法呢。(示例是jQuery的語法,語義版,father是紅色方框,子容器的內容需要再包括在一個標簽內):

var father = $('.father'), child = father.children(), grandfather = father.parent();
var marginTop = grandfather.offset().top - father.offset().top + (grandfather.height() - child.height())/2;
child.css('margin-top', marginTop+'px');

額,之前好像沒看清楚題目,謝謝 @Humphry 提醒。如果隻是容器要垂直居中的話可以用JavaScript計算兩者高度相減除以2,並給與margin-top,或者直接使用CSS3的calc()計算margin-top的值就好了,關於這個你可以看看這個:http://www.qianduan.net/calc-at-at-at-page-intelligent-layout.html

var father = $('.father'), child = father.children();
var marginTop = (father.height() - child.height())/2;
child.css('margin-top', marginTop+'px');
小葫芦

這個實現比較簡單,希望能解答你的問題,我是這麼理解的。

http://jsfiddle.net/BNnXN/

熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板