CSS重难点总结

1,764 阅读4分钟

在重新回顾一遍CSS之后,我认为CSS有三大难点,分别为float平均布局、层叠上下文定位、浏览器渲染原理,先从第一个讲起。

float 平均布局

假设有这么一个需求,在800px宽度的屏幕里,使用float浮动定位以4幅图片平均布局的方式放置若干幅图片,HTML如下:

<div class="imageList clearfix">
    <div class="image"></div>
    <div class="image"></div>
    <div class="image"></div>
    <div class="image"></div>
    <div class="image"></div>
    <div class="image"></div>
    <div class="image"></div>
</div>

我们容易想到,设图片宽度为x,间距为y,有4x + 3y = 800,且尽量满足x,y为整数,可以得到x = 191,y = 12,我们写一下CSS

.imageList{
  width: 800px;
}

.clearfix:after{
  content: '';
  display: block;
  clear: both;
}

.imageList .image{
  width: 191px;
  height: 191px;
  background: #000;
  border: 10px solid red;
  float: left;
  margin-bottom: 10px;
  margin-right: 12px;
}

预览

写完发现似乎有些不对劲,图片并没有按照我们设想的方式排布,一排只有3张,这是为什么呢?

原来是一排放不下了,一张图片191+12 = 203,203 * 3 = 609,只剩下191,而第四张图片依然有12px的margin-right... 浏览器发现空间不够,就只能把第四张图片移到下一行了。

怎么解决这个问题呢?

给图片加一个父类,样式设为margin-right: -12px;,HTML更新为

  <div class="imageList">
    <div class="x clearfix">
      <div class="image"></div>
      <div class="image"></div>
      <div class="image"></div>
      <div class="image"></div>
      <div class="image"></div>
      <div class="image"></div>
      <div class="image"></div>
    </div>
  </div>

CSS加入一条

.imageList > .x{
  margin-right: -12px;
}

bingo!神奇的事情发生了!

神奇

究竟是什么原理呢?开发者工具可以帮助我们一探究竟。

原理

由于4张图片的总宽度是812px,父元素只有800px是容纳不下的,但是-12px的margin-right使得父元素向右“撑胖”了12px,这样就刚好容纳这4张图片了。

CSS的奥妙之处还远大于此。

层叠上下文定位

有这样一个问题,z-index=10的元素一定在z-index=5元素的上面吗?

答案是不一定

看一个例子,HTML:

<body>
  <div class="container">
    <div class="a">1
      <div class="a1">10</div>
    </div>
    <div class="b">
      <div class="b1">5</div>
    </div>
  </div>
</body>

CSS

.container{
  border: 1px solid red;
  height: 200px;
  position: relative;
}

.a{
  position: relative;
  z-index: 1;
  border: 1px solid blue;
}

.a1{
  z-index:10;
  height: 50px;
  width: 50px;
  background: red;
}

.b1{
  position: relative;
  z-index: 5;
  height: 50px;
  width: 50px;
  background: blue;
  top: -20px;
  color: white;
}

实际效果

最终效果

5居然在10上面?

其实是5在1中的10上面(a1父元素a的z-index=1)。

这又是为什么呢?

这就是CSS中的层叠上下文,每一个层叠上下文都是一个小世界,这个小世界里面的z-index与外界无关,只有同一个小世界的z-index才能比较大小。

那么哪些CSS属性会创建这个小世界呢?

权威回答在这里,层叠上下文的形成

其中我认为比较重要的几个,分别是:

  • z-index不为默认的auto(只要设置了z-index,且值不为auto)
  • 根元素HTML
  • position: fixed/sticky;

讲完了前两个重难点,还有一个就是浏览器的渲染原理。

浏览器的渲染原理

讲一下浏览器的渲染过程:

  1. 根据HTML构建HTML树
  2. 根据CSS构建CSS树
  3. 根据HTML和CSS构建渲染树,render tree
  4. Layout布局(文档流、盒模型、计算大小和位置)
  5. Paint绘制(边框颜色、文字颜色、阴影等)
  6. Composite合成(根据层叠关系展示画面)

重点在后三步,每一步都会消耗一定的性能,所以js更新样式,有三种方式:

  1. 三步全走,如div.remove()当前元素消失,其他元素relayout
  2. 走后两步,如改变背景颜色,只repaint+composite,不改变layout
  3. 只走最后一步,如transform,所以这就是为什么动画用transform而不改变left之类定位属性的原因,更节能

CSS动画的实现

有两种实现方法,分别是transition和animation

transition作用是补充中间帧,语法为:

transition: 属性名 时长 动画效果 延迟;

一个简单例子

#heart{
  display: inline-block;
  position: relative;
  transition: all 1s;
}
#heart:hover{
  transform: scale(1.2);
}

animation的重点是写好中间的关键帧,

语法为:

animation: 时长 过渡方式 延迟 重复次数 方向 填充模式 是否暂停 动画名

@keyframes 动画名:关键帧百分比1{} 关键帧百分比2{} 关键帧百分比3{}../

一个简单例子,跳动的心

#heart{
  display: inline-block;
  position: relative;
  margin: 100px;
  animation: .5s infinite 3s alternate-reverse beating;
}

@keyframes beating {
  0%{
    transform: scale(1.0);
  }
  100%{
    transform: scale(1.2);
  }
}