Python全栈与JavaScript的对比及交叉应用
python全栈-js
文章目录
js中一切皆对象,都是object的子类。
JavaScript 是一种轻量级的脚本语言。所谓“脚本语言”,指的是它不具备开发操作系统的能力,而是只用来编写控制其他大型应用程序的“脚本”。
JavaScript 是一种嵌入式(embedded)语言。它本身提供的核心语法不算很多,只能用来做一些数学和逻辑运算。
html是设计结构,css是美化结构,js是让结构动起来。nodejs是写服务器的,语言是js
JavaScript与ECMAScript的关系
关于JavaScript版本
在学习这门语言的初期,使用的是ES5版本。新版本后期讲。
js基础
JavaScript 程序的单位是行(line),也就是一行一行地执行。一般情况下,每一行就是一个语句
写js语言,可以在html里面的body使用script标签,里面写js语言,就像css在style里面一样。
var num=10;
var是关键字,是声明语句的,num是名字/变量,=是赋值。10是值。
语句以分号结尾,一个分号就表示一个语句结束
标识符(identifier)指的是用来识别各种值的合法名称。最常见的标识符就是变量名
标识符是由:字母、美元符号($)、下划线(_)和数字组成,其中数字不能开头
中文是合法的标识符,可以用作变量名(不推荐)
以下关键字不需要强行记忆!
arguments、break、case、catch、class、const、continue、debugger、default、delete、do、else、enum、eval、export、extends、false、finally、for、function、if、implements、import、in、instanceof、interface、let、new、null、package、private、protected、public、return、static、super、switch、this、throw、true、try、typeof、var、void、while、with、yield。
js里面有大小写的区别。
变量与常量
变量,常量,变量提升
console.log(变量) 可以在浏览器的终端输出变量的值
变量提升
JavaScript 引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。这造成的结果,就是所有的变量的声明语句,都会被提升到代码的头部,这就叫做变量提升(hoisting)。
说白了,就是不管你在什么位置创建的变量,在代码执行的时候,所有的声明变量语句都会被放到代码开头。
但是只有声明放在开头var num;但是num没有赋值。赋值还是在原来的位置,所以在声明变量之前可以访问,但是只有空值。
使用const定义常量后,常量无法改变(扩展知识,ES6新特性)
JavaScript引入到HTML文件中
嵌入html中,引入本地js,引入网络js
<body>
<script>
var age = 20
</script>
</body>
script标签一定要放在body的最下面,所有容器的底部
<body>
<script src="js路径"> </script>
</body>
<body>
<script src="http://code.jquery.com/jquery1.2.1.min.js"> </script>
</body>
为什么要使用网络js呢?因为很多设计用的人多了,就有人去整理js文件。比如轮播图效果,直接在网上搜,,然后把人家提供的网络js文件导入进去。因为人家是写好的js,不会出什么大错误,我们可以直接放在head里面
引入过来的代码按照人家说的方式用就可以Swiper CDN – Swiper中文网
cdn是人家的网络js数据包
使用教程Swiper使用方法 – Swiper中文网
JavaScript注释与常见输出方式
JavaScript注释
JavaScript输出方式
数据类型
数值,字符串,布尔,null,undefined,对象,symbol
// 数值
var num= 10;
// 字符串
var str1= '你好'
var str2= "hi"
// 布尔
var flat=true;
var flat1=false;
// null
// undefined
var a1;
console.log(num); // 输出的就是undefined。null也是一样的,null是旧版本的输出
// 对象 里面都是键值对
var teacher1={
age:19,
name:'lily'
}
// symbol
上面是所有的数据类型。这些数据类型有不同的分类方式
数值,字符串,布尔
对象:因为一个对象往往是多个原始类型的值的合成,可以看作是一个存放各种值的容器
至于undefined和null,一般将它们看成两个特殊值
typeof 显示数据类型
JavaScript 有三种方法,可以确定一个值到底是什么类型。而我们现在需要接触到的就是typeof
跟python的type一样
数值返回number
字符串返回string
布尔值返回boolean
对象返回object
unll和undefined的区别
使用方式:typeof 变量
输出:console.log(typeof num);
算数运算符之加法运算符
数值相加,非数值相加,与字符串相加
任何类型和字符串相加,都是字符串类型
运算符之算术运算符
运算符之赋值运算符
有:=,+=,-=,*=,/=,%=
=是赋值,不是等于
运算符之比较运算符
有:<,>,<=,>=,==,!=,!==,===
小于,大于,小于等于,大于等于,等于,不等于,严格不等于,严格等于
严格等于是有类型的限制。不能是类型不同。两个等于只需要值相等,三个等于是有类型和值同时相等。
运算符之布尔运算符
把表达式变成布尔值,真假值。
取反!,且&&,或||
空格也是字符,空格不算假
运算符之位运算符
位运算符直接处理每一个比特位(bit),是非常底层的运算,好处是速度极快,缺点是很不直观,许多场合不能使用它们,否则会使代码难以理解和查错
左移运算符<< 就是*2的n次方(n代表位移次数)
右移运算符>> 就是/2的n次方 (n代表位移次数,不同点,出现小数时要取整)
运算符优先级
查文档就行了
如果多个运算符混写在一起,常常会导致令人困惑的代码,所以一般我们可以通过多条语句完成,记住所有运算符的优先级,是非常难的,也是没有必要的
添加小括号()
分离开来,写成多条语句
类型转换
自动转换,强制转换
控制语句
if
if (布尔值){ 语句; } if (布尔值) 不加大括号,只能跟一条语句 语句;
和c语言一样
if…else
if (m === 3) { // 满足条件时,执行的语句 } else if(m===2){ // 不满足条件时,执行的语句 }else{ }
if…else if…else
switch
switch (fruit) {
case "banana":
// ...
break;
case "apple":
// ...
break;
default:
// ...
}
和c语言一模一样
三元运算符 (条件) ? 正确 : 错误
for
for (初始化表达式; 条件; 迭代因子) {
语句
}
for (var i=0;i<10;i++){
console.log(i);
}
//c++
for (int i=0;i<10;i++){
printf(i);
}
//python
for i in range(10)
print(i)
while
while (条件) {
语句;
}
break,continue
字符串
字符串就是零个或多个排在一起的字符,放在单引号或双引号之中
单引号字符串的内部,可以使用双引号。双引号字符串的内部,可以使用单引号
如果要在单引号字符串的内部,使用单引号,就必须在内部的单引号前面加上反斜杠,用来转义。双引号字符串内部使用双引号,也是如此
字符串默认只能写在一行内,分成多行将会报错。如果长字符串必须分成多行,可以在每一行的尾部使用反斜杠
如果真的有多行文本的需求,可以使用字符串拼接的方式实现。使用加号连接。
转义
反斜杠(\)在字符串内有特殊含义,用来表示一些特殊字符,所以又称为转义符
\0 :null(\u0000)
\b :后退键(\u0008)
\f :换页符(\u000C)
\n :换行符(\u000A)
\r :回车键(\u000D)
\t :制表符(\u0009)
\v :垂直制表符(\u000B)
’ :单引号(\u0027)
" :双引号(\u0022)
\ :反斜杠(\u005C)
length 属性
length属性返回字符串的长度,该属性也是无法改变的
str1.length
字符串转码
所谓 Base64 就是一种编码方法,可以将任意值转成 0~9、A~Z、a-z、+和/这64个字符组成的可打印字符。使用它的主要目的,不是为了加密,而是为了不出现特殊字符,简化程序的处理.JavaScript 原生提供两个 Base64 相关的方法
btoa():任意值转为 Base64 编码
atob():Base64 编码转为原来的值
var string = 'Hello World!'; btoa(string) // "SGVsbG8gV29ybGQh" atob('SGVsbG8gV29ybGQh') // "Hello World!"
中文是不可以进行处理的.如果非要处理中文,需要在增加两个方法encodeURIComponent(), decodeURIComponent()
function b64Encode(str) {
return btoa(encodeURIComponent(str));
}
function b64Decode(str) {
return decodeURIComponent(atob(str));
}
b64Encode('你好') //"JUU0JUJEJUEwJUU1JUE1JUJE"
b64Decode('JUU0JUJEJUEwJUU1JUE1JUJE') // "你好"
charAt()
charAt 方法返回指定位置的字符,参数是从 0 开始编号的
人话:把字符串看成一个字符串数组,按照下标取数组的元素。
var s = new String('itbaizhan');
s.charAt(1) // "t" 取下标为1的元素
s.charAt(s.length - 1) // "n" 取最后一个元素
如果参数为负数,或大于等于字符串的长度, charAt 返回空字符串
concat()
concat 方法用于连接两个字符串,返回一个新字符串,不改变原字符串,s1.concat(s2)
该方法可以接受多个参数,console.log(‘’.concat(one, two, three) )
如果参数不是字符串, concat 方法会将其先转为字符串,然后再连接
和使用加号拼接字符串的效果是一样的
slice(start[,end]) 左闭右开
slice 方法用于从原字符串取出子字符串并返回,不改变原字符串。它的第一个参数是子字符串的开始位置,第二个参数是子字符串的结束位置(不含该位置)
就是从字符串里面取子串
'itbaizhan'.slice(0, 4) // "it"
如果省略第二个参数,则表示子字符串一直到原字符串结束
如果参数是负值,表示从结尾开始倒数计算的位置,即该负值加上字符串长度
'itbaizhan'.slice(-7) // "baizhan"
substring()
和slice一样,但是slice不支持,第二个参数比第一个参数靠前。
substring在发现第二个参数比第一个参数靠前的时候,会自动交换两个参数的位置,属于是slice的修复版
如果参数是负数, substring 方法会自动将负数转为0
由于这些规则违反直觉,因此不建议使用 substring 方法,应该优先使用 slice
substr(index,len)
和slice,substring的作用是一样的
但是参数的意义不一样了,第一个参数是子串截取的下标,第二个参数是子串要截取的长度
如果第一个参数是负数,表示倒数计算的字符位置。如果第二个参数是负数,将被自动转为0,因此会返回空字符串
第二个参数为0,就是空字符串拉
indexOf()/lastIndexOf()
就是找子串的起始位置。可以有第二个参数,第二个参数是从字符串第几位元素开始寻找子串。
indexof是正着找子串,lastindexof是反着找子串。
indexOf 方法用于确定一个字符串在另一个字符串中第一次出现的位置,返回结果是匹配开始的位置。如果返回 -1 ,就表示不匹配
indexOf 方法还可以接受第二个参数,表示从该位置开始向后匹配
lastIndexOf 方法的用法跟 indexOf 方法一致,主要的区别是 lastIndexOf 从尾部开始匹配, indexOf 则是从头部开始匹配
trim()
trim 方法用于去除字符串两端的空格,返回一个新字符串,不改变原字符串
该方法去除的不仅是空格,还包括制表符( \t 、 \v )、换行符( \n )和回车符( \r )
toLowerCase()/toUpperCase()
toLowerCase 方法用于将一个字符串全部转为小写, toUpperCase 则是全部转为大写。它们都返回一个新字符串,不改变原字符串
itbaizhan'.charAt(0).toUpperCase() +'itbaizhan'.substr(1);
search(子串)/replace()
search 方法确定原字符串是否匹配某个子字符串,返回值为匹配的第一个位置。如果没有找到匹配,则返回 -1 。
search和indexof作用类型。都是找子串的。目前他们的效果作用都是一样的,在学了正则表达式之后就不一样了。
replace 方法用于替换匹配的子字符串'sxtbaizhan'.replace('sxt', 'it')
replace多处用在删除子串。就是根据我们提供的子串1,和一个空字符串的子串2,就能完成字符串删除子串的操作
split(要分隔的字符[,返回数量])
split 方法按照给定规则分割字符串,返回一个由分割出来的子字符串组成的数组
如果分割规则为空字符串,则返回数组的成员是原字符串的每一个字符。
如果省略参数,则返回数组的唯一成员就是原字符串
split 方法还可以接受第二个参数,限定返回数组的最大成员数。
返回的是子串数组。
数组
数组(array)是按次序排列的一组值。每个值的位置都有编号(从0开始),整个数组用方括号表示。
var arr = ['it',10,true,{}];
数组也可以先定义后赋值。
任何类型的数据,都可以放入数组
数组可以嵌套数组arr[1][2]
数组的本质上,数组属于一种特殊的对象。typeof运算符会返回数组的类型是object
length属性是可写的。如果人为设置一个小于当前成员个数的值,该数组的成员会自动减少到length设置的值.数据丢失
var arr = ['sxt', 'baizhan', 'it']; arr.length = 2; // ['sxt', 'baizhan']
如果访问数组的时候,指定下标,下标超过数组的最大长度,就会返回undefined
遍历
for(var i = 0; i < a.length; i++) { console.log(a[i]); }
for (var i in arr) { console.log(arr[i]); }
构造函数
当数组的某个位置是空元素,即两个逗号之间没有任何值,我们称该数组存在空位.空位的打印结果是undefined
var a = ['sxt', , 'it']; a.length // 3
如果参数是一个正整数,返回数组的成员都是空位。虽然读取的时候返回 undefined ,但实际上该位置没有任何值。虽然可以取到 length 属性,但是取不到键名
new就是构造方法
Array 是 JavaScript 的原生对象,同时也是一个构造函数,可以用它生成新的数组
var arr = new Array(2); arr.length // 2
如果没有使用 new ,运行结果也是一样的。var arr = Array(2);
不建议使用它生成新数组,直接使用数组字面量是最简约的做法.字面量:var arr=[];
Array.isArray()检测数组
这个方法就是Array.isArray(),参数写检测的变量c
Array.isArray 方法返回一个布尔值,表示参数是否为数组。它可以弥补typeof 运算符的不足.
字符串不是数组
使用typeof对一个数组进行检测,只会得到一个object的对象类型
push()/pop() 栈
push 方法用于在数组的末端添加一个或多个元素,并返回添加新元素后的数组长度。注意,该方法会改变原数组
pop 方法用于删除数组的最后一个元素,并返回该元素。注意,该方法会改变原数组
对空数组使用 pop 方法,不会报错,而是返回 undefined
push 和 pop 结合使用,就构成了“后进先出”
shift()/unshift() 队列
shift 方法可以遍历并清空一个数组
while (item = list.shift()) { console.log(item); }
unshift 方法可以接受多个参数,这些参数都会添加到目标数组头部
join()
join 方法以指定参数作为分隔符,将所有数组成员连接为一个字符串返回。如果不提供参数,默认用逗号分隔
如果数组成员是 undefined 或 null 或空位,会被转成空字符串
数组的 join 配合字符串的 split 可以实现数组与字符串的互换
concat() 合并
concat 方法用于多个数组的合并。它将新数组的成员,添加到原数组成员的后部,然后返回一个新数组,原数组不变
除了数组作为参数, concat 也接受其他类型的值作为参数,添加到目标数组尾部。
[1, 2, 3].concat(4, 5, 6,[7,8,9])
本来是只接收数组对象的,但是数值也可以转换成数组类型使用,如果是数组,就会直接合并到原数组
reverse() 翻转数组
reverse 方法用于颠倒排列数组元素,返回改变后的数组。注意,该方法将改变原数组
因为字符串没法直接翻转,可以使用split把字符串切割成单字符数组,再使用reverse翻转,再使用join拼接
str1.split(“”).reverse().join()
slice(start,end) 截取数组,左闭右开
和字符串的截取方式一样
slice 方法用于提取目标数组的一部分,返回一个新数组,原数组不变
它的第一个参数为起始位置(从0开始),第二个参数为终止位置(但该位置的元素本身不包括在内)。如果省略第二个参数,则一
直返回到原数组的最后一个成员
如果 slice 方法的参数是负数,则表示倒数计算的位置
如果第一个参数大于等于数组长度,或者第二个参数小于第一个参数,则返回空数组
如果无参,就把数组全都打印出来
splice(index,len,新元素1,元素2…)
splice 方法用于删除原数组的一部分成员,并可以在删除的位置添加新的数组成员,返回值是被删除的元素。注意,该方法会改变原数组
splice 的第一个参数是删除的起始位置(从0开始),第二个参数是被删除的元素个数。如果后面还有更多的参数,则表示这些就是要被插入数组的新元素
起始位置如果是负数,就表示从倒数位置开始删除
如果只是单纯地插入元素, splice 方法的第二个参数可以设为 0
如果只提供第一个参数,等同于将原数组在指定位置拆分成两个数组
sort() 字典顺序排序
sort 方法对数组成员进行排序,默认是按照字典顺序排序。排序后,原数组将被改变
sort 方法不是按照大小排序,而是按照字典顺序。也就是说,数值会被先转成字符串,再按照字典顺序进行比较,所以101排在11的前面
indexOf()/lastIndexOf() 查找
indexOf 方法返回给定元素在数组中第一次出现的位置,如果没有出现则返回 -1
indexOf 方法还可以接受第二个参数,表示搜索的开始位置
lastIndexOf 方法返回给定元素在数组中最后一次出现的位置,如果没有出现则返回 -1
这两个方法不能用来搜索 NaN 的位置,即它们无法确定数组成员是否包含 NaN
这是因为这两个方法内部,使用严格相等运算符( === )进行比较,而 NaN 是唯一一个不等于自身的值
函数
JavaScript 有三种声明函数的方法
function print(s) { console.log(s);}
var print = function(s) { console.log(s);};
函数的重复声明
如果同一个函数被多次声明,后面的声明就会覆盖前面的声明
函数名的提升
JavaScript 引擎将函数名视同变量名,所以采用function命令声明函数时,整个函数会像变量声明一样,被提升到代码头部。
但是,如果采用赋值语句定义函数,JavaScript 就会报错.赋值语句没有变量提升
// 会报错,因为赋值定义的函数,不能提升
test():
var test(a,b){
console.log(a,b);
}
不能在条件语句中声明函数
根据 ES5 的规范,不得在非函数的代码块中声明函数,最常见的情况就是if语句
函数的属性和方法
函数作用域
Javascript 有两种作用域:一种是全局作用域,变量在整个程序中一直存在;另一种是函数作用域,变量只在函数内部存在
函数外部声明的变量就是全局变量
在函数内部定义的变量,外部无法读取,称为“局部变量”
函数内部定义的变量,会在该作用域内覆盖同名全局变量
局部变量只能在函数内部声明,在其他区块中声明,一律都是全局变量
函数内部的变量提升
只要不在函数体内声明的变量,都是全局变量,比如for,if语句里面的都是全局变量
函数参数
如果全局变量是以参数传入函数,或者是重新使用var声明的变量。在函数内部修改变量,在函数体外部不受影响。
如果全局变量是在函数体内部直接使用,就会被修改。
由于 JavaScript 允许函数有不定数目的参数,所以需要一种机制,可以在函数体内部读取所有参数。这就是arguments对象的由来
var list = function (one) {
console.log(arguments[0]);
console.log(arguments[1]);
console.log(arguments[2]);
}
list(1, 2, 3)
函数返回值
JavaScript函数提供两个接口实现与外界的交互,其中参数作为入口,接收外界信息;返回值作为出口,把运算结果反馈给外界
在函数体内,使用 return 语句可以设置函数的返回值。
如果return语句后不跟任何值就相当于返回一个undefined
如果函数中不写return,则也会返回undefined
在函数中return后的语句都不会执行
立即调用的函数(IIFE)
在 Javascript 中,圆括号()是一种运算符,跟在函数名之后,表示调用该函数。比如,print()就表示调用print函数。那如何让函数自己执行呢
(function(){ /* code */ }());
// 或者
(function(){ /* code */ })();
var a = 10;
var b = 20;
(function (x,y){console.log(x+y);})(a,b);
eval 命令
将字符串当作语句执行,一般用作代码加密。
eval('var a = 1;');
对象
JavaScript 语言中,对象就是一组“键值对”(key-value)的集合,是一种无序的复合数据集合
var user = {
name: 'itbaizhan',
age: '13'
};
对象的每一个键名又称为“属性”(property),它的“键值”可以是任何数据类型。如果一个属性的值为函数,通常把这个属性称为“方法”,它可以像函数那样调用
如果属性的值还是一个对象,就形成了链式引用user.container.frontEnd
对象属性
读取对象的属性,有两种方法,一种是使用点运算符,还有一种是使用方括号运算符
user.hello
user[‘hello’]
如果使用方括号运算符,键名必须放在引号里面,否则会被当作变量处理
点运算符和方括号运算符,不仅可以用来读取值,还可以用来赋值
JavaScript 允许属性的“后绑定”,也就是说,你可以在任意时刻新增属性,没必要在定义对象的时候,就定义好属性
查看一个对象本身的所有属性,可以使用 Object.keys(对象名) 方法
delete 命令用于删除对象的属性,删除成功后返回 true。。。。delete user.name
删除一个不存在的属性, delete 不报错,而且返回 true
只有一种情况, delete 命令会返回 false ,那就是该属性存在,且不得删除(使用率极低)
for (var i in user) {
console.log(user[i]);
}
对象方法
Object 对象的方法分成两类: Object 本身的方法与 Object 的实例方法
跟python的类一样。有类方法,和类的实例方法两种。
Object 对象本身的方法,就是直接定义在 Object 对象的方法
Object 的实例方法,就是定义在 Object 原型对象 Object.prototype 上的方法。它可以被 Object 实例直接使用
Object.prototype.print = function (name) {
console.log(name);
};
var user = {
name:'itbaizhan'
}
user.print(user.name);
就行数组的方法一样,Array.isArray(num1)和num1.pop() 这两种形式。第一种是对象方法,第二种是实例方法。
Math对象 数学
静态属性
Math是 JavaScript 的原生对象,提供各种数学功能。
该对象不是构造函数,不能生成实例,所有的属性和方法都必须在Math对象上调用
Math.E:常数e。
Math.LN2:2 的自然对数。
Math.LN10:10 的自然对数。
Math.LOG2E:以 2 为底的e的对数。
Math.LOG10E:以 10 为底的e的对数。
Math.PI:常数 PI。
Math.SQRT1_2:0.5 的平方根。
Math.SQRT2:2 的平方根。Math.E // 2.718281828459045
Math.LN2 // 0.6931471805599453
Math.LN10 // 2.302585092994046
Math.LOG2E // 1.4426950408889634
Math.LOG10E // 0.4342944819032518
Math.PI // 3.141592653589793
Math.SQRT1_2 // 0.7071067811865476
Math.SQRT2 // 1.4142135623730951
这些属性都是只读的,不能修改
静态方法
借Math的方法
因为Math的方法在某些场合下比较适用,我们想要直接适用Math的一些方法,比如求最大值的max
但是,Math.max不接受一个数组作为参数。所以需要使用其他方法使用这个方法。
Math.max.apply(null,arr); apply方法,可以借用math里面的方法,第一个参数为null,第二个参数是数组。然后就可以求出数组的最大值了。
三角函数方法
Math.sin():返回参数的正弦(参数为弧度值)
Math.cos():返回参数的余弦(参数为弧度值)
Math.tan():返回参数的正切(参数为弧度值)
Math.asin():返回参数的反正弦(返回值为弧度值)
Math.acos():返回参数的反余弦(返回值为弧度值)
Math.atan():返回参数的反正切(返回值为弧度值)
生成随机颜色
利用random生成随机数,然后我们自定义一个字符串,也就是十六进制的所有位0123456789abcdef。
利用随机数乘以16,然后取整,再从字符串提取字符。最后把字符拼接。得到随机颜色。
要向下取整,防止超过最大值。
Date对象 日期
Date()
'Tue Oct 26 2022 10:31:34 GMT+0800 (中国标准时间)'
即使带有参数, Date 作为普通函数使用时,返回的还是当前时间
Date(2000, 1, 1)
// 'Tue Oct 26 2022 10:31:34 GMT+0800 (中国标准时间)'
var today = new Date();
作为构造函数时, Date 对象可以接受多种格式的参数,返回一个该参数对应的时间实例
// 参数为时间零点开始计算的毫秒数
new Date(1635215676627)
// Tue Oct 26 2021 10:34:36 GMT+0800 (中国标准时间)
// 参数为日期字符串
new Date('January 6, 2020');
// Mon Jan 06 2020 00:00:00 GMT+0800 (中国标准时间)
// 参数为多个整数,
// 代表年、月、日、小时、分钟、秒、毫秒
new Date(2020, 0, 1, 0, 0, 0, 0)
// Wed Jan 01 2020 00:00:00 GMT+0800 (中国标准时间)
new Date('2022-2-15')
new Date('2022/2/15')
new Date('02/15/2022')
new Date('2022-FEB-15')
new Date('FEB, 15, 2022')
new Date('FEB 15, 2022')
new Date('Feberuary, 15, 2022')
new Date('Feberuary 15, 2022')
new Date('15 Feb 2022')
new Date('15, Feberuary, 2022')
日期的运算
传两个日期,做差,得到毫秒
var d1 = new Date(2000, 2, 1);
var d2 = new Date(2000, 3, 1);
d2 - d1 // 2678400000 毫秒
静态方法
Date.now 方法返回当前时间距离时间零点(1970年1月1日 00:00:00 UTC)的毫秒数,相当于 Unix 时间戳乘以1000
Date.parse 方法用来解析日期字符串,返回该时间距离时间零点(1970年1月1日 00:00:00)的毫秒数
Date.parse(‘Aug 9, 2022’)
Date.parse(‘January 26, 2022 13:51:50’)
Date.parse(‘Mon, 25 Dec 2022 13:30:00 GMT’)
Date.parse(‘Mon, 25 Dec 2022 13:30:00 +0430’)
Date.parse(‘2022-10-10’)
Date.parse(‘2022-10-10T14:48:00’)
实例方法/to类
Date 的实例对象,有几十个自己的方法,分为三类
to 类:从 Date 对象返回一个字符串,表示指定的时间。
get 类:获取 Date 对象的日期和时间。
set 类:设置 Date 对象的日期和时间。
toUTCString 方法返回对应的 UTC 时间,也就是比北京时间晚8个小时
toDateString 方法返回日期字符串(不含小时、分和秒)
toTimeString 方法返回时间字符串(不含年月日)
toLocaleDateString 方法返回一个字符串,代表日期的当地写法(不含小时、分和秒)
toLocaleTimeString 方法返回一个字符串,代表时间的当地写法(不含年月日)
实例方法/get类
Date 对象提供了一系列 get* 方法,用来获取实例对象某个方面的值
getTime():返回实例距离1970年1月1日00:00:00的毫秒数
getDate():返回实例对象对应每个月的几号(从1开始)
getDay():返回星期几,星期日为0,星期一为1,以此类推
getYear():返回距离1900的年数
getFullYear():返回四位的年份
getMonth():返回月份(0表示1月,11表示12月)
getHours():返回小时(0-23)
getMilliseconds():返回毫秒(0-999)
getMinutes():返回分钟(0-59)
getSeconds():返回秒(0-59)
实例方法/set类
Date 对象提供了一系列 set* 方法,用来设置实例对象的各个方面
setDate(date) :设置实例对象对应的每个月的几号(1-31)
setYear(year) : 设置距离1900年的年
setFullYear(year [, month, date]) :设置四位年份
setHours(hour [, min, sec, ms]) :设置小时(0-23)
setMilliseconds() :设置毫秒(0-999)
setMinutes(min [, sec, ms]) :设置分钟(0-59)
setMonth(month [, date]) :设置月份(0-11)
setSeconds(sec [, ms]) :设置秒(0-59)
setTime(milliseconds) :设置毫秒时间戳
唯一id
因为时间戳是不可重复的,所以,一般使用随机数拼接时间戳。创建唯一的id。只要时间戳不一样,id就永远不一样。所以id越长越安全。
三十六进制:0123456789abcdefghijklmnopqrstuvwxyz
使用tostring(36) 可以把时间戳变成三十六进制的字符串。这样就是随机数+时间戳的三十六进制的超级防伪了。
DOM
是js与页面交互的桥梁
js通过DOM操作浏览器
DOM 是 JavaScript 操作网页的接口,全称为“文档对象模型”(Document Object Model)。它的作用是将网页转为一个JavaScript 对象,从而可以用脚本进行各种操作(比如对元素增删内容)
浏览器会根据 DOM 模型,将结构化文档HTML解析成一系列的节点,再由这些节点组成一个树状结构(DOM Tree)。所有的节点和最终的树状结构,都有规范的对外接口
DOM 只是一个接口规范,可以用各种语言实现。所以严格地说,DOM 不是 JavaScript 语法的一部分,但是 DOM 操作是 JavaScript最常见的任务,离开了 DOM,JavaScript 就无法控制网页。另一方面,JavaScript 也是最常用于 DOM 操作的语言
节点
DOM 的最小组成单位叫做节点(node)。文档的树形结构(DOM树),就是由各种不同类型的节点组成。每个节点可以看作是文档树的一片叶子
节点的类型有七种
Document:整个文档树的顶层节点,就是html文件
DocumentType:doctype标签,html的声明
Element:网页的各种HTML标签,所有的元素
Attribute:网页元素的属性(比如class=“right”)
Text:标签之间或标签包含的文本
Comment:注释
DocumentFragment:文档的片段
一个文档的所有节点,按照所在的层级,可以抽象成一种树状结构。这种树状结构就是 DOM 树。它有一个顶层节点,下一层都是顶层节点的子节点,然后子节点又有自己的子节点,就这样层层衍生出一个金字塔结构,倒过来就像一棵树
除了根节点,其他节点都有三种层级关系
父节点关系(parentNode):直接的那个上级节点
子节点关系(childNodes):直接的下级节点
同级节点关系(sibling):拥有同一个父节点的节点
Node.nodeType属性
不同节点的nodeType属性值和对应的常量如下
文档节点(document):9,对应常量Node.DOCUMENT_NODE
元素节点(element):1,对应常量Node.ELEMENT_NODE
属性节点(attr):2,对应常量Node.ATTRIBUTE_NODE
文本节点(text):3,对应常量Node.TEXT_NODE
文档片断节点(DocumentFragment):11,对应常量Node.DOCUMENT_FRAGMENT_NODE
属性
指向 DOCTYPE 节点,即文档类型(Document Type Declaration,简写DTD)节点。HTML 的文档类型节点,一般写成 !DOCTYPE html 。如果网页没有声明 DTD,该属性返回null
var doctype = document.doctype; // "<!DOCTYPE html>"
document.documentElement 文档的根节点(root)html
document.body,document.head
document.forms 属性返回所有 form 表单节点
document.images 属性返回页面所有 img 图片节点
document.scripts 属性返回所有 script 节点
一般是判断页面有没有js文件
var scripts = document.scripts;
if (scripts.length !== 0 ) {
console.log('当前网页有脚本');
}
document.domain 属性返回当前文档的域名
获取元素
document.getElementsByTagName 方法搜索 HTML 标签名,返回符合条件的元素。它的返回值是一个类似数组对象(HTMLCollection 实例),可以实时反映 HTML 文档的变化。如果没有任何匹配的元素,就返回一个空集
通过这种方法可以获取页面中的元素,也可以使用innerHTML修改元素里面的内容
document.getElementsByTagName(‘p’);
包括了所有 class 名字符合指定条件的元素,元素的变化实时反映在返回结果中
由于 class 是保留字,所以 JavaScript 一律使用 className 表示 CSS 的class
参数可以是多个 class ,它们之间使用空格分隔
document.getElementsByName() 通过name属性定位
document.getElementById() 通过id获取
document.querySelector() 通过css的选择器,就是.box等等,这个是只返回一个
document.querySelectorAll() 和上面的用法一样,这里是返回符合的所有元素
创建元素
document.createElement 生成元素节点
document.createTextNode 生成文本
document.createAttribute 生成属性
添加元素,父元素.appendChild(子元素)
Element对象_属性
Element对象对应网页的 HTML 元素。每一个 HTML 元素,在DOM 树上都会转化成一个Element节点对象(以下简称元素节点)
div.classList.add('foo', 'bar');
Element获取元素位置一
clientHeight 获取元素高度包括 padding 部分,但是不包括 border 、 margin
clientWidth 获取元素宽度包括 padding 部分,但是不包括 border 、 margin
clientLeft 元素节点左边框(left border)的宽度
clientTop 元素节点上边框(top border)的高度
scrollHeight 元素总高度,它包括 padding ,但是不包括 border 、 margin 包括溢出的不可见内容
scrollWidth 元素总宽度,它包括 padding ,但是不包括 border 、 margin 包括溢出的不可见内容
scrollLeft 元素的水平滚动条向右滚动的像素数量
scrollTop 元素的垂直滚动条向下滚动的像素数量
offsetHeight 元素的 CSS 垂直高度(单位像素),包括元素本身的高度、padding 和 border
offsetWidth 元素的 CSS 水平宽度(单位像素),包括元素本身的高度、padding 和 border
offsetLeft 到定位父级左边界的间距
offsetTop 到定位父级上边界的间距
Element对象_方法
document.getElementById('my-span').focus();
类似百度网页,一打开百度,默认就点击了搜索框。
Attribute属性的操作
HTML 元素包括标签名和若干个键值对,这个键值对就称为“属性”(attribute)
元素节点提供四个方法,用来操作属性
Element.getAttribute 方法返回当前元素节点的指定属性。如果指定属性不存在,则返回 null
div.getAttribute('align')
是返回属性的值。
Element.setAttribute 方法用于为当前元素节点新增属性。如果同名属性已存在,则相当于编辑已存在的属性
d.setAttribute('align', 'center');
myImage.setAttribute('src','images/girl.png');
Element.hasAttribute 方法返回一个布尔值,表示当前元素节点是否包含指定属性
d.hasAttribute('align')
Element.removeAttribute 方法用于从当前元素节点移除属性
d.removeAttribute('class');
dataset 属性
<div id="box" data-itbaizhan="itbaizhan">
比如data-itbaizhanText 节点
文本节点( Text )代表元素节点( Element )和属性节点( Attribute )的文本内容。
使用率很低
Text节点属性
// 读取文本内容
document.querySelector('p').firstChild.data
// 设置文本内容
document.querySelector('p').firstChild.data ='Hello World';
文本写在标签里面,这个文本是标签的子元素。文本和元素本身不是平级的。
Text节点方法
就是对text节点进行增删查改
DocumentFragment 节点(了解)
本质是一个临时文件,存放我们想要创建的标签。然后一次性插入页面
DocumentFragment 节点本身不能被插入当前文档,插入文档中的是他里面的子元素
多次操作DOM,可以使用 DocumentFragment 变成一次操作,节省了内存的消耗
Node 节点属性
所有 DOM 节点都继承了 Node 接口,拥有一些共同的属性和方法。这是 DOM 操作的基础
节点的类型有七种
Document:整个文档树的顶层节点
DocumentType:doctype标签 (用不到)
Element:网页的各种HTML标签
Attribute:网页元素的属性(比如class=“right”)
Text:标签之间或标签包含的文本
Comment:注释
DocumentFragment:文档的片段
nodeName 属性返回节点的名称
不同节点的 nodeName 属性值如下
nodeValue 属性返回一个字符串,表示当前节点本身的文本值,该属性可读写
textContent 属性返回当前节点和它的所有后代节点的文本内容
nextSibling 属性返回紧跟在当前节点后面的第一个同级节点。如果当前节点后面没有同级节点,则返回 null
previousSibling 属性返回当前节点前面的,距离最近的一个同级节点。如果当前节点前面没有同级节点,则返回 null
parentNode 属性返回当前节点的父节点。对于一个节点来说,它的父节点只可能是三种类型:元素节点(element)、文档节点(document)和文档片段节点(documentfragment)
firstChild/lastChild
childNodes 属性返回一个类似数组的对象( NodeList 集合),成员包括当前节点的所有子节点
appendChild 方法接受一个节点对象作为参数,将其作为最后一个子节点,插入当前节点。该方法的返回值就是插入文档的子节点
hasChildNodes 方法返回一个布尔值,表示当前节点是否有子节点
cloneNode 方法用于克隆一个节点。它接受一个布尔值作为参数,表示是否同时克隆子节点。它的返回值是一个克隆出来的新节点
insertBefore 方法用于将某个节点插入父节点内部的指定位置
removeChild 方法接受一个子节点作为参数,用于从当前节点移除该子节点。返回值是移除的子节点
replaceChild 方法用于将一个新的节点,替换当前节点的某一个子节点
CSS操作
操作 CSS 样式最简单的方法,就是使用网页元素节点的 setAttribute 方法直接操作网页元素的 style 属性
div.setAttribute( 'style', 'background-color:red;' + 'border:1px solid black;' );
var divStyle =
document.querySelector('div').style;
divStyle.backgroundColor = 'red';
divStyle.border = '1px solid black';
divStyle.width = '100px';
divStyle.height = '100px';
divStyle.fontSize = '10em';
var divStyle = document.querySelector('div').style;
divStyle.cssText = 'background-color: red;border: 1px solid black;height: 100px;width: 100px;';
事件处理
就是控件与槽函数之间的桥梁,事件
事件处理程序分为:
1 HTML事件处理
2 DOM0级事件处理
3 DOM2级事件处理
4 IE事件处理 (弃用)
HTML事件
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>Js事件详解--事件处理</title>
</head>
<body>
<div id="div">
<button id="btn1" onclick="demo()">按钮</button>
</div>
<script>
function demo(){
alert("hello html事件处理");
}
</script>
</body>
</html>
就是在元素里面增加onclick属性,后面写要执行的事件。
<body>
<div id="div">
<button id="btn1">按钮</button>
</div>
<script>
var btn1=document.getElementById("btn1");
btn1.onclick=function(){alert("HelloDOM0级事件处理程序1");}//被覆盖掉
btn1.onclick=function(){alert("HelloDOM0级事件处理程序2");}
</script>
</body>
和html事件一样,就是不用特意添加onclick属性了。
<body>
<div id="div">
<button id="btn1">按钮</button>
</div>
<script>
var btn1=document.getElementById("btn1");
btn1.addEventListener("click",demo1);
btn1.addEventListener("click",demo2);
btn1.addEventListener("click",demo3);
function demo1(){
alert("DOM2级事件处理程序1")
}
function demo2(){
alert("DOM2级事件处理程序2")
}
function demo3(){
alert("DOM2级事件处理程序3")
}
btn1.removeEventListener("click",demo2);
</script>
</body>
使用addEventListener方法,不用onclick了,第一个参数是事件,比如click,第二个是槽函数
IE事件处理程序(基本没人用)
常见事件类型
1 鼠标事件
2 键盘事件
3 表单事件
4 窗口事件
5 焦点/剪贴板事件
6 网页状态事件
7 Touch事件 手机或者pad端的点击,触摸事件
鼠标事件
鼠标事件指与鼠标相关的事件,具体的事件主要有以下一些
1 click:按下鼠标时触发
2 dblclick:在同一个元素上双击鼠标时触发
3 mousedown:按下鼠标键时触发
4 mouseup:释放按下的鼠标键时触发
5 mousemove:当鼠标在节点内部移动时触发。当鼠标持续移动时,该事件会连触发。
6 mouseenter:鼠标进入一个节点时触发,进入子节点不会触发这个事件
7 mouseleave:鼠标离开一个节点时触发,离开父节点不会触发这个事件
8 mouseover:鼠标进入一个节点时触发,进入子节点会再一次触发这个事件
9 mouseout:鼠标离开一个节点时触发,离开父节点也会触发这个事件
10 wheel:滚动鼠标的滚轮时触发
这些方法在使用的时候,除了DOM2级事件,都需要添加前缀on,比如onclick,ondblclick
鼠标的按下和抬起的应用:如果我们点错了某个东西,想要挽回的时候,会把鼠标按住移开。不触发鼠标抬起函数。有一定的容错率。
mousemove在使用的时候,被调用的次数特别高,以像素为单位,每移动超过像素,就会触发,导致对应的函数每秒几百次的执行。后期通过优化减少函数的执行次数。
事件6,7在鼠标移动到容器内部的子节点时,就是鼠标已经在容量里面了,然后再移动到内部的子节点,就不会触发事件。
事件8,9,当鼠标在容器内移动到子节点的时候,会触发一次函数。进入子节点的时候,会触发两个事件,一个是离开父节点,一个是进入子节点
事件1,5的使用频率比较高
事件流
处理子父容器的函数执行顺序。
场景:一个按钮在盒子里面,按钮和盒子都有鼠标点击事件。按下按钮的时候,是先触发按钮点击,还是先触发盒子点击呢?
在浏览器刚兴起的时代,有两家公司,微软IE和网景Net
微软的执行顺序是—-事件冒泡流。先子元素再父元素。
网景是—事件捕获流,先父元素再子元素。
事件冒泡
事件捕获
使用事件捕获的方法:
d.addEventListener('click', function(){ console.log('div被点击') },true);
后面的true就是使用事件捕获流Event事件对象
事件发生以后,会产生一个事件对象,作为参数传给监听函数。
事件对象:event
监听函数:就是我们写的网站或者app,是一个巨大的无限循环。这个无限循环一直在等待用户的输入。比如按下鼠标等等,等待的过程,也就是监听的过程。一旦某个事件和我们写的槽函数对应上了。就会执行这个槽函数。这个过程叫监听过程,整体叫监听函数
Event对象属性
1 Event.currentTarget
2 Event.Target
3 Event.type
Event.currentTarget属性返回事件当前所在的节点,即正在执行的监听函数所绑定的那个节点
Event.target属性返回原始触发事件的那个节点,即事件最初发生的节点。事件传播过程中,不同节点的监听函数内部的Event.target与Event.currentTarget属性的值是不一样的,前者总是不变的,后者则是指向监听函数所在的那个节点对象
Event.type属性返回一个字符串,表示事件类型。事件的类型是在生成事件的时候。该属性只读
currentTarget与target的区别
event方法
Event.preventDefault方法取消浏览器对当前事件的默认行为。阻止默认行为。
stopPropagation方法阻止事件在 DOM 中继续传播,防止再触发定义在别的节点上的监听函数,但是不包括在当前节点上其他的事件监听函数
键盘事件
键盘事件由用户击打键盘触发,主要有keydown、keypress、keyup三个事件
1 keydown:按下键盘时触发。
2 keypress:按下有值的键时触发,即按下 Ctrl、Alt、Shift、Meta 这样无值的键,这个事件不会触发。对于有值的键,按下时先触发keydown事件,再触发这个事件。
3 keyup:松开键盘时触发该事件
当我们按下一个按键的时候,事件12会不停的被触发。事件1的按键按下,事件2是按键被持续按下。
按下ctrl,只有事件1被触发。抬起只有事件3触发,没有事件2,因为ctrl没有值
表单事件
用于搜索框
表单事件是在使用表单元素及输入框元素可以监听的一系列事件
1 input事件
2 select事件
3 Change事件
4 reset事件
5 submit事件
input事件
select事件 当在 input、textarea 里面选中文本时触发
Change 事件 当 input、select、textarea 的值发生变化时触发。它与input事件的最大不同,就是不会连续触发,只有当全部修改完成时才会触发
reset 事件,submit 事件 这两个事件发生在表单对象 form 上,而不是发生在表单的成员上。
input和change常用
窗口事件
scroll 事件
resize 事件
焦点/剪贴板事件
焦点事件
四个事件
1 focus:元素节点获得焦点后触发,该事件不会冒泡。
2 blur:元素节点失去焦点后触发,该事件不会冒泡。
3 focusin:元素节点将要获得焦点时触发,发生在focus事件之前。该事件会冒泡。(少用)
4 focusout:元素节点将要失去焦点时触发,发生在blur事件之前。该事件会冒泡。(少用)
username.onfocus = function(e){
console.log("获得焦点",e.target.value);
}
username.onblur = function(e){
console.log("失去焦点",e.target.value);
}
username.focus();打开页面的时候,默认获得焦点
cut:剪贴
copy:复制
paste:粘贴
password.oncut = function(e){
console.log("剪切");
}
password.oncopy = function(e){
console.log("复制");
}
比如学习通考试的时候阻止拷贝
网页状态事件
1 load
2 DOMContentLoaded 事件
3 readystatechange 事件
Load事件
DOMContentLoaded 事件
readystatechange 事件
Touch事件
只能用dom2级事件处理
只能是手机和pad上才能触发
触摸引发的事件,有以下几种。可以通过TouchEvent.type属性,查看到底发生的是哪一种事件
1 touchstart:用户开始触摸时触发
2 touchend:用户不再接触触摸屏触发
3 touchmove:用户移动触摸点时触发,高频触发
4 touchcancel:系统取消touch事件的时候触发(使用率低)就是我们的手指移到屏幕外。系统自动干掉了
事件代理(事件委托)
让父元素处理子元素的事件
由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。这种方法叫做事件的代理(delegation)
应用场景:超多列表项的控件,在之前我们是给每个列表项添加函数,每个列表项有自己的方法。当列表项足够多的时候,一个个添加就很麻烦,便有了事件代理。
通过js动态添加的元素,不能预先写函数,只能使用这种事件代理
获取用户点击的列表项:e.target.innerHTML
this关键字
btn.onclick = function () {
console.log(this); //this==e.target
};
var username = 'sxt';
var user = {
username:"itbaizhan",
getName:function(){
console.log(username); 打印的是sxt,因为没有指定对象user
console.log(this.username);
}
}
函数_闭包概念
闭包(closure)是 Javascript 语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现
理解闭包,理解变量作用域
1 全局变量(全局作用域)
2 局部变量(函数级作用域)
在其他语言里面,是一个大括号是一个作用域。
在js语言里面,除了函数里面声明的变量都是全局变量,哪怕是循环里面创建的变量,或者if语句里面的变量,都是全局作用域。
在函数外部自然无法读取函数内的局部变量
这里有一个地方需要注意,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量
function getNum(){
var n = 100;
function getN(){
alert(n); // 100
}
return getN;
}
既然 getN 可以读取 getNum 中的局部变量,那么只要把 getN 作为返回值,我们不就可以在 getNum 外部读取它的内部变量了吗
闭包就是能够读取其他函数内部变量的函数 getn就是闭包
闭包特点
读取函数内部的变量
变量的值始终保持在内存中
使用闭包的注意点
由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题
闭包应用场景
为了储存临时变量
遇到的问题:我们调用count,拿到的返回值都是16,也就是循环结束的结果。我们没有办法得到中间值
function count() {
var arr = [];
for (var i = 1; i <= 3; i++) {
arr.push(function (){return i * i;});
}
return arr;
}
解决方法:
function count() {
var arr = [];
for (var i = 1; i <= 3; i++) {
arr.push( (function (n){return (function (){return n * n;})})(i) );
}
return arr;
}
就是把循环体内的函数外部再套一层函数。(function (n){return 原来的函数})(i)
闭包中的this关键字
在闭包里面使用this,this永远指向的是window
除非在闭包里面给this重新赋值。var aaa = this,return aaa.name
DOM实操
创建两个文件夹css和js。要和html分离开来。
导入js:<script src="test.js"></script>
为什么没有闭包的时候,总是越界访问呢
在 JavaScript 中,尤其是在使用 var
声明变量时,如果没有采用闭包,确实会导致越界访问的问题。这个现象主要是由于变量作用域和事件处理程序的异步特性引起的。
- 变量的作用域和提升
当你使用 var
声明一个变量时,变量具有函数作用域,而不是块作用域。这意味着在同一个函数内部,无论你在哪里声明了这个变量,它都是可见的。而在 for
循环中,i
被定义在函数的作用域下,所以所有的事件处理程序实际上都是引用同一个 i
变量。
- 循环和事件处理程序的执行时机
在 for
循环中,迭代会快速完成,随着每一次迭代,i
的值都会增加。
事件处理程序的执行是异步的,也就是说,事件处理程序不是在 for
循环运行时立即执行的,而是在循环完成后,当用户与界面进行交互(例如鼠标悬停时)时才会被调用。
为什么闭包可以解决这个问题
当你使用闭包时,它会创建一个新的作用域,每次循环迭代捕获当时的 i
值。这意味着每个事件处理程序会有自己独立的 i
值,确保它能正确地引用到对应的列表项。
for (var i = 0; i < lis.length; i++) {
(function(i) { // 创建闭包
lis[i].onmouseenter = function() {
console.log(i); // 这里输出的是当前的 i 值
};
})(i); // 立即执行函数,把当前的 i 值传入
}
在这里的流程是:
- 每次迭代时,你创建了一个新的函数,并且将当前的
i
作为参数传入。 - 对于每一次的
onmouseenter
,它都“记住”了当时的i
的值。 - 因此,当你悬停在某个
li
元素上时,输出的i
是在其各自上下文中存在的值,而不是循环结束后的i
值。
总结
越界访问的原因:循环结束后使用的同一个 i
变量,导致所有事件处理程序都引用最后的值(即 lis.length
),这是一个无效索引,造成越界。
闭包的优势:通过使用闭包,可以为每次迭代创建一个新作用域,捕获当时的 i
值,从而使得每个事件处理程序可以正确地使用它们各自的值,避免了越界访问。
有循环和事件处理的时候,使用闭包包一下就完了。
也可以使用this解决作用域问题,在对象里面重新设定一个属性。因为this只能在对象里面访问变量,变相的开辟新的作用域。也能实现闭包的功能。
Tab切换
鼠标移动到列表项刷新盒子内容。
类似css里面的hover
网页的效果:
网页源代码效果:
// 获取页面元素
var lis = document.querySelector(".box-up").querySelectorAll("li");
var divs = document.querySelector(".box-down").querySelectorAll("div");
// 给列表项添加事件
for (var i = 0; i < lis.length; i++) {
(function (i){
lis[i].onmouseenter = function() {
for (var j =0;j<lis.length; j++) {
lis[j].removeAttribute("class");
divs[j].removeAttribute("class");
}
lis[i].setAttribute("class", "tab-li");
divs[i].setAttribute("class", "tab-div");
}
}(i))
}
看似是for循环,每循环一次就执行一次内部的方法。实际上是事件处理部分,也就是内部的方法,会比for循环慢一拍。导致内部的方法,每次都只能拿无效的数据。所以,使用了闭包,强制给传进去的参数i提供一个作用域。来让这个方法生效。
使用for循环的目的:给每个列表项都添加鼠标事件。
<div class="box">
<div class="box-up">
<ul>
<li class="tab-li">百度</li>
<li>新浪</li>
<li>淘宝</li>
<li>京东</li>
<li>网易</li>
</ul>
</div>
<div class="box-down">
<div class="tab-div">百度内容</div>
<div>新浪内容</div>
<div>淘宝内容</div>
<div>京东内容</div>
<div>网易内容</div>
</div>
</div>
需要先给一个列表项,或者div容器设定class属性。只需要一个就行。我们使用js通过事件,把这个class属性再移到对应的列表项。就完成了鼠标滑动效果。
瀑布流
页面布局
瀑布流
瀑布流特点
1 多列布局
2 每列等宽
3 上拉加载
动态设置内容居中
使用场景:之前设计小米网页的时候,使用的是固定距离,或者响应式布局。都是通过css实现的。还是有点不尽人意。现在通过js来实现内容居中操作。
考虑的问题:内容居中,两边空余多少。使用js获取页面宽度,然后除以图片的宽度,得到一行最多几张图片。然后用最大值向下取整,减一。余出一张图片的宽度作为两边的留白。
效果:这样设计的网页,在多大的屏幕上都可以适配,小屏幕可能一行显示两三张图片,大一点的屏幕就五六页。超大屏幕看具体尺寸,一行就几十页了。更灵活。
步骤
获取所有需要操作的元素
获取父元素,为了设置宽度和左右的外边距(利用的还是css的特性)
获取子元素,方便计算单个图片容器的宽度(包括外边距)
获取屏幕的宽度
获取一个图片元素容器的宽度
动态计算页面横向最多可放置的图片数量
动态设置样式,让内容左右居中
// 设置页面左右居中效果
function waterFlow(){
// 获取页面盒子
var box = document.getElementsByClassName("box")[0];
// 获取图片容器
var boximg = document.getElementsByClassName("boximg");
// 获取屏幕宽度
var screenWidth = document.documentElement.clientWidth;
// 获取图片容器宽度
var chlidWidth = boximg[0].offsetWidth; // 要使用offsetWidth,内边距和边框也算进去
// 计算一行图片个数
var number1 = Math.floor(screenWidth/chlidWidth)-1;
// 设置页面盒子的左右宽度和居中效果
box.style.cssText = "width:" + number1*chlidWidth + "px;margin:0 auto;";
}
waterFlow();
在获取父级容器的时候,我使用的是class属性,但是这个页面里面貌似有两个box容器。必须选第一个,也就是我们创建的那个。不然就定位不到了。
动态设置图片位置
获取一列中最小高度,后续每张图片都是放在一列中最小高度的下面
1 获取第一行图片高度,放入到数组中
2 获取后续图片,放入到数组高度最小的对应图片下面
设置一个数组,数组元素的个数,就是列数。越过第一行图片的处理,然后从第二行图片开始,进行图片摆放
把第一行图片的高度进入数组。然后使用Math对象,求数组最小值。然后把新图片放在最小高度的列上,再更新最小高度列的高度,就是把新图片的高度加上去。
因为在上一步,我们已经得到了全部的图片元素了。只需要给他们设置对应绝对定位的相对位置,就ok了。
思路:假设是三列图片,那么数组里面就有三个元素,每一个元素代表这一列的高度。后面的图片,只需要根据第一行图片的左边距对齐,和这一列的高度对齐,然后放图片上去,再更新这一列的高度即可。
// 设置页面左右居中效果
function waterFlow() {
// 获取页面盒子
var box = document.getElementsByClassName("box")[0];
// 获取图片容器
var boximg = document.getElementsByClassName("boximg");
// 获取屏幕宽度
var screenWidth = document.documentElement.clientWidth;
// 获取图片容器宽度
var chlidWidth = boximg[0].offsetWidth; // 要使用offsetWidth,内边距和边框也算进去
// 计算一行图片个数
var number1 = Math.floor(screenWidth / chlidWidth) - 1;
// 设置页面盒子的左右宽度和居中效果
box.style.cssText = "width:" + number1 * chlidWidth + "px;margin:0 auto;";
setimg(boximg, number1)
}
waterFlow();
// 放置图片
function setimg(boximg, number1) {
// 生成一个存放每列高度的数组
var arr = [];
// 使用循环给每个图片设置位置
for (var i = 0; i < boximg.length; i++) {
if (i < number1) {
arr[i] = boximg[i].offsetHeight; // 第一行不用设置位置,但是要把高度记录
} else {
// 获取最矮列
var minheight = Math.min.apply(null, arr);
console.log(minheight);
// 获取最矮列是第几列
var minheightIndex = arr.indexOf(minheight);
console.log(minheightIndex);
// 设计绝对定位
boximg[i].style.position = "absolute";
boximg[i].style.top =minheight +'px';
boximg[i].style.left = boximg[minheightIndex].offsetLeft + 'px';
// 更新最矮列的高度
arr[minheightIndex] += boximg[i].offsetHeight;
}
}
}
现在还有一个问题,由于图片资源加载的比dom结构慢很多,页面都已经布局好了还没有图片,等图片加载出来的时候,由于页面结构已经固定,导致图片重叠。所以,我们要把dom的加载放在图片加载之后。
就可以解决图片重叠的问题了,这个是网络问题,不是代码结构问题。还好有办法可以解决。
window.onload = function(){
waterFlow();
}
// 设置页面左右居中效果
function waterFlow() {}
// 放置图片
function setimg(boximg, number1) {}
页面触底
就是判断用户是不是把网页滑到了最底部,意味着我们该增加新的图片了。
记得使用window.onscroll一直监听页面的高度,然后判断页面是否触底
逻辑:用户页面滚动的高度+浏览器窗口的高度=整个文件的高度 触发
解决方案1:滚动高度+窗口高度 > 文件高度 – 100px (用户快看完的时候,就加载资源,不要让用户看到底)
解决方案2:滚动高度+窗口高度 > 文件高度 – 最后一张图片的高度
需要获取的高度:
- 窗口高度:document.documentElement.clienHeight
- 滚动高度:document.documentElement.scrollTop
- 页面高度-最后一张图片高度,也就是最后一个图片的offsetTop的值。
window.onload = function(){
waterFlow();
// 监听页面滑动高度
window.onscroll = function(){
if (checkbottom()){
console.log('页面触底了')
}
}
}
// 设置页面左右居中效果
function waterFlow() {}
// 放置图片
function setimg(boximg, number1) {}
// 页面触底测试
function checkbottom(){
// 滚动高度
var scrollheight = document.documentElement.scrollTop;
// 窗口高度
var pagesheight = document.documentElement.clientHeight;
// 获取最后一个图片
var boximg = document.getElementsByClassName("boximg");
// 获取最后一个图片的offsetTop的值
var lastTop = boximg[boximg.length-1].offsetTop;
// 返回布尔值,告诉外面页面触底了
return lastTop < scrollheight + pagesheight ? true : false;
}
上拉加载
事先准备好新的图片数据。(学了前后端交互之后,就是后端提供数据了)
动态创建元素,使用append添加到现有的图片里面
重新加入瀑布流效果
window.onload = function(){
waterFlow();
// 提取准备好新图片资源
dataimg=[
{src:'img/1.jpeg'},
{src:'img/2.jpeg'},
{src:'img/3.jpeg'},
{src:'img/4.jpeg'},
{src:'img/5.jpeg'},
{src:'img/6.jpeg'},
{src:'img/7.jpeg'},
{src:'img/8.jpeg'},
{src:'img/9.jpeg'},
{src:'img/10.jpeg'},
{src:'img/11.jpg'},
]
// 监听页面滑动高度
window.onscroll = function(){
if (checkbottom()){
// console.log('页面触底了')
// 获取盛放所有图片的元素box
var box = document.getElementsByClassName("box")[0];
//循环创建新图片
for (var i = 0; i < dataimg.length; i++) {
// 创建图片容器
var boximg = document.createElement("div");
// 设置图片容器的属性
boximg.setAttribute('class','boximg');
// 创建图片节点
var img = document.createElement("img");
// 设置图片属性,添加图片的路径
img.setAttribute('src',dataimg[i].src);
// 把图片放进图片容器div
boximg.appendChild(img);
// 把图片容器放进页面
box.appendChild(boximg);
}
// 再次对页面排序
waterFlow();
}
}
}
轮播图
轮播图是用一套图片以一定时间间隔(如5秒)进行循环播放,一段时间内呈现给用户不同的内容展示方式
1 自动循环播放
2 指示器聚焦导航(就是轮播图下面的点)
动态切换
var imgs = document.querySelectorAll('.imgbox>img');
var btnleft = document.getElementsByClassName('btnleft')[0];
var btnright = document.getElementsByClassName('btnright')[0];
var navli = document.querySelectorAll('.navli li');
// 设置显示图片序号
var index = 3; // 默认是0,显示第一张
btnright.onclick = function () {
index++;
if (index > imgs.length - 1) {
index = 0;
}
findimg();
}
btnleft.onclick = function () {
index--;
if (index < 0) {
index = imgs.length - 1;
}
findimg();
}
function findimg() {
console.log(index)
// 清空图片的属性,以便显示想要显示的图片
for (var i = 0; i < imgs.length; i++) {
imgs[i].removeAttribute('class');
}
imgs[index].setAttribute('class', 'selectimg');
// 清空指示器的属性
for (var j = 0; j < imgs.length; j++) {
navli[j].removeAttribute('class');
}
navli[index].setAttribute('class', 'selectli');
}
.navbar{
width: 100%;
height: 320px;
margin: 200px 200px;
}
.imgbox{
width: 672px;
height: 320px;
position: relative;
}
.imgbox>img{
position: absolute;
width: 672px;
height: 320px;
display: none;
z-index: 6;
}
.imgbox>.selectimg{
display: block;
}
.btn img{
position: absolute;
width: 55px;
height: 55px;
top: 50%;
margin-top: -27.5px;
z-index: 8;
}
.btnleft{
left: 10px;
}
.btnright{
right: 10px;
}
.navli{
height: 20px;
position: absolute;
bottom: 10px;
right: 10px;
z-index: 10;
}
.navli li{
list-style: none;
width: 20px;
height: 20px;
background-color: rgba(0,0,0,0.5);
border-radius: 10px;
float: left;
margin-left: 10px;
}
.navli .selectli{
background-color: rgba(255,255,255,0.8);
}
<div class="navbar">
<div class="imgbox">
<img class="selectimg" src="img/1.jpg" alt="">
<img src="img/2.jpg" alt="">
<img src="img/3.jpg" alt="">
<img src="img/4.jpg" alt="">
<img src="img/5.jpg" alt="">
<div class="btn">
<img class="btnleft" src="img/left.png" alt="">
<img class="btnright" src="img/right.png" alt="">
</div>
<div class="navli">
<ul>
<li class="selectli"><a href="#"></a></li>
<li><a href="#"></a></li>
<li><a href="#"></a></li>
<li><a href="#"></a></li>
<li><a href="#"></a></li>
</ul>
</div>
</div>
</div>
放大镜
通过鼠标滑动,对页面某个部分的区域放大
需要需要准备两种图片,一个是网站的展示图片,一张是展示图片的放大版
<div class="sbox" >
<div class="sbox1"></div>
</div>
<div class="mbox"></div>
*{
margin:0;
padding:0;
}
body{
background: rgba(0,0,0,0.02);
}
.sbox{
position: absolute;
left: 100px;
top: 100px;
width: 450px;
height: 450px;
background-image: url('../img/xiao.jpg');
border: 1px solid black;
}
.mbox{
position: absolute;
left: 550px;
top: 100px;
width: 500px;
height: 500px;
background-image: url('../img/da.jpg');
border: 1px solid black;
}
.sbox1{
position: absolute;
left: 0;
top: 0;
width: 300px;
height: 300px;
background-color: rgba(188, 208, 22, 0.77);
}
现在是一个静态的放大镜效果,后面就是使用js让它动起来。
让内部的黄色预览块跟随鼠标移动,首先是使用class定位各个元素。
使用event.clientX event.clientY获取鼠标相对于页面的坐标
// 找元素
var sbox = document.getElementsByClassName("sbox")[0];
var sbox1 = document.getElementsByClassName("sbox1")[0];
var mbox = document.getElementsByClassName("mbox")[0];
// console.log(sbox); 打印一下节点,看看是不是成功匹配
// console.log(sbox1);
// console.log(mbox);
// 添加鼠标事件:鼠标移动到图片的时候,触发鼠标悬浮事件,鼠标离开图片,触发离开事件
sbox.onmouseenter = function(){
sbox1.style.display = "block";
mbox.style.display = "block";
}
sbox.onmouseleave = function(){
sbox1.style.display = "none";
mbox.style.display = "none";
}
// 添加鼠标事件,鼠标在图片上移动的时候获取鼠标的位置。
sbox.onmousemove = function(e){
var mx = e.clientX - sbox.offsetLeft - sbox1.offsetWidth/2;
var my = e.clientY - sbox.offsetTop - sbox1.offsetHeight/2;
console.log(mx,my);
console.log(sbox.offsetLeft+sbox.offsetWidth);
console.log(sbox.offsetTop);
// 处理边界问题
if (mx<0){
mx=0;
}
if (my<0){
my=0;
}
if (mx>sbox1.offsetWidth/2 ){
mx=sbox1.offsetWidth/2;
}
if (my>sbox1.offsetHeight/2 ){
my=sbox1.offsetHeight/2;
}
// 移动预览块
sbox1.style.left = mx+"px";
sbox1.style.top = my+"px";
// 因为大小图片的差异,大图片在移动的时候,要乘以图片之间的比值。大/小。图片的大小是已知的,就不使用属性拿到图片容器大小了
var rate = 800/450;
mbox.style.backgroundPositionX = - mx * rate+"px"; // 需要使用backgroundPositionX设置背景图片的位置
mbox.style.backgroundPositionY = - my * rate+"px";
}
这个是给mbox的绝对定位left和top使用了和sbox1一样的鼠标轨迹处理方式。
这个是在处理mbox 的top,我以mbox自己的offsetTop和鼠标移动距离作为新的top值。就导致top只有累加。同样的,刚开始处理mbox的left时,也有这个现象,一直往右跑。上传这些动画有点麻烦,就不放了。
也是因为使用的mbox.style.left才导致这个问题,要使用mbox.style.backgroundPositionX和mbox.style.backgroundPositionY
回到顶部
对于比较长的网页,在用户浏览位置时刻有一个回到顶部这个按钮,可以增加用户体验。
触发条件:页面滚动高度超出窗口高度
总算是用到了position:fixed 这个定位了。。。
因为这个功能很简单,就不独立js和css文件了。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>测试</title>
<style>
.box{
height: 500px; /*把页面撑开*/
}
.btn{
width: 60px;
height: 60px;
background-color: black;
color: white;
position: fixed;
bottom: 50px;
right: 50px;
padding: 10px;
display: none;
}
</style>
</head>
<body>
<div class="box">第1个</div>
<div class="box">第2个</div>
<div class="box">第3个</div>
<div class="box">第4个</div>
<div class="box">第5个</div>
<button class="btn">回到顶部</button>
<script>
// 获取按钮元素
var btn = document.getElementsByClassName("btn")[0];
// 获取浏览器窗口高度
var yemianheight = document.documentElement.clientHeight;
// 获取浏览位置高度
window.onscroll = function () {
var scrollheight = document.documentElement.scrollTop;
console.log(scrollheight);
if (yemianheight > scrollheight) {
btn.style.display = "none";
}else {
btn.style.display = "block";
}
};
btn.onclick = function () {
document.documentElement.scrollTop=0;
}
</script>
</body>
</html>
浏览器环境BOM
JavaScript 是浏览器的内置脚本语言,一旦网页内嵌了 JavaScript脚本,浏览器加载网页,就会去执行脚本,从而达到操作浏览器的目的,实现网页的各种动态效果
代码嵌入网页的方法
加载使用的协议
Swiper CDN
如果你不想将Swiper文件放在你的项目中,可以使用Swiper的CDN服务。
风险提示:CDN由于不稳定性,在生产环境中请谨慎使用。
未压缩版本 压缩之后的 map文件
script 元素工作原理
浏览器加载 JavaScript 脚本,主要通过 script 元素完成。正常的网页加载流程是这样的
1 浏览器一边下载 HTML 网页,一边开始解析。也就是说,不等到下载完,就开始解析
2 解析过程中,浏览器发现 script 元素,就暂停解析,把网页渲染的控制权转交给 JavaScript 引擎
3 如果 script 元素引用了外部脚本,就下载该脚本再执行,否则就直接执行代码
如果script元素的位置靠上,网站在解析html代码的时候,先执行script的js文件,但是scriipt下面的网页结构还没加载,就会造成js的失败
4 JavaScript 引擎执行完毕,控制权交还渲染引擎,恢复往下解析 HTML 网页
加载外部脚本时,浏览器会暂停页面渲染,等待脚本下载并执行完成后,再继续渲染。原因是 JavaScript 代码可以修改 DOM,所以必须把控制权让给它,否则会导致复杂的线程竞赛的问题。
如果外部脚本加载时间很长(一直无法完成下载),那么浏览器就会一直等待脚本下载完成,造成网页长时间失去响应,浏览器就会呈现“假死”状态,这被称为“阻塞效应”。
为了避免这种情况,较好的做法是将 script 标签都放在页面底部,而不是头部。这样即使遇到脚本失去响应,网页主体的渲染也已经完成了,用户至少可以看到内容,而不是面对一张空白的页面。
把script放在靠上的位置,还不出问题的解决方法
defer 属性
为了解决脚本文件下载阻塞网页渲染的问题,一个方法是对
<script src="./js/index.js" defer></script>
defer 属性的运行流程如下
1 浏览器开始解析 HTML 网页
2 解析过程中,发现带有 defer 属性的 script 元素
3 浏览器继续往下解析 HTML 网页,同时并行下载 script 元素加载的外部脚本
4 浏览器完成解析 HTML 网页,此时再回过头执行已经下载完成的脚本
有了 defer 属性,浏览器下载脚本文件的时候,不会阻塞页面渲染
解决“阻塞效应”的另一个方法是对 script 元素加入 async 属性
<script src="./js/index1.js" async></script>
async 属性的作用是,使用另一个进程下载脚本,下载时不会阻塞渲染
1 浏览器开始解析 HTML 网页
2 解析过程中,发现带有 async 属性的 script 标签
3 浏览器继续往下解析 HTML 网页,同时并行下载 script 标签中的外部脚本
4 脚本下载完成,浏览器暂停解析 HTML 网页,开始执行下载的脚本
5 脚本执行完毕,浏览器恢复解析 HTML 网页
async 属性可以保证脚本下载的同时,浏览器继续渲染。需要注意的是,一旦采用这个属性,就无法保证脚本的执行顺序。哪个脚本先下载结束,就先执行那个脚本
回流和重绘
渲染树转换为网页布局,称为“布局流”(flow);布局显示到页面的这个过程,称为“绘制”(paint)。它们都具有阻塞效应,并且会耗费很多时间和计算资源
页面生成以后,脚本操作和样式表操作,都会触发“回流”(reflow)和“重绘”(repaint)。
什么是回流和重绘
回流:当节点树中的一部分因为元素的规模尺寸,布局,隐藏等改变而需要重新构建
重绘:当节点数中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的
回流和重绘并不一定一起发生,回流必然导致重绘,重绘不一定需要回流。比如改变元素颜色,只会导致重绘,而不会导致回流;改变元素的布局,则会导致重绘和回流
什么时候会造成回流和重绘
回流
1 页面初始加载
2 改变字体,元素尺寸(width,height,border,position),改变元素内容
3 添加或删除元素,如果元素本身被display:none(隐藏不占位)会回流,visibility:hidden(隐藏且占位)则会发生重绘
4 定位为fixed的元素,滚动条拖动时会回流
5 调整窗口大小
重绘
text-decoration
backgtound-image
background-repeat
background-position
outline-color
outline
outline-style
border-radius
outline-width
box-shadow
background-size
优化技巧
1 读取 DOM 或者写入 DOM,尽量写在一起,不要混杂。不要读取一个 DOM 节点,然后立刻写入,接着再读取一个 DOM 节点(就是一个元素尽可能就获取一次,然后一直使用。操作相同的步骤放在一起,比如给某个容器改变大小。)
2 缓存 DOM 信息
3 不要一项一项地改变样式,而是使用 CSS class 一次性改变样式(就是事先写好所有的class样式,但是不给元素添加上,在js里面动态添加,就是一次性改变样式,不是一点点增加样式)
4 使用documentFragment操作 DOM
5 动画使用absolute定位或fixed定位,这样可以减少对其他元素的影响
6 只在必要时才显示隐藏元素
7 使用虚拟DOM(virtual DOM)库 (目前了解即可)
// 引发两次回流
box.style.top = '100px';
console.log(box.style.top);//=>'100px'
box.style.left = '100px';
// 引发一次回流 // 相同的操作放在一起
box.style.top = '100px';
box.style.left = '100px';
console.log(box.style.top);//=>'100px'
定时器
JavaScript 提供定时执行代码的功能,叫做定时器(timer),主要由 setTimeout() 和 setInterval() 这两个函数来完成。它们向任务队列添加定时任务
window对象提供的window.setTimeout(); 因为是浏览器提供的,可以不写window参数即可调用
setTimeout()延迟执行
setTimeout 函数用来指定某个函数或某段代码,在多少毫秒之后执行。它返回一个整数,表示定时器的编号,以后可以用来取消这个定时器
setTimeout 函数接受两个参数,第一个参数 func|code 是将要推迟执行的函数名或者一段代码,第二个参数 delay 是推迟执行的毫秒数
还有一个需要注意的地方,如果回调函数是对象的方法,那么setTimeout 使得方法内部的 this 关键字指向全局环境,而不是定义时所在的那个对象。this默认指向window,可以修改。
哪怕是在对象里面使用this,如果不是特意给this赋值,this一直指向window
setInterval()重复间隔执行
setInterval 函数的用法与 setTimeout 完全一致,区别仅仅在于 setInterval 指定某个任务每隔一段时间就执行一次,也就是无限次的定时执行
因为是重复执行,需要类似一个break来终止定时器,直接使用清除定时器就可以
定时器应用
当我们给列表项设置鼠标悬浮事件的时候,我们可能会查看不止一个列表项,但也不是每个列表项都查看。我们可能看完第一个内容想直接看第五个内容。但是我们的鼠标经过了234列表项,页面也进行了渲染,浪费了一定的资源。
所以,要解决的事件:当鼠标的悬浮时间足够短的时候,不触发鼠标悬浮事件
var timer = null; //定时器
var delay = 300; // 设置停留时间
for (var i = 0; i < lis.length; i++) {
(function (i) {
lis[i].onmouseenter = function () {
timer = setTimeout(function () { // 当鼠标悬浮的时候,启动定时器
for (var j = 0; j <lis.length; j++) {
lis[j].removeAttribute("class");
divs[j].removeAttribute("class")
}
lis[i].setAttribute("class","select")
divs[i].setAttribute("class", "div-select")
}, delay) // delay是定时器等待时间
}}(i));
lis[i].onmouseout = function () { // 当鼠标离开的时候,清除定时器
clearTimeout(timer);
}
}
防抖(debounce)
对于短时间内连续触发的事件(上面的滚动事件),防抖的含义就是让某个时间期限(如上面的1000毫秒)内,事件处理函数只执行一次
防抖严格算起来应该属于性能优化的知识,但实际上遇到的频率相当高,处理不当或者放任不管就容易引起浏览器卡死。
先说一个常见的功能,很多网站会提供这么一个按钮:用于返回顶部。这个按钮只会在滚动到距离顶部一定位置之后才出现,那么我们现在抽象出这个功能需求– 监听浏览器滚动事件,返回当前滚条与顶部的距离这个需求很简单,直接写
在运行的时候会发现存在一个问题:这个函数的默认执行频率,太高了!。 高到什么程度呢?以chrome为例,我们可以点击选中一个页面的滚动条,然后点击一次键盘的【向下方向键】,会发现函数执行了8-9次!
然而实际上我们并不需要如此高频的反馈,毕竟浏览器的性能是有限的,不应该浪费在这里,所以接着讨论如何优化这种场景。基于上述场景,首先提出第一种思路:在第一次触发事件时,不立即执行函数,而是给出一个期限值比如200ms,然后
1 如果在200ms内没有再次触发滚动事件,那么就执行函数
2 如果在200ms内再次触发滚动事件,那么当前的计时取消,重新开始计时
效果:如果短时间内大量触发同一事件,只会执行一次函数
理论上,当用户快速上下滚动页面,就会一直不触发函数。当用户停止滑动的时候,才会触发。
var timer = null;
var delay = 300;
function debounce(){
if(timer){ // 如果定时器存在就清除定时器,如果是第一次,就跳过
clearTimeout(timer)
}
timer = setTimeout(function(){
console.log(111);
},delay) // 设置定时器
}
debounce();
实现:既然前面都提到了计时,那实现的关键就在于setTimeout这个函数,由于还需要一个变量来保存计时,考虑维护全局纯净,可以借助闭包来实现
- 定时器
- 闭包
- 事件机制 事件只触发一次,在触发之后,事件内部的代码在反复执行
function debounce(fn,delay){ // 抽象成独立的函数,可以复用
var timer = null //借助闭包,局部作用域,不污染全局
return function() {
if(timer){
clearTimeout(timer)
}
timer = setTimeout(fn,delay) // 简化写法
}
}
function backTop(){
console.log(111);
}
//window.onscroll = function(){
// debounce(bcakTop,300);
//}
window.onscroll = debounce(bcakTop,300);
节流(throttle)
持续触发的时候,一定间隔执行一次。停止触发,就执行最后一次。
节流严格算起来应该属于性能优化的知识,但实际上遇到的频率相当高,处理不当或者放任不管就容易引起浏览器卡死
继续思考,使用上面的防抖方案来处理问题的结果是如果在限定时间段内,不断触发滚动事件(比如某个用户闲着无聊,按住滚动不断的拖来拖去),只要不停止触发,理论上就永远不会输出当前距离顶部的距离
但是如果产品同学的期望处理方案是:即使用户不断拖动滚动条,也能在某个时间间隔之后给出反馈呢?
其实很简单:我们可以设计一种类似控制阀门一样定期开放的函数,也就是让函数执行一次后,在某个时间段内暂时失效,过了这段时间后再重新激活(类似于技能冷却时间)
效果:如果短时间内大量触发同一事件,那么在函数执行一次之后,该函数在指定的时间期限内不再工作,直至过了这段时间才重新生效
实现
这里借助setTimeout来做一个简单的实现,加上一个状态位valid来表示当前函数是否处于工作状态
function throttle(fn, delay) {
var valid = true
return function () {
if (!valid) {
//休息时间 暂不接客
return
}
// 工作时间,执行函数并且在间隔期内把状态位设为无效
valid = false
setTimeout(function () {
fn()
valid = true;
}, delay)
}
}
function showTop() {
var scrollTop = document.documentElement.scrollTop;
console.log('滚动条位置:' + Math.floor(scrollTop));
}
window.onscroll = throttle(showTop, 500)
分析代码:首先,有三个方法,一个是抽象出来的节流定时器throttle,一个是我们需要使用定时器的高频触发方法showTop,还有一个是触发的事件。
关于节流定时器的原理:
- 首先使用的是事件机制,也就是事件触发和事件执行的机制。
- 我们使用window.onscroll页面滚动来作为事件触发的条件。事件触发之后,要执行的是throttle方法,这个方法的内部是我们想要实现节流的函数showTop。由于js的事件机制是方法只需要执行一次,后续再次使用该方法的时候,直接 使用方法的函数体。
- 直白来说,就是虽然界面滚动调用了throttle方法,后续随着界面滚动也在不断的调用throttle方法。但是js的事件机制,就是方法只会调用一次,后续再调用方法,实际是直接调用方法内部的代码。就像缓存一样,拿到了这个方法执行后的结果,下次再遇到这个方法,直接使用结果。
- 在throttle里面使用了闭包,保证了节流的标识符的唯一性,后续不论如何调用这个节流方法,标识一直是一个。
- 第一次进入闭包的时候,由于valid是true,不会被if判断拦住。可以顺利的进入if后面的代码,也就是定时器,在后面的代码修改了valid的值为false。此时的结果就是,valid为false,定时器工作。
- 第二次进入闭包的时候,valid拿着false,被if条件拦住,无法触发定时器。也就是用于无法在闭包外面修改valid的值了。
- 直到定时器开始工作,执行了内部的fn函数,也就是showTop方法。此时,valid才被调为true。也就是可以开启下次的定时器了。
window 对象_属性
浏览器里面, window 对象(注意, w 为小写)指当前的浏览器窗口。它也是当前页面的顶层对象,即最高一层的对象,所有其他对象都是它的下属
在js里面创建的所有变量,都是在window对象底下。比如var a=10;a实际是window.a。
由于window对象是浏览器默认窗口,自带超多子变量和方法。我们若是一直使用直接声明的方式,全都是全局变量,很容易造成变量冲突。为了不总是使用全局变量,我们只能使用js 的作用域。因为js 的作用域分为两个,全局作用域和函数级作用域。
我们只能构造一个自执行函数,并且把我们要声明的变量放进去。
在自执行函数内部声明的变量,不在window里面。
(function (){
var a=10;
})();
也可以使用闭包,声明变量,但是闭包相对比较麻烦。推荐使用自执行函数
window 对象_方法
Navigator 对象
window.navigator 属性指向一个包含浏览器和系统信息的 Navigator 对象。(就像我们直接声明的a变量一样,可以使用window.a调用,也可以直接使用a调用。)
脚本通过这个属性了解用户的环境信息,浏览器版本,用户使用的设备信息
navigator.userAgent 属性返回浏览器的 User Agent 字符串,表示用户设备信息,包含了浏览器的厂商、版本、操作系统等信息
就是爬虫表头里面的user-agent键值对
useragent主要是打印用户设备信息,可以大致准确地识别手机浏览器,方法就是测试是否包含mobi 字符串
var ua = navigator.userAgent.toLowerCase(); //由于浏览器版本或者使用环境的差异,会有大小写的不同,所以统一转成小写
if (ua.indexOf("mobi") > -1) {
// 手机浏览器
} else {
// 非手机浏览器
}
Navigator.plugins 属性返回一个类似数组的对象,成员是 Plugin 实例对象,表示浏览器安装的插件,比如 Flash、ActiveX 等。由于是一个数组,需要使用循环遍历。
Navigator.platform 属性返回用户的操作系统信息,比如 MacIntel 、 Win32 、Linux x86_64 等
Navigator.language 属性返回一个字符串,表示浏览器的首选语言。该属性只读
Navigator.languages 属性返回一个数组,表示用户可以接受的语言
Screen 对象
Screen 对象表示当前窗口所在的屏幕,提供显示设备的信息。window.screen 属性指向这个对象
History 对象
window.history 属性指向 History 对象,它表示当前窗口的浏览历史
History 对象保存了当前窗口访问过的所有页面网址
移动到以前访问过的页面时,页面通常是从浏览器缓存之中加载,而不是重新要求服务器发送新的网页。
Cookie 对象
有cookie之前,所有的数据只能存储在服务器,现在有了cookie浏览器也可以储存一定的数据量。
Cookie 是服务器保存在浏览器的一小段文本信息,每个 Cookie 的大小一般不能超过4KB。浏览器每次向服务器发出请求,就会自动附上这段信息
1 对话(session)管理:保存登录状态、购物车等需要记录的信息
2 个性化信息:保存用户的偏好,比如网页的字体大小、背景色等等
3 追踪用户:记录和分析用户行为
Cookie 不是一种理想的客户端存储机制。它的容量很小(4KB),缺乏数据操作接口,而且会影响性能。客户端存储建议使用 Web storage API 。只有那些每次请求都需要让服务器知道的信息,才应该放在 Cookie 里面
每个 Cookie 都有以下几方面的元数据/字段
Cookie 的名字/键
Cookie 的值(真正的数据写在这里面)
到期时间(超过这个时间会失效)
所属域名(默认为当前域名)
生效的路径(默认为当前网址)
不同浏览器对 Cookie 数量和大小的限制,是不一样的。一般来说,单个域名设置的 Cookie 不应超过30个,每个 Cookie 的大小不能超过 4KB。超过限制以后,Cookie 将被忽略,不会被设置
Cookie 属性
直接使用document.cookie就是获取cookie。如果使用等号赋值,就可以设置cookie新的键值对。比如,document.cookie = “name=zhao” ,在浏览器的cookie里面就有了一个name:zhao的键值对了
document.cookie = "name=iwen;Expires=Fri, 31 Dec 2021 16:00:00 GMT"
封装cookie操作
由于浏览器只会给有网络请求的网站储存cookie,所以,我们本地的html网站是无法创建自己的cookie
使用xampp搭建自己的服务器,在xampp的安装文件夹里面打开htdocs目录,并在xampp里面点击apache后面的start启动服务器。
htdocs就是服务器的根目录了,我们要把根目录当成开发目录
xampp是一个php服务器。集成式服务器。
我们需要在pycharm里面打开文件,打开这个根目录,自己配置一下python解释器。
然后新建一个文件夹,这里是cookie_test,推荐使用纯英文,防止出现bug。然后在这个文件夹里面创建一个html文件。
去浏览器输入:localhost/cookie_test/index.html 就可以以网络请求的方式,打开一个本机域名的网站了。localhost是本机域名。不可以在pycharm里面直接使用右键打开文件。
使用网络请求方式打开的文件,浏览器就有cookie缓存了。
使用document.cookie="name=it;"
就可以在浏览器看到cookie缓存了
必须以服务器的形式创建html。
cookie的键值对有三个重要属性:key,value,time(过期时间)
过期时间设置成负数,就是过期了,变相的删除cookiejs
var cookie = {
set: function (name, value, days) {
var d = new Date();
d.setDate(d.getDate() + days);
document.cookie = name + "=" + value + ";expires=" + d + ";";
},
get: function (name) {
var cookiesArr = document.cookie.split("; ")
for (var i = 0; i < cookiesArr.length; i++) {
var newArr = cookiesArr[i].split("=");
if (name === newArr[0]) {
return newArr[1]
}
}
},
unset: function (name) {
this.set(name, "", -1)
}
}
cookie.set('lily','abcd',5)
console.log(cookie.get('lily'));
cookie.unset('lily');
必须在服务器上运行,才可以让浏览器缓存cookie。退出这个服务器先点stop,然后点quit。一般会有一点提示内容,不用管。
apply、call和bind函数
无论是apply、call还是bind其实都是改变this的指向
之前的this指向:
- 事件中的this,指向当前DOM元素
- 对象中的this,指向当前调用者
- 闭包中的this,指向window
- 定时器中的this,指向window
- call、apply、bind改变this指向
var obj = {
name:"小张",
getName:function(){
console.log(this.name)
}
}
var newObj = {
name:'小王'
}
obj.getName.call(newObj); // 小王
obj.getName.apply(newObj); // 小王
obj.getName.bind(newObj)(); // 小王
我们调用的是obj对象里面的getname方法
如果是直接调用的方式,输出的是obj里面的name
现在使用了call和apply,bind方法。这些方法的特点是,第一个参数都是this指针,默认指向的是调用这些方法的对象,也就是obj。
我们可以人为传参,把newobj作为新的对象传进去。反正getname都是从对象里面取出name的值。obj有name,newobj也有name属性。所以可以成功调用。
其中bind是返回要执行的函数,需要再次作为方法去执行一次。就是在后面再加一个括号。
obj.getName.call(newObj,'北京');
obj.getName.apply(newObj,['上海']);
obj.getName.bind(newObj,'深圳')();
call接收参数的类型是基本类型,也就是参数可以直接写在this指针后面。
apply接收的参数类型必须是数组类型,参数必须写在数组里面,把这个数组放在this指针后面。
var arr = [10,20,30,40,50];
console.log(Math.max.apply(null, arr));
console.log(Math.max.call(null, 10,20,30,40,50));
console.log(Math.max(10, 20, 30, 40, 50));
这是我们之前使用Math的max方法,对数组求最大值。
首先是第一种,使用了apply方法,第一个参数是this指针,写null,默认就是window,第二个参数是数组参数
然后是第二种,使用了call方法,由于call方法只能写基本类型的参数。所以只能把数组拆开,作为参数传进去。但是Math.max方法本身就可以接收基本类型的数据作为参数。所以Math.max.call方法基本不用。
null代表指向window对象,这里是因为Array本身时候window对象的子元素
面向对象
在构造函数中的 this 指向当前实例对象
面向对象编程(Object Oriented Programming,缩写为 OOP)是目前主流的编程范式(编写代码的风格)
要了解面向对象编程,要先了解对象是什么?
对象其实是一个抽象概念的具体实例,例如:
1 人类:张三
2 动物:猫
抽象概念:人类、动物 特别常见称呼:类、模板
对象:张三、猫 特别常见称呼:实例对象
对象是一个抽象概念的具体实例,那么我们要生成一个对象,需要有一个模板,表示某一类实物的共同特征,然后对象根据这个模板生成。JavaScript 语言使用构造函数(constructor)作为对象的模板。所谓”构造函数”,就是专门用来生成实例对象的函数。
构造函数就是一个普通的函数,但具有自己的特征和用法
function People(){
this.name = "张三"
}
构造函数的首字母大写:例如 People 中的 P 是大写的
1 函数体内部使用了 this 关键字,代表了所要生成的对象实例
2 生成对象的时候,必须使用 new 命令
function People(){
this.name = "张三"
}
var p = new People(); // 实例对象
p.name // 张三
就是因为js里面,对象的创建方式和函数的创建方式是一样的。只能通过名称来区分对象和普通函数。
规定:第一个参数首字母大写是对象,第一个单词首字母小写,第二个单词首字母大写是函数
在js里面构造函数就是对象。function People(){}
学习面向对象
new 命令
new 命令的作用,就是执行构造函数,返回一个实例对象.
new就是类似var的声明对象关键字
function People(name) {
this.name = name;
}
var p1 = new People("张三");
var p2 = new People("李四");
p1.name // 张三
p2.name // 李四
在构造函数中的 this 指向当前实例对象
prototype原型
JavaScript 通过构造函数生成新对象,因此构造函数可以视为对象的模板。实例对象的属性和方法,可以定义在构造函数内部
function People(name,age) {
// 属性
this.name = name;
this.age = age;
// 方法;
this.sayHello = function(){
console.log("Hello");
}
}
var p1 = new People("张三",20);
var p2 = new People("李四",30);
console.log(p1.sayHello === p2.sayHello); //false
通过构造函数为实例对象定义属性和方法,虽然很方便,但是有一个缺点。同一个构造函数的多个实例之间,无法共享属性和方法,从而造成对系统资源的浪费
这个问题的解决方法,就是 JavaScript 的原型对象(prototype)
prototype 原型对象的所有属性和方法,都能被实例对象共享。
也就是说,如果属性和方法定义在原型上,那么所有实例对象就能共享,节省了内存。
怎么为对象指定原型呢,JavaScript 规定,每个函数都有一个prototype 属性,指向一个对象
function People() { }
console.log(People.prototype);
对于普通函数来说,该属性基本无用。但是,对于构造函数来说,生成实例的时候,该属性会自动成为实例对象的原型。
function People(name,age) {
// 属性
this.name = name;
this.age = age;
}
// 原型属性
People.prototype.color = "黄种人";
// 原型方法
People.prototype.sayHello = function(){
console.log("Hello");
}
var p1 = new People("张三",20);
var p2 = new People("李四",30);
console.log(p1.sayHello === p2.sayHello); //true
console.log(p1.color === p2.color); // true
原型对象的属性不是实例对象自身的属性。只要修改原型对象,变动就立刻会体现在所有实例对象上。
People.prototype.color = "白种人";
p1.color // 白种人
p2.color // 白种人
如果实例对象自身就有某个属性或方法,它就不会再去原型对象prototype寻找这个属性或方法
p1.color = "白种人" // 实例对象自带的属性,优先级高于prototype的属性
console.log(p1.color); // "白种人"
console.log(p2.color); // "黄种人"
总结一下,原型对象的作用,就是定义所有实例对象共享的属性和方法。
实例方法、静态方法、实例属性、静态属性
在JavaScript中有静态方法和实例方法,静态方法是函数自己定义的,而实例方法是通过原型来定义。它们的区别是静态方法是可以直接用 类名.方法名 去调用的,而实例方法是不可以的,它必须要用实例才可以去调用 实例.方法名 。
function Person(name){
this.name = name;
}
// 实例方法
Person.prototype.getName = function(){
console.log(this.name);
}
// 静态方法
Person.getAge = function(age){
console.log(age);
}
var p = new Person("it");
p.getName(); // 实例方法,用实例对象调用
Person.getAge(10); // 静态方法,用类名调用
JavaScript内置的Array方法则分为实例方法和静态方法
var arr = [10,20,30];
// 实例方法
arr.push(40);
// 静态方法
Array.isArray(arr);
实例属性和静态属性与方法类似,实例属性是通过实例对象调用的实例对象.属性 ,静态属性是通过类名调用的 类名.属性
function Person(name){
// 实例属性
this.name = name;
}
// 静态属性
Person.age = 20;
var p = new Person("itbaizhan");
console.log(p.name);
console.log(Person.age);
_proto_ (了解即可)
实例对象的 _proto_ 属性(前后各两个下划线),返回该对象的原型。该属性可读写
function People(){}
var p = new People();
console.log(p.__proto__ === People.prototype); // true
就是实例对象,可以通过这种方式找到实例对象的原型
根据语言标准, _proto_ 属性只有浏览器才需要部署,其他环境可以没有这个属性
在app里面不能使用proto,主要是验证实例对象的关系
constructor (了解)
prototype 对象有一个 constructor 属性,默认指向 prototype 对象所在的构造函数
function Person(){}
console.log(Person.prototype.constructor === Person); // true
就是使用类和类方法prototype,可以看到类的实例对象的原型
实例对象的原型可以使用constructor方法,看到类本身,也就是类自己
通俗的讲,就是为了将实例原型对象暴露出来, 比如你写了一个插件,别人得到的都是你实例化后的对象, 如果别人想扩展下对象,就可以用 constructor.prototype 去修改或扩展原型对象
var p;
(function(){
function Person(){ // 函数级作用域,外部不可访问
this.name = "张三";
this.age = 20;
}
Person.prototype.getName = function(){
console.log(this.name);
}
p = new Person(); // 给外面的p实例化一个对象
})();
console.log(Person); //Person无法在函数作用域之外访问。
console.log(p.constructor); // 使用实例化对象的constructor属性,可以得到类对象
// 通过实例对象扩展方法
p.constructor.prototype.getAge = function(){
console.log(this.age);
}
p.getName();
p.getAge();
原型 链
JavaScript 规定,所有对象都有自己的原型对象(prototype)。一方面,任何一个对象,都可以充当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。
因此,就会形成一个“原型链”(prototype chain):对象到原型,再到原型的原型…
如果一层层地上溯,所有对象的原型最终都可以上溯到Object.prototype ,即 Object 构造函数的 prototype 属性。也就是说,所有对象都继承了 Object.prototype 的属性。这就是所有对象都有 valueOf 和toString 方法的原因,因为这是从 Object.prototype 继承的。
那么, Object.prototype 对象有没有它的原型呢?回答是 Object.prototype 的原型是 null 。 null 没有任何属性和方法,也没有自己的原型。因此,原型链的尽头就是 null 。
读取对象的某个属性时,JavaScript 引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。如果直到最顶层的 Object.prototype 还是找不到,则返回undefined 。如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”,就近原则。
instanceof 检测对象
检测对象类型的:
- typeof 检测是不是基本类型,字符串,数值,布尔
- isArray 检测是不是数组
- instanceof 检测是不是类的实例对象
instanceof 运算符返回一个布尔值,表示对象是否为某个构造函数的实例
function Person(){}
var p = new Person();
console.log(p instanceof Person); // true
console.log(p instanceof object); // true
由于 instanceof 检查整个原型链,因此同一个实例对象,可能会对多个构造函数都返回 true 。
instanceof 运算符的一个用处,是判断值的类型
var arr = [10,20,30];
var obj = {};
console.log(arr instanceof Array);
console.log(obj instanceof Object);
数组是一个对象,可以使用instanceof检测。基本类型的变量不可以使用。
Object 对象的相关方法
var sxt = {
java: {
value: "全体系"
},
web: {
value: '大前端'
}
};
var itbaizhan = Object.create(sxt, {
python:{
value:"全方向"
}
});
console.log(itbaizhan.java.value);
console.log(itbaizhan.python);
对象的继承
面向对象编程很重要的一个方面,就是对象的继承。A 对象通过继承 B 对象,就能直接拥有 B 对象的所有属性和方法。这对于代码的复用是非常有用的。
大部分面向对象的编程语言,都是通过“类”(class)实现对象的继承。传统上,JavaScript 语言的继承不通过 class,而是通过“原型对象”(prototype)实现
传统语言,c语言等,都是通过class来继承。但是js里面是通过prototype继承。
在ES6里面也支持class继承了。ES6版本已经提供了 class 语法,我们会在讲解ES6的时候单独讲解使用方式
function Person(name, age) { // 父类
this.name = name;
this.age = age;
}
Person.prototype.getName = function () {
console.log(this.name);
}
Person.prototype.getAge = function(){
console.log(this.age);
}
function Student(name, age, major) { // 子类
//调用父类的构造函数
Person.call(this, name, age); // 使用call继承父类属性
this.major = major; // 使用this,添加子类独有的属性
}
for (var p in Person.prototype) { // 以循环的方式继承父类原型中的方法
Student.prototype[p] = Person.prototype[p];
}
Student.prototype.showMajor = function () { // 子类的方法
console.log(this.major);
}
Student.prototype.getName = function(){ // 重写父类的方法
console.log("Student:" + this.name);
}
var student = new Student("itbaizhan", "20","it");
student.showMajor();
student.getName();
student.getAge();
多重继承
JavaScript 不提供多重继承功能,即不允许一个对象同时继承多个对象。但是,可以通过变通方法,实现这个功能
就是一个子类继承多个父类。
- 在子类里面依次使用call方法,继承父类的属性。
- 使用for循环可以继承父类的方法。还是依次继承。
function Sxt() {
this.hello = 'hello';
}
function Itbaizhan() {
this.world = 'world';
}
function Sum() {
Sxt.call(this); // 继承两个父类的属性
Itbaizhan.call(this);
}
// 继承 Sxt
Sum.prototype = Object.create(Sxt.prototype); // 和使用for循环继承的效果是一样的
// 继承链上加入 Itbaizhan
Object.assign(Sum.prototype, Itbaizhan.prototype); // assign是合并两个原型
// 重新指定构造函数
Sum.prototype.constructor = Sum;
var s = new Sum();
console.log(s.hello); // 'hello'
console.log(s.world); // 'world'
严格模式
除了正常的运行模式,JavaScript 还有第二种运行模式:严格模式(strict mode)。顾名思义,这种模式采用更加严格的 JavaScript语法。
同样的代码,在正常模式和严格模式中,可能会有不一样的运行结果。一些在正常模式下可以运行的语句,在严格模式下将不能运行。
早期的 JavaScript 语言有很多设计不合理的地方,但是为了兼容以前的代码,又不能改变老的语法,只能不断添加新的语法,引导程序员使用新语法。
总之,严格模式体现了 JavaScript 更合理、更安全、更严谨的发展方向。
启用方法
进入严格模式的标志,是一行字符串 use strict
可以放在两个位置:
1 use strict 放在脚本文件的第一行,整个脚本都将以严格模式运行
2 use strict 放在函数体的第一行,则整个函数以严格模式运行
显式报错
严格模式下,使用 eval 或者 arguments 作为标识名,将会报错。
正常模式下,如果函数有多个重名的参数,可以用 arguments[i] 读取。严格模式下,这属于语法错误
正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,全局变量必须显式声明
禁止 this 关键字指向全局对象
禁止使用 with 语句(没讲)
arguments 不再追踪参数的变化
保留字
为了向将来 JavaScript 的新版本过渡,严格模式新增了一些保留字(implements、interface、let、package、private、protected、public、static、yield等)。使用这些词作为变量名将会报错
正则表达式
在前端,正则表达式主要应用在输入框的验证上。比如让用户输入密码,需要有特殊格式。还有手机号的验证。
正则表达式(regular expression)用来按照“给定模式”匹配文本。比如,正则表达式给出一个 Email 地址的模式,然后用它来确定一个字符串是否为 Email 地址。
一种是使用字面量,以斜杠表示开始和结束
var regex = /xyz/;
另一种是使用RegExp构造函数 (不推荐)
var regex = new RegExp('xyz');
正则的执行的方法主要有两种,一种是 test() 方法,另一种是 exec() 方法
如果正则模式是一个空字符串,则匹配所有字符串(无意义)
/it/.test('itbaizhan') // true
之后的正则表达式会特别复杂,而不是现在是字符匹配
字符串方法
var str = 'itbaizhan';
var reg1 = /it/;
console.log(str.search(reg1)); // 返回0
匹配规则
字面量字符和元字符
正则表达式和字符串的最大区别是,字符串只能匹配一个字符串,而正则表达式可以匹配多个字符串。
字面量字符
大部分字符在正则表达式中,就是字面的含义,比如/a/匹配a,/b/匹配b。如果在正则表达式之中,某个字符只表示它字面的含义(就像前面的a和b),那么它们就叫做“字面量字符”
元字符
除了字面量字符以外,还有一部分字符有特殊含义,不代表字面的意思。它们叫做“元字符”(metacharacters),主要有以下几个
1 点字符(.)
2 位置字符
3 选择符(|)
点字符(.)
点字符(.)匹配除回车(\r)、换行(\n) 、行分隔符(\u2028)和段分隔符(\u2029)以外的所有字符。类似通配符
位置字符
位置字符用来提示字符所处的位置,主要有两个字符
^ 表示字符串的开始位置
$ 表示字符串的结束位置
/^it/.test('itbaizhan');
/it$/.test('learn it');
/^it$/.test('it'); // 既有开头又有结尾,只能严格匹配it自己了。
竖线符号(|)在正则表达式中表示“或关系”(OR),即cat|dog表示匹配cat或dog
转义符
类似^在正则表达式里面是一种匹配规则,想要匹配字符串里面的^,必须加上反斜杠,取消转义
正则表达式中那些有特殊含义的元字符,如果要匹配它们本身,就需要在它们前面要加上反斜杠。比如要匹配+,就要写成 +
正则表达式中,需要反斜杠转义的,一共有12个字符: ^、.、[、$、(、)、|、*、+、?、{和\
有12个含有特殊函数的字符
\\+
字符类
字符类表示有一系列字符可供选择,只要匹配其中一个就可以了。所有可供选择的字符都放在方括号内,比如 [xyz] 表示x、y、z之中任选一个匹配
字符类分为两类
1 脱字符(^)
2 连字符(-)
脱字符(^)
如果方括号内的第一个字符是[^],则表示除了字符类之中的字符,其他字符都可以匹配。比如, [^xyz]
表示只要不都是由xyz,就可以匹配
/[^abc]/.test('hello world'); // true
/[^abc]/.test('abcapp'); // true
因为不是完全由abc构成的/[^abc]/.test('abcabc'); // false
因为这个字符串完全由abc三个字符构成/[^abc]/.test('aabbcc')
内容必须全部都为abc才是false,否则为true连字符(-)
某些情况下,对于连续序列的字符,连字符(-)用来提供简写形式,表示字符的连续范围。比如,[abc]可以写成[a-c],[0123456789]可以写成[0-9],同理[A-Z]表示26个大写字母
合法的字符类简写形式
[0-9.,] 所有数字还有点和逗号
[0-9a-fA-F] 所有数字,所有大小写字母a到f
[a-zA-Z0-9-] 所有数字,所有大小写字母,还有杠
[1-31] !!!!!是数字1到3!!!!!
预定义模式和重复类
预定义模式指的是某些常见模式的简写方式
\d 匹配0-9之间的任一数字,相当于
[0-9]
。
\D 匹配所有0-9以外的字符,相当于[^0-9]
。
\w 匹配任意的字母、数字和下划线,相当于[A-Za-z0-9_]
。
\W 除所有字母、数字和下划线以外的字符,相当于[^A-Za-z0-9_]
。
\s 匹配空格(包括换行符、制表符、空格符等),相等于[\t\r\n\v\f] 。
\S 匹配非空格的字符,相当于[^ \t\r\n\v\f]
。
\b 匹配词的边界。
\B 匹配非词边界,即在词的内部。
模式的精确匹配次数,使用大括号({})表示。{n}表示恰好重复n次,{n,}表示至少重复n次,{n,m}表示重复不少于n次,不多于m次。
/lo{2}k/.test('look'); // true 两个字符o,只能是两个
/lo{2,5}k/.test('looook'); // true 2到5个字符o
量词符和贪婪模式
量词符用来设定某个模式出现的次数
? 问号表示某个模式出现0次或1次,等同于{0, 1}。
星号表示某个模式出现0次或多次,等同于{0,}。
加号表示某个模式出现1次或多次,等同于{1,}。
贪婪模式默认情况下量词符 + 和 * 都是最大可能匹配,即匹配直到下一个字符不满足匹配规则为止。
模式是/a+/,表示匹配1个a或多个a,那么到底会匹配几个a呢?因为默认是贪婪模式,会一直匹配到字符a不出现为止,所以匹配结果是3个a
如果想将贪婪模式改为非贪婪模式,可以在量词符后面加一个问号。
模式结尾添加了一个问号/a+?/,这时就改为非贪婪模式,一旦条件满足,就不再往下匹配。
修饰符和组匹配
修饰符(modifier)表示模式的附加规则,放在正则模式的最尾部
修饰符有三个:
1 g修饰符
2 i修饰符
3 m修饰符
g 修饰符
默认情况下,第一次匹配成功后,正则对象就停止向下匹配了。g修饰符表示全局匹配(global),加上它以后,正则对象将匹配全部符合条件的结果,主要用于搜索和替换。
还有一个使用特点:多次匹配问题
var regex = /b/;
var str = 'abba';
regex.test(str); // true
regex.test(str); // true
regex.test(str); // true
没有g的时候,都是匹配第一个b
正则模式不含g修饰符,每次都是从字符串头部开始匹配。所以,连续做了三次匹配,都返回true
var regex = /b/g;
var str = 'abba';
regex.test(str); // true
regex.test(str); // true
regex.test(str); // false
正则模式含有g修饰符,每次都是从上一次匹配成功处,开始向后匹配。因为字符串abba只有两个b,所以前两次匹配结果为true,第三次匹配结果为false
有g的时候,在从上一次匹配结束的位置继续匹配
i 修饰符
默认情况下,正则对象区分字母的大小写,加上i修饰符以后表示忽略大小写(ignorecase)
m修饰符
m 修饰符表示多行模式(multiline),会修改 ^ 和 $ 的行为。默认情况下(即不加 m 修饰符时), ^ 和 $ 匹配字符串的开始处和结尾处,加上 m 修饰符以后, ^ 和 $ 还会匹配行首和行尾,即 ^ 和 $ 会识别换行符( \n )
修饰符组合
常见的就是i和g的组合"ABBA".replace(/b/ig,"t"); // AttA
既忽略大小写,又全局匹配
组匹配
正则表达式的括号表示分组匹配,括号中的模式可以用来匹配分组的内容
/fred+/.test(‘fredd’); // true
/(fred)+/.test(‘fredfred’); // true第一个模式没有括号,结果+只表示重复字母d
第二个模式有括号,结果+就表示匹配fred这个词。
正则应用场景
身份证正则验证
1 老版本:15位全数字组成
2 新版本:18位,前17位全数字,第十八位数字或者 x.因为有大小写x,所以加i
/(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|x)$)/i
手机号
第一位是1,第二个是3456789.然后是9位数
/^1(3|4|5|6|7|8|9)\d{9}$/
大部分的正则表达式是不需要我们自己编写的,我们需要做的是能读懂即可
由于正则表达式的应用次数也比较多。早就有人整理好了。我们能看懂,会用就行。
正则表达式在线测试 | 菜鸟工具
jQuery
简化js
现在是否还需要学习jQuery,毫无疑问到目前为止,我们仍然需要学习jQuery,原因如下:
1 各大网站还在应用(京东、百度)
2 一些广告页面、落地页还在应用
3 源码非常优秀,有助于理解JavaScript
4 其实对DOM操作并不能完全移除,只要涉及到DOM操作,jQuery是非常方便的
说人话:
jQuery简介
jQuery 是一个高效、精简并且功能丰富的 JavaScript 工具库。它提供的 API 易于使用且兼容众多浏览器,这让诸如 HTML 文档遍历和操作、事件处理、动画操作更加简单。
jQuery最大的优点就是简化DOM操作
下载jQuery jQuery
下载下来就是一个js文件。
有三个版本,map版本忽略。剩俩,一个是源码未压缩uncompressed。一个是压缩后的源码。压缩之后,用法不变,但是体积小。
一般是使用压缩过的jQuery源代码,这样用户访问的时候,加载更快。
jQuery分为三个大版本:1.x 2.x 3.x
1.x版本
兼容ie678,使用最为广泛的,官方只做BUG维护,功能不再新增。因此一般项目来说,使用1.x版本就可以了,最终版本:1.12.4(2016年5月20日)
2.x版本
不兼容ie678,很少有人使用,官方只做BUG维护,功能不再新增。如果不考虑兼容低版本的浏览器可以使用2.x,最终版本:2.2.4(2016年5月20日)
3.x版本
不兼容ie678,只支持最新的浏览器。除非特殊要求,一般不会使用3.x版本的,很多老的jQuery插件不支持这个版本。目前该版本是官方主要更新维护的版本。最新版本:3.6.0
jQuery重点讲解知识点
1 选择器
2 DOM操作
3 CSS操作
4 事件处理
5 遍历
6 动画
选择器之基础选择器
首先,这个html结构,我们要用js操作,分别是一个类选择器,一个元素选择器,一个id选择器.
在js里面定位这些元素:
<div class="box">类选择器</div>
<div class="box">类选择器</div>
<span>元素选择器</span>
<a id="it" href="#">ID选择器</a>
这是js的操作
// 类选择器
var div1 =document.getElementsByClassName("box")[0]
var div2 =document.getElementsByClassName("box")[1] // 需要重新起名,还有
// 元素选择器
var span =document.getElementsByTagName("span")[0]
// ID选择器
var a =document.getElementById("it");
使用jQuery优化:
// 类选择器
$(".box")
// 元素选择器
$("span")
// ID选择器
$("#it")
选择器之属性选择器
attribute: 属性
Selector: 选择器
name: 选中的属性
value: 属性值
<div>
<input type="radio" name="user" value="name" />
<span>name</span>
</div>
<div>
<input type="radio" name="user" value="age" />
<span>age</span>
</div>
<script>
// 想要选中第一个div里面的span标签,并修改文本
$('input[value="name"]').next().html("username");
</script>
首先使用$(‘input’) 定位到input标签,这时候还不太严谨。有两个input。
再使用属性选择器,$(‘input[value=“name”]’) 现在定位到了第一个input标签
然后选择与input同级的span标签,$(‘input[value=“name”]’).next()
修改span标签里面的内容$(‘input[value=“name”]’).next().html(“username”);
在jQuery里面的html() 就是js里面的 innerhtml()
选择指定属性值等于给定字符串或以该字符串为前缀(该字符串后跟一个连字符“-” )的元素
<a href="#" alt="sxt">尚学堂</a>
<a href="#" alt="sxt-web">尚学堂-前端</a>
<a href="#" alt="sxtitbaizhan">itbaizhan</a>
$('a[alt|="sxt"]')
在属性里面杠,代表单词连接的意思,在杠前面的,是前缀。
比如第二个a标签里面的alt的sxt就是整个属性值的前缀。
这个管道符的匹配规则是,要么完全匹配value值,要么是以value值开头的属性值。即第一个a标签是完美匹配,第二个a标签是前缀匹配
选择指定属性具有包含一个给定的子字符串的元素。(选择给定的属性是以包含某些值的元素)
是包含value值的属性,都可以匹配上。只要bao’h
选择指定属性用空格分隔的值中包含一个给定值的元素
<input name="sxt web" /> name属性有多个值,用空格隔开的
<script>$('input[name~="sxt"]');</script>
选择指定属性是以给定值结尾的元素。这个比较是区分大小写的
<input name="sxt-web" />
<input name="sxt web" />
<input name="bjsxtweb" />
<script>$('input[name$="web"]')</script>
不论是空格还是连字符以及完整单词,只要结尾可以匹配就行。
选择指定属性是以给定字符串开始的元素
$是匹配任意结尾,这个是匹配任意开头词汇
选择器之jQuery扩展
在匹配的集合中选择索引值为index的元素。index下标计算是从0开始的
<ul class="nav">
<li>List 1, item 1</li>
<li>List 1, item 2</li>
<li>List 1, item 3</li>
</ul>
$(“li:eq(2)”)
选择所引值为偶数的元素
这是基于0的索引,所以 :even 选择器是选择第一个元素,第三个元素,依此类推在匹配。
$(“tr:even”)
选择索引值为奇数元素
这是基于0的索引,所以 :odd 选择器是选择第二个元素,第四个元素,依此类推在匹配。
选择第一个匹配的元素$(“tr:first”)
选择最后一个匹配的元素$(“tr:last”)
选择匹配集合中所有大于给定index(索引值)的元素。$(“td:gt(4)”)
选择匹配集合中所有索引值小于给定index参数的元素
DOM操作
给元素添加class,值得注意的是这个方法不会替换一个样式类名。它只是简单的添加一个样式类名到元素上
$(“p”).addClass(‘myClass yourClass’);
在js里面是使用classname给标签添加属性。如果一个标签本来就有class属性,在js里面使用class重新赋值会覆盖标签本身的class属性。
但是jQuery的这个addclass方法,不会覆盖,只会追加新属性
也可以添加多个class属性
移除元素中每个匹配元素上一个,多个或全部样式,通过class名字移除元素
$(‘p’).removeClass(‘myClass yourClass’) 如果有指定的属性,就删除指定属性
移除全部class,,,$(‘p’).removeClass() 如果没有指定的属性,就清空全部属性
这是一个开关方法,如果class存在则删除,如果class不存在则添加
判断一个元素上是否具有某个class,存在返回true,不存在返回false
var flag = $('#mydiv').hasClass('foo')
if(flag){
$('#mydiv').html("div具有foo")
}
获取元素中的HTML内容$(‘div.demo-container’).html();
元素的html内容$(‘div.demo-container’).html(‘111’);
用于获取标签中的内容$(“input”).text();
也可以设置标签内容$(“input”).text(“username”)
html 和 text的区别 :
val()
用于获取标签中的内容$(“input”).val();
也可以设置标签内容$(“input”).val(“username”)
!!!var只能获取输入框的的内容,或者多行文本框
获取匹配的元素的属性的值 或 设置匹配元素的一个或多个属性
$("img").attr({
src: "images/1.jpeg",
title: "jQuery",
alt: "jQuery Logo"
});
为匹配的元素集合中的每个元素中移除一个属性img.removeAttr("title")
在每个匹配的元素外层包上一个任意类型的html元素$("p").wrap("<div class='container'></div>");
可以自动识别标签,还能在标签里面写属性
将匹配元素集合的父级元素删除,保留自身在原来的位置 $(“p”).unwrap();
在所有匹配元素外面包一层HTML结构 $("p").wrapAll("<div></div>");
是把所以的p标签同时放在一个div容器里面。wrap是给每个p标签分别套一个div容器
如果p标签很分散,根本不在一起,这个操作也会把p标签集合到一起。夹在p中间的标签会被剔除去
在匹配元素里的内容外包一层结构$("p").wrapInner("<b></b>");
是给p标签里面的内容,包裹一个标签
在每个匹配元素里面的末尾处插入参数内容 $("p").append("<i>Hello</i>");
放在p标签里面的,最后一个元素的后面
将参数内容插入到每个匹配元素的前面(元素内部)
放在一个元素里面的第一个元素前面
在匹配元素集合中的每个元素后面插入参数所指定的内容,作为其兄弟节点
在选中元素的后面插入。和被选中元素是同级的。
根据参数设定,在匹配元素的前面插入内容,作为其兄弟节点
从DOM中移除集合中匹配元素的所有子节点
把这个元素里面所有的子元素清空.这个元素不会删除
将匹配元素集合从DOM中删除。把选中的元素移除。$("p").remove();
用集合的匹配元素替换每个目标元素$("<b>World</b>").replaceAll("p");
用前面括号的内容,替换选中的元素。这个是先写替换内容,再找
用提供的内容替换集合中所有匹配的元素 $("p").replaceWith("<div>Hello</div>");
先是选中元素,然后用后面括号的内容替换。这个是先找再替换
CSS操作
获取和设置匹配元素的样式。
var color =$("div").css("background-color");
通过这种方式可以获取div元素的背景颜色的值
$("p").css("color", "red");
如果在属性后面写了属性值,就是给元素设置属性。
没写属性值,就是获取。写了属性值,就是设置。
也可以同时设置多个样式:
$("p").css( width:"200px", height:"200px", color:"red" );
获取当前元素的高度值宽度值或设置元素的高度值宽度值
当前元素的计算高度值和宽度值,包括padding,但是不包括border
获取当前元素的宽度值和高度值,包括padding,border和选择性的margin
参数里面可以写布尔值true,当参数是true的时候,会算上外边距margin。
获取元素的当前坐标,或设置每一个元素的坐标,坐标相对于文档
设置元素位置$("div").offset({ top: 100, left: 100 });
没有参数的时候,就是获取
获取元素的当前坐标,相对于 offset parent 的坐标
.position() 方法可以取得元素相对于父元素的偏移位置。
与 .offset()不同, .offset() 是获得该元素相对于 documet 的当前坐标
当把一个新元素放在同一个容器里面另一个元素附近时,用 .position() 更好用。
获取元素的当前水平和垂直滚动条的位置。设置每个匹配元素的水平和垂直滚动条位置
就是把一个容器设置的很小,但是他内部的元素设置的很大。所以外部的容器就有一个滚动条,来滚动查看内部的元素。
这个属性可以设置滚动条的初始位置。 $(".container").scrollLeft(300);
无参的时候,就是获取滚动条的位置。
事件
可以是事件绑定
$("#button").on("click", function(event){
console.log("事件处理器")
});
也可以是事件委托,第一个参数是事件触发,第二个参数是代理的子元素类型,这里是ul标签下面的li标签。第三个参数是触发的事件
$("#ul").on("click", "li", function(e){
console.log(e.target); // 打印点击的li标签
});
$("#btn").one("click", function() {console.log("这是一个只能触发一次的事件.");});
鼠标事件
和js的种类一样,但是用起来方便
$("#btn").click(function() { alert("点击事件");});
.hover() 将两个事件函数绑定到匹配元素上,分别当鼠标指针进入和离开元素时被执行
.mouseenter() 鼠标进入事件 从父元素进入子元素的时候,不会触发事件
.mouseleave() 鼠标离开事件
.mousemove() 鼠标滑动事件
.mouseover() 鼠标进入事件(支持事件冒泡)从父元素进入子元素的时候,会触发离开父元素事件和进入子元素事件
.mouseout() 鼠标离开事件(支持事件冒泡)
表单事件
键盘事件
keydown() 添加键盘按下事件
keypress() 添加键盘事件,按下/抬起都会触发
keyup() 添加键盘抬起事件
使用e.keycode 获取输入字符的ascll值
浏览器事件
事件对象
也是js里面的event,一模一样,有一点点的优化
想要使用js对象在jQuery里面,只能是$(js对象) 需要套一个壳,就能把js对象当成jQuery对象去使用了
遍历
$("li").map(function(index,element){ console.log(index,element); })
$("li").each(function(index,ele){ console.log(index,ele);})
就是js对象变成jQuery对象,使用$(js对象)
jQuery对象变成js对象,如果只需要获取一个,就使用$(‘li’).get()方法
如果获取多个对象,就使用map或者each
主要是js对象和jQuery对象不能混用。
层级关系(树遍历)
不是二叉树的遍历,而是元素之间的关系,同级关系,父子级关系等等
动画
$("div").animate({ width: "200px", opacity: 0.5 }, 1500);
jQuery实操
菜单功能
知识点:鼠标滑动事件,获取子元素,滑动展示效果
this在事件中指向事件的触发元素。
this是一个js对象,需要转化成jQuery对象使用。
$("div>ul>li>ul").addClass("ul1");
$('div>ul>li').hover(
function(){
$(this).children('ul').slideDown(500);
},
function(){
$(this).children('ul').slideUp(500);
})
.box{
width: 500px;
height: 50px;
margin: 100px auto;
}
*{
margin: 0;
padding: 0;
}
.box>ul>li{
float: left;
width: 99px;
height: 50px;
background-color: cornflowerblue;
border-right: 1px solid white;
list-style: none;
line-height: 50px;
text-align: center;
font-size: 20px;
font-weight: 700;
}
.box>ul>li>ul{
display: none;
}
.box>ul>li>ul>li{
list-style: none;
background-color: sandybrown;
border-bottom: 1px solid white;
}
.box>ul>li>ul>li:hover{
color: red;
}
淡入淡出轮播图
知识点:
鼠标点击左右按钮,鼠标滑动指示器,动画淡入淡出,定时器
找到指示器的同级元素,移除高亮效果。就是之前的for循环,移除class属性的操作。
细节:鼠标悬浮的时候,保持不变,不跳转图片。鼠标悬浮的时候清除定时器,鼠标移开的时候设置定时器。
// 展示图片下标
var idx = 0;
var len = $('.img-box li').length;
// 鼠标点击事件
var rightHandler = function () {
$('.select-img').removeClass('select-img').fadeOut(2000)
$('.select-li').removeClass('select-li')
idx++;
if (idx > len - 1) {
idx = 0;
}
$('.img-box').find('li').eq(idx).addClass('select-img').fadeIn(2000);
$('.box1').find('li').eq(idx).addClass('select-li')
}
$('.btnl').click(function () {
$('.select-img').removeClass('select-img').fadeOut(2000)
$('.select-li').removeClass('select-li')
idx--;
if (idx < 0) {
idx = len - 1;
}
$('.img-box').find('li').eq(idx).addClass('select-img').fadeIn(2000);
$('.box1').find('li').eq(idx).addClass('select-li')
})
$('.btnr').click(rightHandler)
// 自动切换
var timer = setInterval(rightHandler, 3000);
// 鼠标悬浮清空定时器,鼠标移开设置定时器
$('.img-box li').hover(
function () {
clearInterval(timer);
},
function () {
timer = setInterval(rightHandler, 3000);
})
.box{
width: 560px;
height: 300px;
margin: 100px auto;
border: 5px solid black;
position: relative;
}
body{
background-color: #e0d8d8;
}
*{
margin: 0;
padding: 0;
}
.img-box li{
list-style: none;
display: none;
position: absolute;
}
.img-box li:first-child{
display: block;
}
.btn-box img{
position: absolute;
top: 50%;
margin-top: -27.5px;
width: 55px;
height: 55px;
}
.btnl{
position: absolute;
left:0;
}
.btnr{
position: absolute;
right:0;
}
.box1{
height: 20px;
float: left;
position: absolute;
right: 20px;
bottom: 20px;
}
.box1 li{
width: 20px;
height: 20px;
border-radius: 50%;
background-color: orange;
list-style: none;
float: left;
margin-left: 10px;
}
.box1 .select-li{
background-color: #2092a2;
border: 3px solid #565b67;
box-sizing: border-box;
}
<div class="box">
<div class="img-box">
<ul>
<li class="select-img"><a href="#"><img src="images/0.jpg" alt=""></a></li>
<li><a href="#"><img src="images/1.jpg" alt=""></a></li>
<li><a href="#"><img src="images/2.jpg" alt=""></a></li>
<li><a href="#"><img src="images/3.jpg" alt=""></a></li>
<li><a href="#"><img src="images/4.jpg" alt=""></a></li>
</ul>
</div>
<div class="btn-box">
<img src="images/btnL.png" alt="" class="btnl">
<img src="images/btnR.png" alt="" class="btnr">
</div>
<div class="box1">
<ul>
<li class="select-li"></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
</div>
自动折叠面板
点击事件,动画效果
首先设计的是鼠标移到h3标签的时候展示折叠内容,鼠标移开的时候h3的展示内容收回
有点小bug,太灵活了,无法直接从第一个框到第三个框。要么加节流或者防抖。
还是小改一下,改成鼠标点击打开当前展示卡,同时关闭其他已经打开的展示卡。
// $('h3').hover(
// function(){
// $(this).siblings('p').slideDown();
// },
// function(){
// $(this).siblings('p').slideUp();
// })
$('h3').click(function () {
if ($(this).siblings('p').attr('style') && $(this).siblings('p').attr('style').includes('block')) {
$(this).siblings('p').slideUp();
$(this).children('span').html('>');
return
}
$(this).siblings('p').slideDown();
$(this).children('span').html('V');
$(this).parent('li').siblings('li').children('p').slideUp();
$(this).parent('li').siblings('li').children('h3').children('span').html('>');
})
.box h3 {
height: 30px;
line-height: 30px;
font-size: 20px;
background-color: skyblue;
border-bottom: 2px solid black;
}
.box h3 span {
float: right;
margin-right: 10px;
}
.box ul {
list-style: none;
}
.box ul li p {
display: none;
}
回到顶部
和前面的DOM实操是一样的
只是加了一个jQuery的动画执行时间。点击按钮的时候,是比较慢的速度划上去的。不是一瞬间上去。此外没有别的创新点了。
$(".btn").click(function(){
$('html').animate({
scrollTop: '0'
},500)
})
这是按钮的点击事件,就是加了个动画的执行时间。还有一些滚动条件的设置,没啥创新的。不放了。
到此,前端学习完成。一共学了四个知识板块,html,css,js,jQuery(苍蝇再小也是肉)
作者:兆。