块级元素居中方案

1. 方案一

html:

1
<div class="main"></main>

css:

1
2
3
4
5
6
7
8
9
10
.main{
width:400px;
height:200px;
background:#eee;
position:absolute;
left:50%;
top:50%;
margin-left:-200px;
margin-top:-100px;
}

利用定位,移动宽高的一半,然后margin负值回去一半的一半。

2.方案二

html:

1
<div class="main"></div>

css:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
body {
height: 100%;
}
.main{
width: 400px;
height: 200px;
background:#eee;
position:absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
margin: auto;
}

最常用,直接一个布局搞定,在水平方向垂直方向都可以自定义的居中。

3.方案三

html:

1
2
3
<div class="main">
hello world
</div>

css:

1
2
3
4
5
6
7
.main{
background:#eee;
position:absolute;
left:50%;
top:50%;
transform: translate(-50%,-50%);
}

这个其实和第一个非常类似,他们是做了一个位移,位移的方位分别是 x 和 y 两个轴上。移动自身的 50% 注意这个地方位移不是指外部容器的 50% 而是自身。

Stack Overflow中的解释

4.方案四

html:

1
2
3
4
<body>
<div class="main">
</div>
</body>

css:

1
2
3
4
5
6
7
8
9
10
11
12
13
html,body{
width: 100%;
height: 100%;
display: -webkit-flex;
display: flex;
justify-content:center;
align-items:center;
}
.container{
width: 400px;
height: 200px;
background: #ccc;
}

Webpack 入门

从 webpack v4.0.0 开始,可以不用引入一个配置文件。然而,webpack 仍然还是高度可配置的。在开始前你需要先理解四个核心概念

  • 入口(entry)
  • 输出(output)
  • loader
  • 插件(plugins)

1.入口(entry)

*入口起点(entry point)**指示 webpack 应该使用哪个模块,来作为构建其内部依赖图*的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。

1
2
3
module.exports = {
entry: './path/to/my/entry/file.js'
};

2.出口(output)

output 属性告诉 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件,默认值为 ./dist

1
2
3
4
5
6
7
8
9
const path = require('path');

module.exports = {
entry: './path/to/my/entry/file.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js'
}
};

3.loader

loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript 和 Json)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。

在更高层面,在 webpack 的配置中 loader 有两个目标:

  1. test 属性,用于标识出应该被对应的 loader 进行转换的某个或某些文件。
  2. use 属性,表示进行转换时,应该使用哪个 loader。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const path = require('path');

const config = {
output: {
filename: 'my-first-webpack.bundle.js'
},
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
}
};

module.exports = config;

4.插件(plugins)

​ loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。

​ 想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new 操作符来创建它的一个实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装
const webpack = require('webpack'); // 用于访问内置插件

const config = {
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
},
plugins: [
new HtmlWebpackPlugin({template: './src/index.html'})
]
};

module.exports = config;

webpack 提供许多开箱可用的插件!查阅我们的插件列表获取更多信息。

5.模式

通过选择 developmentproduction 之中的一个,来设置 mode 参数,你可以启用相应模式下的 webpack 内置的优化

1
2
3
module.exports = {
mode: 'production'
};

JavaScript 模块化

1. 模块化过程

1. 全局函数

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 全局函数模式: 将不同的功能封装成不同的全局函数
* 问题: Global被污染了, 很容易引起命名冲突
*/
//数据
let data = 'atguigu.com'
function foo() {
console.log('foo()')
}
function bar() {
console.log('bar()')
}

2. 对象空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* namespace模式: 简单对象封装
* 作用: 减少了全局变量
* 问题: 不安全(数据不是私有的, 外部可以直接修改)
*/
let myModule = {
data: 'atguigu.com',
foo() {
console.log(`foo() ${this.data}`)
},
bar() {
console.log(`bar() ${this.data}`)
}
}

3. 立即执行函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* IIFE模式: 匿名函数自调用(闭包)
* IIFE : immediately-invoked function expression(立即调用函数表达式)
* 作用: 数据是私有的, 外部只能通过暴露的方法操作
* 问题: 如果当前这个模块依赖另一个模块怎么办?
*/
(function (window) {
//数据
let data = 'atguigu.com'

//操作数据的函数
function foo() { //用于暴露有函数
console.log(`foo() ${data}`)
}

function bar() {//用于暴露有函数
console.log(`bar() ${data}`)
otherFun() //内部调用
}

function otherFun() { //内部私有的函数
console.log('otherFun()')
}

//暴露行为
window.myModule = {foo, bar}
})(window)

4.依赖注入的自执行函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* IIFE模式增强 : 引入依赖
* 这就是现代模块实现的基石
*/
(function (window, $) {
//数据
let data = 'atguigu.com'

//操作数据的函数
function foo() { //用于暴露有函数
console.log(`foo() ${data}`)
$('body').css('background', 'red')
}

function bar() {//用于暴露有函数
console.log(`bar() ${data}`)
otherFun() //内部调用
}

function otherFun() { //内部私有的函数
console.log('otherFun()')
}

//暴露行为
window.myModule = {foo, bar}
})(window, jQuery)

2.CommonJS

​ commonJs 是同步进行依赖引入的,并且他是相当于一个文件一个模块,对于模块的导出采用了 module.exports={} 或者使用 exports={} 来完成的,而模块的引入则是通过 let mo=require("./module.js") 但是注意 nodejs 采用的就是 CommonJs 所以他是直接可用这个语句的,但是浏览器不行,所以浏览器需要使用 bowserify 工具打包编译才能让浏览器识别。

1.在服务端应用

1.下载安装node.js

2.创建项目结构

1
2
3
4
5
6
7
8
9
10
|-modules
|-module1.js
|-module2.js
|-module3.js
|-app.js
|-package.json
{
"name": "commonJS-node",
"version": "1.0.0"
}

3.下载第三方模块

  • npm install uniq –save

    4.模块化编码

  • module1.js

    1
    2
    3
    4
    5
    module.exports = {
    foo() {
    console.log('moudle1 foo()')
    }
    }
  • module2.js

    1
    2
    3
    module.exports = function () {
    console.log('module2()')
    }
  • module3.js

    1
    2
    3
    4
    5
    6
    7
    exports.foo = function () {
    console.log('module3 foo()')
    }

    exports.bar = function () {
    console.log('module3 bar()')
    }
  • app.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    /**
    1. 定义暴露模块:
    module.exports = value;
    exports.xxx = value;
    2. 引入模块:
    var module = require(模块名或模块路径);
    */
    "use strict";
    //引用模块
    let module1 = require('./modules/module1')
    let module2 = require('./modules/module2')
    let module3 = require('./modules/module3')

    let uniq = require('uniq')
    let fs = require('fs')

    //使用模块
    module1.foo()
    module2()
    module3.foo()
    module3.bar()

    console.log(uniq([1, 3, 1, 4, 3]))

    fs.readFile('app.js', function (error, data) {
    console.log(data.toString())
    })

    5.通过node运行app.js

命令: node app.js

2.在浏览器的运行

1.创建项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
|-js
|-dist //打包生成文件的目录
|-src //源码所在的目录
|-module1.js
|-module2.js
|-module3.js
|-app.js //应用主源文件
|-index.html
|-package.json
{
"name": "browserify-test",
"version": "1.0.0"
}

2.下载browserify

  • 全局: npm install browserify -g
  • 局部: npm install browserify –save-dev
    1. 定义模块代码
  • module1.js
    1
    2
    3
    4
    5
    module.exports = {
    foo() {
    console.log('moudle1 foo()')
    }
    }
  • module2.js
    1
    2
    3
    module.exports = function () {
    console.log('module2()')
    }
  • module3.js
    1
    2
    3
    4
    5
    6
    7
    exports.foo = function () {
    console.log('module3 foo()')
    }

    exports.bar = function () {
    console.log('module3 bar()')
    }
  • app.js (应用的主js)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //引用模块
    let module1 = require('./module1')
    let module2 = require('./module2')
    let module3 = require('./module3')

    let uniq = require('uniq')

    //使用模块
    module1.foo()
    module2()
    module3.foo()
    module3.bar()

    console.log(uniq([1, 3, 1, 4, 3]))

3.打包处理js:

  • browserify js/src/app.js -o js/dist/bundle.js

4.页面使用引入:

1
<script type="text/javascript" src="js/dist/bundle.js"></script> 

3. AMD

异步模块定义。显示声明依赖注入

  1. 定义模块采用 define
  2. 导出模块在回调函数中 return
  3. 引入模块使用 require

1.下载require.js, 并引入

  • 官网: http://www.requirejs.cn/

  • github : https://github.com/requirejs/requirejs

  • 将require.js导入项目: js/libs/require.js

    2.创建项目结构

    1
    2
    3
    4
    5
    6
    7
    8
    |-js
    |-libs
    |-require.js
    |-modules
    |-alerter.js
    |-dataService.js
    |-main.js
    |-index.html

    3.定义require.js的模块代码

  • dataService.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    define(function () {
    let msg = 'test'

    function getMsg() {
    return msg.toUpperCase()
    }

    return {getMsg}
    })
  • alerter.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    define(['dataService', 'jquery'], function (dataService, $) {
    let name = 'Tom2'

    function showMsg() {
    $('body').css('background', 'gray')
    alert(dataService.getMsg() + ', ' + name)
    }

    return {showMsg}
    })

    4.应用主(入口)js: main.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    (function () {
    //配置
    require.config({
    //基本路径
    baseUrl: "js/",
    //模块标识名与模块路径映射
    paths: {
    "alerter": "modules/alerter",
    "dataService": "modules/dataService",
    }
    })
    //引入使用模块
    require( ['alerter'], function(alerter) {
    alerter.showMsg()
    })
    })()

6.页面使用模块:

1
<script data-main="js/main" src="js/libs/require.js"></script>

7.使用第三方基于require.js

1.将jquery的库文件导入到项目:

js/libs/jquery-1.10.1.js

2.在main.js中配置jquery路径

1
2
3
paths: {
'jquery': 'libs/jquery-1.10.1'
}

3.在alerter.js中使用jquery

1
2
3
4
5
6
7
8
define(['dataService', 'jquery'], function (dataService, $) {
var name = 'xfzhang'
function showMsg() {
$('body').css({background : 'red'})
alert(name + ' '+dataService.getMsg())
}
return {showMsg}
})

8.第三方不基于require.js的框架

1.将angular.js和angular-messages.js导入项目

  • js/libs/angular.js
  • js/libs/angular-messages.js

2.在main.js中配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
(function () {
require.config({
//基本路径
baseUrl: "js/",
//模块标识名与模块路径映射
paths: {
//第三方库
'jquery' : 'libs/jquery-1.10.1',
'angular' : 'libs/angular',
'angular-messages' : 'libs/angular-messages',
//自定义模块
"alerter": "modules/alerter",
"dataService": "modules/dataService"
},
/*
配置不兼容AMD的模块
exports : 指定导出的模块名
deps : 指定所有依赖的模块的数组
*/
shim: {
'angular' : {
exports : 'angular'
},
'angular-messages' : {
exports : 'angular-messages',
deps : ['angular']
}
}
})
//引入使用模块
require( ['alerter', 'angular', 'angular-messages'], function(alerter, angular) {
alerter.showMsg()
angular.module('myApp', ['ngMessages'])
angular.bootstrap(document,["myApp"])
})
})()

3.页面

1
2
3
4
<form name="myForm">
用户名: <input type="text" name="username" ng-model="username" ng-required="true">
<div style="color: red;" ng-show="myForm.username.$dirty&&myForm.username.$invalid">用户名是必须的</div>
</form>

4. ES6

​ es6这种方式就是定义模块直接向我们定义一个对象或者函数一样,而需要导出就是采用 export 导出函数或者对象或变量。在一个模块中可以多次 export 但是在引入的时候必须采用对象解构的方式导入我们 export 的内容。

1
2
3
4
5
//------------------module.js
export foo=()=>{}
export boo=()=>{}
//------------------main.js
import {foo,boo} from './module.js'

​ 如果我们使用默认导出则是可以直接用一个变量来接受我们导出的内容。

1
2
import module from './module.js'
module.foo();

1.定义package.json文件

1
2
3
4
{
"name" : "es6-babel-browserify",
"version" : "1.0.0"
}

2.安装babel-cli, babel-preset-es2015和browserify

1
2
npm install babel-cli browserify -g  // babel 的命令行工具
npm install babel-preset-es2015 --save-dev // babel的工具包,babel的工具包作用有很多比如这里 es6 转 es5 还有把jsx转html等

3.定义.babelrc文件

1
2
3
{
"presets": ["es2015"] //在babel工作之前会读取这个文件,然后表明babel调用什么模块做什么事情,这个就是 es6转es5 比如说还有 react 就是转jsx语法
}

4.编码

  • js/src/module1.js
    1
    2
    3
    4
    5
    6
    7
    export function foo() {
    console.log('module1 foo()');
    }
    export let bar = function () {
    console.log('module1 bar()');
    }
    export const DATA_ARR = [1, 3, 5, 1]
  • js/src/module2.js
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    let data = 'module2 data'

    function fun1() {
    console.log('module2 fun1() ' + data);
    }

    function fun2() {
    console.log('module2 fun2() ' + data);
    }

    export {fun1, fun2}
  • js/src/module3.js
    1
    2
    3
    4
    5
    6
    export default {
    name: 'Tom',
    setName: function (name) {
    this.name = name
    }
    }
  • js/src/app.js
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import {foo, bar} from './module1'
    import {DATA_ARR} from './module1'
    import {fun1, fun2} from './module2'
    import person from './module3'

    import $ from 'jquery'

    $('body').css('background', 'red')

    foo()
    bar()
    console.log(DATA_ARR);
    fun1()
    fun2()

    person.setName('JACK')
    console.log(person.name);

    5.编译

  1. 使用Babel将ES6编译为ES5代码(但包含CommonJS语法) : babel js/src -d js/lib

  2. 使用Browserify编译js : browserify js/lib/app.js -o js/lib/bundle.js

    6.页面中引入测试

    1
    <script type="text/javascript" src="js/lib/bundle.js"></script>

    7.引入第三方模块(jQuery)

