zachary.guo 发表于 2013-2-8 00:45:44

有关 HTML 水平垂直居中问题的讨论

      关于 HTML 水平垂直居中问题,网上的讨论已经是相当地多了。这里,我把网上搜索的结果,并附加上解题思路,总结出来,希望对大家有所帮助。

      首先,我们需要明确一个概念,到底是谁要相对于谁水平垂直居中,相应的样式应该写在谁的里面。这里,我们统称为:inner element 相对于 outer element 水平垂直居中。下面首先讲到的是文字在 div 中居中,这个不属于这个范畴。因为盛放文字的 div 若是置于其它的 div(outer div) 中,而且文字 div 要整体相对于这个层居中,这才是我们要讨论的话题。这个时候,盛放文字的层就是 inner element,它所相对于的那个层居中的元素就是 outer element。

      我们首先先看看如何使得某段文字处于 div 水平和垂直方向的中间,下面分情况来讨论。

      1. 单行文字
      单行文字,有两种情况:① div 的高度显示指定;② div 的高度未指定。对于 ②,文字的字体大小就决定了 div 的高度,此种情况没有必要讨论。对于 ①,只需要将 line-height 的值设置的和 height 值一致即可。至于宽度,没指定,写多少字就多宽,指定了宽度,设置 text-align 为 center 即可。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><style>div {    /* 为了看清 div 的轮廓,我给 div 加上边框 */    border: 1px solid black;    width: 400px;    height: 100px;    line-height: 100px;    text-align: center;    font-size: 12px;}</style><div>我确保这段文字就只有一行。</div>

      2. 多行文字(div 的高度未知)
      这种情况,div 的高度是随文字的多少而变化的。只需设置 padding 值即可,看你需要 padding 多少了,比较随意。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><style>div {    /* 为了看清 div 的轮廓,我给 div 加上边框 */    border: 1px solid black;    width: 400px;    font-size: 12px;    padding: 20px;}</style><div>    如果一段内容,它的高度是可变的那么我们就可以使用使得上下的 padding 值相同的方法即可。同样的,这也是一种 "看起来" 的垂直居中方式,它只不过是使文字把 div 完全填充的一种访求而已。</div>

      3. 多行文字(div 的高度指定,即固定高度的 div)
      CSS 中的 vertical-align 属性只会对拥有 valign 特性的 (X)HTML 标签起作用,但是在 CSS 中还有一个 display 属性能够模拟 <table>,所以我们可以使用这个属性来让 <div> 模拟 <table> 就可以使用 vertical-align了。注意,display:table 和 display:table-cell 的使用方法,前者必须设置在父元素上,后者必须设置在子元素上,因此我们要为需要定位的文本再增加一个 <div> 元素。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><style>div.outer {    /* 为了看清 div 的轮廓,我给 div 加上边框 */    border: 1px solid black;    width: 400px;    height: 300px;    overflow: auto;    font-size: 12px;    display: table;}    div.inner {    display: table-cell;    vertical-align: middle;    padding: 20px;}</style><div class="outer"><div class="inner">        CSS 中的 vertical-align 属性只会对拥有 valign 特性的 (X)HTML 标签起作用,但是在 CSS 中还有一个 display 属性能够模拟 table,所以我们可以使用这个属性来让 div 模拟 table 就可以使用 vertical-align了。注意,display:table 和 display:table-cell 的使用方法,前者必须设置在父元素上,后者必须设置在子元素上,因此我们要为需要定位的文本再增加一个 div 元素。</div></div>
      这个方法应该是很理想了,但是不幸的是,只有标准浏览器中(FF,Opera, Safari,IE8 等)才能正确地理解 display:table 和 display:table-cell,因此这种方法在不支持 W3C 标准的浏览器(IE6 和 IE7)下是无效的。嗯,这让人很郁闷!不过我们还其它的办法。

      在不支持 W3C 标准的 IE 浏览器中,在高度的计算上存在着缺陷的。对父元素进行定位后,如果再对子元素进行百分比计算时,计算的基础似乎是有继承性的(如果定位的数值是绝对数值没有这个问题,但是使用百分比计算的基础将不再是该元素的高度,而从父元素继承来的定位高度)。例如,我们有下面这样一个(X)HTML代码段:
