前端基础之JavaScript

96 阅读23分钟

JS介绍

JavaScript 是一种轻量级的脚本语言。所谓“脚本语言”,指的是它不具备开发操作系统的能力,而是只用来编写控制其他大型应用程序的“脚本”。 JavaScript 是一种嵌入式(embedded)语言

JS基础

语句

JavaScript 程序的单位是行(line),也就是一行一行地执行。一般情况下,每一行就是一个语句 var num = 10;

标识符

标识符(identifier)指的是用来识别各种值的合法名称。最常见的标识符就是变量名 标识符是由:字母、美元符号($)、下划线(_)和数字组成,其中数字不能开头 提示:中文是合法的标识符,可以用作变量名(不推荐)

保留关键字

JavaScript有一些保留字,不能用作标识符: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。

变量

声明变量:关键字 变量名 = 值

如: var num = 10;

变量提升:JavaScript 引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。这造成的结果,就是所有的变量的声明语句,都会被提升到代码的头部,这就叫做变量提升(hoisting)

测试案例:

console.log(num);
var num = 10; 
打印的是: undefined
变量提升是声明,但并未赋值

引入文件

嵌入到HTML文件中

<body>
    <script>
        var age = 20
    </script>
</body>

引入本地独立JS文件

<body>
    <script type="text/javascript" src="./itbaizhan.js"></script>
</body>

引入网络来源文件

<body>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"> </script>
</body>

注释

单行注释: //
多行注释:
/*
多行
注释
*/
嵌入HTML文件中的注释
<!-- 注释 -->
注释快捷键: ctrl+/

常见输出方式

//弹窗展示
alert("要输出的内容");
document.write("要输出的内容"); 
​
// 在控制台输出内容
console.log("要输出的内容");

数据类型

ES5有5种:数值、字符串、布尔值、undefined、null、对象 ES6新增:Symbol、BigInt

原始类型(基础类型):数值、字符串、布尔值

var age = 20;
var name = "新手起步";
var learn = true;

合成类型(复合类型)对象:存放各种值得容器

var user = {
    name:"新手起步",
    age:20,
    learn:true
}

运算符

typeof

介绍:JS有三种方法判断值的类型,分别是:typeof、instanceof、Object.prototype.toString.call() 实例:

typeof 123     // ”number“
typeof '123'    // "string"
typeof false   // "boolean"
typeof {}        // "object"

null和undefined的区别: null与undefined都可以表示“没有”,含义非常相似。将一个变量赋值为undefined或null,老实说,语法效果几乎没区别。既然含义与用法都差不多,为什么要同时设置两个这样的值,这不是无端增加复杂度,令初学者困扰吗?这与历史原因有关

算术运算符

加减乘除、自增自减、余数

10 + 10; // 20
100 - 10; // 90
10 * 2; //20
10 / 5; 2
13 % 5 // 3
​
var x = 1;
var y = 1;
++x // 2
--y // 0

自增和自减运算符有一个需要注意的地方,就是放在变量之后,会先返回变量操作前的值,再进行自增/自减操作;放在变量之前,会先进行自增/自减操作,再返回变量操作后的值。

赋值运算符

= 赋值运算符 +=, x += y 等同于 x = x + y -=,x-= y 等同于 x = x - y *=,x *= y 等同于 x = x * y /=,x /= y 等同于 x = x / y %=,x %= y 等同于 x = x % y

比较运算符

<,小于
>,,大于 
<=,小于等于
>=,大于等于
==,相等,比较值
===, 严格相等,比较值和类型
!=,不等于
!==,严格不相等

逻辑运算符

取反 !, 且运算 &&,或运算 || 取反:非布尔值取反,取反运算符会将其转为布尔值。以下6个值取反后为true,其余值均为false undefined、null、false、0、NaN、空字符串 且运算:同时满足条件才行 或运算:满足一个条件即可

语句

条件语句

if else:if代码块后面,还可以跟一个else代码块,表示不满足条件时,所要执行的代码 if else 嵌套 switch:每个case代码块内部的break语句不能少,否则会接下去执行下一个case代码块,而不是跳出switch结构。