1.下载jQuery模块:

npm install jquery@1 --save

2.在app.js中引入并使用

1
2
import $ from 'jquery'
$('body').css('background', 'red')

ECMAScript 从入门到精通

1. es5

1. 严格模式

1.理解:

  • 除了正常运行模式(混杂模式),ES5添加了第二种运行模式:”严格模式”(strict mode)。

  • 顾名思义,这种模式使得Javascript在更严格的语法条件下运行

    2.目的/作用

    • 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为
    • 消除代码运行的一些不安全之处,为代码的安全运行保驾护航
    • 为未来新版本的Javascript做好铺垫

      3.使用

  • 在全局或函数的第一条语句定义为: ‘use strict’;

  • 如果浏览器不支持, 只解析为一条简单的语句, 没有任何副作用

    4.语法和行为改变

  • 必须用var声明变量
  • 禁止自定义的函数中的this指向window,也就是不允许使用全局函数
  • 创建eval作用域,本来的eval中定义的变量直接做变量提升为全局的作用域。可能会被攻击。
  • 对象不能有重名的属性

2.object的拓展方法

ES5给Object扩展了一些静态方法, 常用的2个:

1.Object.create(prototype, [descriptors])

  • 作用: 以指定对象为原型创建新的对象

  • 为新的对象指定新的属性, 并对属性进行描述

    • value : 指定值
    • writable : 标识当前属性值是否是可修改的, 默认为false
    • configurable: 标识当前属性是否可以被删除 默认为false
    • enumerable: 标识当前属性是否能用for in 枚举 默认为false
    1
    2
    3
    4
    5
    6
    7
    var obj = {name : 'curry', age : 29}
    obj1 = Object.create(obj, {
    sex : {
    value : '男',
    writable : true
    }
    });

    obj1 的原型对象就是 obj,并企里面有一个 sex 值,但是要注意设置 sex的一些属性,比如现在 sex 就是只能够读写,不能被删除,也不能采用 for in 遍历。

    2.Object.defineProperties(object, descriptors)

  • 作用: 为指定对象定义扩展多个属性

    • get :用来获取当前属性值得回调函数
    • set :修改当前属性值得触发的回调函数,并且实参即为修改后的值
    • 存取器属性:setter,getter一个用来存值,一个用来取值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var obj2 = {
firstName : 'curry',
lastName : 'stephen'
};
Object.defineProperties(obj2, {
fullName : {
get : function () {
return this.firstName + '-' + this.lastName
},
set : function (data) {
var names = data.split('-');
this.firstName = names[0];
this.lastName = names[1];
}
}
});

可以看到新设置的 fullName 属性和其他的是不一样的,因为我们设置了 get 和 set 那么他就会惰性求值,只有我们在调用这个属性的时候会调用 get 方法求出元素的值,set 也是在设置值的时候才会调用。如果没有 get 就获取不到值,没有set则不能设置值。

除了上面这种设置 get 和 set 的方式之外还可以使用对象本身的方法:

  1. get propertyName(){} 用来得到当前属性值的回调函数
  2. set propertyName(){} 用来监视当前属性值变化的回调函数
1
2
3
4
5
6
7
8
9
10
11
12
var obj = {
firstName : 'kobe',
lastName : 'bryant',
get fullName(){
return this.firstName + ' ' + this.lastName
},
set fullName(data){
var names = data.split(' ');
this.firstName = names[0];
this.lastName = names[1];
}
};

代码同理。

3. Array 拓展

  1. Array.prototype.indexOf(value) : 得到值在数组中的第一个下标
  2. Array.prototype.lastIndexOf(value) : 得到值在数组中的最后一个下标
  3. Array.prototype.forEach(function(item, index){}) : 遍历数组
  4. Array.prototype.map(function(item, index){}) : 遍历数组返回一个新的数组,返回加工之后的值
  5. Array.prototype.filter(function(item, index){}) : 遍历过滤出一个新的子数组, 返回条件为true的值

在原型上定义了这么多方法,意味着 array 的实例都可以调用这些方法。

4. 函数拓展

1.Function.prototype.bind(obj)

作用: 将函数内的this绑定为obj, 并将函数返回

2.区别bind()与call()和apply()?

都能指定函数中的this,但是call()/apply()是立即调用函数而bind()是将函数返回。所以我们的 bind 一般是用来指定回调函数的 this。

2. es6

1. let

1.作用:

  • 与var类似, 用于声明一个变量

2.特点:

  • 在块作用域内有效,以前只有文件和函数作用域,这个东西有块级作用域
  • 不能重复声明,否则会报错
  • 不会预处理, 不存在提升

3.应用:

  • 循环遍历加监听
  • 使用let取代var是趋势

2. const

1.作用:

定义一个常量

2.特点:

不能修改其它特点同let

3.应用:

保存不用改变的数据

3. 结构赋值

1.理解:

从对象或数组中提取数据, 并赋值给变量(多个)

2.对象的解构赋值

1
let {n, a} = {n:'tom', a:12}

3.数组的解构赋值

1
let [a,b] = [1, 'atguigu'];

4.用途

给多个形参赋值

5. 例子

对象

1
2
3
4
let obj = {name : 'kobe', age : 39,site:{github:'aaa',weibo:'bbb'}};
//对象的解构赋值
let {age,site:{github}} = obj;
console.log(age,github);

数组

1
2
3
let arr = ['abc', 23, true];
let [, , c, d] = arr;
console.log(c, d); //按照下标寻找

参数解构

1
2
3
4
function person1({name, age}) {
console.log(name, age);
}
person1(obj);

4.模板字符串

简化字符串的拼接,模板字符串必须用 `` 包含, 变化的部分使用${xxx}定义

1
console.log(`我叫:${obj.name}, 我的年龄是:${obj.age}`);

5.对象的简写方法

1.同名属性省略名字

2.省略function关键字

1
2
3
4
5
6
7
let x = 1;
let y = 2;
let point = {
x,
y,
setX (x) {this.x = x}
};

6.箭头函数

1.作用: 定义匿名函数

2.基本语法:

  • 没有参数: () => console.log(‘xxxx’)
  • 一个参数: i => i+2
  • 大于一个参数: (i,j) => i+j
  • 函数体不用大括号: 默认返回结果
  • 函数体如果有多个语句, 需要用{}包围,若有需要返回的内容,需要手动返回

3.使用场景: 多用来定义回调函数

4.箭头函数的特点:

  1. 简洁
  2. 箭头函数没有自己的this,箭头函数的this不是调用的时候决定的,而是在定义的时候处在的对象就是它的this
  3. 扩展理解: 箭头函数的this看外层的是否有函数,如果有,外层函数的this就是内部箭头函数的this,如果没有,则this是window。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 这个this是window因为内层的箭头函数往外找找到有函数,则就看他的this,而他往外找找不到说明就是window
let obj = {
name : 'kobe',
age : 39,
getName : () => {
btn2.onclick = () => {
console.log(this);//obj
};
}
};
// 这个this是window因为内层的箭头函数往外找找到有函数,则就看他的this,而他是一个普通函数就看是谁调用他了。如果是obj调用的话 this 就是 obj
let obj = {
name : 'kobe',
age : 39,
getName : () => {
btn2.onclick = () => {
console.log(this);//obj
};
}
};

7. 打包解包运算符

1.rest(可变)参数

用来取代arguments 但比arguments灵活,他只能作为最后部分形参参数,也就是打包最后一部分形参

1
2
3
4
5
6
7
function add(...values) {
let sum = 0;
for(value of values) {
sum += value;
}
return sum;
}

2.扩展运算符

1
2
3
let arr1 = [1,3,5];
let arr2 = [2,...arr1,6];
arr2.push(...arr1);

8. 形参默认值

1
2
3
function(a=1){

}

9. promise

1.理解:

​ Promise对象: 代表了未来某个将要发生的事件(通常是一个异步操作),有了promise对象, 可以将异步操作以同步的流程表达出来, 避免了层层嵌套的回调函数(俗称’回调地狱’),ES6的Promise是一个构造函数, 用来生成promise实例。

2.使用promise基本步骤(2步):

  • 创建promise对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    let promise = new Promise((resolve, reject) => {
    //初始化promise状态为 pending
    //执行异步操作
    if(异步操作成功) {
    resolve(value);//自动修改promise的状态为 fullfilled ,设置了promise的状态以后然后调用then或者catch,根据promise的状态执行对应的then或者catch内容。或者说在then里面有两个函数,分别是成功和失败,他会地总调用。 里面的value 就就会传递给then里面的回调成功的入参。
    } else {
    reject(errMsg);//自动修改promise的状态为 rejected
    }
    })
  • 调用promise的then()

    1
    2
    3
    4
    promise.then(function(
    result => console.log(result), //这个result就是resolve里面的参数
    errorMsg => alert(errorMsg)
    ))

3.promise对象的3个状态

  • pending: 初始化状态
  • fullfilled: 成功状态
  • rejected: 失败状态

4.应用:

  • 使用promise实现超时处理

  • 使用promise封装处理ajax请求
    let request = new XMLHttpRequest();
    request.onreadystatechange = function () {
    }
    request.responseType = ‘json’;
    request.open(“GET”, url);
    request.send();

5. 流程

10. symbol

ES5中对象的属性名都是字符串,容易造成重名,污染环境

1.概念:

ES6中的添加了一种原始数据类型symbol(已有的原始数据类型:String, Number, boolean, null, undefined, 对象)

2.特点:

​ 1、Symbol属性对应的值是唯一的,解决命名冲突问题
​ 2、Symbol值不能与其他数据进行计算,包括同字符串拼串
​ 3、for in, for of遍历时不会遍历symbol属性

3.使用:

​ 1、调用Symbol函数得到symbol值

1
2
3
let symbol = Symbol();
let obj = {};
obj[symbol] = 'hello';

​ 2、传参标识

​ 2、传参标识

1
2
3
4
let symbol = Symbol('one');
let symbol2 = Symbol('two');
console.log(symbol);// Symbol('one')
console.log(symbol2);// Symbol('two')

​ 3、内置Symbol值

​ 3、内置Symbol值

除了定义自己使用的Symbol值以外,ES6还提供了11个内置的Symbol值,指向语言内部使用的方法。对象的Symbol.iterator属性,指向该对象的默认遍历器方法

11. iterator

1.概念:

iterator是一种接口机制,为各种不同的数据结构提供统一的访问机制

2.作用:

为各种数据结构,提供一个统一的、简便的访问接口;
使得数据结构的成员能够按某种次序排列
ES6创造了一种新的遍历命令for…of循环,Iterator接口主要供for…of消费。

3.工作原理:

  • 创建一个指针对象,指向数据结构的起始位置。
  • 第一次调用next方法,指针自动指向数据结构的第一个成员
  • 接下来不断调用next方法,指针会一直往后移动,直到指向最后一个成员
  • 每调用next方法返回的是一个包含value和done的对象,{value: 当前成员的值,done: 布尔值}
    • value表示当前成员的值,done对应的布尔值表示当前的数据的结构是否遍历结束。
    • 当遍历结束的时候返回的value值是undefined,done值为false

原生具备iterator接口的数据(可用for of遍历)
1、Array
2、arguments
3、set容器
4、map容器
5、String

4. 模拟为对象部署 iterator 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
let iteratorableObj={
[Symbol.iteraor]:function(){
return {
let nextIndex=0;
next:function(){
return {
value:this[nextIndex++],
done:this.length==nextIndex
}
}
}
}
}

12. generator

1.概念:

  1. ES6提供的解决异步编程的方案之一
  2. Generator函数是一个状态机,内部封装了不同状态的数据,
  3. 用来生成遍历器对象
  4. 可暂停函数(惰性求值), yield可暂停,next方法可启动。每次返回的是yield后的表达式结果

2.特点:

  1. function 与函数名之间有一个星号

  2. 内部用yield表达式来定义不同的状态

    例如:
    ​ function* generatorExample(){
    ​ let result = yield ‘hello’; // 状态值为hello
    ​ yield ‘generator’; // 状态值为generator
    ​ }

  3. generator函数返回的是指针对象({next:function{….}}),而不会执行函数内部逻辑

  4. 调用next方法函数内部逻辑开始执行,遇到yield表达式停止,返回{value: yield后的表达式结果/undefined, done: false/true}

  5. 再次调用next方法会从上一次停止时的yield处开始,直到最后

  6. yield语句返回结果通常为undefined, 当调用next方法时传参内容会作为启动时yield语句的返回值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
function* generatorTest() {
console.log('函数开始执行');
let ret=yield 'hello';
console.log(ret); // 这个值就是 aaa 就是这次启动的时候next传入的值
console.log('函数暂停后再次启动');
yield 'generator';
}
// 生成遍历器对象
let Gt = generatorTest();
// 执行函数,遇到yield后即暂停
console.log(Gt); // 遍历器对象
let result = Gt.next(); // 函数执行,遇到yield暂停
console.log(result); // {value: "hello", done: false}
result = Gt.next('aaa'); // 函数再次启动
console.log(result); // {value: 'generator', done: false}
result = Gt.next();
console.log(result); // {value: undefined, done: true}表示函数内部状态已经遍历完毕

// 真正的为对象部署接口采用Symbol.iterator属性;
let myIterable = {};
myIterable[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 4;
};
for(let i of myIterable){
console.log(i);
}
let obj = [...myIterable];
console.log(obj);

