JavaScript

JS中两大类数据类型

基本数据类型

  • Number、String、Boolean、Undefined、Null

复杂数据类型

  • Object、Array、Function、RegExp、Date、Map、Set、Symbol

NaN(不是一个数,但它是一个数字类型的值)

运算中不能得到数字,结果往往都是NaN

字符串的常用方法

image-20211004122542981

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()方法可以用来检测数组

数组的常用方法

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

相等判断时的区别

  • 基本类型进行相等判断时,会比较是否相等

  • 引用类型进行相等判断时,会比较是否相等,也就是说它会比较是否为内存中的同一个东西

image-20211004220228184

深克隆和浅克隆

使用 arr1 = arr2 的语法不会克隆数组

浅克隆:只克隆数组的第一层,如果是多维数组,或者数组中的项是其它引用类型值,则不克隆其他层

浅克隆就是遍历原数组中的每一项,把遍历到的项推入到结果数组中

深克隆:克隆数组的所有层,要使用递归技术,在后面课程介绍

函数声明提升

fun();
function fun() {
  alert("...");
}

函数表达式不能提升

如果函数是用函数表达式的写法定义的,则没有提升特性

fun();
var fun = function() {
  alert("...");
}
//把函数赋值给了一个变量,变量只提升定义不提升值
//此时变量值为 undefined,undefined不能加圆括号运行

函数优先提升

image-20211005105507355

先弹出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);

实现深克隆

image-20211005202223910

复习浅克隆

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

image-20211006165806336 image-20211006170105738

形参也是局部变量

image-20211006170846916

作用域链

在函数嵌套中,变量会从内到外逐层寻找它的定义

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

立即调用函数表达式,一旦被定义,就立即被调用

image-20211006220412167

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 元素对事件做出反应

设置事件监听的方法主要有 onxxxaddEventListener() 两种

最简单的给元素设置事件监听的方法就是设置它们的onxxx属性

oBox.onclick = function () {
  //点击盒子时,将执行这里的语句
}
var oBox = document.getElementById('box');
oBox.onclick = function() {
	alert('我是点击事件函数');
};
image-20211008153447930 image-20211008153940280 image-20211008154240418

​ oninput 当用户正在修改域的内容

image-20211008154748389

事件传播 (先从外到内,再从内到外)

从外到内:捕获阶段
从内到外:冒泡阶段

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就是这次事件的“事件对象”
};

鼠标位置

image-20211008162917224

事件源元素:最内层的子元素

e.charCode 和 e.keyCode属性

  • e.charCode属性通常用于 onkeypress事件中,表示用户输入的字符的“字符码”
  • e.keyCode属性通常用语 onkeydown 事件和 onkeyup 中,表示用户按下的按键的“键码”
image-20211008164154583 image-20211008164254254
<!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>的事件处理函数都是不同的函数

事件委托

利用事件冒泡机制,将后代元素事件委托给祖先元素

image-20211008175833782

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的方法

image-20211008193132443

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);
}
  • window.navigator 属性可以检索navigator对象,它内部含有用户此次活动的浏览器的相关属性和标识
image-20211008193936709

History对象

  • window.history 对象提供了操作浏览器会话历史的接口

  • 常用操作就是模拟浏览器回退按钮

history.back();		// 等同于点击浏览器的回退按钮
history.go(-1);		// 等同于history.back();

Location对象

  • window.location 标示当前所在网址,可以通过给这个属性赋值命令浏览器进行页面跳转

重新加载当前页面

  • 可以调用 location 的 reload 方法重新加载当前页面,参数 true 表示强制从服务器强制加载

返回顶部按钮制作

posted @ 2021-10-14 09:37  ztw1002  阅读(141)  评论(0编辑  收藏  举报