9

本文首发于我的博客
在上文《你不知道的CSS(一)》中,介绍了兄弟选择器美化表单,font-size:0消除间隙,overflow清除浮动,border绘制三角形等7个实用技巧。由于文章长度限制,还遗留了一些技巧没有介绍,考虑到日后可能会有更多的技巧需要补充进来,便将上文改名为你不知道的CSS(一),名字其实有点浮夸,希望能完善为一个系列,也希望该系列中介绍的技巧能够帮助到更多人解决实际开发中遇到的问题。在这里感谢SegmentFault的小编在微博上的推荐。本文将重点介绍CSS中未知高度容器的垂直居中技巧。同样每个技巧将结合demo或者图示来说明(如果demo无法打开,请自备梯子,原因你懂得?)。

未知高度容器的多种垂直居中方法

在已知父子高度的情况下,实现垂直居中是很容易的事。marginpaddingabsolute + 负margin , 甚至于 line-height都是可行的方案。这里不再展开,文章主要来介绍在父容器高度固定,自容器高度自适应的情况下,来实现其垂直居中于父级盒子的几种方案。为了使案例更真实,我们来模拟一个垂直居中于页面中的弹出层(modal)。

先运行下Demo 过过瘾?……

定义如下模态框的基本样式(部分样式使用bootstrap

.vh-modal {
  height: 640px;
  border: 1px solid #ccc;
  position: relative;
  .vh-modal-content {
    min-width: 50%;
    max-width: 80%;
    background: #fff;
    border: 1px solid rgba(0, 0, 0, .2);
    box-shadow: 0 5px 15px rgba(0, 0, 0, .5);
  }
  .vh-modal-title {
    padding: 20px;
    font-size: 20px;
    border-bottom: 1px solid #ccc;
    text-align: left;
    margin: 0;
  }
  .vh-modal-body {
    padding: 20px;
    text-align: left;
  }
  .vh-modal-foot {
    text-align: right;
    padding: 20px;
    border-top: 1px solid #ccc;
  }
}

(伪)元素占位方案 推荐

利用(伪)元素和display:inline-block的方案来实现垂直居中是我个人常用的也是推荐大家使用的方法。

.vh-modal-1 {
  text-align: center; //水平居中
  font-size: 0; //消除空隙, 见 https://smohan.net/blog/6gr77h
  &::before,
  >.vh-modal-content {
    display: inline-block;
    vertical-align: middle;
    font-size: 14px;
  }
  &::before {
    content: '';
    height: 100%;
  }
}
<div class="vh-modal vh-modal-1">
  <div class="vh-modal-content">
    <h3 class="vh-modal-title">模态框</h3>
    <div class="vh-modal-body">...</div>
    <div class="vh-modal-foot">
      <button class="btn btn-primary">确定</button>
    </div>
  </div>
</div>

伪(元素)实现垂直居中

如上图中的::before你也可以使用一个真实的元素代替。

absolute + transform方案

使用absolute绝对定位子元素,并且设置其top:50%; left:50%,然后再利用css3transform: translate(-50%, -50%); 设置负值偏移回来也是一种有效的垂直居中方案,但要注意其兼容性以及不要将子容器置于父容器半个像素的位置上(如500.5px),否则子容器会出现模糊

.vh-modal-2 {
  >.vh-modal-content {
    //尽可能的不要让该元素的宽度或者高度出现奇数,否则可能会导致模糊
    display: inline-block; //为了自适应宽度,可以固定宽度
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
  }
}

transform法还有一个缺点,就是当子容器高度超出视窗高度的时候,它会被直接截断(如下图),而不是想象中的随着浏览器滚动到顶部而滚动显示完全(模态框的头部被截掉了)。
transform法子元素直接被截断

table-cell方案

使用div来模拟table的行为也可以实现垂直居中。缺点是要在子容器外层再包裹一个父元素vh-modal-cell用来模拟table-cell

.vh-modal-3 {
  display: table;
  width: 100%;
  .vh-modal-cell {
    display: table-cell;
    vertical-align: middle;
    text-align: center;
  }
  .vh-modal-content {
    display: inline-block;
  }
}
<div class="vh-modal vh-modal-3">
  <div class="vh-modal-cell">
    <div class="vh-modal-content">
       ...
    </div>
  </div>
</div>
<!-- 
模拟了table布局
<table style="width: 100%;">
  <tr>
      <td style="text-align: center; vertical-align: middle;">
          类似于直接使用table布局
      </td>
  </tr>
</table>
-->

基于flex的方案 强烈推荐

毫无疑问,flex盒模型是最佳的实践方案。目前几乎所有现代浏览器都支持flex布局,尤其是移动端(部分机型UC浏览器效果太差,差评?)。
基于flex盒模型的水平垂直居中有如下两种方案:

<div class="vh-modal vh-modal-4(5)">
  <div class="vh-modal-content">
    ...
  </div>
</div>

align-items & justify-content方案

.vh-modal-4 {
  display: flex;
  align-items: center;
  justify-content: center;
  >.vh-modal-content {}
}

flex + margin方案

这个方案是最神奇的,仅仅给子元素设置了margin:auto;属性,一切就这么发生了?。

.vh-modal-5 {
  display: flex;
  margin: 0;
  >.vh-modal-content {
    margin: auto;
  }
}

counter来模拟/装饰有序清单

counter实现多级有序清单

就如截图中圈出的那样,类似这种多层级的数字,我们大概第一反应是使用JavaScript循环列表,利用其index拼接而成的。事实上,仅仅使用css的counter属性也可以实现该功能,甚至实现起来更高效。博客的【热门文章】栏目的索引就是使用counter属性实现的。

ol {
  counter-reset: decimal;
  list-style-type: none; //去掉默认的list-style 
  li {
    &::before {
      counter-increment: decimal;   
      content: counters(decimal, '.') ' ';
    }
  }
}

项目实例

就像图上那样,我们很早就已经将counter属性用在真实项目中了,而这仅仅只是counter属性一个简单的使用场景,它甚至可以帮助你完成一个简单的购物车计费场景(如图,当然,真实项目中没有人这么干)。

counter实现购物车计费

预览多级别列表和购物车demo

CSS计数器是CSS2.1中自动计数编号部分的实现。作为由CSS维护的变量counter属性还有很多有趣的使用场景,具体就不展开了。请参考MDN上的使用CSS计数器章节。

table-layout来控制表格单元格宽度

你也许遇到过给表格设置了宽度,但是不起作用的问题。这是因为单元格的宽度是根据其内容进行调整的。刨根揭底,是因为表格有个叫做table-layout的属性,其浏览器默认值是auto在作怪。当我们把这个值设置为fixed的时候,我们给th/td标签设置的宽度就起作用了。用法很简单:

table {
 table-layout: fixed;
 width: 100%;
}

没有对比就没有伤害

截图是设置table-layout: fixed;前后对比图,左边用蓝色标注的是默认行为的表格,右边是设置了table-layout: fixed;后的样式。显而易见的,默认情况下,单元格宽度受其内容约束。而设置了table-layout: fixed;后,其单元格宽度变得可控了。预览demo

caret-color来自定义光标的样式

在文本框中input/textarea中如果要改变光标的颜色,可以通过设置文本的颜色color:#f00来搞定。但是假如我们只想改变光标的颜色,而不想改变文本的颜色的话,caret-color属性是一个实现方案。预览demo

input,
textarea,
[contenteditable] {
  caret-color: red;
}

自定义光标颜色

user-select来禁用文本选中

在远古时代,如果你不想让别人选中你页面的内容,JavaScript是不可或缺的。而在文明社会中,只需要一句user-select:none的CSS样式就可以解决。IE6-9不支持该属性,可以通过给body添加 onselectstart="return false;"的内联JavaScript语句搞定。

body{
  user-select: none; //页面中的文本不能被选中
}

参考文档

本文首发于我的博客


Smohan
882 声望61 粉丝