console.log('-------------------------------');
// 案例练习
/*
* 需求:
* 1、发送ajax请求获取新闻内容
* 2、新闻内容获取成功后再次发送请求,获取对应的新闻评论内容
* 3、新闻内容获取失败则不需要再次发送请求。
*
* */
function* sendXml() {
// url为next传参进来的数据
let url = yield getNews('http://localhost:3000/news?newsId=2');
yield getNews(url);
}
function getNews(url) {
$.get(url, function (data) {
console.log(data);
let commentsUrl = data.commentsUrl;
let url = 'http://localhost:3000' + commentsUrl;
// 当获取新闻内容成功,发送请求获取对应的评论内容
// 调用next传参会作为上次暂停是yield的返回值
sx.next(url);
})
}

let sx = sendXml();
// 发送请求获取新闻内容
sx.next();

13. async

这个其实是generator的语法糖,这个是 es2017 的内容,但是已经被广泛的用开了。

1.概念:

真正意义上去解决异步回调的问题,同步流程表达异步操作

2.语法:

1
2
3
4
async function foo(){
await 异步操作;
await 异步操作;
}

3.特点:

  1. 不需要像Generator去调用next方法,遇到await等待,当前的异步操作成功就往下执行
  2. 返回的总是Promise对象,可以用then方法进行下一步操作
  3. async取代Generator函数的星号*,await取代Generator的yield 。await 的返回值就是 reslove 和 reject 的参数
  4. 语意上更为明确,使用简单,经临床验证,暂时没有任何副作用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
async function timeout(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
})
}

async function asyncPrint(value, ms) {
console.log('函数执行', new Date().toTimeString());
await timeout(ms);
console.log('延时时间', new Date().toTimeString());
console.log(value);
}

console.log(asyncPrint('hello async', 2000));

// await
async function awaitTest() {
let result = await Promise.resolve('执行成功'); // 这个直接用来创建并设定 Promise 的状态
console.log(result);
let result2 = await Promise.reject('执行失败'); // 这个直接用来创建并设定 Promise 的状态
console.log(result2);
let result3 = await Promise.resolve('还想执行一次');// 执行不了,失败了就不会继续执行下去
console.log(result3);
}
awaitTest();


// 案例演示
async function sendXml(url) {
return new Promise((resolve, reject) => {
$.ajax({
url,
type: 'GET',
success: data => resolve(data),
error: error => reject(error)
})
})
}

async function getNews(url) {
let result = await sendXml(url); //如果这里报错了就会执行 reject 导致控制台抛异常,但是为了用户体验我们可以在错误的时候使用resolve但是要不让下面的函数继续执行。
let result2 = await sendXml(result.commentsUrl);
console.log(result, result2);
}
getNews('http://localhost:3000/news?id=2')

14. class

  1. 通过class定义类/实现类的继承
  2. 在类中通过constructor定义构造方法,因为原型中的 constructor 就是我们定义的工厂方法,在我们使用工厂方法的时候。
  3. 通过new来创建类的实例
  4. 通过extends来实现类的继承
  5. 通过super调用父类的构造方法
  6. 重写从父类中继承的一般方法
  7. 在class中写方法只能通过省略 function 的简写方式或者箭头函数书写,否则会报错

15. 数组扩展

  1. Array.from(v) 将伪数组转成真数组
  2. Array.of(v1,v2,v3….) 将一系列的元素变成数组
  3. Array.find((value,index,arr)=>{return true/false}) 找出第一个满足条件的元素
  4. Array.findIndex((value,index,arr)=>{return true/false}) 同上只是返回的不是元素而是下标

16.Obj拓展

  1. Object.is(v1, v2)

    判断2个数据是否完全相等

  2. Object.assign(target, source1, source2..)

    将源对象的属性复制到目标对象上

  3. 直接操作 proto 属性

    1
    2
    let obj2 = {};
    obj2.__proto__ = obj1;

17. 深度克隆

1、数据类型:

  • 数据分为基本的数据类型(String, Number, boolean, Null, Undefined)和对象数据类型
  • 基本数据类型:
    特点: 存储的是该对象的实际数据
  • 对象数据类型:
    特点: 存储的是该对象在栈中引用,真实的数据存放在堆内存里

2、复制数据

  • 基本数据类型存放的就是实际的数据,可直接复制
    let number2 = 2;
    let number1 = number2;
  • 克隆数据:对象/数组
    1、区别: 浅拷贝/深度拷贝
    判断: 拷贝是否产生了新的数据还是拷贝的是数据的引用
    知识点:对象数据存放的是对象在栈内存的引用,直接复制的是对象的引用
    let obj = {username: ‘kobe’}
    let obj1 = obj; // obj1 复制了obj在栈内存的引用
    2、常用的拷贝技术
    1). arr.concat(): 数组浅拷贝
    2). arr.slice(): 数组浅拷贝
    3). JSON.parse(JSON.stringify(arr/obj)): 数组或对象深拷贝, 但不能处理函数数据
    4). 浅拷贝包含函数数据的对象/数组
    5). 深拷贝包含函数数据的对象/数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// 复制的对象的方式
// 浅度复制
let obj = {username: 'kobe', age: 39, sex: {option1: '男', option2: '女'}};
let obj1 = obj;
console.log(obj1);
obj1.sex.option1 = '不男不女'; // 修改复制的对象会影响原对象
console.log(obj1, obj);

console.log('-----------');
// Object.assign(); 浅复制
let obj2 = {};
Object.assign(obj2, obj);
console.log(obj2);
obj2.sex.option1 = '男'; // 修改复制的对象会影响原对象
console.log(obj2, obj);

// 深度克隆(复制)

function getObjClass(obj) {
let result = Object.prototype.toString.call(obj).slice(8, -1);
if(result === 'Null'){
return 'Null';
}else if(result === 'Undefined'){
return 'Undefined';
}else {
return result;
}
}
// for in 遍历数组的时候遍历的是下标
let testArr = [1,2,3,4];
for(let i in testArr){
console.log(i); // 对应的下标索引
}

// 深度克隆
function deepClone(obj) {
let result, objClass = getObjClass(obj);
if(objClass === 'Object'){
result = {};
}else if(objClass === 'Array'){
result = [];
}else {
return obj; // 如果是其他数据类型不复制,直接将数据返回
}
// 遍历目标对象
for(let key in obj){
let value = obj[key];
if(getObjClass(value) === "Object" || 'Array'){
result[key] = deepClone(value);
}else {
result[key] = obj[key];
}
}
return result;
}


let obj3 = {username: 'kobe',age: 39, sex: {option1: '男', option2: '女'}};
let obj4 = deepClone(obj3);
console.log(obj4);
obj4.sex.option1 = '不男不女'; // 修改复制后的对象不会影响原对象
console.log(obj4, obj3);

3.es7

  1. 指数运算符(幂): **
  2. Array.prototype.includes(value) : 判断数组中是否包含指定value

JavaScript 从入门到精通

1. this

​ 解析器在调用函数每次都会向函数内部传递进一个隐含的参数,这个隐含的参数就是this,this指向的是一个对象, 这个对象我们称为函数执行的 上下文对象,根据函数的调用方式的不同,this会指向不同的对象,以方法的形式调用时,this就是调用方法的那个对象。当我们直接调用一个全局的函数的时候其实就是 window 对像上面的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function fun(){
console.log(this.name);
}

//创建一个对象
var obj = {
name:"孙悟空",
sayName:fun
};

var obj2 = {
name:"沙和尚",
sayName:fun
};

var name = "全局的name属性";

//以函数形式调用,this是window
//fun();

//以方法的形式调用,this是调用方法的对象
//obj.sayName();
obj2.sayName();

2. 构造函数

创建一个构造函数,专门用来创建Person对象的, 构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写构造函数和普通函数的区别就是调用方式的不同,普通函数是直接调用,而构造函数需要使用new关键字来调用
构造函数的执行流程:

  1. 立刻创建一个新的对象
  2. 将新建的对象设置为函数中this,在构造函数中可以使用this来引用新建
  3. 逐行执行函数中的代码
  4. 将新建的对象作为返回值返回

使用同一个构造函数创建的对象,我们称为一类对象,也将一个构造函数称为一个类。我们将通过一个构造函数创建的对象,称为是该类的实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Person(name , age , gender){
this.name = name;
this.age = age;
this.gender = gender;
this.sayName = function(){
alert(this.name);
};
}

function Dog(){

}

var per = new Person("孙悟空",18,"男");
var per2 = new Person("玉兔精",16,"女");
var per3 = new Person("奔波霸",38,"男");

var dog = new Dog();

使用instanceof可以检查一个对象是否是一个类的实例语法:

对象 instanceof 构造函数

如果是,则返回true,否则返回false

3. 原型

​ 我们所创建的每一个函数,解析器都会向函数中添加一个属性prototype,这个属性对应着一个对象,这个对象就是我们所谓的原型对象,如果函数作为普通函数调用prototype没有任何作用,当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,我们可以通过__proto__来访问该属性
原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。
​ 当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。以后我们创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中,这样不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法了。

​ 使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true。可以使用对象的hasOwnProperty() 来检查对象自身中是否含有该属性,使用该方法只有当对象自身中含有属性时,才会返回true。

​ 原型对象也是对象,所以它也有原型,当我们使用一个对象的属性或方法时,会现在自身中寻找,自身中如果有,则直接使用,如果没有则去原型对象中寻找,如果原型对象中有,则使用, 如果没有则去原型的原型中寻找,直到找到Object对象的原型,Object对象的原型没有原型,如果在Object原型中依然没有找到,则返回 null。

1
console.log(mc.__proto__.__proto__.hasOwnProperty("hasOwnProperty"));

构造函数和让他的原型对象是互相引用的:

4. call 和 apply

  • 这两个方法都是函数对象的方法,需要通过函数对象来调用
  • 当对函数调用call()和apply()都会调用函数执行
  • 在调用call()和apply()可以将一个对象指定为第一个参数
    此时这个对象将会成为函数执行时的this
  • call()方法可以将实参在对象之后依次传递
  • apply()方法需要将实参封装到一个数组中统一传递
  • this的情况:
    1.以函数形式调用时,this永远都是window
    2.以方法的形式调用时,this是调用方法的对象
    3.以构造函数的形式调用时,this是新创建的那个对象
    4.使用call和apply调用时,this是指定的那个对象

5. 数组的方法

1. slice()

  • 可以用来从数组提取指定元素

  • 该方法不会改变元素数组,而是将截取到的元素封装到一个新数组中返回

  • 参数:
    1.截取开始的位置的索引,包含开始索引
    2.截取结束的位置的索引,不包含结束索引

    第二个参数可以省略不写,此时会截取从开始索引往后的所有索引可以传递一个负值,如果传递一个负值,则从后往前计算 -1 倒数第一个 -2 倒数第二个

2.splice()

  • 可以用于删除数组中的指定元素
  • 使用splice()会影响到原数组,会将指定元素从原数组中删除,并将被删除的元素作为返回值返回
  • 参数:
    第一个,表示开始位置的索引
    第二个,表示删除的数量
    第三个及以后,可以传递一些新的元素,这些元素将会自动插入到开始位置索引前边

3.concat()

可以连接两个或多个数组,并将新的数组

  • 该方法不会对原数组产生影响

4. join()

​ 该方法可以将数组转换为一个字符串,该方法不会对原数组产生影响,而是将转换后的字符串作为结果返回,在join()中可以指定一个字符串作为参数,这个字符串将会成为数组中元素的连接符如果不指定连接符,则默认使用,作为连接符。

5. reverse()

​ 该方法用来反转数组(前边的去后边,后边的去前边)该方法会直接修改原数组

6.sort()

​ 可以用来对数组中的元素进行排序也会影响原数组,默认会按照Unicode编码进行排序.

​ 即使对于纯数字的数组,使用sort()排序时,也会按照Unicode编码来排序,所以对数字进排序时,可能会得到错误的结果。我们可以自己来指定排序的规则,我们可以在sort()添加一个回调函数,来指定排序规则。

6. Date

创建一个Date对象如果直接使用构造函数创建一个Date对象,则会封装为当前代码执行的时间。创建一个指定的时间对象需要在构造函数中传递一个表示时间的字符串作为参数日期的格式 月份/日/年 时:分:秒

1. getDate()

​ 获取当前日期对象是几日。

2. getDay()

​ 获取当前日期对象时周几会返回一个0-6的值 0 表示周日1表示周一

3. getMonth()

​ 获取当前时间对象的月份会返回一个0-11的值0 表示1月1 表示2月11 表示12月

4.getFullYear()

​ 获取当前日期对象的年份

5. getTime()

​ 获取当前日期对象的时间戳

7. Math

1.Math.ceil()

  • 可以对一个数进行向上取整,小数位只有有值就自动进1

2.Math.floor()

可以对一个数进行向下取整,小数部分会被舍掉

3.Math.round()

可以对一个数进行四舍五入取整

4.Math.random()

可以用来生成一个0-1之间的随机数

8. 事件冒泡

​ 所谓的冒泡指的就是事件的向上传导,当后代元素上的事件被触发时,其祖先元素的相同事件也会被触发。在开发中大部分情况冒泡都是有用的,如果不希望发生事件冒泡可以通过事件对象来取消冒泡。

1
2
3
4
5
6
7
s1.onclick = function(event){
event = event || window.event;
alert("我是span的单击响应函数");
//取消冒泡
//可以将事件对象的cancelBubble设置为true,即可取消冒泡
event.cancelBubble = true;
};

1. 事件的委派

​ 指将事件统一绑定给元素的共同的祖先元素,这样当后代元素上的事件触发时,会一直冒泡到祖先元素
从而通过祖先元素的响应函数来处理事件。事件委派是利用了冒泡,通过委派可以减少事件绑定的次数,提高程序的性能。