<div id="outer"><div id="inner">    <div id="content"></div>   </div></div>
      现在,outer 层就是我们的大容器,在 Firefox,Chrome 等现代浏览器中,使用两个层(包括外围的容器层),并使用 display 和 vertical-align 属性即可垂直居中。这里,IE6 和 IE7 需要三个层(包括外围的容器层)。outer 层的宽高度已知,content 层的高度未知,因为我们不知道 content 层要写多少文字,所以我们引入了 inner 层,它将 content 层包裹起来,这样,content 层里有多少文字,那么,inner 层的高度就和 content 层的高度一样(这还依赖于它们各自 position 的设置),都为文字占用的高度。不过,我们仍然要记住,inner 层和 content 层的高度是未知的。我们使 inner 层的 position 为 absolute(若为 relative,那么 inner 层的宽度将继承 outer 层的宽度,这不是我们想要的效果,我们要的效果是和 content 层一致的宽高度),top 为 50%,那么,inner 层的左上角位于 outer 层垂直方向上的正中间(50% 是根据 outer 层的高度来计算的,计算的基础是它父亲元素的高度)。这个时候,我们只需要将 content 层的位置向上偏移 content 或 inner 层的高度的一半即可。可惜,我们并不知道它们的高度。这就是为什么我们要引入三个层的原因了。由于 innner 层的 position 为 absolute,如果 content 层的 position 也为 absolute,那么 inner 层将没有高宽度,我们只能使 content 层的 postion 为 relative,只有这样,inner 层和 content 层才会有相同的高宽度。在 content 层上设置 top 为 -50%,那么它就是按 outer 层的高度来计算了。这样就达到我们的目的了。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><style>div.outer {    border: 1px solid black;    width: 600px;    height: 400px;    font-size: 12px;    /* 或者 position: absolute; */    position: relative;}    div.inner {    /* 必须的 */    position: absolute;    top: 50%;}    div.content {    /* 必须的 */position: relative;top: -50%;}</style><div class="outer"><div class="inner">    <div class="content">       现在,outer 层就是我们的大容器,在 Firefox,Chrome 等现代浏览器中,使用两个层(包括外围的容器层),并使用 display 和 vertical-align 属性即可垂直居中。这里,IE6 和 IE7 需要三个层(包括外围的容器层)。outer 层的宽高度已知,content 层的高度未知,因为我们不知道 content 层要写多少文字,所以我们引入了 inner 层,它将 content 层包裹起来,这样,content 层里有多少文字,那么,inner 层的高度就和 content 层的高度一样(这还依赖于它们各自 position 的设置),都为文字占用的高度。不过,我们仍然要记住,inner 层和 content 层的高度是未知的。我们使 inner 层的 position 为 absolute(若为 relative,那么 inner 层的宽度将继承 outer 层的宽度,这不是我们想要的效果,我们要的效果是和 content 层一致的宽高度),top 为 50%,那么,inner 层的左上角位于 outer 层垂直方向上的正中间(50% 是根据 outer 层的高度来计算的,计算的基础是它父亲元素的高度)。这个时候,我们只需要将 content 层的位置向上偏移 content 或 inner 层的高度的一半即可。可惜,我们并不知道它们的高度。这就是为什么我们要引入三个层的原因了。由于 innner 层的 position 为 absolute,如果 content 层的 position 也为 absolute,那么 inner 层将没有高宽度,我们只能使 content 层的 postion 为 relative,只有这样,inner 层和 content 层才会有相同的高宽度。在 content 层上设置 top 为 -50%,那么它就是按 outer 层的高度来计算了。这样就达到我们的目的了。   </div></div></div>
      为了将就 IE6 和 IE7,我们不得不使得支持 W3C 标准的浏览器(Firefox, Chrome)也使用三个层来实现居中的效果。我们也只能使用 CSS Hack 来兼容这若干个浏览器了。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><style>div.outer {    border: 1px solid black;    width: 600px;    height: 400px;    font-size: 12px;    /* 或者 position: absolute; */    _position: relative;      display: table;}    div.inner {    /* 必须的 */    _position: absolute;    _top: 50%;      display: table-cell;vertical-align: middle;}    div.content {    /* 必须的 */_position: relative;_top: -50%;}</style><div class="outer"><div class="inner">    <div class="content">       为了将就 IE6 和 IE7,我们不得不使得支持 W3C 标准的浏览器(Firefox, Chrome)也使用三个层来实现居中的效果。我们也只能使用 CSS Hack 来兼容这若干个浏览器了。   </div></div></div>

      文字处于 div 的中间的问题我们已经讨论完了,现在,我们要将盛放文字的层视为 inner element。我们现在要使文字层处于这个页面的正中间,这个时候,body 就是我们的 outer element 了。

      这种情况,由于 inner element 的宽高度已知(其实,只是盛放文字的 div 高度被设定,文字到底有多少,还是不知道),我们首先使得 inner element 的左上角位于 outer element 的正中心。此时需要设置 inner element 的 position 为 absolute,使得其 top 和 left 均为 50%。再将 margin-top 设置为 -height/2,margin-left 设置为 -width/2。这样,就达到了我们所要的效果。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html><head>    <style>      html, body {      margin: 0;      padding: 0;      width: 100%;      height: 100%;      }            div.outer {      /* 为了看清整个 inner element 的轮廓,这里设置 inner element 的边框为黑色,1px */      border: 1px solid black;      width: 600px;      height: 400px;      font-size: 12px;      position: absolute;      top: 50%;      left: 50%;      /* margin-left = -width/2 = -600/2 - -300 */      margin-left: -300px;      /* margin-top = -height/2 = -400/2 = -200 */      margin-top: -200px;                display: table;      }            div.inner {      /* 必须的 */      _position: absolute;      _top: 50%;                display: table-cell;      vertical-align: middle;      /* 水平居中 */      text-align: center;      }            div.content {      /* 必须的 */      _position: relative;      _top: -50%;      }    </style></head>    <body>    <div class="outer">       <div class="inner">         <div class="content">              这是一段文字,我要使得它们水平垂直居中于页面!这样的代码,在 IE6 和 IE7 下仍然不能很好的支持,而且在这两种浏览器下的效果还不一样,不管啦,只要能支持现代浏览器就行了,懒得为这些浏览器的 bug 折腾了。      </div>      </div>    </div></body></html>
      事实上,上述计算 margin-left 和 margin-top 的方法并不准确。以 margin-left 为例子,准确的计算方法为:-(border-left + padding-left + width + padding-right + border-right)/2。实际操作时,几像素的差距可以忽略不计,视觉上觉得是居中的即可。若 padding 或 border 设置的值很大的话,就应该考虑精确算法了。
页: [1]
查看完整版本: 有关 HTML 水平垂直居中问题的讨论