CSS汇总

322 阅读13分钟

CSS 的 引入

4种引入方式

  • 行内样式(行间样式、内联样式、行嵌样式): 在网页元素上通过 style="" 属性直接写样式。如:

    <div style="color: green; margin-top: 30px;border: 1px solid red;width: 500px">行内样式实例1</div>
    
  • 内部样式表: 在网页上创建嵌入的样式表,通常写在 <head></head> 里面。如:

    <style>
        p {
            color: #6478de;
            border: red 1px solid;
        }
    </style>
    
  • 链入外部样式表: 将网页链接到外部样式表。先创建一个 CSS 文件,再在 HTML 中通过 <link> 链接此 CSS 文件。一般写在 <style></style> 的前面。如:

    <link rel="stylesheet" type="text/css" href="qt_02_style.css">
    
  • 导入外部样式表: 通过 @import 引入其他的 CSS 文件(不建议使用)。如:

    <style>
        @import "qt_02_style.css";
    </style>
    

以上 4 种 CSS 引用方式的区别:

  • 行内样式只作用于当前标签。HTML页面不纯净,后期维护不方便。
  • 内部样式作用于当前文件。CSS 代码写在 HTML 文档中,如果一个网站有很多 HTML 页面,每个文件都会变大,后期维护难度也大,如果 HTML 文件很少,CSS 代码也不多,也可以使用这种方式。
  • 外部样式可以被多个 HTML 文件引用。实现了 HTML 代码与 CSS 的完全分离,使得前期开发和后期维护都十分方便。

<link> 引入和 @import的区别

  • 使用限制

    • <link> 是 XHTML 标签,除了可以加载 CSS 外,还可以定义 RSS 等其他事务,通过 <link> 标签中的 href="" 属性来引入外部文件。
    • @import 属于 CSS 范畴,只能加载 CSS ,应该写在 CSS 中,且导入语句应写在样式表的开头,否则无法正确导入外部文件。
  • 加载执行机制

    • <link> 引用 CSS 时,在页面载入的时候可以同时加载样式,样式加载和结构加载是异步操作。可以防止访问网页时先加载完文字、图片等结构数据,然后再加载样式的问题。
    • @import 需要网页结构完全载入以后加载样式文件
  • 兼容性问题

    • <link> 是 XHTML 标签,无兼容问题
    • @import 是在 CSS2.1 提出的,低版本浏览器不支持。
  • dom操作

    • <link> 支持使用 JavaScript 控制 DOM来改变样式。
    • @import 不支持。