2. 事件绑定

addEventListener() 通过这个方法也可以为元素绑定响应函数,参数:

  1. 事件的字符串,不要on
  2. 回调函数,当事件触发时该函数会被调用
  3. 是否在捕获阶段触发事件,需要一个布尔值,一般都传false

使用addEventListener()可以同时为一个元素的相同事件同时绑定多个这样的事件。当事件被触发时,响应函数将会按照函数的绑定顺序执行。

3. 事件传播

将事件传播分成了三个阶段

  1. 捕获阶段:在捕获阶段时从最外层的祖先元素,向目标元素进行事件的捕获,但是默认此时不会触发事件
  2. 目标阶段:事件捕获到目标元素,捕获结束开始在目标元素上触发事件
  3. 冒泡阶段:事件从目标元素向他的祖先元素传递,依次触发祖先元素上的事件

如果希望在捕获阶段就触发事件,可以将addEventListener()的第三个参数设置为true,一般情况下我们不会希望在捕获阶段触发事件,所以这个参数一般都是false。

8.数据类型

undefined 指的是变量定义了但是没有赋值过,而 null 则是指他被赋值了,并且赋值为 null

9. 闭包

​ 闭包在一个函数内部声明了一个内部函数,并且这个内部函数引用了外部函数的变量。

闭包:闭包是包含被引用变量的对象,或者简单的理解为嵌套的内部函数

​ 闭包在执行外部函数的时候就产生了,不用等到内部不函数执行。

1.闭包的作用:

  1. 使用函数内部的变量在函数执行完后, 仍然存活在内存中(延长了局部变量的生命周期

  2. 让函数外部可以操作(读写)到函数内部的数据(变量/函数)

2. 生命周期

  1. 产生: 在嵌套内部函数定义执行完时就产生了(不是在调用)
  2. 死亡: 在嵌套的内部函数成为垃圾对象时

3. 应用

​ 具有特定功能的js文件,将所有的数据和功能都封装在一个函数内部(私有的),只向外暴露一个包信n个方法的对象或函数,模块的使用者, 只需要通过模块暴露的对象调用方法来实现对应的功能。

方案一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function myModule() {
//私有数据
var msg = 'Hello'
//操作数据的函数
function doSomething() {
console.log('doSomething() '+msg.toUpperCase())
}
function doOtherthing () {
console.log('doOtherthing() '+msg.toLowerCase())
}
//向外暴露对象(给外部使用的方法)
return {
doSomething: doSomething,
doOtherthing: doOtherthing
}
}

方案二:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(function () {
//私有数据
var msg = 'My atguigu'
//操作数据的函数
function doSomething() {
console.log('doSomething() '+msg.toUpperCase())
}
function doOtherthing () {
console.log('doOtherthing() '+msg.toLowerCase())
}
//向外暴露对象(给外部使用的方法)
window.myModule2 = {
doSomething: doSomething,
doOtherthing: doOtherthing
}
})()

4. 缺点及解决方案

1.缺点

  • 函数执行完后, 函数内的局部变量没有释放, 占用内存时间会变长

  • 容易造成内存泄露

    2.解决

  • 能不用闭包就不用

  • 及时释放,将闭包设置为null

CSS3 从入门到精通

1. 选择器

1. 一些注意的点

1. love & hate (lvht)

​ 这是什么意思呢?其实是我们在写 a 表的伪类的时候会有一些优先级的问题,如果说我们没有按照这个优先级写css 的话很可能会导致我们样式不生效或者说被覆盖的问题。具体来说就是 link , visitedhovertarget

2. nth-child(n)

​ 注意这个的 n 是从 1 开始的而不是 0 开始的,另外如果我们写了 #wrap p:nth-child(1) 的含义是指 id 为 wrap 下面的第一个子元素,并且这个字元素必须是 p 才会应用样式。

​ 同样的还有一个类似的 就是 nth-of-type(n) 这个其实和上面的很像只是他的要求稍微低一些。#wrap p:nth-of-type(1) 比如这个就是表示 wrap 下面的第一个 p 标签。

​ 上面两个的重要的区别(坑!)。就是 nth-of-type 是以元素标签为中心的,下面看个例子来理解这句话。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
#wrap .inner:nth-of-type(1){
border: 1px solid;
}
/*#wrap div:nth-of-type(1){
border: 1px solid;
}
#wrap p:nth-of-type(1){
border: 1px solid;
}
#wrap span:nth-of-type(1){
border: 1px solid;
}
#wrap h1:nth-of-type(1){
border: 1px solid;
}
#wrap h2:nth-of-type(1){
border: 1px solid;
}*/

/*nth-of-type以元素为中心*/
</style>
</head>
<body>
<div id="wrap">
<div class="inner">div</div>
<p class="inner">p</p>
<span class="inner">span</span>
<h1 class="inner">h1</h1>
<h2 class="inner">h2</h2>
</div>
</body>
</html>

​ 注意上面的代码看起来就是第一个被选中,但是由于它是以元素为中心的那么,他会被展开成下面的语法结构。所以说所有的都会被选中。而 nth-child 则是正常的选中第一个。

2. 字体图标

​ 字体图标减少了页面的请求,而且不失真非常可靠,推荐使用。如果我们需要 使用一些特殊的紫图需要自定义字体因为为了兼容不同的用户,但是这样会增加网络负担。

1
2
3
4
@font-face{
font-family:"test";
src:url('');/*字体文件的位置*/
}

​ 使用字体图标,主要就是ttf,wof还有一个样式表,直接引入样式表就可以使用了。

3. 新的UI方案

1. 文本新增样式

1. opactiy

​ 他不是继承属性。但是子元素也会被透明掉。

2. rgba

​ 这个前三个就是 rgb 最后一个是透明度。正式有了这个现在的文字透明背景不透明或者背景透明文字不透明就好做了,直接给某一方设置rgba而另外一方设置正常的颜色就好。而不用opacity导致前后后透明。

1
2
3
a{
color:rgba(1,1,1,.5);
}

​ 创建一个模糊背景的图片。首先需要有一个图片背景(让图片做背景一般都是图片有多大背景就能看到多少图片,为了让图片完全的铺满背景需要把 background-size:100% 100%),然后需要对他做一个模糊使用了一个函数 filter:blur(10px) 就是把它模糊掉。然后需要一个外面的黑色遮罩,让他出现半透明的状态就可以看到后面的内容了。

​ **小技巧:如果希望我们内层的元素铺满外层的内容区可以使用绝对定位然后让他的四个方向都为 0 **

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no" />
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
#wrap{
height: 100px;
background: rgba(0,0,0,.5);
position: relative;
}
#wrap #bg{
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: url(img/avatar.jpg) no-repeat;
background-size:100% 100% ;
z-index: -1;
filter: blur(10px);
}
img{
margin: 24px 0 0 24px;
}
</style>
</head>
<body>
<div id="wrap">
<img src="img/avatar.jpg" width="64px" height="64px"/>
<div id="bg"></div>
</div>
</body>
</html>

3. 文字阴影

text-shadow用来为文字添加阴影,而且可以添加多层,阴影值之间用逗号隔开。(多个阴影时,第一个阴影在最上边)

默认值:none 不可继承



​ 可选。可以在偏移量之前或之后指定。如果没有指定颜色,则使用UA(用户代理)选择的颜色。

​ 必选。这些长度值指定阴影相对文字的偏移量。
指定水平偏移量,若是负值则阴影位于文字左边。
指定垂直偏移量,若是负值则阴影位于文字上面。
​ 如果两者均为0,则阴影位于文字正后方(如果设置了 则会产生模糊效果)。

​ 可选。这是 值。如果没有指定,则默认为0。
​ 值越大,模糊半径越大,阴影也就越大越淡

1
2
3
h1{
text-shadow:gray 10px 10px 10px,pink 10px 10px 10px;
}

4. 文字排版

​ 溢出显示省略号,最常用的!但是使用这个东西盒子需要是一个 block

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>无标题文档</title>
<style>
div{
width: 200px;
height: 200px;
border: 1px solid;
margin: 0 auto;
/*这三样式是一直在一起的不能去掉,并且盒子不能靠内容撑开*/
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>
</head>
<body>
<div>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</div>
</body>
</html>

2. 盒模型阴影

​ 首先是需要进行一个盒子的居中:

1
2
3
4
5
6
7
8
9
10
11
12
13
div{
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 0;
margin: auto;
width: 100px;
height: 100px;
background: pink;
text-align: center;
line-height: 100px;
}

​ 注意需要margin为auto而不能为0,盒子唏嘘也要有长宽。

box-shadow
​ 以逗号分割列表来描述一个或多个阴影效果,可以用到几乎任何元素上。 如果元素同时设置了 border-radius ,阴影也会有圆角效果。多个阴影时和多个 text shadows 规则相同(第一个阴影在最上面)。

默认值: none 不可继承

值:
​ inset
​ 默认阴影在边框外。
​ 使用inset后,阴影在边框内。

​ 这是头两个 值,用来设置阴影偏移量。
设置水平偏移量,如果是负值则阴影位于元素左边。
设置垂直偏移量,如果是负值则阴影位于元素上面。
​ 如果两者都是0,那么阴影位于元素后面。
​ 这时如果设置了 则有模糊效果。

​ 这是第三个 值。值越大,模糊面积越大,阴影就越大越淡。
​ 不能为负值。默认为0,此时阴影边缘锐利。

​ 这是第四个 值。取正值时,阴影扩大;取负值时,阴影.收缩。默认为0,此时阴影与元素同样大。

​ 阴影颜色,如果没有指定,则由浏览器决定

1
2
3
div{
box-shadow: -10px -10px 10px 0px black , 20px 20px 10px -10px deeppink;
}

3. box-sizing

​ 由于我们一般的额 height 和 width 的值指的是内容区的大小,所以如果我们用了 padding的话会导致整个盒子的变化,那么为了不让这种计算出现,可以使用 box-sizing:border-box 就可以了。此时采用padding的话就是从内容区扣掉的。

4. border-radius

​ 圆角

5. 背景

  • background-color 默认为transparent 也就是透明的

  • background-image 指定图片做背景,有的图片在z轴上面绘制,先写得图片会在最后绘制也就是层叠图片的时候是按照写得顺序从上往下绘制的。

  • no-repeat

  • background-position 如果图片的大小大于框的大小,这个属性就可以决定图片是从哪开始显示的。

  • background-origin 表示图片是从哪开始渲染的,它有三个值:border-box ,padding-box ,content-box 分别就是从边框,内边距开始的地方,内容区开始的地方。

  • background-clip:从那个位置开始裁剪,属性值也是和origin一样的。

  • background-size:百分比: 指定背景图片相对背景区(background positioning area)的百分比。背景区由background-origin设置,默认为盒模型的内容区与内边距 auto: 以背景图片的比例缩放背景图片。

    注意:
    单值时,这个值指定图片的宽度,图片的高度隐式的为auto
    两个值: 第一个值指定图片的宽度,第二个值指定图片的高度

6. 线性渐变

注意:渐变是针对于图片的而不是颜色。
为了创建一个线性渐变,你需要设置一个起始点和一个方向(指定为一个角度)。你还要定义终止色。终止色就是你想让浏览器去平滑的过渡过去,并且你必须指定至少两种,当然也会可以指定更多的颜色去创建更复杂的渐变效果。

​ 如果在里面设置了透明度他也会跟着渐变的,就是 rgba

-默认从上到下发生渐变
​ linear-gradient(red,blue);

-改变渐变方向:(top bottom left right)
​ linear-gradient(to 结束的方向,red,blue);

-使用角度
​ linear-gradient(角度,red,blue);

-颜色节点的分布(第一个不写为0%,最后一个不写为100%)
​ linear-gradient(red 长度或者百分比,blue 长度或者百分比);
-重复渐变
​ repeating-linear-gradient(60deg,red 0,blue 30%);

1
2
3
p{
background:repeating-linear-gradient(135deg,black 0px,black 10px,white 10px,white 20px);
}

注意这个写法他的意思就是1-10是黑到黑的渐变就是一个完整的黑色,而10-20 是白到白的渐变。为什么要这么做,主要就是我们使用了 repeating-linear-gradient 这个 repeat 就是重复的渐变,如果我们不是设置的渐变而是直接的纯色的话他就不会重复了。所以这种方式巧妙的让这些黑白重复了。发廊灯的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}

html,body{
height: 100%;
overflow: hidden;
}

#wrap{
width: 40px;
height: 300px;
border: 1px solid;
margin: 100px auto;
overflow: hidden;
}
#wrap > .inner{
height: 600px;
background:repeating-linear-gradient(135deg,black 0px,black 10px,white 10px,white 20px);
}

</style>
</head>
<body>
<div id="wrap">
<div class="inner"></div>
</div>
</body>
<script type="text/javascript">
var inner = document.querySelector("#wrap > .inner");
var flag =0;

setInterval(function(){
flag++;
if(flag==300){
flag=0;
}
inner.style.marginTop = -flag+"px";
},1000/60)

</script>
</html>

光板动画,iPhone的开机动画:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}

html,body{
height: 100%;
overflow: hidden;
background: black;
text-align: center;
}

h1{
/*transition: 3s;*/
margin-top: 50px;
display: inline-block;
color: rgba(255, 255, 255,.3);
font:bold 80px "微软雅黑";
background: linear-gradient(120deg,rgba(255,255,255,0) 100px ,rgba(255,255,255,1) 180px ,rgba(255,255,255,0) 260px);
background-repeat:no-repeat ;
-webkit-background-clip: text ;
}

/*h1:hover{
background-position: 500px 0;
}*/
</style>
</head>
<body>
<h1>atguigu尚硅谷</h1>
</body>
<script type="text/javascript">
var h1 = document.querySelector("h1");
var flag =-160;

