CSS常见居中讨论
先来一个常见的案例,把一张图片和下方文字进行居中:
首先处理左右居中,考虑到 img 是一个行内元素,下方的文字内容也是行内元素,因此直接用 text-align即可:
<style> .container{ text-align: center; } </style> <div class="container"> <img src="Mars.png"> <p>火星</p> </div>
实际的效果如下:
火星
这样做的问题是,直接在最外层的 container 设置一个 text-align 的属性,导致所有子元素都会继承,假设图片标题下方还有文字描述,那么这个文字描述也会被居中,如下所示:
火星
火星(Mars)是太阳系八大行星之一,天文符号是♂
但实际上是希望文字描述向左对齐,这样就不得不在文字描述添加一个 text-align:left 的属性,覆盖父元素的属性,并且如果还有更多的子元素也需要这样做,覆盖属性本身就是下策,因此这里采用其它的办法。由于显示的图片是变化的,宽高是不定的,所以一般会显示地给图片设置一个宽高。如果知道宽度就可以用margin: 0 auto 的方法,左右的 margin 值设置为 auto,浏览器就会自动设置左右的 margin 值为容器剩余宽度的一半:
火星
火星(Mars)是太阳系八大行星之一,天文符号是♂
代码如下:
<style> figure{ width: 100px; margin: 0 auto; } figcaption{ text-align: center; } </style> <div class="container"> <figure> <img src="Mars.png"> <figcaption>火星</figcaption> </figure> <p class="desc">火星(Mars)是太阳系八大行星之一,天文符号是♂</p> </div>
需要注意的是,这个办法对上下居中不适用。
使用 margin: 0 auto 可以说是最常见的左右居中方法,不仅适用于块元素也适用于行内元素。很多网页的布局都是主体内容固定宽度同时居中显示,例如淘宝 PC 端:
查看最外层的容器的样式就可以发现使用了 margin: 0 auto:
接下来讨论垂直居中,麻烦的是垂直居中。不过垂直居中有一个比较通用的办法,那就是借助 table-cell 的垂直居中。方法是给父容器添加以下属性:
.container{
display: table-cell;
vertical-align: middle;
}
效果如下:
火星
使用 table-cell 的缺点是容器的 magin 属性失效了,因为 margin 不适用于表格布局。所以如果要把 container 居中的话,使用 margin: 0 auto 就不起作用了。解决的办法是在 container 外层再套多一个容器,然后让这个 display 为 block 的容器 margin: 0 auto 就可以了。
另外一个缺点是 table-cell 的元素设置宽高为百分比的时候将不起作用,常见的场景是要将宽度设置为外层容器宽度的 100%,解决办法是将 container 的宽度设置成一个很大的值,例如 3000px,这样就达到 100% 的目的。还有一个问题是不兼容 IE6/7,但现在生产环境基本上不需要兼容 IE6/7 了。
这种方法有失效的时候,那就是 container 需要设置 position 为 absolute 时。因为设置 position: absolute 就会把(非 flex)元素的 display 强制设置成 block。解决办法还是模仿上面的,外层再套多一个容器,将 absolute 作用于这个容器,副作用是设置内层 container 的 height 和 width 为百分比时会失效 (除非将它的 position 也设置成 absolute)。由于这个原因,导致有一种情况不能使用 display: table-cell 垂直居中。
这种场景就是需要在页面弹个框,这个框的位置需要在当前屏幕左右上下居中:
通常需要将这个框的 positiion 设置成 absolute,这个时候 table-cell 就不能发挥作用了,即使你在外面再套多两层,最外层为 absolute,里层为 table-cell,但由于里层无法设置 height 为外层的 100%,也就是说高度无法刚好占满整个屏幕,所以不能起作用。
解决办法是使用 relative 定位,设置 top 为 50%,再设置 margin-top 为元素高度的负的一半。一开始设置 top 50%,将弹框的起始位置放到页面中间,然后再设置 margin-top 为弹框高度的一半取负,这样使得弹框在页面中间位置再往上移一半自身的高度,这样就刚好在正中间了,左右居中也可类似处理:
<style> .mask{ position: absolute; width: 100%; height: 100%; } .outer{ position: relative; top: 50%; left: 50%; margin-top: -100px; margin-left: -100px; } .container{ width: 200px; height: 200px; display: table-cell; vertical-align: middle; } </style> <div class="mask"> <div class="outer"> <div class="container"><span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">></span>
</div>
效果如下:
火星
这种办法的缺点是需要知道高度,无法根据内容长短自适应。所以就有了transform 的方法,将 margin-top 一个具体的负值改成 transform: translate(0, -50%),由于 translate 里面的面百分比是根据元素本身的高度计算的,于是就可以达到自适应的效果。将上面 outer 样式改为:
.outer{ position: relative; top: 50%; left: 50%;
width: 200px; transform: translate(-50%, -50%); }
这个办法十分地方便,为了提高兼容性,需要添加 -ms- 和 -webkit- 前缀,缺点是 IE8 不支持。
上面的两种办法:margin-top 一个负值和 translate -50% 都有一个潜在的弊端, 就是如果设置 left 为 50% 是借助 position 为 absolute 的话,可能会导致换行:
<style> .container{ position: relative; } .nav{ position:absolute; left: 50%; transform: translate(-50%, 0); bottom: 0; } </style> <div class="container"> <figure> <img src="Mars.png"> </figure> <p class="nav"><span>地形</span><span>气候</span><span>运动</span></p> </div>
效果如下:
地形气候运动
可以看到,本来应该在一行显示的 p 元素却换行了,这是因为在一个 relative 元素里面 absolute 定位的子元素会尽可能地不超过容器的边界,通过把行内元素换行的方式。由于设置 left 为 50%,导致 p 元素超了边界,所以就换行了,即使再 translate -50% 但已经晚了。即使是交换下两者的位置也是一样的效果,看得出浏览器执行的顺序始终是以 absolute 的定位优先。所以这种方法还是有不适合的场景,主要是用于左右居中定位为 absolute 的的情况。
另外一个 CSS3 居中的办法是使用 flex 布局,flex 布局居中十分容易和方便,只需要在父容器添加三行代码,例如上面的居中情况,可将.nav 的样式改为:
.nav{ position:absolute; bottom: 0; display: flex; align-items: center; justify-content: center; width: 100%; }
效果如下:
地形气候运动
flex 布局在自适应领域的功能真的是非常强大,不过IE 的兼容性不好,所以说可能的话,不要太去照顾 IE 了。
上面讨论的都是一些复合元素的居中,接下来分析单纯的行内元素的垂直居中。
主要是要借助 vertical-align: middle。如下,有一张图片和文字:
<div class="container"> <img src="Mars.png"> <span>photo</span> </div>
如果不做任何处理,那么默认的垂直居中是以 baseline 为基准:
为了让它们能够垂直居中,需要改变它们的居中方式:
.container img, .container span{ vertical-align: middle; }
注意每个元素都需要设置,效果:
如果 container 的高度比图片还要高:
为了让中间的内容能够在 container 里上下居中,可以设置文字的 line-height 为 container 的高度,那么文字就上下居中了,由于照片是和文字是垂直居中的,所以照片在 container 里也上下居中了,代码:
.container span{ vertical-align: middle; line-height: 150px; }
效果:
这也就给了一个启示,如果需要垂直居中一个 div 里的比 div 高度小的照片,可以添加一个元素,让它的 line-height 等于 div 的高度,如下:
<style> .container{ width: 150px; height: 150px; text-align: center; } .container img{ vertical-align: middle; } .container:before{ content: ""; vertical-align: middle; line-height: 150px; } </style> <div class="container"> <img style="vertical-align:middle;" src="Mars.png"> </div>
或者是弄一个 inline-block 的元素,设置 height 为 100%,这种的兼容性更好:
.container:before{ content: ""; display: inline-block; vertical-align: middle; height: 100%; }
上下居中效果:
还有另外一种方法借助 absolute 定位和 margin: auto:
.container{ position: relative; }.container img</span>{<span style="color: rgba(255, 0, 0, 1)"> position</span>:<span style="color: rgba(0, 0, 255, 1)"> absolute</span>;<span style="color: rgba(255, 0, 0, 1)"> left</span>:<span style="color: rgba(0, 0, 255, 1)"> 0</span>;<span style="color: rgba(255, 0, 0, 1)"> top</span>:<span style="color: rgba(0, 0, 255, 1)"> 0</span>;<span style="color: rgba(255, 0, 0, 1)"> right</span>:<span style="color: rgba(0, 0, 255, 1)"> 0</span>;<span style="color: rgba(255, 0, 0, 1)"> bottom</span>:<span style="color: rgba(0, 0, 255, 1)"> 0</span>;<span style="color: rgba(255, 0, 0, 1)"> margin</span>:<span style="color: rgba(0, 0, 255, 1)"> auto</span>; }</pre>
这个方法的神奇之处在于最后的 margin: auto,这种方法也适用于 div,但是需要给 div 显式指定一个 height,不然 div 的 height 会达到外层容器的 100%。
如果图片比 container 大,这种方法就不适用了。因为有一种比较常见的场景是:照片有一边和 container 一样高,另外一边按照片的比例缩放,照片居中显示,超出的截断,这种应该叫“占满”(occupy)布局。这种情况,只需要把 left/right/top/bottom/ 设成一个很大的负值即可:
.container img{ position: absolute; left: -9999px; top: -9999px; right: -9999px; bottom: -9999px; margin: auto; } .container{ overflow: hidden; }
效果:
综合上面的讨论,左右居中常用 text-align 和 margin: 0 auto,上下居中一种办法是借助 table-cell,另外一种是设置 top: 50% 和 margin-top/translate(0, -50%) 结合的办法,还有就是使用 flex 布局,对于行内元素设置 vertical-align: middle,同时借助一个高度为 100% 的元素达到垂直居中的效果。最后是 position: absolute 和 margin: auto 结合使用的办法。可以说没有一个方法可以 100% 适用,可以根据不同的情况合理结合使用。
如果上文有不妥的地方,或者读者有其它的居中方式,还请指出
个人博客: http://yincheng.site/css-align
参考:
1. How to vertically align an image inside div StackOverflow
2. A Complete Guide to Flexbox