有关 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>&nbsp;&nbsp;&nbsp;&nbsp;如果一段内容,它的高度是可变的那么我们就可以使用使得上下的 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">
    &nbsp;&nbsp;&nbsp;&nbsp;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">
     &nbsp;&nbsp;&nbsp;&nbsp;现在,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">
     &nbsp;&nbsp;&nbsp;&nbsp;为了将就 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">
          &nbsp;&nbsp;&nbsp;&nbsp;这是一段文字,我要使得它们水平垂直居中于页面!这样的代码,在 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 设置的值很大的话,就应该考虑精确算法了。

你可能感兴趣的:(html,浏览器,css,chrome,firefox)