三元运算

(条件) ? 表达式1 : 表达式2

for循环

for (初始化表达式; 条件; 迭代因子) { 语句 }

1.初始化表达式(initialize):确定循环变量的初始值,只在循环开始时执行一次。

2.布尔表达式(test):每轮循环开始时,都要执行这个条件表达式,只有值为真,才继续进行循环。

3.迭代因子(increment):每轮循环的最后一个操作,通常用来递增循环变量

var x = 3;
for (var i = 0; i < x; i++) {
  console.log(i);
}

1~100的和

var sum=0;
for(var i=1;i<=100;i++){
    sum+=i;
}
console.log(sum);

输出1000以内的奇数

for(i = 0 ; i<1000; i ++){
    if( i % 2 ==1){
        console.log( i + " ");   
    }
}

九九乘法表

for(var i = 1;i <= 9;i++){ //9行
    document.write("<br>"); 
    for(var j = 1;j <= i;j++){
        sum = i * j;
        document.write(i ,"*",j ,"=",sum," ");
    }
}

while循环

While语句包括一个循环条件和一段代码块,只要条件为真,就不断循环执行代码块。 while (条件) { 语句; }

跳出语句

break,跳出代码块或循环 continue,立即终止本轮循环,开始下一轮循环

JS常用API

字符串

length

返回字符串的长度

var s = 'itbaizhan';
s.length // 9

charAt(index)

返回指定位置的字符,index从0开始计算,如果参数为负数,或大于等于字符串的长度,charAt返回空字符串

var s = new String('itbaizhan');
​
s.charAt(1) // "t"
s.charAt(s.length - 1) // "n"'itbaizhan'.charAt(-1) // ""
'itbaizhan'.charAt(9) // ""

concat()

字符串拼接,拼接两个字符串返回一个新字符串,不改变原字符串

var s1 = 'itbaizhan';
var s2 = 'sxt';
​
s1.concat(s2) // "itbaizhansxt"
s1 // "itbaizhan"
​
拼接多个字符串
'sxt'.concat('itbaizhan', 'bjsxt') 
// "sxtitbaizhanbjsxt"
​
如果参数不是字符串,`concat`方法会将其先转为字符串,然后再连接
var one = 1;
var two = 2;
var three = '3';
​
''.concat(one, two, three) // "123"
案例:
var one = 1;
var two = 2;
var three = '3';
​
console.log(''.concat(one, two, three) )
console.log(one + two + three )
​
// 123 33

substring()

截取字符串

substring方法用于从原字符串取出子字符串并返回,不改变原字符串。它的第一个参数表示子字符串的开始位置,第二个位置表示结束位置(返回结果不含该位置)
'itbaizhan'.substring(0, 2) // "it"
​
如果省略第二个参数,则表示子字符串一直到原字符串的结束
'itbaizhan'.substring(2) // "baizhan"
​
如果第一个参数大于第二个参数,`substring`方法会自动更换两个参数的位置
'itbaizhan'.substring(9, 2) // "baizhan"
// 等同于
'itbaizhan'.substring(2, 9) // "baizhan"
​
如果参数是负数,`substring`方法会自动将负数转为0
'itbaizhan'.substring(-3) // "itbaizhan"
'itbaizhan'.substring(2, -3) // "it"

substr()

等同于substring

indexOf()

获取字符串中第一次出现的位置