选择器

  • id选择器(#box),选择id为box的元素
  • 类选择器(.one),选择类名为one的所有元素
  • 标签选择器(div),选择标签为div的所有元素
  • 后代选择器(#box div),选择id为box元素内部所有的div元素
  • 子选择器(.one>one_1),选择父元素为.one的所有.one_1的元素
  • 相邻同胞选择器(.one+.two),选择紧接在.one之后的所有.two元素
  • 并级选择器(div,p),选择div、p的所有元素
  • 属性选择器([attribute=value])

优先级

!important>行内样式>ID选择器>类、伪类、属性>元素、伪元素>继承>通配符

为什么说css的选择器一般不要超过三级?

css匹配的规则是从右往左开始匹配,例如#markdown .content h3匹配规则如下:

  • 先找到h3标签元素
  • 然后去除祖先不是.content的元素
  • 最后去除祖先不是#markdown的元素

如果嵌套的层级更多,页面中的元素更多,那么匹配所要花费的时间代价自然更高

所以我们在编写选择器的时候,可以遵循以下规则:

  • 不要嵌套使用过多复杂选择器,最好不要三层以上
  • 使用id选择器就没必要再进行嵌套
  • 通配符和属性选择器效率最低,避免使用

布局

能不能讲一讲Flex布局,以及常用的属性?

阮一峰的grid系列

BFC

Block Fromatting Context, 即块级格式上下文

触发条件

一个HTML元素要创建BFC,则满足下列的任意一个或多个条件即可: 下列方式会创建块格式化上下文:

  • 根元素()
  • 浮动元素(元素的 float 不是 none)
  • 绝对定位元素(元素的 position 为 absolute 或 fixed)
  • 行内块元素(元素的 display 为 inline-block
  • 表格单元格(元素的 display为 table-cell,HTML表格单元格默认为该值)
  • 表格标题(元素的 display 为 table-caption,HTML表格标题默认为该值)
  • 匿名表格单元格元素(元素的 display为 table、table-row、 table-row-group、table-header-group、table-footer-group(分别是HTML table、row、tbody、thead、tfoot的默认属性)或 inline-table)
  • overflow 值不为 visible 的块元素 -弹性元素(display为 flex 或 inline-flex元素的直接子元素)
  • 网格元素(display为 grid 或 inline-grid 元素的直接子元素) 等等。

BFC渲染规则

(1)BFC垂直方向边距重叠

(2)BFC的区域不会与浮动元素的box重叠

(3)BFC是一个独立的容器,外面的元素不会影响里面的元素

(4)计算BFC高度的时候浮动元素也会参与计算

应用场景

1. 防止浮动导致父元素高度塌陷

2. 避免外边距折叠


重绘和回流

如何触发?

回流触发时机

回流这一阶段主要是计算节点的位置和几何信息,那么当页面布局和几何信息发生变化的时候,就需要回流,如下面情况:

  • 添加或删除可见的DOM元素
  • 元素的位置发生变化
  • 元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等)
  • 内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代
  • 页面一开始渲染的时候(这避免不了)
  • 浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)

还有一些容易被忽略的操作:获取一些特定属性的值

offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight

这些属性有一个共性,就是需要通过即时计算得到。因此浏览器为了获取这些值,也会进行回流

除此还包括getComputedStyle方法,原理是一样的

重绘触发时机

触发回流一定会触发重绘

可以把页面理解为一个黑板,黑板上有一朵画好的小花。现在我们要把这朵从左边移到了右边,那我们要先确定好右边的具体位置,画好形状(回流),再画上它原有的颜色(重绘)

除此之外还有一些其他引起重绘行为:

  • 颜色的修改
  • 文本方向的修改
  • 阴影的修改

如何避免?

  • 避免频繁使用 style,而是采用修改class的方式。
  • 使用createDocumentFragment进行批量的 DOM 操作。
  • 对于 resize、scroll 等进行防抖/节流处理。
  • 添加 will-change: tranform ,让渲染引擎为其单独实现一个图层,当这些变换发生时,仅仅只是利用合成线程去处理这些变换,而不牵扯到主线程,大大提高渲染效率。当然这个变化不限于tranform, 任何可以实现合成效果的 CSS 属性都能用will-change来声明。这里有一个实际的例子,一行will-change: tranform拯救一个项目,点击直达
  • 使用css3硬件加速,可以让transformopacityfilters这些动画不会引起回流重绘

减少获取offset属性

例如,多次修改一个把元素布局的时候,我们很可能会如下操作

const el = document.getElementById('el')
for(let i=0;i<10;i++) {
    el.style.top  = el.offsetTop  + 10 + "px";
    el.style.left = el.offsetLeft + 10 + "px";
}

每次循环都需要获取多次offset属性,比较糟糕,可以使用变量的形式缓存起来,待计算完毕再提交给浏览器发出重计算请求

// 缓存offsetLeft与offsetTop的值
const el = document.getElementById('el')
let offLeft = el.offsetLeft, offTop = el.offsetTop

// 在JS层面进行计算
for(let i=0;i<10;i++) {
  offLeft += 10
  offTop  += 10
}

// 一次性将计算结果应用到DOM上
el.style.left = offLeft + "px"
el.style.top = offTop  + "px"

避免改变样式,使用类名去合并样式

const container = document.getElementById('container')
container.style.width = '100px'
container.style.height = '200px'
container.style.border = '10px solid red'
container.style.color = 'red'

使用类名去合并样式

<style>
    .basic_style {
        width: 100px;
        height: 200px;
        border: 10px solid red;
        color: red;
    }
</style>
<script>
    const container = document.getElementById('container')
    container.classList.add('basic_style')
</script>

前者每次单独操作,都去触发一次渲染树更改(新浏览器不会),

都去触发一次渲染树更改,从而导致相应的回流与重绘过程

合并之后,等于我们将所有的更改一次性发出

设置display: none

将其从页面上去掉,然后再进行后续操作,这些后续操作也不会触发回流与重绘,这个过程称为离线操作

let container = document.getElementById('container')
container.style.display = 'none'
container.style.width = '100px'
container.style.height = '200px'
container.style.border = '10px solid red'
container.style.color = 'red'
...(省略了许多类似的后续操作)
container.style.display = 'block'

响应式布局

实现方式

响应式设计的基本原理是通过媒体查询检测不同的设备屏幕尺寸做处理,为了处理移动端,页面头部必须有meta声明viewport

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no”>

属性对应如下:

  • width=device-width: 是自适应手机屏幕的尺寸宽度
  • maximum-scale:是缩放比例的最大值
  • inital-scale:是缩放的初始化
  • user-scalable:是用户的可以缩放的操作

实现响应式布局的方式有如下:

  • 媒体查询
  • 百分比
  • vw/vh
  • rem

媒体查询

CSS3中的增加了更多的媒体查询,就像if条件表达式一样,我们可以设置不同类型的媒体条件,并根据对应的条件,给相应符合条件的媒体调用相对应的样式表

使用@Media查询,可以针对不同的媒体类型定义不同的样式,如:

@media screen and (max-width: 1920px) { ... }

当视口在375px - 600px之间,设置特定字体大小18px

@media screen (min-width: 375px) and (max-width: 600px) {
  body {
    font-size: 18px;
  }
}

通过媒体查询,可以通过给不同分辨率的设备编写不同的样式来实现响应式的布局,比如我们为不同分辨率的屏幕,设置不同的背景图片

比如给小屏幕手机设置@2x图,为大屏幕手机设置@3x图,通过媒体查询就能很方便的实现

百分比

通过百分比单位 " % " 来实现响应式的效果

比如当浏览器的宽度或者高度发生变化时,通过百分比单位,可以使得浏览器中的组件的宽和高随着浏览器的变化而变化,从而实现响应式的效果

heightwidth属性的百分比依托于父标签的宽高,但是其他盒子属性则不完全依赖父元素:

  • 子元素的top/left和bottom/right如果设置百分比,则相对于直接非static定位(默认定位)的父元素的高度/宽度
  • 子元素的padding如果设置百分比,不论是垂直方向或者是水平方向,都相对于直接父亲元素的width,而与父元素的height无关。
  • 子元素的margin如果设置成百分比,不论是垂直方向还是水平方向,都相对于直接父元素的width
  • border-radius不一样,如果设置border-radius为百分比,则是相对于自身的宽度

可以看到每个属性都使用百分比,会照成布局的复杂度,所以不建议使用百分比来实现响应式

#vw/vh

vw表示相对于视图窗口的宽度,vh表示相对于视图窗口高度。 任意层级元素,在使用vw单位的情况下,1vw都等于视图宽度的百分之一

与百分比布局很相似,在以前文章提过与%的区别,这里就不再展开述说

rem

在以前也讲到,rem是相对于根元素htmlfont-size属性,默认情况下浏览器字体大小为16px,此时1rem = 16px

可以利用前面提到的媒体查询,针对不同设备分辨率改变font-size的值,如下:

@media screen and (max-width: 414px) {
  html {
    font-size: 18px
  }
}

@media screen and (max-width: 375px) {
  html {
    font-size: 16px
  }
}

@media screen and (max-width: 320px) {
  html {
    font-size: 12px
  }
}

为了更准确监听设备可视窗口变化,我们可以在css之前插入script标签,内容如下:

//动态为根元素设置字体大小
function init () {
    // 获取屏幕宽度
    var width = document.documentElement.clientWidth
    // 设置根元素字体大小。此时为宽的10等分
    document.documentElement.style.fontSize = width / 10 + 'px'
}

//首次加载应用,设置一次
init()
// 监听手机旋转的事件的时机,重新设置
window.addEventListener('orientationchange', init)
// 监听手机窗口变化,重新设置
window.addEventListener('resize', init)

无论设备可视窗口如何变化,始终设置remwidth的1/10,实现了百分比布局

除此之外,我们还可以利用主流UI框架,如:element uiantd提供的栅格布局实现响应式

小结

响应式设计实现通常会从以下几方面思考:

  • 弹性盒子(包括图片、表格、视频)和媒体查询等技术
  • 使用百分比布局创建流式布局的弹性UI,同时使用媒体查询限制元素的尺寸和内容变更范围
  • 使用相对单位使得内容自适应调节
  • 选择断点,针对不同断点实现不同布局和内容展示

如何实现单行/多行文本溢出的省略样式?

单行文本溢出省略

  • text-overflow:规定当文本溢出时,显示省略符号来代表被修剪的文本
  • white-space:设置文字在一行显示,不能换行
  • overflow:文字长度超出限定宽度,则隐藏超出的内容
<style>
    p{
        overflow: hidden;
        line-height: 40px;
        width:400px;
        height:40px;
        border:1px solid red;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
</style>
<p> 这是一些文本这是一些文本这是一些文本这是一些文本这是一些文本这是一些文本这是一些文本这是一些文本这是一些文本这是一些文本</p >

多行文本溢出省略

通过伪元素绝对定位到行尾并遮住文字,再通过 overflow: hidden 隐藏多余文字

    <style>
      .demo {
        position: relative;
        line-height: 20px;
        height: 40px;
        width: 200px;
        overflow: hidden;
        border: 1px solid plum;
      }
      .demo::after {
        content: '...';
        position: absolute;
        bottom: 0;
        right: 0;
        height: 20px;
        background-color: #fff;
        padding: 0 5px;
      }
    </style>
  </head>

  <body>
    <div class="demo">
      这是一段很长的文本这是一段很长的文本这是一段很长的文本这是一段很长的文本这是一段很长的文本这是一段很长的文本这是一段很长的文本这是一段很长的文本
    </div>
  </body>

实现三角形

    <style>
      .border {
        width: 0;
        height: 0;
        border: 50px solid;
        border-color: transparent #ffeead transparent transparent;
      }
    </style>
  </head>
  <body>
    <div class="border"></div>
  </body>

让Chrome支持小于12px

Zoom

zoom 的字面意思是“变焦”,可以改变页面上元素的尺寸,属于真实尺寸

其支持的值类型有:

  • zoom:50%,表示缩小到原来的一半
  • zoom:0.5,表示缩小到原来的一半

使用 zoom 来”支持“ 12px 以下的字体

代码如下:

<style type="text/css">
    .span1{
        font-size: 12px;
        display: inline-block;
        zoom: 0.8;
    }
    .span2{
        display: inline-block;
        font-size: 12px;
    }
</style>
<body>
    <span class="span1">测试10px</span>
    <span class="span2">测试12px</span>
</body>

-webkit-transform:scale()

针对chrome浏览器,加webkit前缀,用transform:scale()这个属性进行放缩

注意:使用scale属性只对可以定义宽高的元素生效,所以,下面代码中将span元素转为行内块元素

<style type="text/css">
    .span1{
        font-size: 12px;
        display: inline-block;
        -webkit-transform:scale(0.8);
    }
    .span2{
        display: inline-block;
        font-size: 12px;
    }
</style>
<body>
    <span class="span1">测试10px</span>
    <span class="span2">测试12px</span>
</body>

-webkit-text-size-adjust:none

该属性用来设定文字大小是否根据设备(浏览器)来自动调整显示大小

属性值:

  • percentage:字体显示的大小;
  • auto:默认,字体大小会根据设备/浏览器来自动调整;
  • none:字体大小不会自动调整
html { -webkit-text-size-adjust: none; }

这样设置之后会有一个问题,就是当你放大网页时,一般情况下字体也会随着变大,而设置了以上代码后,字体只会显示你当前设置的字体大小,不会随着网页放大而变大了

所以,我们不建议全局应用该属性,而是单独对某一属性使用

需要注意的是,自从chrome 27之后,就取消了对这个属性的支持。同时,该属性只对英文、数字生效,对中文不生效

总结

Zoom 非标属性,有兼容问题,缩放会改变了元素占据的空间大小,触发重排

-webkit-transform:scale() 大部分现代浏览器支持,并且对英文、数字、中文也能够生效,缩放不会改变了元素占据的空间大小,页面布局不会发生变化

-webkit-text-size-adjust对谷歌浏览器有版本要求,在27之后,就取消了该属性的支持,并且只对英文、数字生效


css布局案例

css布局小技巧


趣味小挑战

【等边三角形】轨迹动画

1:1.73:2

    <style>
      .container {
        position: absolute;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%);
        width: 100px;
        height: 100px;
        background-color: pink;
      }
      .spot {
        width: 20px;
        height: 20px;
        background-color: royalblue;
        border-radius: 50%;
        animation: move 4s infinite;
      }
      /* 都是相对于起点的坐标 */
      @keyframes move {
        0% {
        }
        33% {
          transform: translate(calc(100px - 100%), 0);
        }
        67% {
          transform: translate(calc(50px - 50%), calc(87px - 100%));
        }
        100% {
          transform: translate(0, 0);
        }
      }
      .triangle {
        border: 100px solid;
        border-top-width: 173px;
        border-bottom-width: 173px;
        width: 0;
        height: 0;
        border-color: lightcoral royalblue plum lightgreen;
      }
    </style>

    <div class="triangle"></div>
    <div class="container">
      <div class="spot"></div>
    </div>

自定义checkbox

    <style>
      #box {
        position: relative;
        width: 40px;
        height: 40px;
        box-sizing: border-box;
      }
      #box::before {
        box-sizing: border-box;
        content: '';
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        border: lightgreen 5px solid;
      }
      #box:checked::before {
        box-sizing: border-box;
        content: 'o';
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        line-height: calc(100% - 20px);
        font-size: 40px;
        text-align: center;
        font-weight: bold;
        color: lightcoral;
        background-color: #fff;
      }
    </style>
    
    <input type="checkbox" name="" id="box" />

多重阴影效果

磨砂玻璃效果

    <style>
      * {
        margin: 0;
        padding: 0;
      }

      body {
        width: 100vw;
        height: 3000px;
        background: url(../pic/samuel-scrimshaw-361584-unsplash.jpg) no-repeat 0 0 / cover fixed;
        /* background-size: cover; */
        /* 必写 */
        /* background-attachment: fixed; */
      }

      div {
        position: absolute;
        background: inherit;
        background: url(../pic/samuel-scrimshaw-361584-unsplash.jpg) no-repeat 0 0 / cover fixed;
        width: 500px;
        height: 300px;
        border: white 1px solid;
        overflow: hidden;
      }

      div::before {
        content: '';
        width: 550px;
        height: 550px;
        background: inherit;
        position: absolute;
        left: -25px;
        right: 0;
        top: -25px;
        bottom: 0;
        box-shadow: inset 0 0 0 3000px rgba(255, 255, 255, 0.3);
        filter: blur(10px);
      }
    </style>
  </head>

  <body>
    <div></div>
  </body>