JavaScript
JS中两大类数据类型
基本数据类型
- Number、String、Boolean、Undefined、Null
复杂数据类型
- Object、Array、Function、RegExp、Date、Map、Set、Symbol
NaN(不是一个数,但它是一个数字类型的值)
运算中不能得到数字,结果往往都是NaN
字符串的常用方法
undefined(既是值又是类型)
Null
null表示“空”,它是“空对象”
当我们需要将对象销毁、数组销毁、删除事件监听时,通常将他们设置为null
box.onclick = null;
用 typeof 检测 null 结果是 object
Number(undefined); // NaN
Number(null); // 0
使用prompt() 函数弹出输入框
4/0 结果 Infinity
undefined == null //true
undefined === null //false
隐式类型转换
如果参与数学运算的某操作数不是数字型,那么JavaScript 会自动将此操作数转换为数字型
break 非常重要
如果不书写break,则后面的所有case都将被视为匹配,直到遇见break
三元运算符
条件表达式 ? 表达式1 : 表达式2
toFixed(1)
//保留一位小数
break:立即终止循环
continue:跳过循环中的一个迭代
随机数函数
Math.random()
//得到0到1之间的小数
parseInt(Math.random() * (b-a+1)) + a;
//得到[a,b]区间的整数
// 数组的定义方法1
var arr1 = ['A','B','C','D'];
// 数组的定义方法2
var arr2 = new Array(33,44,55,66);
// 数组的定义方法3
var arr3 = new Array(4);
Array.isArray()方法可以用来检测数组
数组的常用方法
arr.push(66);
arr.pop(66);
//pop()方法不仅会删除数组末项,而且会返回被删除的项
//shift()方法不仅会删除数组首项,而且会返回被删除的项
splice()方法 (替换数组中的指定项)
arr.splice(3,2,66,77,88,99);
splice() 方法可以用于在指定位置插入新项
splice() 方法可以用于删除指定项
splice() 方法会以数组形式返回被删除的项
var arr = ['A','B','C','D','E','F','G'];
arr.splice(2,4); //没有设置替换的新项,仅删除4项
console.log(arr); //['A','B','G']
slice() 方法
slice() 方法用于得到子数组,类似于字符串的slice() 方法
slice(a,b)截取的子数组从下标为a的项开始,到下标为b(但不包括下标为b的项)结束
join() 和 split() 方法
- 数组的 join() 方法可以使数组转为字符串
字符串的 split() 方法可以使字符串转为数组
join() 的参数表示以什么字符作为连接符,留空默认以逗号分隔
split() 的参数表示以什么字符拆分字符串,一般不能留空
concat() 方法 (合并连结多个数组)
reverse() 方法 (将一个数组中的全部项顺序置反)
indexOf() 和 includes() 方法
-
indexOf() 方法的功能是搜索数组中的元素,并返回它所在的位置,如果元素不存在,则返回 -1
-
includes() 方法是判断一个数组是否包含一个指定的值,返回布尔值
冒泡排序
var arr = [6,2,9,3,8,1];
for (var i = 1; i < arr.length; i++) {
if (var j = arr.length - 1; j >= i; j--) {
// 判断项的大小
if (arr[j] < arr[j-1]) {
var temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
}
}
}
console.log(arr);
引用类型
- 基本类型:number、boolean、string、undefined
- 引用类型:array、object、function、regexp
相等判断时的区别:
-
基本类型进行相等判断时,会比较值是否相等
-
引用类型进行相等判断时,会比较址是否相等,也就是说它会比较是否为内存中的同一个东西
深克隆和浅克隆
使用 arr1 = arr2 的语法不会克隆数组
浅克隆:只克隆数组的第一层,如果是多维数组,或者数组中的项是其它引用类型值,则不克隆其他层
浅克隆就是遍历原数组中的每一项,把遍历到的项推入到结果数组中
深克隆:克隆数组的所有层,要使用递归技术,在后面课程介绍
函数声明提升
fun();
function fun() {
alert("...");
}
函数表达式不能提升
如果函数是用函数表达式的写法定义的,则没有提升特性
fun();
var fun = function() {
alert("...");
}
//把函数赋值给了一个变量,变量只提升定义不提升值
//此时变量值为 undefined,undefined不能加圆括号运行
函数优先提升
先弹出B 再弹出A
函数的参数
undefined 进行任何运算,结果都是NaN
arguments:实参,是一个类数组对象
类数组对象:所有属性均为从0开始的自然数序列,并且有length属性,和数组类似,可用方括号书写下表访问对象的某个属性值,但是不能调用数组的方法,不管传入多少实参,永远能够计算它们的和
函数的返回值
function sum(a,b) {
return a+b;
}
var result = sum(3,5);
函数的返回值可以被变量接收
函数有 return 语句就必须有变量接收
调用一个有返回值的函数,可被当做一个普通值,从而可以出现在任何可以书写值的地方
调用函数时,一旦遇见 return 即退出函数
- 结合 if 语句的时候,往往不需要写 else 分支
sort() 方法
数组排序可以使用sort() 方法,sort()方法的参数又是一个函数
这个函数中的a、b分别表示数组中靠前和靠后的项,如果需要将它们交换位置,则返回任意正数;否则就返回负数
var arr = [33,22,55,11];
arr.sort(function (a, b) {
if (a > b) {
return 1;
} else {
return -1;
}
});
递归
函数的内部自己调用自己
递归的要素
-
边界条件:递归出口
-
递归模式:大问题是如何分解为小问题的,也称为递归体
function factorial(n) {
if (n == 1) return 1;
return n * factorial(n - 1)
}
var result = factorial(6);
alert(result);
实现深克隆
复习浅克隆
使用 var arr2 = arr1 这样的语句不能实现数组的克隆
浅克隆:准备一个空的结果数组,然后使用 for 循环比那里原数组,将遍历到的项都推入结果数组
浅克隆只克隆数组的一层,如果数组是多维数组,则克隆的项会“藕断丝连”
//实现浅克隆
//准备原数组
var arr1 = [33,44,11,22];
//准备一个结果数组
var result = [];
//遍历原数组,将遍历到的项都推入到结果数组中
for(var i = 0; i < arr1.length; i++) {
result.push(arr1[i]);
}
//输出结果数组
console.log(result);
//测试是否实现了克隆,本质上是内存中的不同数组了
console.log(arr1 == result);
//测试这样的克隆是浅克隆,“藕断丝连”
arr1[4].push(99);
console.log(result);
实现深克隆
使用递归思想,整体思路和浅克隆类似
但如果遍历到项是基本类型值,则直接推入结果数组,如果遍历到的项是数组,则重复执行浅克隆操作
var arr1 = [33,44,11,22,[77,88]];
// 函数,这个函数会被递归
function deepClone(arr) {
// 结果数组,“每一层”都有一个结果数组
var result = [];
for (var i = 0; i < arr.length; i++) {
//如果遍历到的项是数组
if (Array.isArray(arr[i])) {
//递归
result.push(deepClone(arr[i]));
}else{
//遍历到的是基本类型值,就直接推入到解雇数组中,相当于递归的出口
result.push(arr[i]);
}
}
// 返回结果数组
return result;
}
// 测试一下
var arr2 = deepClone(arr1);
console.log(arr2);
// 是否藕断丝连
console.log(arr1[4] == arr2);
全局变量和局部变量
JS的变量只在其定义时所在的 function 内部有意义
function func() {
var a = 10;
}
fun();
console.log(a); //报错
遮蔽效应
若全局和局部都存在变量a,则在局部内,局部会遮蔽全局的变量a
形参也是局部变量
作用域链
在函数嵌套中,变量会从内到外逐层寻找它的定义
不加 var 将定义全局变量
什么是闭包
函数本身 和 该函数声明时所处的环境状态的组合
函数能够“记忆住”其定义时所处的环境,即使函数不在其定义的环境中被调用,也能访问定义时所处的环境的变量
function fun() {
var name = '慕课网';
function innerFun() {
alert(name);
}
return innerFun;
}
var inn = fun();
inn();
JS中,每次创建函数时都会创建闭包,但是闭包特性往往需要将函数“换一个地方”执行,才能被观察出来
闭包非常使用,它允许我们将数据与操作该数据的函数关联起来
闭包的功能 - 记忆性 and 模拟私有变量
- 记忆性
当闭包产生时,函数所处环境的状态会始终保持在内存中,不会在外层函数调用后被自动清除
//创建体温检测函数,可以检查体温n是否正常,函数会返回布尔值
//但是不同的小区有不同的体温检测标准,A小区合格线是37.1,B小区合格线是37.3
function createCeckTemp(standardTemp) {
function checkTemp(n) {
alert('你的体温正常');
} else {
alert('你的体温偏高');
}
return checkTemp;
}
//创建一个checkTemp函数,它以37.1度为标准线
var checkTemp_A = createCheckTemp(37.1);
//创建一个checkTemp函数,它以37.3度为标准线
var checkTemp_B = createCheckTemp(37.3);
checkTemp_A(37.2);
checkTemp_A(37.2);
checkTemp_B(37.2);
checkTemp_B(37.0);
闭包用途2 - 模拟私有变量(JS中只能用闭包来模拟)
案例:定义一个变量a,要求能保证这个a只能被进行指定操作(+1、*2)
//封装一个函数,这个函数的功能就是私有化变量
function fun() {
//定义一个局部变量a
var a = 0;
return {
getA: function() {
return a;
},
add: function() {
a++;
},
pow: function() {
a *= 2;
}
};
}
var obj = fun();
// 如果想在fun函数外面使用a,唯一的方法就是调用getA()方法
console.log(obj.getA());
// 想让变量a进行加1操作
obj.add();
obj.add();
obj.add();
console.log(obj.getA());
obj.pow();
console.log(obj.getA());
不能滥用闭包,可能会造成内存泄漏(程序中已动态分配的内存由于某种原因未释放或无法释放)
闭包面试题
function addCount() {
var count = 0;
return function () {
count = count + 1;
console.log(count);
};
}
var fun1 = addCount();
var fun2 = addCount();
fun1();
fun2();
fun2();
fun1();
立即执行函数 IIFE
立即调用函数表达式,一旦被定义,就立即被调用
IIFE 的作用1 - 为变量赋值
var age = 12;
var sex = '男';
var title = (function () {
if (age < 18) {
return '小朋友'
} else {
if (sex == '男') {
return '先生';
} else {
return '女士';
}
}
})();
alert(title);
IIFE 的作用2 - 将全局变量变为局部变量
IIFE可以在一些场合(比如for循环中)将全局变量变为局部变量,语法显得紧凑
var arr = [];
for (var i = 0; i < 5; i++) {
(function (i) { // IIFE
arr.push(function () {
alert(i);
});
})(i);
}
arr[2](); // 弹出2
总结
难点:递归、递归算法题,作用域,IIFE
DOM 是 JS 操控 HTML 和 CSS 的桥梁
文档对象模型,是JS操作HTML文档的接口
DOM 最大的特点就是将文档表示为节点树
<!-- 现在想用JS在“牛奶”后面插入一个p标签对,内容是可乐
<div id="box">
<p>牛奶</p>
<p>咖啡</p>
<p>果汁</p>
-->
<div id="box">
<p>牛奶</p>
<p>咖啡</p>
<p>果汁</p>
</div>
<script>
document.getElementById("box");
</script>
访问元素节点
对节点进行操作,第一步就是要得到它
访问元素节点主要依靠 document对象,几乎所有DOM的功能都封装在了document对象中,document对象也表示整个HTML文档,它是DOM节点树的根
document在JS内部是一个对象
访问元素节点的常用方法
方法 | 功能 |
---|---|
document.getElementById() | 通过id得到元素 |
document.getElementByTagName() | 通过标签名得到元素 |
document.getElementByClassName() | 通过类名得到元素数组 |
document.querySelector() | 通过选择器得到元素 |
document.querySelectorAll() | 通过选择器得到元素数组 |
document.getElementById()
<div id="box">我是一个盒子</div>
<p id="para">我是一个段落</p>
<script>
var box = document.getElementById('box');
var para = document.getElementById('para');
</script>
window.onload = function() {} 事件 (延迟运行)
使页面加载完毕后,再执行指定的代码
getElementsByTagName()
通过标签名得到节点数组(方便批量操作)
任何一个节点元素也可以调用getElementsByTagName()方法,从而得到其内部的某种类的元素节点
<div id="box1">
<p>我是段落</p>
<p>我是段落</p>
<p>我是段落</p>
</div>
<div id="box2">
<p>我是段落</p>
<p>我是段落</p>
<p>我是段落</p>
</div>
<script>
//先得到box1
var box1 = document.getElementById('box1');
//得到box1中的p标签的数组
var ps_inbox1 = box1.getElementsByTagName('p');
// ps_inbox1 是数组
</script>
getElementsByClassName()
通过类名得到节点数组
querySelector()
通过选择器得到元素
<div id='box1'>
<p>我是段落</p>
<p class='spec'>我是段落</p>
<p>我是段落</p>
</div>
<script>
var the_p = document.querySelector('#box1 .spec');
console.log(the_p);
</script>
- querySelector()方法只能得到页面上一个元素
querySlectorAll()
通过选择器得到元素数组
<ul id="list1">
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
</ul>
<ul id="list2">
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
</ul>
<script>
var lis_inlist1 = document.querySelectorAll('#list1 li');
console.log(lis_inlist1);
</script>
节点的关系
父节点:parentNode
子节点:childNodes
前/后兄弟节点:previousSibling/nextSibling
文本节点也属于节点
排除文本节点的干扰
关系 | 考虑所有节点 | 只考虑元素节点 |
---|---|---|
子节点 | childNodes | children |
父节点 | parentNode | 同 |
第一个子节点 | firstChild | firstElementChild |
最后一个子节点 | lastChild | lastElementChild |
前一个兄弟节点 | previousSibling | previousElementSibling |
后一个兄弟节点 | nextSibling | nextElementSibling |
常见的节点关系函数
<div id='box'>
<p>我是段落</p>
<p>我是段落</p>
<p>我是段落</p>
<p id='fpara'>我是段落A</p>
<p id='para'>我是段落B</p>
<p>我是段落C</p>
</div>
<script>
var box = document.getElementById('box');
var para = document.getElementById('para');
// 封装一个函数,这个函数可以返回元素的所有子元素节点,类似children的功能
function getChildren(node) {
// 结果数组
var children = [];
// 遍历node这个节点的所有子节点,判断每一个子节点的nodeType属性是不是1,如果是1就推入结果数组
for (var i = 0; i < node.childNodes.length; i++) {
if(node.childNodes[i].nodeType == 1) {
children.push();
}
}
return children;
}
console.log(getChildren(box));
console.log(getChildren(para));
// 封装一个函数,这个函数可以返回元素的前一个元素兄弟节点,类似previousElementSibling功能
function getElementPrevSibling(node) {
var o = node;
// 使用while语句
while (o.previousSibling != null) {
if (o.previousSibling.nodeType == 1) {
//结束循环,找到了
return o.previousSibling;
}
//让o称为它的前一个节点
o = o.previousSibling;
}
return null;
}
console.log(getElementPrevSibling(para));
console.log(getElementPrevSibling(fpara));
//封装第三个函数,这个函数可以返回元素的所有元素兄弟节点
function getAllElementSibling(node) {
//前面的元素兄弟节点
var prevs = [];
//后面的元素兄弟节点
var nexts = [];
var o = node;
while(o.previousSibling != null) {
if (o.previousSibling.nodeType == 1) {
prevs.push(o.previousSibling);
}
o = o.previousSibling;
return prevs;
}
}
</script>
改变元素节点中的内容
- innerHTML(能以HTML语法设置节点中的内容)
- innerText(只能以纯文本的形式设置节点中的内容)
改变元素节点的CSS样式
<script>
oBox.style.backgroundColor = 'red';
// 驼峰形式
oBox.style.backgroundImage = 'url(images/1.jpg)';
oBox.style.fontSize = '32px';
</script>
改变元素节点的HTML属性
- 符合W3C标准的属性,如src、href等,只需要直接打点进行更改即可
oImg.src = 'images/2.jpg';
- 不符合W3C标准的属性,要使用 setAttribute() 和 getAttribute() 来设置、读取
oBox.setAttribute('data-n', 10);
//此时盒子就被设置上了 data-n = '10' 这个属性
var n = oBox.getAttribute('data-n');
alert(n);
节点的创建
document.createElement() 方法用于创建一个指定tagname的HTML元素
var oDiv = document.createElement('div');
创建出来的是孤儿节点,并没有被挂载到DOM树上,我们无法看见它
必须继续使用appendChild() 或 insertBefore() 方法将孤儿节点插入到DOM树上
父节点.appendChild(孤儿节点);
//insertBefore()方法可以将孤儿节点挂载到它的内部,称为它的"标杆子节点"之前的节点
父节点.insertBefore(孤儿节点,标杆节点);
案例:动态创建出一个20行12列的表格
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
</head>
<body>
<table id="mytable"></table>
</body>
<script type="text/javascript">
var mytable = document.getElementById('mytable');
for (var i = 0; i < 20; i++) {
//创建了新的tr标签
var tr = document.createElement('tr');
for (var j = 0; j < 12; j++) {
//创建了新的td标签
var td = document.createElement('td');
// 让tr追加td标签
tr.appendChild(tr);
}
// 让mytable追加tr标签
mytable.appendChild(tr);
</script>
</html>
移动节点
如果将已经挂载到DOM树上的节点称为 appendChild() 或者 insertBefore() 的参数,这个节点将会被移动
新父节点.appendChild(已经有父亲的节点);
新父节点.insertBefore(已经有父亲的节点,标杆子节点);
- 这意味着一个节点不能同时位于DOM树的两个位置
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
</head>
<body>
<div id="box1">
<p id="para">我是段落</p>
</div>
<div id="box2"></div>
</body>
<script type="text/javascript">
var box1 = document.getElementById('box1');
var box2 = document.getElementById('box2');
var para = document.getElementById('para');
var ps_inbox2 = box2.getElementsByTagName('p');
// box2.appendChild(para);
box2.insertBefore(para, ps_inbox2[0]);
</script>
</html>
删除节点
removeChild() 方法从DOM中删除一个子节点
父节点.removeChild(要删除的子节点);
节点不能主动删除自己,必须由父节点删除它
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
</head>
<body>
<div id="box">
<p>我是p节点0</p>
<p>我是p节点1</p>
<p>我是p节点2</p>
</div>
</body>
<script type="text/javascript">
var box = document.getElementById('box');
var the_first_p = box.getElementsByTagName('p')[0];
box.removeChild(the_first_p);
</script>
</html>
克隆节点
cloneNode() 方法可以克隆节点,克隆出的节点是“孤儿节点”
var 孤儿节点 = 老节点.cloneNode(true/false);
参数是一个布尔值,表示是否采用深度克隆:如果为true,则该节点的所有后代节点也都会被克隆,如果为false,则只克隆该节点本身
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
</head>
<body>
<div id="box1">
<ul>
<li>牛奶</li>
<li>咖啡</li>
<li>可乐</li>
</ul>
</div>
<div id="box2"></div>
</body>
<script type="text/javascript">
var box1 = document.getElementById('box1');
var box2 = document.getElementById('box2');
var theul = box1.getElementsByTagName('ul')[0];
// 克隆节点
var new_ul = theul.cloneNode(true);
//不加true只会克隆ul节点
box2.appendChild(new_ul);
</script>
</html>
事件监听
事件:用户与网页的交互动作
书写 JS 让 HTML 元素对事件做出反应
设置事件监听的方法主要有 onxxx 和 addEventListener() 两种
最简单的给元素设置事件监听的方法就是设置它们的onxxx属性
oBox.onclick = function () {
//点击盒子时,将执行这里的语句
}
var oBox = document.getElementById('box');
oBox.onclick = function() {
alert('我是点击事件函数');
};
oninput 当用户正在修改域的内容
事件传播 (先从外到内,再从内到外)
从外到内:捕获阶段
从内到外:冒泡阶段
onxxx 这样的写法只能监听冒泡阶段
addEventListener() 方法
- DOM 0级事件监听:只能监听冒泡阶段
oBox.onclick = function () {
};
- DOM 2级事件监听:
oBox.addEventListener('click', function () {
// 这是事件处理函数
}, true);
// true监听捕获阶段
// false监听冒泡阶段
Attention
- 最内部元素不再区分捕获和冒泡阶段,会先执行写在前面的监听,然后执行后写的监听
- 如果给元素设置相同的两个或多个同名事件,则DOM 0级写法后面写的会覆盖先写的;而DOM 2级会按顺序执行
事件对象
事件处理函数提供一个形式参数,它是一个对象,封装了本次事件的细节
这个参数通常用单词 event 或字母e 来表示
oBox.onmousemove = function(e) {
// 对象e就是这次事件的“事件对象”
};
鼠标位置
事件源元素:最内层的子元素
e.charCode 和 e.keyCode属性
- e.charCode属性通常用于 onkeypress事件中,表示用户输入的字符的“字符码”
- e.keyCode属性通常用语 onkeydown 事件和 onkeyup 中,表示用户按下的按键的“键码”
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
</head>
<body>
<input type="text" name="" id="field" value="" />
</body>
<script type="text/javascript">
var field = document.getElementById('field');
var oInfo = document.getElementById('info');
oField.onkeypress = function(e) {
oInfo.innerText = '你输入的字符的字符码是' + e.charCode;
};
</script>
</html>
小案例
按方向键可以控制页面上的盒子移动
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
</head>
<style type="text/css">
#box {
position: absolute;
top: 200px;
left: 200px;
width: 100px;
height: 100px;
background-color: orange;
}
</style>
<body>
<div id="box"></div>
</body>
<script type="text/javascript">
var oBox = document.getElementById("box");
// 全局变量 t 和 l,分别表示盒子的top属性值和left属性值
var t = 200;
var l = 200;
// 监听document对象的键盘按下事件监听,表示当用户在整个网页上按下按键的时候
document.onkeydown = function (e) {
switch(e.keyCode) {
case 37:
l -= 3;
break;
case 38:
t -= 3;
break;
case 39:
l += 3;
break;
case 40:
t += 3;
break;
}
// 更改样式
oBox.style.left = l + 'px';
oBox.style.top = t + 'px';
};
</script>
</html>
e.preventDefault()方法
阻止默认事件产生的“默认动作”
案例:当鼠标在盒子中向下滚动时,数字+1;反之数字-1
鼠标滚轮事件是 从内眸色wheel,它的事件对象e提供deltaY属性表示鼠标滚动方向,向下滚动时返回正值,向上滚动时返回负值
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
</head>
<style type="text/css">
#box {
width: 200px;
height: 200px;
background-color: #333;
}
</style>
<body>
<div id="box"></div>
<h1 id="info">0</h1>
</body>
<script type="text/javascript">
var oBox = document.getElementById('box');
var oInfo = document.getElementById('info');
// 全局变量就是info中显示的数字
var a = 0;
// 给box盒子添加鼠标滚轮事件监听
oBox.onmousewheel = function (e) {
// 阻止默认事件:当用户在盒子里面滚动鼠标滚轮的时候,此时不会印发页面的滚动条的滚动
e.preventDefault();
if(e.deltaY > 0) {
a--;
}else{
a++;
}
oInfo.innerText = a;
}
</script>
</html>
e.stopPropagation() 方法
阻止事件继续传播
事件委托
批量添加事件监听
案例:页面上有一个无序列表,它内部有20个<li>
元素,请批量给它们添加点击事件监听,实现效果:点击哪个<li>
元素,哪个<li>
元素就变红
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
<style type="text/css">
</style>
</head>
<body>
<ul id="list">
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
</ul>
</body>
<script type="text/javascript">
var oList = document.getElementById('list');
var lis = oList.getElementsByTagName('li');
// 书写循环语句,批量给元素添加监听
for (var i = 0; i < lis.length; i++) {
lis[i].onclick = function () {
// 在这个函数中,this表示点击的这个元素,this涉及函数上下文的知识
this.style.color = 'red';
}
}
</script>
</html>
批量添加事件监听的性能问题
每一个事件监听注册都会消耗一定的系统内存
每个<li>
的事件处理函数都是不同的函数
事件委托
利用事件冒泡机制,将后代元素事件委托给祖先元素
e.target 和 e.currentTarget 属性
事件委托通常结合使用 e.target 属性
属性 | 属性描述 |
---|---|
target | 触发此事件的最早元素,即“事件源元素” |
currentTarget | 事件处理程序附加到的元素 |
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
</head>
<body>
<ul id="list">
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
</ul>
</body>
<script type="text/javascript">
var oList = document.getElementById('list');
oList.onclick = function (e) {
// e.target表示用户真正点击的那个元素
e.target.style.color = 'red';
};
</script>
</html>
- 当有大量类似元素需要批量添加事件监听时,使用事件委托可以减少内存开销
- 当有动态元素节点上树时,使用事件委托可以让新上树的元素具有事件监听
定时器和延时器
setInterval() 函数可以重复调用一个函数,在每次调用之间具有固定的事件间隔
setInterval(function () {
// 这个函数将自动被以固定间隔事件调用
}, 2000);
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
</head>
<body>
<script type="text/javascript">
var a = 0;
setInterval(function() {
console.log(a += 1);
},1000);
</script>
</body>
</html>
setInterval() 函数可以接收第3、4...个参数,它们将按顺序传入函数
setInterval(function (a, b) {
// 形式参数a的值是88,形式参数b的值是66
},2000,88,66};
具名函数也可以传入setInterval()
var a = 0;
function fun() {
console.log(++a);
}
setInterval(fun, 1000);
//具名函数当做第一个参数,这里没有圆括号!
fun() 函数
fun() 函数的执行(函数会立刻执行,丢失封装性)
清除定时器 clearInterval()
var timer = setInterval(function () {
},2000);
//点击按钮时,清除定时器
oBtn.onclick = function() {
clearInterval(timer);
// 清除定时器的时候,要传入定时器变量
}
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
</head>
<body>
<h1 id="info">0</h1>
<button type="button" id="btn1">开始</button>
<button type="button" id="btn2">暂停</button>
<script type="text/javascript">
var oInfo = document.getElementById('info');
var oBtn1 = document.getElementById('btn1');
var oBtn2 = document.getElementById('btn2');
var a = 0;
var timer;
oBtn1.onclick = function() {
// 防止定时器叠加,应该设置定时器之前,清除定时器
clearInterval(timer);
// 更改全局变量timer的值为一个定时器实体
timer = setInterval(function() {
oInfo.innerText = ++a;
}, 1000);
};
oBtn2.onclick = function() {
clearInterval(timer);
};
</script>
</body>
</html>
延时器
- setTimeout() 函数可以设置一个延时器,当指定事件到了之后,会执行一次函数,不再重复执行
setTimeout(function() {
// 这个函数会在2s后执行一次
},2000);
清除延时器:clearTimeout()
setInterval() 和 setTimeout() 是两个异步语句
JS 和 CSS3 结合实现动画
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
<style type="text/css">
#box {
width: 100px;
height: 100px;
background-color: orange;
position: absolute;
top: 100px;
left: 100px;
}
</style>
</head>
<body>
<button id="btn" type="button">按我运动</button>
<div id="box"></div>
</body>
<script type="text/javascript">
var btn = document.getElementById('btn');
var box = document.getElementById('box');
// 标示量,指示当前盒子在左边还是右边
var pos = 1; //1左2右
btn.onclick = function() {
box.style.transition = 'all 2s linear 0s';
if (pos == 1) {
box.style.left = '1100px';
pos = 2;
}else if (pos == 2) {
box.style.left = '100px'
pos = 1;
}
};
</script>
</html>
函数节流
var lock = true;
function 需要节流的函数() {
//如果锁是关闭状态,则不执行
if (!lock) return;
// 函数核心语句
// 关锁
lock = false;
// 指定毫秒数后将锁打开
setTimeout(function () {
lock = true;
},2000);
}
BOM JS与浏览器窗口交互的接口
浏览器改变尺寸、滚动条滚动相关的特效
window对象
- window对象是当前JS脚本运行所处的窗口,这个窗口中包含DOM解构,window.document属性就是document对象
- 在有标签页功能的浏览器中,每个标签都拥有自己的window对象
全局变量是window的属性
多个js文件之间是共享全局作用域的
内置函数普遍是window的方法
resize 事件
窗口大小改变之后就会出发resize事件
window.onresize = function() {
var root = document.documentElement;
console.log('窗口改变尺寸了',root.clientWidth, root.clientHeight);
}
scroll事件
- 在窗口被卷洞之后就会出发 scroll 事件
- 可以使用 window.onscroll 或者 window.addEventListener('scroll')来绑定事件处理函数
window.onscroll = function() {
console.log('窗口卷动了', window.scrollY);
}
Navigator 对象
- window.navigator 属性可以检索navigator对象,它内部含有用户此次活动的浏览器的相关属性和标识
History对象
-
window.history 对象提供了操作浏览器会话历史的接口
-
常用操作就是模拟浏览器回退按钮
history.back(); // 等同于点击浏览器的回退按钮
history.go(-1); // 等同于history.back();
Location对象
- window.location 标示当前所在网址,可以通过给这个属性赋值命令浏览器进行页面跳转
重新加载当前页面
- 可以调用 location 的 reload 方法重新加载当前页面,参数 true 表示强制从服务器强制加载