setInterval(function(){
flag+=10;
if(flag==600){
flag=-160;
}
h1.style.backgroundPosition = flag+"px";
},30)

</script>
</html>

7. 径向渐变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
#test{
width: 400px;
height: 300px;
border: 1px solid;
margin: 0 auto;
background-image:radial-gradient( closest-corner circle at 20px 20px,yellow, green 50%,pink) ;
}
</style>
</head>
<body>
<div id="test">

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

4.过渡

1. transition-property

​ 这个属性是用来指定哪些属性需要做渐变的,而且并不是所有的属性都是可以做渐变的,可以在mdn上查询到。多个属性可以采用逗号分隔。默认值就是 all

2. transition-duration

​ 这个用来规定动画间隔的时间,注意如果这个列表不满足上面的property的个数,他默认采用复制来匹配,比如上面有三个属性,而duration只有两个时间 3s,5s 那么最后的结果应该是 3s.5s.3s,5s 这样上面三个属性是 3s,5s ,3s 注意点就是他们需要带上单位。

3. transition-timing-function

​ 这个就是用来指定整个动画的运行的过程或者速率:

属性值:
​ 1、ease:(加速然后减速)默认值,ease函数等同于贝塞尔曲线(0.25, 0.1, 0.25, 1.0).
​ 2、linear:(匀速),linear 函数等同于贝塞尔曲线(0.0, 0.0, 1.0, 1.0).
​ 3、ease-in:(加速),ease-in 函数等同于贝塞尔曲线(0.42, 0, 1.0, 1.0).
​ 4、ease-out:(减速),ease-out 函数等同于贝塞尔曲线(0, 0, 0.58, 1.0).
​ 5、ease-in-out:(加速然后减速),ease-in-out 函数等同于贝塞尔曲线(0.42, 0, 0.58, 1.0)
​ 6、cubic-bezier: 贝塞尔曲线,这是一个函数里面传入对应的参数就可以呈现出不同的曲线

http://cubic-bezier.com 在这个地址里面就能找到对应的被萨尔曲线对应的函数值和参数了。

比如说下面的这个曲线的意思就是横轴代表的是时间而纵轴代表的就是距离或者位置了,或者说速度。在这给对方代表的就是速度。

​ 7、step-start:等同于steps(1,start)
​ step-end:等同于steps(1,end)

​ start表示整个时间的开始,而end表示整个时间的结束。

​ steps(,[,[start|end]]?)
​ 第一个参数:必须为正整数,指定函数的步数
​ 第二个参数:指定每一步的值发生变化的时间点(默认值end)在每步的时间开始还是结束的时候执行。

​ 多个列表会使用默认值,如果不够的话。

4. transition-delay

​ 表示在多久之后才开始动画。这个如果缺省了参数不够列表的长度的话也是会重复列表的。

5. transitionend

​ 表示动画执行结束,这是一个js事件可以使用js事件来监听,他是基于属性的一个事件,也就是等一个属性变化完成以后他就会调用一次而不是针对于元素的。

1
2
node.addEventListener("transitionend",function(){}); //这种添加事件的方式是 dom2 事件
node.ontransitionend(function(){}) //这种是 dom0 事件

6. 过渡的一些坑

1. 浏览器解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}

html{
height: 100%;
}
body{
width: 60%;
height: 60%;
border: 1px solid;
margin: 100px auto 0;
}

#test{
width: 100px;
height: 100px;
background: pink;
text-align: center;
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 0;
margin: auto;

transition-property: width;
transition-duration: 2s;
transition-timing-function: linear;
}

body:hover #test{
transition-property: height;
width: 200px;
height: 200px;
}

</style>
</head>
<body>
<div id="test">
</div>
</body>
</html>

可以看到上面是鼠标画上去的时候回选中 test 这个元素,然后将它动画设置为 height 变化,由于浏览器解析 css 太快了,在划上去的瞬间内存被修改了也就是变换的元素被修改了所以一开始变化的应该是height。而移除的时候同理变化的是 width

2. transition在元素首次渲染还没有结束的情况下是不会触发的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}

html{
height: 100%;
}
body{
width: 60%;
height: 60%;
border: 1px solid;
margin: 100px auto 0;
}

#test{
width: 100px;
height: 100px;
background: pink;
text-align: center;
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 0;
margin: auto;

transition-property: width;
transition-duration: 2s;
transition-timing-function: linear;
}
</style>
</head>
<body>
<div id="test">
</div>
</body>
<script type="text/javascript">
//transition在元素首次渲染还没有结束的情况下是不会被触发的
// window.onload=function(){
var test = document.querySelector("#test");
test.style.width="300px";
// }
</script>
</html>

​ 如果没有打开注释部分的内容我们的动画是不会执行的,而是直接跳到300的样子,如果我们打开了注释意外这我们所有的元素已经渲染完了,这个时候再去做上面的额动画是会被执行的。

​ 那么这么想的话我们如果加上一个 settimeout 也是没问题的。

3.坑

1. 在绝大部分变换样式切换时,如果变换函数的位置 个数不相同也不会触发过渡

2. transition必须在transform之后声明才有效!!!!

5. 2D变幻

注意所有的变幻都是和transition配合使用的。

1. transform

1.rotate(angle) 旋转

正值:顺时针旋转 rotate(360deg)
负值:逆时针旋转 rotate(-360deg)
只能设单值。正数表示顺时针旋转,负数表示逆时针旋转

2. translate 移动

X方向平移:transform: translateX(tx)
Y方向平移:transform: translateY(ty)
二维平移:transform: translate(tx[, ty]); 如果ty没有指定,它的值默认为0。

可设单值,也可设双值。
​ 正数表示XY轴正向位移,负数为反向位移。设单值表示只X轴位移,Y轴坐标不变,
​ 例如transform: translate(100px);等价于transform: translate(100px,0);

3. skew 倾斜

transform:skewX(45deg);
​ X方向倾斜:transform: skewX(angle)
​ skewX(45deg):参数值以deg为单位 代表与y轴之间的角度
​ Y方向倾斜:transform: skewY(angle)
​ skewY(45deg):参数值以deg为单位 代表与x轴之间的角度
​ 二维倾斜:transform: skew(ax[, ay]); 如果ay未提供,在Y轴上没有倾斜
​ skew(45deg,15deg):参数值以deg为单位 第一个参数代表与y轴之间的角度
​ 第二个参数代表与x轴之间的角度
单值时表示只X轴扭曲,Y轴不变,如transform: skew(30deg);等价于 transform: skew(30deg, 0);
考虑到可读性,不推荐用单值,应该用transform: skewX(30deg);。skewY表示只Y轴扭曲,X轴不变
正值:拉正斜杠方向的两个角
负值:拉反斜杠方向的两个角

4. scale 缩放

transform:scale(2);
X方向缩放:transform: scaleX(sx);
Y方向缩放:transform: scaleY(sy);
二维缩放 :transform: scale(sx[, sy]); (如果sy 未指定,默认认为和sx的值相同)

要缩小请设0.01~0.99之间的值,要放大请设超过1的值。
例如缩小一倍可以transform: scale(.5);放大一倍可以transform: scale(2);

如果只想X轴缩放,可以用scaleX(.5)相当于scale(.5, 1)。
同理只想Y轴缩放,可以用scaleY(.5)相当于scale(1, .5)

正值:缩放的程度
负值:不推荐使用(有旋转效果)
单值时表示只X轴,Y轴上缩放粒度一样,如transform: scale(2);等价于transform: scale(2,2);

2. transform-origin

参考点 transform-origin 这个值可以是:

top left 左上

10px 10px 参照左上角的向右向下10px

10% 10% 同px参照的是自身的宽高。

6. 3D 旋转

1. transform

里面的一些函数和2D的一模一样只是多了 X,Y,Z的后缀例如 rotateY(360deg)

2. perspective

​ 景深,让3D物体看起来有近大远小的感觉。但是这个属性只能用在舞台上,也就是如果要过三D动画,必须有两层外面一层叫做舞台,里面才是真实的物体。

3. transform-style

建立有层级的 3d 舞台,他作用于舞台。

7.动画

1. 常用属性

1. animation-name

​ 指定所使用的关键帧的名字,比如 animation-name: move; 关键帧的样子:

1
2
3
4
5
6
7
8
@keyframes move{
from{
transform: translateY(-100px);
}
to{
transform: translateY(100px);
}
}

2. animation-duration

​ 设置动画持续的时间。需要带上单位 animation-duration:3s ;

3.animation-direction

​ 动画翻转,他变化的是关键帧的 from 到 to 的两个顺序,并且变化了animation-timing-function 动画的速度变化曲线。