indexOf方法用于确定一个字符串在另一个字符串中第一次出现的位置,返回结果是匹配开始的位置。如果返回-1,就表示不匹配
​
'hello world'.indexOf('o') // 4
'itbaizhan'.indexOf('sxt') // -1
​
indexOf`方法还可以接受第二个参数,表示从该位置开始向后匹配
'hello world'.indexOf('o', 6) // 7

trim()

去除字符串两端的空格,返回新字符串;但去掉的不仅仅是空格,还包括制表符(\t、\v)、换行符(\n)和回车符(\r)
'  hello world  '.trim()
// "hello world"
​
'\r\nitbaizhan \t'.trim() // 'itbaizhan'                                                       
ES6扩展方法,trimEnd()和trimStart()方法
"   itbaizhan   ".trimEnd(); //     itbaizhan
"   itbaizhan   ".trimStart(); // itbaizhan   

split()

根据规则分割字符串,返回分割出来的字符串数组
'it|sxt|baizhan'.split('|') // ["it", "sxt", "baizhan"]
​
如何分割规则是空字符串,则数组是原字符串的每一个字符
'a|b|c'.split('') // ["a", "|",  "b","|", "c"]
​
如何参数省略,返回数组的唯一成员是原字符串
'it|sxt|bz'.split() // [it|sxt|bz]
​
split方法还可以接受第二个参数,限定返回数组的最大成员数
'it|sxt|bz'.split('|', 0) // []
'it|sxt|bz'.split('|', 1) // ["it"]
'it|sxt|bz'.split('|', 2) // ["it", "sxt"]
'it|sxt|bz'.split('|', 3) // ["it", "sxt", "bz"]
'it|sxt|bz'.split('|', 4) // ["it", "sxt", "bz"]

数组

数组是按次序排列的集合,不同于Java,js的数组可以存储不同的类型,index从0开始

创建数组

// 定义时赋值
var arr = ['sxt', 'baizhan', 'it'];
​
// 先定义后赋值
var arr = [];
arr[0] = 'sxt';
arr[1] = 'baizhan';
arr[2] = 'it';

如果数组的元素还是数组,就形成了多维数组

var a = [[1, 2], [3, 4]];
a[0][1] // 2
a[1][1] // 4

length属性:成员的数量

['sxt', 'baizhan', 'it'].length // 3

数组的遍历

for循环、while循环

var a = ['sxt', 'baizhan', 'it'];
​
// for循环
for(var i = 0; i < a.length; i++) {
  console.log(a[i]);
}
​
// while循环
var i = 0;
while (i < a.length) {
  console.log(a[i]);
  i++;
}

for...in循环

var a = ['sxt', 'baizhan', 'it'];
​
for (var i in a) {
  console.log(a[i]);
}

数组静态方法

Arrray.isArray()

返回布尔值,校验参数是否是数组,它弥补了typeof运算符的不足

var arr = ["尚学堂", 100, true];
console.log(typeof arr); // object
​
var arr = ['sxt', 'baizhan', 'it'];
Array.isArray(arr) // true

push()

push在数组末端添加一个或多个元素,并返回新数组的长度,改变原数组

var arr = [];
arr.push("尚学堂") // 1
arr.push('itbaizhan') // 2
arr.push(true, {}) // 4
arr // [尚学堂, 'itbaizhan', true, {}]

pop()

pop方法用于删除数组最后一个元素,并返回该元素,改变原数组

var arr = ['尚学堂', 'itbaizhan', 'WEB前端'];
​
arr.pop() // 'WEB前端'
arr // ['尚学堂', 'itbaizhan']

shift()

shift方法用于删除数组的第一个元素,并返回该元素,改变原数组

var arr = ['尚学堂', 'itbaizhan', 'WEB前端'];
​
arr.shift() // '尚学堂'
arr // ['itbaizhan', 'WEB前端']

shift方法遍历清空数组

var list = [1, 2, 3, 4, 5, 6];
var item;
​
while (item = list.shift()) {
  console.log(item);
}
​
list // []

unshift()

var arr = [];
arr.unshift('尚学堂', 'itbaizhan');
arr.unshift('WEB前端');
arr.shift();
arr.unshift('尚学堂');
console.log(arr);
​
 // ['尚学堂', '尚学堂', 'itbaizhan']

unshift方法用于在数组的第一个位置添加元素,并返回添加新元素后数组的长度

var arr = ['尚学堂', 'itbaizhan', 'WEB前端'];
​
arr.unshift('baizhan'); // 4
arr // ['baizhan', '尚学堂', 'itbaizhan', 'WEB前端']

unshift方法可以接受多个参数,这些参数都会添加到目标头部

var arr = [ '尚学堂', 'itbaizhan' ];
arr.unshift('WEB前端', 'baizhan') // 4
arr // [ 'WEB前端', 'baizhan', '尚学堂', 'itbaizhan' ]

join()

var arr = ["尚学堂","百战程序员"];
var myArr = arr.join("");
console.log(myArr);
console.log(myArr.split(""));
​
//尚学堂百战程序员   ['尚', '学', '堂', '百', '战', '程', '序', '员']

join方法指定参数作为分隔符来拼接数组元素,返回拼接后的字符串,如果不提供参数,默认用逗号分隔

var a = [1, 2, 3, 4];
​
a.join(' ') // '1 2 3 4'
a.join(' | ') // "1 | 2 | 3 | 4"
a.join() // "1,2,3,4"

如果数组成员是undefined或null或空位,会被当作空字符串

[undefined, null].join('#')
// '#'
​
['a',, 'b'].join('-')
// 'a--b'

数组的join配合字符串的split可以实现数组和字符串的互换

var arr = ["a","b","c"];
var myArr = arr.join("");
console.log(myArr);
console.log(myArr.split(""));

concat()

[1, 2, 3].concat(4, 5, 6,[7,8,9])
//  [1, 2, 3, 4, 5, 6, 7, 8, 9]

concat方法用于多个数组的合并,它将新数组的成员,添加到原数组成员的后部,然后返回一个新数组,原数组不变

['hello'].concat(['world'])
// ["hello", "world"]['hello'].concat(['world'], ['!'])
// ["hello", "world", "!"]

除了数组作为参数,concat也接受其他类型的值作为参数,添加到目标数组尾部

[1, 2, 3].concat(4, 5, 6)
// [1, 2, 3, 4, 5, 6]

reverse()

reverse方法用户颠倒排列数组元素,返回改变后的数组。注意,该方法将改变原数组

var a = ['a', 'b', 'c'];
​
a.reverse() // ["c", "b", "a"]
a // ["c", "b", "a"]

实现一个字符串反转排列:

var str = "hello";
str.split("").reverse().join("")

indexOf()

var arr = ["尚学堂", "it", "itbaizhan","it"];
arr.indexOf("it",2)
​
// 1

indexOf方法返回给定元素在数组种第一次出现的位置,如果没有则返回-1

var arr = ['a', 'b', 'c'];
​
arr.indexOf('b') // 1
arr.indexOf('y') // -1

indexOf方法还可以接受第二个参数,表示搜索的开始位置

['尚学堂', '百战程序员', 'itbaizhan'].indexOf('尚学堂', 1) // -1

函数

函数的声明

function 命令: function命令声明的代码区块,就是一个函数。function命令后面是函数名,函数名后面是一对圆括号,里面是传入函数的参数。函数体放在大括号里面。

function print(s) {
  console.log(s);
}

函数名的提升

JavaScript 引擎将函数名视同变量名,所以采用function命令声明函数时,整个函数会像变量声明一样,被提升到代码头部

add();
function add() {}

函数参数

函数运行的时候,有时需要提供外部数据,不同的外部数据会得到不同的结果,这种外部数据就叫参数

function square(x) {
    console.log(x * x);
}
​
square(2) // 4
square(3) // 9

函数返回值

JavaScript函数提供两个接口实现与外界的交互,其中参数作为入口,接收外界信息;返回值作为出口,把运算结果反馈给外界

function getName(name){
    return name;
}
​
var myName = getName("itbaizhan")
console.log(myName); // itbaizhan

PS:return 后面不能在添加任何代码,因为不会执行

对象操作

对象概述:类比Java实体类,key-value形式集合,使用方法类比Java实体类,不多赘述

Math对象

function ToInteger(x) {
    x = Number(x);
    return ___(___(x));
}
ToInteger(-10.4); // 向下取整:10// 填入 Math.ceil  Math.abs

Math是 JavaScript 的原生对象,提供各种数学功能。

Math.abs(),返回绝对值

Math.abs(1) // 1
Math.abs(-1) // 1

Math.max(),返回参数中最大的值。如果参数为空, Math.max返回-Infinity。

Math.min(),返回参数中最小的值。如果参数为空,Math.min返回Infinity,

Math.max(2, -1, 5) // 5
Math.min(2, -1, 5) // -1
Math.min() // Infinity
Math.max() // -Infinity

Math.floor(),返回小于参数值的最大整数

Math.ceil(),返回大于参数值的最小整数

Math.floor(3.2) // 3
Math.floor(-3.2) // -4
​
Math.ceil(3.2) // 4
Math.ceil(-3.2) // -3

Math.random(),返回伪随机数。任意范围的随机数生成函数

Math.random() // 0.28525367438365223function getRandomArbitrary(min, max) {
  return Math.random() * (max - min) + min;
}
​
getRandomArbitrary(5, 10)

Date对象

Date.now方法返回当前时间距离时间零点(1970年1月1日 00:00:00 UTC)的毫秒数,相当于 Unix 时间戳乘以1000

Date.now();   // 1635216733395

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)

var d = new Date('January 6, 2022');
d.getDate() // 6
d.getMonth() // 0
d.getYear() // 122
d.getFullYear() // 2022

编写函数获得本年度剩余天数

function leftDays() {
  var today = new Date();
  var endYear = new Date(today.getFullYear(), 11, 31, 23, 59, 59, 999);
  var msPerDay = 24 * 60 * 60 * 1000;
  return Math.round((endYear.getTime() - today.getTime()) / msPerDay);
}

DOM对象

DOM 是 JavaScript 操作网页的接口,全称为“文档对象模型”(Document Object Model)。它的作用是将网页转为一个 JavaScript 对象,从而可以用脚本进行各种操作(比如对元素增删内容)

浏览器会根据 DOM 模型,将结构化文档HTML解析成一系列的节点,再由这些节点组成一个树状结构(DOM Tree)。所有的节点和最终的树状结构,都有规范的对外接口

DOM 只是一个接口规范,可以用各种语言实现。所以严格地说,DOM 不是 JavaScript 语法的一部分,但是 DOM 操作是 JavaScript 最常见的任务,离开了 DOM,JavaScript 就无法控制网页。另一方面,JavaScript 也是最常用于 DOM 操作的语言

节点类型

Document:整个文档树的顶层节点
DocumentType:doctype标签
Element:网页的各种HTML标签
Attribute:网页元素的属性(比如class="right")
Text:标签之间或标签包含的文本
Comment:注释
DocumentFragment:文档的片段

节点树

一个文档的所有节点,按照所在的层级,可以抽象成一种树状结构。这种树状结构就是 DOM 树。它有一个顶层节点,下一层都是顶层节点的子节点,然后子节点又有自己的子节点,就这样层层衍生出一个金字塔结构,倒过来就像一棵树

除了根节点,其他节点有三种层级关系
父节点关系(parentNode):直接的那个上级节点
子节点关系(childNodes):直接的下级节点
同级节点关系(sibling):拥有同一个父节点的节点

Node.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

document获取元素

getElementsByTagName document.getElementsByTagName方法搜索 HTML 标签名,返回符合条件的元素。它的返回值是一个类似数组对象(HTMLCollection实例),可以实时反映 HTML 文档的变化。如果没有任何匹配的元素,就返回一个空集

var paras = document.getElementsByTagName('p');

如果传入*,就可以返回文档中所有 HTML 元素

var allElements = document.getElementsByTagName('*');

getElementsByClassName document.getElementsByClassName方法返回一个类似数组的对象(HTMLCollection实例),包括了所有class名字符合指定条件的元素,元素的变化实时反映在返回结果中

var elements = document.getElementsByClassName(names);

getElementsByName document.getElementsByName方法用于选择拥有name属性的 HTML 元素(比如

、、等),返回一个类似数组的的对象(NodeList实例),因为name属性相同的元素可能不止一个

// 表单为 <form name="itbaizhan"></form>
var forms = document.getElementsByName('itbaizhan');

getElementById document.getElementById方法返回匹配指定id属性的元素节点。如果没有发现匹配的节点,则返回null

var elem = document.getElementById('para1');

注意,该方法的参数是大小写敏感的。

比如,如果某个节点的id属性是main,那么document.getElementById('Main')将返回null

querySelector document.querySelector方法接受一个 CSS 选择器作为参数,返回匹配该选择器的元素节点。如果有多个节点满足匹配条件,则返回第一个匹配的节点。如果没有发现匹配的节点,则返回null

var el1 = document.querySelector('.myclass');

querySelectorAll document.querySelectorAll方法与querySelector用法类似,区别是返回一个NodeList对象,包含所有匹配给定选择器的节点

var elementList = document.querySelectorAll('.myclass');
document创建元素

creatElement document.createElement方法用来生成元素节点,并返回该节点

var newDiv = document.createElement('div');

createTextNode document.createTextNode方法用来生成文本节点(Text实例),并返回该节点。它的参数是文本节点的内容

var newDiv = document.createElement('div');
var newContent = document.createTextNode('Hello');
newDiv.appendChild(newContent);

createAttribute document.createAttribute方法生成一个新的属性节点(Attrbute实例),并返回它

var attribute = document.createAttribute(name);
​
var root = document.getElementById('root');
var it = document.createAttribute('itbaizhan');
it.value = 'it';
root.setAttributeNode(it);

Element对象

属性

Element.id Element.id属性返回指定元素的id属性,该属性可读写

// HTML 代码为 <p id="foo">
var p = document.querySelector('p');
p.id // "foo"

Element.className className属性用来读写当前元素节点的class属性。它的值是一个字符串,每个class之间用空格分割

// HTML 代码 <div class="one two three" id="myDiv"></div>
var div = document.getElementById('myDiv');
div.className

Element.classList classList对象方法: add(className):增加一个class remove(className):移除一个class contains(className):检查当前元素是否包含某个class toggle():将某个class移入或移出当前元素

var div = document.getElementById('myDiv');
​
div.classList.add('myCssClass');
div.classList.add('foo', 'bar');
div.classList.remove('myCssClass');
div.classList.toggle('myCssClass'); // 如果 myCssClass 不存在就加入,否则移除
div.classList.contains('myCssClass'); // 返回 true 或者 false

Element.innerHTML Element.innerHTML属性返回一个字符串,等同于该元素包含的所有 HTML 代码。该属性可读写,常用来设置某个节点的内容。它能改写所有元素节点的内容,包括和元素

el.innerHTML = '';

Element.innerText innerText和innerHTML类似,不同的是innerText无法识别元素,会直接渲染成字符串

获取元素位置

clientHeight 获取元素高度包括padding部分,但是不包括border、margin

// 视口高度
document.documentElement.clientHeight// 网页总高度
document.body.clientHeight

clientWidth 获取元素宽度包括padding部分,但是不包括border、margin

scrollHeight 元素总高度,它包括padding,但是不包括border、margin包括溢出的不可见内容

// 返回网页的总高度
document.documentElement.scrollHeight
document.body.scrollHeight

scrollWidth 元素总宽度,它包括padding,但是不包括border、margin包括溢出的不可见内容

scrollLeft 元素的水平滚动条向右滚动的像素数量

document.documentElement.scrollLeft
document.documentElement.scrollTop

scrollTop 元素的垂直滚动条向下滚动的像素数量

offsetHeight 元素的 CSS 垂直高度(单位像素),包括元素本身的高度、padding 和 border

offsetWidth 元素的 CSS 水平宽度(单位像素),包括元素本身的高度、padding 和 border

offsetLeft 到定位父级左边界的间距

offsetTop 到定位父级上边界的间距

CSS操作

setAttribute设置HTML元素stlye属性

div.setAttribute(
  'style',
  'background-color:red;' + 'border:1px solid black;'
);

元素节点的style属性

var divStyle = document.querySelector('div').style;divStyle.backgroundColor = 'red';
divStyle.border = '1px solid black';
divStyle.width = '100px';
divStyle.height = '100px';
divStyle.fontSize = '10em';

cssText属性

var divStyle = document.querySelector('div').style;
​
divStyle.cssText = 'background-color: red;'
  + 'border: 1px solid black;'
  + 'height: 100px;'
  + 'width: 100px;';

事件

事件处理顺序

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> 

DOM0级事件处理

<body>
    <div id="div">
        <button id="btn1">按钮</button>
    </div>
    <script>
        var btn1=document.getElementById("btn1");
        btn1.onclick=function(){alert("Hello DOM0级事件处理程序1");}//被覆盖掉
        btn1.onclick=function(){alert("Hello DOM0级事件处理程序2");}
    </script>
</body>

DOM2级事件处理

<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>

鼠标事件

click:按下鼠标时触发
dbclick:在同一个元素上双击鼠标时触发
mousedown:按下鼠标键时触发
mouseup:释放按下的鼠标键时触发
mousemove:当鼠标在节点内部移动时触发。当鼠标持续移动时,该事件会连触发
mouseenter:鼠标进入一个节点时触发,进入子节点不会触发这个事件
mouseleave:鼠标离开一个节点时触发,离开父节点不会触发这个事件
mouseover:鼠标进入一个节点时触发,进入子节点会再一次触发这个事件
mouseout:鼠标离开一个节点时触发,离开父节点也会触发这个事件
wheel:滚动鼠标的滚轮时触发
温馨提示:这些方法在使用的时候,除了DOM2级事件,都需要添加前缀on

Event事件

事件发生以后,会产生一个事件对象,作为参数传给监听函数。

Event对象属性

Event.target:返回事件当前所在的节点

// HTML代码为
// <p id="para">Hello</p>
function setColor(e) {
  console.log(this === e.target);
  e.target.style.color = 'red';
}
​
para.addEventListener('click', setColor);

Event.type:返回一个字符串,表示事件类型。事件的类型是在生成事件的时候。该属性只读

Event对象方法

Event.preventDefault()

Event.preventDefault方法取消浏览器对当前事件的默认行为。比如点击链接后,浏览器默认会跳转到另一个页面,使用这个方法以后,就不会跳转了
​
btn.onclick = function(e){
    e.preventDefault(); // 阻止默认事件
    console.log("点击A标签");
}

Event.stopPropagation()

stopPropagation方法阻止事件在 DOM 中继续传播,防止再触发定义在别的节点上的监听函数,但是不包括在当前节点上其他的事件监听函数
​
btn.onclick = function(e){
    e.stopPropagation(); // 阻止事件冒泡
    console.log("btn");
}

键盘事件

keydown

按下键盘时触发

keypress

按下有值的键时触发,即按下 Ctrl、Alt、Shift、Meta 这样无值的键,这个事件不会触发。对于有值的键,按下时先触发keydown事件,再触发这个事件

username.onkeypress = function(e){
    console.log("keypress事件");
}
keyup

松开键盘时触发该事件

keycode

唯一标识

var username = document.getElementById("username");
username.onkeydown = function(e){
    if(e.keyCode === 13){
        console.log("回车");
    }
}

表单事件

input事件
input事件当<input>、<select>、<textarea>的值发生变化时触发。对于复选框(<input type=checkbox>)或单选框(<input type=radio>),用户改变选项时,也会触发这个事件
input事件的一个特点,就是会连续触发,比如用户每按下一次按键,就会触发一次input事件。
​
var username = document.getElementById("username");
username.oninput = function(e){
    console.log(e.target.value);
}
select事件
select事件当在<input>、<textarea>里面选中文本时触发
Change事件
Change事件当<input>、<select>、<textarea>的值发生变化时触发。它与input事件的最大不同,就是不会连续触发,只有当全部修改完成时才会触发
reset事件
reset、submit这两个事件发生在表单对象<form>上,而不是发生在表单的成员上。
表单重置(所有表单成员变回默认值)
submit事件向服务器提交数据
submit事件
<form id="myForm" onsubmit="submitHandle">
    <button onclick="resetHandle">重置数据</button>
    <button>提交</button>
</form>
​
var myForm = document.getElementById("myForm")
function resetHandle(){
    myForm.reset();
}
function submitHandle(){
    console.log("提交");
}

事件代理

父元素系统处理子元素事件

由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。这种方法叫做事件的代理(delegation)

var ul = document.querySelector('ul');
​
ul.addEventListener('click', function (event) {
  if (event.target.tagName.toLowerCase() === 'li') {
    // some code
  }
});

定时器

setTimeout()

setTimeout函数用来指定某个函数或某段代码,在多少毫秒之后执行。它返回一个整数,表示定时器的编号,以后可以用来取消这个定时器 温馨提示:还有一个需要注意的地方,如果回调函数是对象的方法,那么setTimeout使得方法内部的this关键字指向全局环境,而不是定义时所在的那个对象

setTimeout函数接受两个参数,第一个参数func|code是将要推迟执行的函数名或者一段代码,第二个参数delay是推迟执行的毫秒数
var timerId = setTimeout(func|code, delay);
​
setTimeout(function(){
    console.log("定时器")
},1000)
​
var name = "sxt";
var user = {
    name: "itbaizhan",
    getName: function () {
        var that = this;
        setTimeout(function(){
            console.log(that.name);
        },1000)
    }
};
user.getName();

setInterval()

setInterval函数的用法与setTimeout完全一致,区别仅仅在于setInterval指定某个任务每隔一段时间就执行一次,也就是无限次的定时执行

var timer = setInterval(function() {
  console.log(2);
}, 1000)

setInterval实现网页动画

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #someDiv{
            width: 100px;
            height: 100px;
            background: red;
        }
    </style>
</head>
<body>
    <div id="someDiv"></div>
    <script>
        var div = document.getElementById('someDiv');
        var opacity = 1;
        var fader = setInterval(function() {
          opacity -= 0.05;
          if (opacity > 0) {
            div.style.opacity = opacity;
          } else {
            clearInterval(fader);
          }
        }, 30);
​
    </script>
</body>
</html>

取消定时器

var id = setInterval(f, 1000);
clearInterval(id);

防抖

防抖严格算起来应该属于性能优化的知识,但实际上遇到的频率相当高,处理不当或者放任不管就容易引起浏览器卡死

防抖定义: 对于短时间内连续触发的事件(上面的滚动事件),防抖的含义就是让某个时间期限(如上面的1000毫秒)内,事件处理函数只执行一次

滚动条案例:

function showTop  () {
    var scrollTop = document.documentElement.scrollTop;
    console.log('滚动条位置:' + scrollTop);
}
window.onscroll  = showTop
    
    在运行的时候会发现存在一个问题:这个函数的默认执行频率,太!高!了!。 高到什么程度呢?以chrome为例,我们可以点击选中一个页面的滚动条,然后点击一次键盘的【向下方向键】,会发现函数执行了8-9次!
    然而实际上我们并不需要如此高频的反馈,毕竟浏览器的性能是有限的,不应该浪费在这里,所以接着讨论如何优化这种场景。
    基于上述场景,首先提出第一种思路:在第一次触发事件时,不立即执行函数,而是给出一个期限值比如200ms,然后:1.如果在200ms内没有再次触发滚动事件,那么就执行函数 2.如果在200ms内再次触发滚动事件,那么当前的计时取消,重新开始计时
效果:如果短时间内大量触发同一事件,只会执行一次函数

实现:既然前面都提到了计时,那实现的关键就在于setTimeout这个函数,由于还需要一个变量来保存计时,考虑维护全局纯净,可以借助闭包来实现

function debounce(fn,delay){
    let timer = null //借助闭包
    return function() {
        if(timer){
            clearTimeout(timer) 
        }
        timer = setTimeout(fn,delay) // 简化写法
    }
}
// 然后是旧代码
function showTop  () {
    var scrollTop = document.documentElement.scrollTop;
    console.log('滚动条位置:' + scrollTop);
}
window.onscroll = debounce(showTop,300)