​ 他有四个值:

  1. normal 就是正常的每一次执行动画都是从 from 到 to 这样的执行。
  2. reverse 这样关键帧执行的顺序从 to 到 from ,并且会让 animation-timing-function` 动画的速度变化曲线翻转。
  3. alternate 如果有多次启动关键帧,表示从 from到to再从to到from 这样一直往返。
  4. alternate-reverse 同上,只是方向是反的。

4.animation-delay

​ 设置动画还有多久才开始,需要设置单位 animation-delay:1s;

5. animation-iteration-count

​ 设置关键帧执行的次数,也就是动画执行的次数。 animation-iteration-count: 3;

6. animation-fill-mode

​ 设置动画之外的状态,就是规定from和to在动画开始之前的位置和状态。

  1. backwards:from之前的状态与form的状态保持一致
  2. forwards:to之后的状态与to的状态保持一致
  3. both:backwards+forwards

7.animation-play-state

​ 表示当前的动画的状态,可以通过他来暂停动画和开始动画

  1. running 启动动画
  2. paused 暂停动画

8. animation-timing-function

​ 规定了动画的速率,具体和 transition 是一样的。

2. 关键帧

语法:

1
2
3
4
5
@keyframes animiationName{
keyframes-selector{
css-style;
}
}
  1. animiationName:必写项,定义动画的名称
  2. keyframes-selector:必写项,动画所在的时间点 from等价于 0% 而 to等价于100%,我么也可以定义其他的一些百分比。
  3. css-style:css声明

8. flex

​ flex分为两个版本,分别为老版本的和新版本的,新版本的功能更强大但是他不支持很多的老的移动端。但是我们可以使用css后置处理器来处理这种兼容性问题。

1. 基本概念

容器:就是flex布局的容器,也是一个包裹层然后里面样式设置为 flex

项目:item 就是在容器中排列的内容就是项目,项目永远在主轴的正方向排列

主轴:默认是x轴,正方向右

侧轴:默认y轴,正方向向下

2. 容器管理

1. 设置容器

老版本:display:-webkit-box

新版本:display:flex

2. 设置主轴

老版本:-webkit-box-orient:vertical/horizontal

新版本:flex-direction:row/column

3. 设置排列方向,控制主轴方向

老版本:-webkit-box-direction:normal/reverse

新版本:flex-direction:row/column/row-reverse/column-reverse 用一个属性设置主轴以及主轴的方向

4. 富裕空间管理

主侧轴的空白的空间管理。他管理的是富裕空间的位置并不影响项目的空间大小

1.主轴

老版本:-webkit-box-pack:start(右,下)/end(左,上)/center(两边)/jusity(项目之间) 与主轴无关,只和x,y轴有关,前面指的是x轴的情况,后面是y轴的情况

新版本:justify-content:flex-start(主轴正方向)/flex-end(主轴负方向)/center(主轴两边)/space-between(项目之间)/space-around(项目两边)

2. 侧轴

老版本:-webkit-box-align:start(右,下)/end(左,上)/center(两边)/jusity(项目之间) 与主轴无关,只和x,y轴有关,前面指的是x轴的情况,后面是y轴的情况

新版本:align-items:flex-start(侧轴正方向)/flex-end(侧轴负方向)/content(侧轴两边)/base-line(按照基线)/strech(没有的,当没有高度的时候就是等高布局)

3. 项目管理

弹性空间管理就是把富裕空间匀到每一个项目之上。

老版本:-webkit-box-flex:n

新版本:flex-grow:n

相当于每一个项目的弹性因子就是 n 那么我们的富裕空间的分配就是 (nx/n1+n2+n3+…+nx) * 富裕空间大小 简单来说这个弹性因子就表示将来会分到的富裕空间的比例。

4. 新版flex特性

1.容器新增特性

在新版的flex中使用 flex-wrap 来控制侧轴的方向 规定了侧轴的方向那么在元素在主轴的方向排列不下的时候不会对元素进行压缩而是采用向侧轴排列的方式进行,这样就会产生多行多列,此时我们侧轴的富裕空间 align-items 就失效了,必须采用 align-content 来解决。

1. flex-wrap

flex-wrap 属性控制了容器为单行/列还是多行/列。并且定义了侧轴的方向,新行/列将沿侧轴方向堆砌。

默认值:nowrap 不可继承

值:nowrap | wrap | wrap-reverse

2.align-content

align-content 属性定义弹性容器的侧轴方向上有额外空间时,如何排布每一行/列。当弹性容器只有一行/列时无作用
默认值:stretch 不可继承

值:
flex-start
​ 所有行/列从侧轴起点开始填充。第一行/列的侧轴起点边和容器的侧轴起点边对齐。
​ 接下来的每一行/列紧跟前一行/列。
flex-end
​ 所有弹性元素从侧轴末尾开始填充。最后一个弹性元素的侧轴终点和容器的侧轴终点对齐。
​ 同时所有后续元素与前一个对齐。
center
​ 所有行/列朝向容器的中心填充。每行/列互相紧挨,相对于容器居中对齐。
​ 容器的侧轴起点边和第一行/列的距离相等于容器的侧轴终点边和最后一行/列的距离。
space-between
​ 所有行/列在容器中平均分布。相邻两行/列间距相等。
​ 容器的侧轴起点边和终点边分别与第一行/列和最后一行/列的边对齐。
space-around
​ 所有行/列在容器中平均分布,相邻两行/列间距相等。
​ 容器的侧轴起点边和终点边分别与第一行/列和最后一行/列的距离是相邻两行/列间距的一半。
stretch
​ 拉伸所有行/列来填满剩余空间。剩余空间平均的分配给每一行/列

3. flex-flow

flex-flow 属性是设置“flex-direction”和“flex-wrap”的简写

默认值:row nowrap 不可继承

控制主轴和侧轴的位置以及方向

2. 项目新增特性

1.order

设置项目在主轴的排列优先级,数字越大优先级越高。

2.align-self

项目自己管理自己的侧轴的富裕空间,而不归 align-items 管了。

3. flex-basis

flex-basis 在弹性空间管理的时候非常有用的,我们在上面的弹性空间管理的时候也就是 flex-grows 我们直接是把整个容器大小减去所有项目的大小然后除以增长因子的大小来计算的,实际上我们应该减去的不是容器的总大小而是flex-basis 的值,默认值就是容器的大小。所以他们的计算公式应该是:

可用空间 = (容器大小 - 所有相邻项目flex-basis的总和)
可扩展空间 = (可用空间/所有相邻项目flex-grow的总和)
每项获得的伸缩大小 = (伸缩基准值 + (可扩展空间 x flex-grow值))

4.flex-shrink

收缩因子,实际上就是我们所有的item的总大小大于容器的大小他就会进行收缩但是收缩也会有一个收缩因子,但是他的收缩因子的默认值不是 0 而是 1,计算公式:

1.计算收缩因子与基准值乘的总和
2.计算收缩因数
​ 收缩因数=(项目的收缩因子*项目基准值)/第一步计算总和
3.移除空间的计算
​ 移除空间= 项目收缩因数 x 负溢出的空间

5. flex应用

1.等分布局

根据规则我们就需要将赋予空间全部分配给每一个项目,而我们需要的全部的富裕空间就是容器的所有的宽度。

1
2
3
4
5
.item{
flex-grows:1;
flex-shrink:1;
flex-basis:1;
}

上面的这一堆代码为了等分布局,所以我们有一个简写的方式就是

1
2
3
.item{
flex:1;
}

CSS2 从入门到精通

1. 常用的选择器

1. 元素选择器

  • 作用:通过元素选择器可以选择指定的元素
  • 语法:tag{}
1
2
3
4
5
6
p{
color: red;
}
h1{
color: red;
}

2. id 选择器

  • 作用:通过元素的id属性值选中唯一的一个元素
  • 用法: #id{}
1
2
3
#p1{
font-size: 20px;
}

3.类选择器

  • 通过元素的class属性值选中一组元素
  • 语法:.class{}
1
2
3
4
5
6
7
.p2{
color: red;
}

.hello{
font-size: 50px;
}

4. 选择器分组

  • 通过选择器分组可以同时选中多个选择器对应的元素,简单来说就是同时为这些选择的元素应用相同的样式。他也称作为 **并集选择器 **就是上面的每一个逗号分开的相当于一个集合,那么相当于把他们合并起来到一个集合中只要元素命中其中一条规则就会应用样式。
  • 语法:选择器1,选择器2,选择器N{}
1
2
3
#p1 , .p2 , h1{
background-color: yellow;
}

5. 通配选择器

  • 他可以用来选中页面中的所有的元素
  • 语法:*{}
1
2
3
*{
color: red;
}

6. 交集选择器

  • 作用:选择同时满足所有选择器规则的元素,所以也称作为交集选择器,元素必须命中所有的选择条件才能应用样式。下面的例子就相当于必须满足元素名为span并且拥有p3这个类
  • 语法:选择器1选择器2选择器N{}
1
2
3
span.p3{
background-color: yellow;
}

7. 后代选择器

  • 作用:选中指定元素的指定后代元素,下面的例子就是选择id为d1的span后代,注意后代是不用和祖先元素相邻。
  • 语法:祖先元素 后代元素{}
1
2
3
#d1 span{
color: greenyellow;
}

8. 子选择器

  • 作用:选中指定父元素的指定子元素,两者必须是相邻的
  • 语法:父元素 > 子元素
1
2
3
div > span{
background-color: yellow;
}

Tips:
父元素:直接包含子元素的元素
子元素:直接被父元素包含的元素
祖先元素:直接或间接包含后代元素的元素,父元素也是祖先元素
后代元素:直接或间接被祖先元素包含的元素,子元素也是后代元素
兄弟元素:拥有相同父元素的元素叫做兄弟元素

9. 伪类选择器

伪类专门用来表示元素的一种的特殊的状态,比如:访问过的超链接,比如普通的超链接,比如获取焦点的文本框
当我们需要为处在这些特殊状态的元素设置样式时,就可以使用伪类

1
a:link{color: yellowgreen;}
2. :visited 表示访问过的链接

浏览器是通过历史记录来判断一个链接是否访问过,由于涉及到用户的隐私问题,所以使用visited伪类只能设置字体的颜色

3. :hover伪类表示鼠标移入
4. :active表示的是超链接被点击的状态
5.:focus 光标为获得焦点
6.::selection选中的内容

这个伪类在火狐中需要采用另一种方式编写::-moz-selection兼容火狐的

1
p::-moz-selection{background-color: orange;}

兼容大部分浏览器的

1
p::selection{background-color: orange;}

10. 伪元素选择器

使用伪元素来表示元素中的一些特殊的位置.

1. :first-letter 内容中的第一个字母
2. :first-line内容的第一行
3.:before表示元素最前边的部分
4. :after表示元素的最后边的部分

一般before和after都需要结合content这个样式一起使用, 通过content可以向before或after的位置添加一些内容
:after

11. 属性选择器

  • 作用:可以根据元素中的属性或属性值来选取指定元素
  • 语法:
    • [属性名] 选取含有指定属性的元素
    • [属性名=”属性值”] 选取含有指定属性值的元素
    • [属性名^=”属性值”] 选取属性值以指定内容开头的元素
    • [属性名$=”属性值”] 选取属性值以指定内容结尾的元素

      12. 子元素选择器

      1. :first-child 可以选中第一个子元素:
      2.last-child 可以选中最后一个子元素
1
body > p:first-child{background-color: yellow;}p:last-child{background-color: yellow;}
3. :nth-child 可以选中任意位置的子元素

该选择器后边可以指定一个参数,指定要选中第几个子元素
even 表示偶数位置的子元素
odd 表示奇数位置的子元素

1
p:nth-child(odd){background-color: yellow;}

:first-of-type,:last-of-type:nth-of-type,:first-child这些非常的类似,只不过child,是在所有的子元素中排列而type,是在当前类型的子元素中排列

1
p:first-of-type{background-color: yellow;}

13.兄弟选择器

为挨着的兄弟元素添加样式,其中 + 表示后一个,而 ~ 则表示前一个

1
2
3
4
5
6
span ~ p{
background-color: yellow;
}
span + p{
background-color: yellow;
}

2. 样式继承

像儿子可以继承父亲的遗产一样,在CSS中,祖先元素上的样式,也会被他的后代元素所继承,利用继承,可以将一些基本的样式设置给祖先元素,这样所有的后代元素将会自动继承这些样式。但是并不是所有的样式都会被子元素所继承,比如:背景相关的样式都不会被继承 边框相关的样式 定位相关

3. 样式优先级

优先级的规则:

  1. 内联样式 , 优先级 1000
  2. id选择器,优先级 100
  3. 类和伪类, 优先级 10
  4. 元素选择器,优先级 1
  5. 通配* , 优先级 0
  6. 继承的样式,没有优先级
  7. 并集选择器的优先级是单独计算
  8. 可以在样式的最后,添加一个!important,则此时该样式将会获得一个最高的优先级,将会优先于所有的样式显示甚至超过内联样式,但是在开发中尽量避免使用!important

    4. 长度单位

    1.像素 px

    像素是我们在网页中使用的最多的一个单位,一个像素就相当于我们屏幕中的一个小点,我们的屏幕实际上就是由这些像素点构成的但是这些像素点,是不能直接看见。不同显示器一个像素的大小也不相同,显示效果越好越清晰,像素就越小,反之像素越大。

    2. 百分比 %

    也可以将单位设置为一个百分比的形式,这样浏览器将会根据其父元素的样式来计算该值使用百分比的好处是,当父元素的属性值发生变化时,子元素也会按照比例发生改变在我们创建一个自适应的页面时,经常使用百分比作为单位

    3. em

    em和百分比类似,它是相对于当前元素的字体大小来计算的 1em = 1font-size 使用em时,当字体大小发生改变时,em也会随之改变当设置字体相关的样式时,经常会使用em

    4. 行间距

    在CSS并没有为我们提供一个直接设置行间距的方式, 我们只能通过设置行高来间接的设置行间距,行高越大行间距越大 使用line-height来设置行高 行高类似于我们上学单线本,单线本是一行一行,线与线之间的距离就是行高, 网页中的文字实际上也是写在一个看不见的线中的,而文字会默认在行高中垂直居中显示。
    行间距 = 行高 - 字体大小

    通过设置line-height可以间接的设置行高,
    可以接收的值:
    1.直接就收一个大小
    2.可以指定一个百分数,则会相对于字体去计算行高
    3.可以直接传一个数值,则行高会设置字体大小相应的倍数
1
2
3
4
5
p{
line-height: 200%;
line-height: 20px;
line-height: 2;
}

对于单行文本来说,可以将行高设置为和父元素的高度一致, 这样可以是单行文本在父元素中垂直居中

5. 文本样式

1. text-transform可以用来设置文本的大小写

可选值:

  • none 默认值,该怎么显示就怎么显示,不做任何处理
  • capitalize 单词的首字母大写,通过空格来识别单词
  • uppercase 所有的字母都大写
  • lowercase 所有的字母都小写

    2.text-decoration可以用来设置文本的修饰

    可选值:
  • none:默认值,不添加任何修饰,正常显示
  • underline 为文本添加下划线
  • overline 为文本添加上划线
  • line-through 为文本添加删除线

超链接会默认添加下划线,也就是超链接的text-decoration的默认值是underline如果需要去除超链接的下划线则需要将该样式设置为none

3.letter-spacing可以指定字符间距

4. word-spacing可以设置单词之间的距离

实际上就是设置词与词之间空格的大小

5.text-align用于设置文本的对齐方式

可选值:

  • left 默认值,文本靠左对齐
  • right , 文本靠右对齐
  • center , 文本居中对齐
  • justify , 两端对齐通过调整文本之间的空格的大小,来达到一个两端对齐的目的

    6.text-indent用来设置首行缩进

    当给它指定一个正值时,会自动向右侧缩进指定的像素 如果为它指定一个负值,则会向左移动指定的像素, 通过这种方式可以将一些不想显示的文字隐藏起来 这个值一般都会使用em作为单位

6. 盒模型

使用width来设置盒子内容区的宽度,使用height来设置盒子内容区的高度。width和height只是设置的盒子内容区的大小,而不是盒子的整个大小,盒子可见框的大小由内容区,内边距和边框共同决定。 **内联元素不能设置 height 和 width ,如果非要设置必须修改 display 。

1. 为元素设置边框

要为一个元素设置边框必须指定三个样式

1. border-width:边框的宽度

使用border-width可以分别指定四个边框的宽度如果在border-width指定了四个值,则四个值会分别设置给 上 右 下 左,按照顺时针的方向设置的
如果指定三个值,则三个值会分别设置给 上 左右 下
如果指定两个值,则两个值会分别设置给 上下 左右
如果指定一个值,则四边全都是该值

2.border-color:边框颜色

和宽度一样,color也提供四个方向的样式,可以分别指定颜色

3. border-style:边框的样式

  • none,默认值,没有边框
  • solid 实线
  • dotted 点状边框
  • dashed 虚线
  • double 双线

2. 内边距(padding)

指的是盒子的内容区与盒子边框之间的距离* 一共有四个方向的内边距,内边距会影响盒子的可见框的大小,元素的背景会延伸到内边距.

3. 外边距

外边距指的是当前盒子与其他盒子之间的距离,他不会影响可见框的大小,而是会影响到盒子的位置。由于页面中的元素都是靠左靠上摆放的,所以注意当我们设置上和左外边距时,会导致盒子自身的位置发生改变。_
margin还可以设置为auto,auto一般只设置给水平方向的margin如果只指定,左外边距或右外边距的margin为auto则会将外边距设置为最大值垂直方向外边距如果设置为auto,则外边距默认就是0如果将left和right同时设置为auto,则会将两侧的外边距设置为相同的值,就可以使元素自动在父元素中居中,所以我们经常将左右外边距设置为auto。

1. 垂直外边距重叠

垂直外边距的重叠在网页中相邻垂直方向的外边距会发生外边距的重叠所谓的外边距重叠指兄弟元素之间的相邻外边距会取最大值而不是取和.
如果父子元素的
垂直外边 距相邻 **了,则子元素的外边距会设置给父元素.

2. 如何解决外边距重叠

  1. 在两个元素之家加其他元素
  2. 添加 border
  3. 添加 padding

注意:内联元素是不支持垂直方向的外边距的,其他的都和块级元素相同。

7. 元素展示方式

1. display

将一个内联元素变成块元素, 通过display样式可以修改元素的类型
可选值:

  • inline:可以将一个元素作为内联元素显示
  • block: 可以将一个元素设置块元素显示
  • inline-block:将一个元素转换为行内块元素可以使一个元素既有行内元素的特点又有块元素的特点既可以设置宽高,又不会独占一行
  • none: 不显示元素,并且元素不会在页面中继续占有位置使用该方式隐藏的元素,不会在页面中显示,并且不再占据页面的位置

    2. visibility

    可以用来设置元素的隐藏和显示的状态
    可选值:
  • visible 默认值,元素默认会在页面显示
  • hidden 元素会隐藏不显示,使用 visibility:hidden;隐藏的元素虽然不会在页面中显示,但是它的位置会依然保持

3. overflow

子元素默认是存在于父元素的内容区中,
理论上讲子元素的最大可以等于父元素内容区大小
如果子元素的大小超过了父元素的内容区,则超过的大小会在父元素以外的位置显
超出父元素的内容,我们称为溢出的内容
父元素默认是将溢出内容,在父元素外边显示,
通过overflow可以设置父元素如何处理溢出内容:
可选值:
- visible,默认值,不会对溢出内容做处理,元素会显示
- hidden, 溢出的内容,会被修剪,不会显示
- scroll, 会为父元素添加滚动条,通过拖动滚动条该属性不论内容是否溢出,都会添加水平
- auto,会根据需求自动添加滚动条,需要水平就添加水平需要垂直就添加垂直都不需要就都不加

8. 文档流

文档流文档流处在网页的最底层,它表示的是一个页面中的位置,我们所创建的元素默认都处在文档流中
元素在文档流中的特点

1.块元素

1.块元素在文档流中会独占一行,块元素会自上向下排列。
2.块元素在文档流中默认宽度是父元素的100%
3.块元素在文档流中的高度默认被内容撑开

2.内联元素

1.内联元素在文档流中只占自身的大小,会默认从左向右排列,如果一行中不足以容纳所有的内联元素,则换到下一行,继续自左向右。
2.在文档流中,内联元素的宽度和高度默认都被内容撑开

9. 浮动

块元素在文档流中默认垂直排列, 如果希望块元素在页面中水平排列,可以使块元素脱离文档流使用float来使元素浮动,从而脱离文档流
可选值:

  • none,默认值,元素默认在文档流中排列
  • left,元素会立即脱离文档流,向页面的左侧浮动
  • right,元素会立即脱离文档流,向页面的右侧浮动

1. 浮动的规则

当为一个元素设置浮动以后(float属性是一个非none的值), 元素会立即脱离文档流,元素脱离文档流以后,它下边的元素会立即向上移动 元素浮动以后,会尽量向页面的左上或右上漂浮, 直到遇到父元素的边框或者其他的浮动元素
如果浮动元素上边是一个没有浮动的块元素,则浮动元素不会超过块元素。
浮动的元素不会超过他上边的兄弟元素,最多最多一边齐。
浮动的元素不会盖住文字,文字会自动环绕在浮动元素的周围
在文档流中,子元素的宽度默认占父元素的全部当元素设置浮动以后,会完全脱离文档流.块元素脱离文档流以后,高度和宽度都被内容撑开
开启span的浮动内联元素脱离文档流以后会变成块元素

2. 浮动后高度塌陷

在文档流中,父元素的高度默认是被子元素撑开的,也就是子元素多高,父元素就多高。但是当为子元素设置浮动以后,子元素会完全脱离文档流,此时将会导致子元素无法撑起父元素的高度,导致父元素的高度塌陷。由于父元素的高度塌陷了,则父元素下的所有元素都会向上移动,这样将会导致页面布局混乱。
所以在开发中一定要避免出现高度塌陷的问题,我们可以将父元素的高度写死,以避免塌陷的问题出现,但是一旦高度写死,父元素的高度将不能自动适应子元素的高度,所以这种方案是不推荐使用的。

3. 解决高度塌陷问题

1. 开启BFC

根据W3C的标准,在页面中元素都一个隐含的属性叫做Block Formatting Context简称BFC,该属性可以设置打开或者关闭,默认是关闭的。当开启元素的BFC以后,元素将会具有如下的特性:
1.父元素的垂直外边距不会和子元素重叠
2.开启BFC的元素不会被浮动元素所覆盖
3.开启BFC的元素可以包含浮动的子元素
如何开启元素的BFC
1.设置元素浮动使用这种方式开启,虽然可以撑开父元素,但是会导致父元素的宽度丢失而且使用这种方式也会导致下边的元素上移,不能解决问题。这是因为开启浮动的会计元素宽高都是默认被撑起来的,除非自动设置宽高。
2.设置元素绝对定位
3.设置元素为inline-block可以解决问题,但是会导致宽度丢失,不推荐使用这种方式
4.将元素的overflow设置为一个非visible的值
**推荐方式:将overflow设置为hidden是副作用最小的开启BFC的方式。 **
是在IE6及以下的浏览器中并不支持BFC,所以使用这种方式不能兼容IE6。在IE6中虽然没有BFC,但是具有另一个隐含的属性叫做hasLayout,该属性的作用和BFC类似,所在IE6浏览器可以通过开hasLayout来解决该问题开启方式很多,我们直接使用一种副作用最小的:直接将元素的zoom设置为1即可

  • zoom表示放大的意思,后边跟着一个数值,写几就将元素放大几倍
  • zoom:1表示不放大元素,但是通过该样式可以开启hasLayout
  • zoom这个样式,只在IE中支持,其他浏览器都不支持
1
2
3
4
.parent{
zoom:1;
overflow: hidden;
}

2. 清除浮动

可以直接在高度塌陷的父元素的最后,添加一个空白的div, 然后在对其进行清除浮动, 由于这个div并没有受到浮动元素的影响,所以他应该处在原来的元素在的时候的位置这样就相当于原来的元素还在,所以是可以撑开父元素的高度的,这样可以通过这个空白的div来撑开父元素的 基本没有副作用使用这种方式虽然可以解决问题,但是会在页面中添加多余的结构。

1
2
3
.clear{
clear: both;
}

3. 伪类

通过after伪类,选中父元素的后边可以通过after伪类向元素的最后添加一个空白的块元素,然后对其清除浮动,这样做和添加一个div的原理一样,可以达到一个相同的效果,而且不会在页面中添加多余的div,这是我们最推荐使用的方式,几乎没有副作用。

1
2
3
4
5
6
7
8
.clearfix:after{
/*添加一个内容*/
content: "";
/*转换为一个块元素*/
display: block;
/*清除两侧的浮动*/
clear: both;
}

4. 最佳实践

1
2
3
4
5
6
7
8
9
10
.clearfix:before,
.clearfix:after{
content: "";
display: table;
clear: both;
}

.clearfix{
zoom: 1;
}

经过修改后的clearfix是一个多功能的,既可以解决高度塌陷,又可以确保父元素和子元素的垂直外边距不会重叠。子元素和父元素相邻的垂直外边距会发生重叠,子元素的外边距会传递给父元素 使用空的table标签可以隔离父子元素的外边距,阻止外边距的重叠

4. 清除浮动

由于受到box1浮动的影响,box2整体向上移动了100px
我们有时希望清除掉其他元素浮动对当前元素产生的影响,这时可以使用
clear可以用来清除其他浮动元素对当前元素的影响
可选值:
none,默认值,不清除浮动
left,清除左侧浮动元素对当前元素的影响
right,清除右侧浮动元素对当前元素的影响
both,清除两侧浮动元素对当前元素的影响清除对他影响最大的那个元素的浮动

5. 总结

​ 在元素浮动的时候我们将元素看成两层。上面一层是文字相关的而下面一层则是和盒模型相关的。而一个元素开启浮动那么这个元素就被提升了半层。也就是两个排列的 box1 box2 ,在box1 浮动以后由于他之浮动半层,所以box2的盒模型部分会插入到box1下面,也就是box1会有三层,而box2的文字那一层是被卡在外面了,不会进去。

10. 定位

定位指的就是将指定的元素摆放到页面的任意位置 通过定位可以任意的摆放元素 通过position属性来设置元素的定位
可选值:

  • static:默认值,元素没有开启定位
  • relative:开启元素的相对定位
  • absolute:开启元素的绝对定位
  • fixed:开启元素的固定定位(也是绝对定位的一种)

当开启了元素的定位(position属性值是一个非static的值)时, 可以通过left right top bottom四个属性来设置元素的偏移量

  • left:元素相对于其定位位置的左侧偏移量
  • right:元素相对于其定位位置的右侧偏移量
  • top:元素相对于其定位位置的上边的偏移量
  • bottom:元素相对于其定位位置下边的偏移量

通常偏移量只需要使用两个就可以对一个元素进行定位, 一般选择水平方向的一个偏移量和垂直方向的偏移量来为一个元素进行定位

1. 相对定位

当元素的position属性设置为relative时,则开启了元素的相对定位

  1. 当开启了元素的相对定位以后,而不设置偏移量时,元素不会发生任何变化
  2. 相对定位是相对于元素在文档流中原来的位置进行定位
  3. 相对定位的元素不会脱离文档流
  4. 相对定位会使元素提升一个层级
  5. 相对定位不会改变元素的性质,块还是块,内联还是内联

2. 绝对定位

当position属性值设置为absolute时,则开启了元素的绝对定位
绝对定位:

  1. 开启绝对定位,会使元素脱离文档流
  2. 开启绝对定位以后,如果不设置偏移量,则元素的位置不会发生变化
  3. 绝对定位是相对于离他最近的开启了定位的祖先元素进行定位的(一般情况,开启了子元素的绝对定位都会同时开启父元素的相对定位)如果所有的祖先元素都没有开启定位,则会相对于浏览器窗口进行定位
  4. 绝对定位会使元素提升一个层级
  5. 绝对定位会改变元素的性质,内联元素变成块元素,块元素的宽度和高度默认都被内容撑开

    3. 固定定位

    当元素的position属性设置fixed时,则开启了元素的固定定位 固定定位也是一种绝对定位,它的大部分特点都和绝对定位一样 不同的是: 固定定位永远都会相对于浏览器窗口进行定位 固定定位会固定在浏览器窗口某个位置,不会随滚动条滚动IE6不支持固定定位.

4. 总结

浮动元素的包含块为最近的块级祖先元素。而定位则比较复杂:

  • 初始包含块由用户浏览器创建,在html中就是html标签。但是有些浏览器也会采用body作为根元素,大多数浏览器中初始包含块都是一个视窗大小的矩形。但是视口不等于初始包含块。
  • 对于非根元素如果是relative或者static则包含块是最近的块级框。(或者说是他本来应该处于的位置。)
  • 对于非根元素如果是absolute则包含块是最近的开启定位的元素(非static)的内边距的边界,也就是说是由边框的边界确定的。

由于我们的行业规范是 div+css 所以我们不考虑内联元素的情况,因为div是块级元素。

定位是提升一层,和浮动不一样。

注意:

对于一些常用的一些样式我们需要知道他们的默认值是什么比如:

left top right bottom width height 的默认值都是auto不是 0 也不是100%

margin 和 padding 默认则是 0 如果我们采用百分比百分比参照的是父级元素的 width 的而不是当前元素的大小。

11. 常用布局

1. 三列布局

1.定位实现方式

​ 三列的话我们可以让其中的两列分别使用绝对定位,然后由于绝对定位默认是依照浏览器的窗口(或者将body设为realtive 然后相对于 body),所以可以让两边的直接在两边,而中间的使用块元素直接让宽度充满整个空闲部分。

​ 然后由于随着我们浏览器变小我们需要设置一个body的最小宽否则有可能让我们的中间部分压没了。

​ 其实这个是有问题的,就是当我们压缩矿口的时候会出现滚动条如果我们设置了 min-width ,这样我们的右边以浏览器窗口对其就会有问题。

所以不推荐使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style type="text/css">

*{
margin: 0;
padding: 0;
}
body{
/*2*left+right*/
min-width: 600px;
}
div{
height: 100px;
}
#left,#right{
width: 200px;
background: pink;
}
#middle{
background: deeppink;
padding: 0 200px;
}
#left{
position: absolute;
left: 0;
top: 0;
}
#right{
position: absolute;
right: 0;
top: 0;
}
</style>
</head>
<body style="position: relative;">
<div id="left">left</div>
<div id="middle">middle</div>
<div id="right">right</div>
</body>
</html>

2. 使用浮动

将左右两列用浮动分别让他们在两边展示,而中间的为块级元素这样他的盒子布局的那一层就会浮上去,而文字刚好就被卡住。但是这个东西的前提就是内容区必须放在最后内容区的第二层才会自动的浮动上去。这样一来就有一个问题就是内容区是在文档流中最后被加载的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<!--
1.两边固定 当中自适应
2.当中列要完整显示
3.当中列要优先加载
-->
<style type="text/css">

*{
margin: 0;
padding: 0;
}
body{
/*2*left+right*/
min-width: 600px;
}
div{
height: 100px;
}
#left,#right{
width: 200px;
background: pink;
}
#left{
float: left;
}
#right{
float: right;
}
#middle{
background: deeppink;
}
</style>
</head>
<body>
<div id="left">left</div>
<div id="right">right</div>
<div id="middle">middle</div>
</body>
</html>

3. 圣杯布局

​ 首先我们还是使用 float:left 让左右两部的元素浮动起来,但是由于我们的content是块级元素没办法让下面的元素上来,那么我们只能把它也设置为浮动,这样一来他的宽度就是被内容撑开了,我们需要设置他的宽度为body的100%,现在是content占据100%但是它允许下面浮动的元素上去。如果我们使用浮动让他们上去肯定就和上面的方法一样了,我们需要的是中间的元素有限加载也就是他应该出现在文档流的最前面。

​ 可以采用负的margin,可以想象,我们的left的外边界和content是重叠的,那么我们如果移动他的外边界到content的起始位置那么他是不是就上去了呢?确实如此但是注意一个问题就是我们改变了外边距元素的位置实际上是不会动的,也就是在文档流中他还是在下面那一行,right的左边还是left这个块。所以我们right需要上去的时候必须margin为 -200

​ 这样就好了,但是content的内容被挡住了。我们可以设把body变小,然后通过定位完成整个布局。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<!--
1.两边固定 当中自适应
2.当中列要完整显示
3.当中列要优先加载
-->

<!--
浮动: 搭建完整的布局框架
margin 为赋值:调整旁边两列的位置(使三列布局到一行上)
使用相对定位:调整旁边两列的位置(使两列位置调整到两头)
-->

<style type="text/css">
*{
margin: 0;
padding: 0;
}
body{
min-width: 600px;
}
#content{
padding: 0 200px;
}
#header,#footer{
height: 20px;
text-align: center;
border: 1px solid deeppink;
background: gray;
}
#content .middle{
float: left;
width: 100%;
background: pink;
/*padding: 0 200px;*/
}
#content .left{
position: relative;
left: -200px;
margin-left: -100%;
float: left;
width: 200px;
background: yellow;
}
#content .right{
position: relative;
right: -200px;
margin-left: -200px;
float: left;
width: 200px;
background: yellow;
}



.clearfix{
*zoom: 1;
}
.clearfix:after{
content: "";
display: block;
clear: both;
}
</style>
</head>
<body>
<div id="header">header</div>
<div id="content" class="clearfix">
<div class="middle">
<h4>middle</h4>
</div>
<div class="left">left</div>
<div class="right">right</div>
</div>
<div id="footer">footer</div>
</body>
</html>

4. 双飞翼布局

​ 在上面的圣杯布局的基础之上做的改进,其实做到两边都浮动然后只是content的内容不能居中,那么他们之间的区别就是如何让内容居中上。

​ 圣杯是把padding应用在外面的body上,然后使用定位让他们分到两边。我们会考虑为什么不能用padding在conten上呢,实际上我们在content浮动以后使用padding就会把整个元素变大变宽,那么我们以前设置的left和right的margin的值需要重新设置不然就是错乱的。所以他在内部加了一层结构然后使用padding,这样的话由于left和right的margin只和外面的content的外边界有关系,所以里面的padding并不能影响整体的布局。

2. 伪等高布局

​ 如果我们两列高度不一样,需要让两列一一样高,我们采用的套路就是让他们的padding超级大,然后把整个的元素高度很高,然后让margin为负值把边界收回来,那么也就是他们的外部的盒子的外边距收回来了,此时再用 overflow:hidden;就好。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
#wrap{
width: 750px;
border: 1px solid;
margin: 0 auto;
overflow: hidden;
}
#wrap .left{
float: left;
width: 200px;
background: pink;
padding-bottom: 1000px;
margin-bottom: -1000px;
}
#wrap .right{
float: left;
width: 500px;
background: deeppink;
padding-bottom: 1000px;
margin-bottom: -1000px;
}



.clearfix{
*zoom: 1;
}
.clearfix:after{
content: "";
display: block;
clear: both;
}
</style>
</head>
<body>
<div id="wrap" class="clearfix">
<div class="left">
left <br />
left <br />
left <br />
left <br />
left <br />
left <br />
left <br />
left <br />
left <br />
left <br />
left <br />
left <br />
left <br />
left <br />
left <br />
left <br />
left <br />
left <br />
left <br />
left <br />
left <br />
left <br />
left <br />
left <br />
</div>
<div class="right">
right<br />
right<br />
right<br />
right<br />
right<br />
right<br />
right<br />
right<br />
right<br />
right<br />
right<br />
right<br />
right<br />
right<br />
</div>
</div>
</body>
</html>

12. 滚动条

​ 一般的在我们内容大于浏览器视窗的时候会出现滚动条,这时滚动条是应用在文档上的不是在html也不是在body上。如果我们希望修改这个设置我们有一条规则就是:

​ 当我们只设置 body 和 html 中的overflow的时候他们这个属性是应用给文档的,但是如果两者同时使用这个属性那么html的设置给文档而body的设置给body。

​ 实际上我们的固定定位和绝对定位的区别就在于滚动条上面,如果我们自己设置了滚动条那么就可以采用绝对定位来模拟固定定位。在移动端一般我们都是这么做的,因为我们需要禁用掉系统默认的滚动条。

注意:如果我们希望获得视窗的大小的话我们需要从html到body一层层的100%的继承下来不然height只会被内容撑开

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
html{
overflow: hidden;
height: 100%;
}

body{
overflow: auto;
height: 100%;
}

#test{
position: absolute;
left: 50px;
top: 50px;
width: 100px;
height: 100px;
background: pink;
}
</style>
</head>
<body>
<div id="test">

</div>
<div style="height: 1000px;">
csjkahcjk <br />
csjkahcjk <br />
csjkahcjk <br />
csjkahcjk <br />
csjkahcjk <br />
csjkahcjk <br />
csjkahcjk <br />
csjkahcjk <br />
</div>
</body>
</html>

13. 黏贴布局

​ 黏贴布局就是当内容没有达到底部的时候我们的内容在底部不动,而内容太多的时候可以把我们的底部挤到下面去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no"/>
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
html,body{
height: 100%;
}
#wrap{
min-height: 100%;
background: pink;
text-align: center;
overflow: hidden;
}
#wrap .main{
padding-bottom:50px ;
}
#footer{
height: 50px;
line-height: 50px;
background: deeppink;
text-align: center;
margin-top: -50px;
}
</style>
</head>
<body>
<div id="wrap" >
<div class="main">
main <br />
main <br />
main <br />
</div>
</div>
<div id="footer">
footer
</div>
</body>
</html>

记一次微信支付踩过的坑

​ 最近在做一个微信小程序,然后里面涉及到了支付的内容,本来以为微信支付挺简单的,当我开始写支付后台去看微信提供的支付文档的时候我就崩溃了,文档写了跟没写一样。

​ 本来是准备原生的php写后来觉得还是不太安全,而且真的文档不全很麻烦,就去找了一个类库。一会也会介绍一下这个类库的坑。

1 . 微信支付流程

​ 好了,这大概就是微信支付的流程了。下面来具体的说一下:

  1. 首先是小程序调用 wx.login 获取用户当前登陆的 code 这个code是会变化的,并不能作为用户的标识

    1
    2
    3
    4
    5
    wx.login({
    success(res) {
    console.log(res.code); //获取的code
    }
    }
  2. 然后把 code 当做参数请求微信接口获取 opencode 这个就是用户的标识。这一步操作我把它放在了服务端,也就是我们的服务器会去请求微信的接口获取 opencode 返回给小程序。

    后端做的就是请求微信接口:

    1
    $ret = curl_get_https("https://api.weixin.qq.com/sns/jscode2session?appid={$config['appId']}&secret={$config['secret']}&js_code=$code&grant_type=authorization_code");

    可以看到这里后台就是请求了这个 url 里面带了三个动态参数,有appid,secret都是在小程序的管理界面能看到的。code就是我们上一步获取到的,最后一个参数固定的。

  3. 获取到用户的opencode之后就可以让后台请求微信支付接口进行订单生成了。

    后台请求支付接口,生成订单:

    请求地址为:https://api.mch.weixin.qq.com/pay/unifiedorder 一些常见的必传参数是:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    'appid' 微信支付分配的公众账号ID(企业号corpid即为此appId)
    'mch_id' 微信支付分配的商户号,这个需要登录商户平台,申请通过以后直接就能看到的
    'prepayid' $prepay_id
    'noncestr' 随机字符串,长度要求在32位以内,这个随便生成。
    'sign' 通过签名算法计算得出的签名值,这个就是把请求的所有参数按照key的字典序拼串key1=val1&key2=val2,最后拼上密钥key 的key和value
    'package' prepay_id=$prepay_id
    'body' 商品简单描述
    'total_fee' 订单总金额,单位为分
    'notify_url' 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。
    'out_trade_no' 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母
    'spbill_create_ip' 支持IPV4和IPV6两种格式的IP地址。调用微信支付API的机器IP
    'trade_type ' JSAPI -JSAPI支付NATIVE -Native支付APP -APP支付
  4. 上述的操作完成以后,需要写一个通知回调的地址,这个地址里面的逻辑代码就是处理我们数据库订单的内容,比如把订单状态设置为已支付,或者创建订单将货物设置为已发货。这个回调在不出任何问题的情况下我们需要返回一段特定的字符串,否则微信认为逻辑没有执行成功,他会继续回调这个回调地址。如果你发现你的逻辑是错的他一直回调这个地址貌似没有办法让他停止回调,他可能有自己的策略在一定时间后停止回调。成功的字符串是:

    1
    echo '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';

    注意不要再有其他的任何输出,并且字符串中不要有空格。

  5. 最后会返回订单的信息,此时只需要调用微信小程序的 wx.requestPayment 并且传递以下必要参数:

    1
    2
    3
    4
    5
    'timeStamp': data['timestamp'],
    'nonceStr': data['noncestr'],
    'package': data['package'],
    'signType': 'MD5',
    'paySign': data['sign'],

    这时小程序会自动拉起微信支付进行支付。

2. 微信支付类库解析

Composer 使用指南

1. 安装依赖

1
composer install 

这个语句会检查当前目录下的 composer.json 然后检查是否有 composer.lock 如果有这个文件那么就按照 lock 文件中的版本号进行安装依赖,如果没有lock文件就按照 composer.json 中定义的版本进行安装。

这里就需要注意,composer.json 定义的版本号可能是一个区间也就是他可以是一个范围,随着类库的更新而更新,但是 lock文件保证了此项目的版本号是确定的,也就是项目组成员得到的版本号是一致的,就不会有很多麻烦问题了。

composer.json 中的版本的定义方式有:

名称 实例 描述
确切的版本号 1.0.2 你可以指定包的确切版本。
范围 >=1.0 `>=1.0,<2.0``>=1.0,<1.1 >=1.2`
通配符 1.0.* 你可以使用通配符*来指定一种模式。1.0.*>=1.0,<1.1是等效的。
赋值运算符 ~1.2 一个常见的用法是标记你所依赖的最低版本,像 ~1.2 (允许1.2以上的任何版本,但不包括2.0)。~1.2 相当于 >=1.2,<2.0,而 ~1.2.3 相当于 >=1.2.3,<1.3~1.2 只意味着 .2 部分可以改变,但是 1. 部分是固定的。

对的,这样就解决了项目成员的类库版本一致的问题,但是如果我们希望能够升级类库呢?那么就执行

1
composer update

如果只想安装或更新一个依赖,你可以白名单它们:

1
php composer.phar update monolog/monolog [...]

2. Packagist

packagist 是 Composer 的主要资源库。 一个 Composer 的库基本上是一个包的源:记录了可以得到包的地方。但是没必要去国外的网站下载,我们可以采用国内的镜像加速:

方法一: 修改 composer 的全局配置文件(推荐方式)

打开命令行窗口(windows用户)或控制台(Linux、Mac 用户)并执行如下命令:

1
composer config -g repo.packagist composer https://packagist.phpcomposer.com

方法二: 修改当前项目的 composer.json 配置文件:

打开命令行窗口(windows用户)或控制台(Linux、Mac 用户),进入你的项目的根目录(也就是 composer.json 文件所在目录),执行如下命令:

1
composer config repo.packagist composer https://packagist.phpcomposer.com

上述命令将会在当前项目中的 composer.json 文件的末尾自动添加镜像的配置信息(你也可以自己手工添加):

1
2
3
4
5
6
"repositories": {
"packagist": {
"type": "composer",
"url": "https://packagist.phpcomposer.com"
}
}

3. 类库自动加载

除了库的下载,Composer 还准备了一个自动加载文件,它可以加载 Composer 下载的库中所有的类文件。使用它,你只需要将下面这行代码添加到你项目的引导文件中:

1
require 'vendor/autoload.php';

4. 创建项目 create-project

你可以使用 Composer 从现有的包中创建一个新的项目。这相当于执行了一个 git clonesvn checkout 命令后将这个包的依赖安装到它自己的 vendor 目录。

此命令有几个常见的用途:

  1. 你可以快速的部署你的应用。
  2. 你可以检出任何资源包,并开发它的补丁。
  3. 多人开发项目,可以用它来加快应用的初始化。

要创建基于 Composer 的新项目,你可以使用 “create-project” 命令。传递一个包名,它会为你创建项目的目录。你也可以在第三个参数中指定版本号,否则将获取最新的版本。

如果该目录目前不存在,则会在安装过程中自动创建。

1
php composer.phar create-project doctrine/orm path 2.2.*

此外,你也可以无需使用这个命令,而是通过现有的 composer.json 文件来启动这个项目。

默认情况下,这个命令会在 packagist.org 上查找你指定的包。

七牛云关联Windows图床

1. 注册七牛云

七牛云 地址,需要在这里进行注册

2.完成实名认证

需要上传身份证的正反面以及支付宝做一下认证即可。

首先进入个人中心

然后进行实名认证

由于我已经认证过了,所以显示认证完成,未认证的用户需要按照提示认证,一般来说 5分钟就能完成认证。

3. 创建对象存储

只需要填一下名字,然后因为是图床所以肯定是公开的访问权限。

4. 绑定域名

配置完空间以后就是需要关联域名,配置 CNAME

绑定域名,这个域名需要是一个已经备案过的域名。

只需要填写域名这一项即可,最后就是需要到你的域名管理后台配置一个域名的解析,首先打开这个地址 https://portal.qiniu.com/cdn/domain/ 然后找到你的域名选择复制CNAME

然后需要做一个域名解析,比如到阿里云上

等一会会 https://portal.qiniu.com/cdn/domain/ 打开这个链接如果看到解析成功则说明配置完成了。

5. 关联 PicGo

这款软件是一个跨平台的图床软件个人觉得很好用。直接百度就可以下载到。

做完配置,然后确定,设置为默认图床就可以在上传区上传文件了!