DOM
DOM(文档对象模型)是一个跨平台和语言独立的接口,它允许程序和脚本动态地访问和更新文档的内容、结构和样式。在网页浏览器中,DOM 通常表示 HTML 或 XML 文档的对象模型。DOM 将网页内容视为节点树,其中每个节点都是文档中的对象,例如元素、属性、文本等。
以下是 DOM 的一些关键点:
-
文档:在 Web 浏览器中,文档通常是指网页,即 HTML 或 XML 文件。
-
对象模型:对象模型是将文档内容表示为对象集合的一种方式,这些对象可以由程序代码进行操作。
-
节点树:DOM 将文档结构表示为节点树,树中的每个节点代表文档的一部分。例如,HTML 文档中的每个元素、属性和文本都是节点。
-
编程接口:DOM 提供了一组方法(函数)和属性(变量),允许开发人员通过 JavaScript(或其他支持的语言)操作文档。
DOM中的节点类型
在 DOM(文档对象模型)中,节点类型是通过 nodeType
属性来区分的,每个节点类型都有一个对应的常量值。以下是一些主要的 DOM 节点类型及其对应的常量值:
-
元素节点(Element Node)
- 常量值:
Node.ELEMENT_NODE
- 整数值:1
- 示例:
<div>
,<p>
,<a>
- 常量值:
-
属性节点(Attribute Node)
- 常量值:
Node.ATTRIBUTE_NODE
- 整数值:2
- 示例:
href
in<a href="http://example.com">
- 常量值:
-
文本节点(Text Node)
- 常量值:
Node.TEXT_NODE
- 整数值:3
- 示例:文本内容 “Hello, World!” in
<p>Hello, World!</p>
- 常量值:
-
CDATA 节点(CDATASection Node)
- 常量值:
Node.CDATA_SECTION_NODE
- 整数值:4
- 示例:
<![CDATA[...]]>
in XML documents
- 常量值:
-
实体引用节点(EntityReference Node)
- 常量值:
Node.ENTITY_REFERENCE_NODE
- 整数值:5
- 注:在 HTML DOM 中不常用
- 常量值:
-
实体节点(Entity Node)
- 常量值:
Node.ENTITY_NODE
- 整数值:6
- 注:在 HTML DOM 中不常用
- 常量值:
-
处理指令节点(ProcessingInstruction Node)
- 常量值:
Node.PROCESSING_INSTRUCTION_NODE
- 整数值:7
- 示例:处理指令,常用于 XML
- 常量值:
-
注释节点(Comment Node)
- 常量值:
Node.COMMENT_NODE
- 整数值:8
- 示例:
<!-- This is a comment -->
- 常量值:
-
文档节点(Document Node)
- 常量值:
Node.DOCUMENT_NODE
- 整数值:9
- 示例:整个 HTML 或 XML 文档
- 常量值:
-
文档类型节点(DocumentType Node)
- 常量值:
Node.DOCUMENT_TYPE_NODE
- 整数值:10
- 示例:
<!DOCTYPE html>
- 常量值:
-
文档片段节点(DocumentFragment Node)
- 常量值:
Node.DOCUMENT_FRAGMENT_NODE
- 整数值:11
- 示例:轻量级的文档对象,可以包含多个元素而不影响文档结构
- 常量值:
-
符号节点(Notation Node)
- 常量值:
Node.NOTATION_NODE
- 整数值:12
- 注:在 HTML DOM 中不常用
- 常量值:
一、节点的创建、移除和克隆
1.如何改变元素节点中的内容
改变元素节点中的内容可以使用两个相关属性:innerHTML、innerText。innerHTML属性能以HTML形式设置节点中的内容;innerText系统属性只能以纯文本的形式设置节点中的内容。
2.创建节点
想要两步,第一步使用document.createElement()创建节点,第二步再上树,上树有两种情况,如果是在添加在最后,那就使用appendChild();如果不是,使用insertBefore。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><div id="box"><p>我是原本的段落0</p><p>我是原本的段落1</p><p>我是原本的段落2</p></div><script>var oBox = document.getElementById('box');var oPs = oBox.getElementsByTagName('p');// 创建孤儿节点var oP = document.createElement('p');// 设置内部文字oP.innerText = '我是新来的';// 上树// oBox.appendChild(oP);oBox.insertBefore(oP, oPs[0]);</script>
</body></html>
创建出一个20行12列的表格
<!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>td {width: 20px;height: 20px;border: 1px solid #000;}</style>
</head><body><table id="mytable"></table><script>// 请动态创建出一个20行12列的表格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(td);}// 让mytable追加tr标签mytable.appendChild(tr);}</script>
</body></html>
创建9*9乘法表
<!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>td {width: 120px;height: 30px;border: 1px solid #000;}</style>
</head><body><table id="mytable"></table><script>// 请创建九九乘法表var mytable = document.getElementById('mytable');for (var i = 1; i <= 9; i++) {// 创建了新的tr标签var tr = document.createElement('tr');for (var j = 1; j <= i; j++) {// 创建了新的td标签var td = document.createElement('td');// 设置td内部的文字td.innerText = i + '乘' + j + '等于' + (i * j);// 让tr追加td标签tr.appendChild(td);}// 让mytable追加tr标签mytable.appendChild(tr);}</script>
</body></html>
3.移动节点
如果将已经挂载到DOM树上的节点成为appendChild()或者insertBefore()的参数,这个节点将被移动。新父节点.appendChild(已经有父亲的节点);新父节点.insertBefore(已经有父亲的节点,标杆子节点);这意味着一个节点不能同时位于DOM树的两个位置。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><div id="box1"><p id="para">我是段落</p></div><div id="box2"><p>我是box2的原有p标签</p><p>我是box2的原有p标签</p><p>我是box2的原有p标签</p><p>我是box2的原有p标签</p></div><script>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>
</body>
</html>
4.删除节点
removeChild()方法从DOM中删除一个字节点 父节点.removeChild(要删除子节点);子节点不能主动删除自己,必须由父节点删除它。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><div id="box"><p>我是p节点0</p><p>我是p节点1</p><p>我是p节点2</p></div><script>var box = document.getElementById('box');var the_first_p = box.getElementsByTagName('p')[0];box.removeChild(the_first_p);</script>
</body>
</html>
5.克隆节点
cloneNode()方法可以克隆节点,克隆出的节点上“孤儿节点”。
var 孤儿节点=老节点.cloneNode();
var 孤儿节点=老节点.cloneNode(true);
参数是一个布尔值,表示是否采用深度克隆;如果为true,则该节点的所有后代节点也都会被克隆,如果为false,则只克隆该节点本身。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><div id="box1"><ul><li>牛奶</li><li>咖啡</li><li>可乐</li></ul></div><div id="box2"></div><script>var box1 = document.getElementById('box1');var box2 = document.getElementById('box2');var theul = box1.getElementsByTagName('ul')[0];// 克隆节点var new_ul = theul.cloneNode(true);box2.appendChild(new_ul);</script>
</body>
</html>
6.JS修改样式
<!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>.box{width: 200px;height: 200px;border: 1px solid #000;}</style>
</head>
<body><div class="box" id="box">你好</div><script>var oBox = document.getElementById('box');// oBox.style.backgroundColor = 'rgb(100, 200, 123)';// oBox.style.backgroundColor = '#f80';// oBox.style.backgroundImage = 'url(https://www.imooc.com/static/img/index/logo-recommended.png)';// oBox.style.backgroundSize = 'contain';oBox.style.fontSize = '50px';</script>
</body>
</html>
7.nodeType常用属性值
8.访问元素节点
注意:如果有多个id相同的元素 只能找到第一个。 无论id多深,都能找到。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><div id="box1">我是盒子1</div><div id="box2">我是盒子2</div><script>// 得到盒子1var box1 = document.getElementById('box1');// 得到盒子2var box2 = document.getElementById('box2');console.log(box1);console.log(box2);</script>
</body></html>
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><div id="box1"><p>我是段落</p><p>我是段落</p><p>我是段落</p><p>我是段落</p></div><div id="box2"><p>我是段落</p><p>我是段落</p><p>我是段落</p><p>我是段落</p></div><script>// 先得到box1var box1 = document.getElementById('box1');// 得到box1中的p标签的数组var ps_inbox1 = box1.getElementsByTagName('p');console.log(ps_inbox1);</script>
</body>
</html>
9.延迟运行
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script>// 给window对象添加onload事件监听。onload表示页面都加载完毕了。window.onload = function () {// 得到盒子1var box1 = document.getElementById('box1');// 得到盒子2var box2 = document.getElementById('box2');console.log(box1);console.log(box2);};</script>
</head><body><div id="box1">我是盒子1</div><div id="box2">我是盒子2</div>
</body></html>
10.querySelector()
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><div id="box"><p>我是段落</p><p class="spec para">我是段落</p><p>我是段落</p></div><script>var the_p = document.querySelector('#box p:nth-child(1)');console.log(the_p);</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><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>
</body>
</html>
11.节点的关系
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><div id="box"><p>我是段落A</p><p id="para">我是段落B</p><p>我是段落C</p></div><script>var box = document.getElementById('box');var para = document.getElementById('para');// 所有子节点console.log(box.childNodes);// 所有的元素子节点(IE9开始兼容)console.log(box.children);// 第一个子节点console.log(box.firstChild);console.log(box.firstChild.nodeType);//子节点的类型,它是一个数// 第一个元素子节点(IE9开始兼容)console.log(box.firstElementChild);// 最后一个子节点console.log(box.lastChild);console.log(box.lastChild.nodeType);//子节点的类型,它是一个数// 最后一个元素子节点(IE9开始兼容)console.log(box.lastElementChild);// 父节点console.log(para.parentNode);// 前一个兄弟节点console.log(para.previousSibling);// 前一个元素兄弟节点(IE9开始兼容)console.log(para.previousElementSibling);// 后一个兄弟节点console.log(para.nextSibling);// 后一个元素兄弟节点(IE9开始兼容)console.log(para.nextElementSibling);</script>
</body>
</html>
12.常见的节点关系函数
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><div id="box"><p>我是段落</p><p>我是段落</p><p>我是段落</p><p id="fpara">我是段落fpara</p>我是文本<!-- 我是注释 --><p id="para">我是段落para<span>1</span><span>2</span><span>3</span></p><p>我是段落</p><p>我是段落</p><p>我是段落</p></div><script>var box = document.getElementById('box');var para = document.getElementById('para');var fpara = document.getElementById('fpara');// 封装一个函数,这个函数可以返回元素的所有子元素节点(兼容到IE6),类似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(node.childNodes[i]);}}return children;}console.log(getChildren(box));console.log(getChildren(para));// 封装一个函数,这个函数可以返回元素的前一个元素兄弟节点(兼容到IE6),类似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;// 遍历node的前面的节点while(o.previousSibling != null) {if(o.previousSibling.nodeType == 1){prevs.unshift(o.previousSibling);}o = o.previousSibling;}o = node;// 遍历node的后面的节点while(o.nextSibling != null) {if(o.nextSibling.nodeType == 1){nexts.push(o.nextSibling);}o = o.nextSibling;}// 将两个数组进行合并,然后返回return prevs.concat(nexts);}console.log(getAllElementSibling(para));</script>
</body></html>
二、DOM事件
事件监听、事件传播、事件对象、事件委托
1. 事件监听
1.常见的鼠标事件监听
<!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>div{width: 200px;height: 200px;background-color: #333;}</style>
</head>
<body><div id="box"></div><script>var oBox = document.getElementById('box');oBox.onclick = function() {console.log('我是onclick');};oBox.ondblclick = function() {console.log('我是ondblclick');};oBox.onmousedown = function() {console.log('我是onmousedown');};oBox.onmouseup = function() {console.log('我是onmouseup');};oBox.onmouseenter = function() {console.log('我是onmouseenter');};oBox.onmouseleave = function() {console.log('我是onmouseleave');};oBox.onmousemove = function() {console.log('我是onmousemove');};</script>
</body>
</html>
2.常见的键盘事件监听
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body>姓名:<input type="text" id="nameField"><script>var nameField = document.getElementById('nameField');nameField.onkeydown = function () {console.log('我是onkeydown');};nameField.onkeypress = function() {console.log('我是onkeypress');}nameField.onkeyup = function() {console.log('我是onkeyup');}</script>
</body>
</html>
3.事件传播
事件传播是:先从外到内,再从内到外。
(1)onxxx写法
<!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>#box1{width: 202px;height: 202px;border: 1px solid #000;padding: 50px;}#box2{width: 100px;height: 100px;border: 1px solid #000;padding: 50px;}#box3{width: 100px;height: 100px;border: 1px solid #000;}</style>
</head>
<body><div id="box1"><div id="box2"><div id="box3"></div></div></div><script>var oBox1 = document.getElementById('box1');var oBox2 = document.getElementById('box2');var oBox3 = document.getElementById('box3');oBox2.onclick = function () {console.log('我是box2的onclick');};oBox3.onclick = function () {console.log('我是box3的onclick');};oBox1.onclick = function () {console.log('我是box1的onclick');};</script>
</body>
</html>
(2) addEventListener()方法
<!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>#box1{width: 202px;height: 202px;border: 1px solid #000;padding: 50px;}#box2{width: 100px;height: 100px;border: 1px solid #000;padding: 50px;}#box3{width: 100px;height: 100px;border: 1px solid #000;}</style>
</head>
<body><div id="box1"><div id="box2"><div id="box3"></div></div></div><script>var oBox1 = document.getElementById('box1');var oBox2 = document.getElementById('box2');var oBox3 = document.getElementById('box3');oBox2.addEventListener('click', function() {console.log('我是box2的冒泡阶段');}, false);oBox3.addEventListener('click', function() {console.log('我是box3的捕获阶段');}, true);oBox3.addEventListener('click', function() {console.log('我是box3的冒泡阶段');}, false);oBox3.onclick = function () {console.log('我是box3的onclick');};oBox1.addEventListener('click', function() {console.log('我是box1的冒泡阶段');}, false);oBox2.addEventListener('click', function() {console.log('我是box2的捕获阶段');}, true);oBox1.addEventListener('click', function() {console.log('我是box1的捕获阶段');}, true);oBox1.onclick = function () {console.log('我是box1的onclick');};oBox2.onclick = function () {console.log('我是box2的onclick');};</script>
</body>
</html>
4.事件对象
<!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>*{margin: 0;padding: 0;}#box{width: 200px;height: 200px;background-color: #333;margin: 100px;}body{height: 2000px;}#info{font-size: 30px;}</style>
</head>
<body><div id="box"></div><div id="info"></div><script>var oBox = document.getElementById('box');var oInfo = document.getElementById('info');oBox.onmousemove = function (e) {oInfo.innerHTML = 'offsetX/Y:' + e.offsetX + ',' + e.offsetY + '<br>'+ 'clientX/Y:' + e.clientX + ',' + e.clientY + '<br>'+ 'pageX/Y:' + e.pageX + ',' + e.pageY;};</script>
</body>
</html>
5.charCode和keyCode
demo:按键使盒子移动
<!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>#box {position: absolute;top: 200px;left: 200px;width: 100px;height: 100px;background-color: orange;}</style>
</head><body><div id="box"></div><script>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>
</body></html>
6.e.preventDefault()方法
Demo1:阻止文本框的默认行为(默认是敲一个字母就会在文本框里显示,现在阻止这种行为)
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><p>只能输入小写字母和数字:<input type="text" id="field"></p><script>var oField = document.getElementById('field');oField.onkeypress = function (e) {console.log(e.charCode);// 根据用户输入的字符的字符码(e.charCode)// 数字0~9,字符码48~57// 小写字母a~z,字符码97~122if (!(e.charCode >= 48 && e.charCode <= 57 || e.charCode >= 97 && e.charCode <= 122)) {// 阻止浏览器的默认行为e.preventDefault();}};</script>
</body></html>
Demo2:制作鼠标滚轮事件:当鼠标在盒子中向下滚动时,数字+1;反之,数字-1。
<!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>#box {width: 200px;height: 200px;background-color: #333;}body{height: 2000px;}</style>
</head><body><div id="box"></div><h1 id="info">0</h1><script>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>
</body></html>
7.stopPropagation()方法
<!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>div{width: 200px;height: 200px;background-color: #333;}</style>
</head><body><div id="box"><button id="btn">按我</button></div><script>var oBox = document.getElementById('box');var oBtn = document.getElementById('btn');// oBox.onclick = function () {// console.log('我是盒子');// };// oBtn.onclick = function (e) {// 阻止事件继续传播// e.stopPropagation();// console.log('我是按钮');// };oBox.addEventListener('click', function(e) {// 阻止事件继续传播e.stopPropagation();console.log('我是盒子');}, true)//true代表捕获阶段oBtn.addEventListener('click', function() {console.log('我是按钮');}, true);</script>
</body></html>
Demo:制造弹出层
制作一个弹出层,点击按钮显示弹出层,点击网页除它自身外的任意地方,弹出层关闭。
如果不去阻止事件传播,会点击按钮的瞬间就关闭,因为按钮也是网页的任意地方。
<!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>.modal {width: 400px;height: 140px;background-color: #333;position: absolute;top: 50%;left: 50%;margin-top: -70px;margin-left: -200px;display: none;}</style>
</head><body><button id="btn">按我弹出弹出层</button><div class="modal" id="modal"></div><script>var oBtn = document.getElementById('btn');var oModal = document.getElementById('modal');// 点击按钮的时候,弹出层显示oBtn.onclick = function (e) {// 阻止事件继续传播到document身上e.stopPropagation();oModal.style.display = 'block';};// 点击页面任何部分的时候,弹出层关闭document.onclick = function () {oModal.style.display = 'none';};// 点击弹出层内部的时候,不能关闭弹出层的,所以应该阻止事件继续传播oModal.onclick = function (e) {// 阻止事件继续传播到document身上e.stopPropagation();};</script>
</body></html>
8.事件委托
什么是事件委托?
事件委托(Event Delegation)是一种在JavaScript中处理事件的技术,它利用了事件冒泡的原理。在事件冒泡的过程中,事件会从触发它的最深层节点开始,然后逐级向上传播至文档的根节点。事件委托的核心思想是,不必为每一个子节点单独绑定事件处理器,而是将事件处理器绑定到它们的共同祖先节点上,利用事件冒泡,当事件发生时,会冒泡到祖先节点,然后在该祖先节点上根据事件的目标元素(event.target)来决定如何处理事件。
事件委托的几个主要优点如下:
-
性能优化:减少了需要绑定事件处理器的元素数量,从而减少了内存的使用,并可能提高页面的加载速度。
-
动态内容:对于动态添加的元素,无需再次绑定事件处理器,因为事件处理器已经绑定在它们的祖先元素上。
-
简化代码:减少了代码的复杂性和冗余,使得代码更加简洁。
下面是一个简单的例子来说明事件委托:
// 假设有一个列表,我们想为列表中的每个项目添加点击事件
<ul id="list"><li>Item 1</li><li>Item 2</li><li>Item 3</li><!-- 可能还有更多的列表项 -->
</ul>// 我们可以为ul元素绑定一个点击事件,而不是为每个li元素绑定
document.getElementById('list').addEventListener('click', function(event) {// 检查事件的目标元素是否是liif (event.target.tagName === 'LI') {// 如果是,执行相应的操作console.log('List item clicked:', event.target.textContent);}
});
在上面的例子中,不管列表中有多少个<li>
元素,甚至如果以后动态添加了更多的<li>
元素,我们都不需要为它们单独绑定事件处理器。点击任何<li>
元素都会触发绑定在<ul>
上的事件处理器,处理器会检查事件的目标元素,如果是<li>
,则执行相关操作。这就是事件委托的基本用法。
什么时候使用事件委托?
事件委托通常在以下几种情况下使用:
-
动态内容:当你需要为动态添加到DOM中的元素绑定事件时,事件委托非常有用。由于事件处理器绑定在父元素上,新添加的子元素无需再次绑定事件。
-
大量元素:当页面上有大量的元素需要绑定相同的事件处理器时,事件委托可以减少内存的使用,并提高性能。
-
事件冒泡:当你希望利用事件冒泡的特性来减少事件监听器的数量时,可以使用事件委托。
-
简化代码:事件委托可以减少代码量,使得代码更加简洁和易于管理。
以下是具体的使用场景:
-
表格行点击:在一个表格中,你可能想要在点击任意一行时触发一个事件。通过将事件处理器绑定到
<table>
元素上,并检查event.target
是否是<tr>
元素,可以实现这一点。 -
菜单项点击:在一个菜单列表中,每个菜单项都需要绑定点击事件。通过将事件处理器绑定到
<ul>
或<nav>
元素上,可以轻松管理事件。 -
表单输入处理:在表单中,你可能想要在用户输入时执行某些操作,如即时验证。可以将事件处理器绑定到
<form>
元素上,并根据event.target
的类型(如<input>
)来执行不同的操作。 -
拖放操作:在实现拖放功能时,可以在容器元素上设置事件监听器,而不是在每个可拖动的元素上设置。
以下是一个简单的示例,说明在动态内容情况下如何使用事件委托:
// 假设有一个空的列表,我们将动态添加列表项
<ul id="list"></ul>// 动态添加一些列表项
function addItem(text) {const li = document.createElement('li');li.textContent = text;document.getElementById('list').appendChild(li);
}// 使用事件委托绑定点击事件
document.getElementById('list').addEventListener('click', function(event) {if (event.target.tagName === 'LI') {console.log('List item clicked:', event.target.textContent);}
});// 添加一些列表项
addItem('Item 1');
addItem('Item 2');
addItem('Item 3');
在上面的例子中,不管何时添加新的列表项,点击它们都会触发绑定在<ul>
上的事件处理器,因为事件会冒泡到父元素,并由事件委托处理。
Demo1:批量添加点击事件监听
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</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><script>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>
</body></html>
Demo2:新增元素动态绑定事件
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><button id="btn">按我添加新的li列表项</button><ul id="list"></ul><script>var oBtn = document.getElementById('btn');var oList = document.getElementById('list');var lis = oList.getElementsByTagName('li');// 按钮的点击事件oBtn.onclick = function () {// 创建一个新的li列表项,孤儿节点var oLi = document.createElement('li');oLi.innerHTML = '我是列表项';// 上树oList.appendChild(oLi);// 给新创建的这个li节点添加onclick事件监听oLi.onclick = function () {this.style.color = 'red';};};</script>
</body></html>
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><button id="btn">按我创建一个新列表项</button><ul id="list"><li>列表项</li><li>列表项</li><li>列表项</li></ul><script>var oList = document.getElementById('list');var oBtn = document.getElementById('btn');oList.onclick = function (e) {// e.target表示用户真正点击的那个元素e.target.style.color = 'red';};oBtn.onclick = function () {// 创建新的li元素var oLi = document.createElement('li');// 写内容oLi.innerText = '我是新来的';// 上树oList.appendChild(oLi);};</script>
</body></html>
Demo3:注意事件是不是冒泡
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><button id="btn">按我创建一个新列表项</button><ul id="list"><li>列表项</li><li>列表项</li><li>列表项</li></ul><script>var oList = document.getElementById('list');var oBtn = document.getElementById('btn');// onmouseenter这个属性天生就是“不冒泡”的,相当于你事件处理函数附加给了哪个DOM节点// 就是哪个DOM节点自己触发的事件,没有冒泡过程oList.onmouseover = function (e) {// e.target表示用户真正点击的那个元素e.target.style.color = 'red';};oBtn.onclick = function () {// 创建新的li元素var oLi = document.createElement('li');// 写内容oLi.innerText = '我是新来的';// 上树oList.appendChild(oLi);};</script>
</body></html>
<!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>#box {position: absolute;top: 100px;left: 100px;width: 100px;height: 100px;background-color: orange;}</style>
</head><body><button id="btn">开始运动</button><div id="box"></div><script>// 得到元素var btn = document.getElementById('btn');var box = document.getElementById('box');// 全局变量盒子的left值var left = 100;// 按钮监听btn.onclick = function () {var timer = setInterval(function () {// 改变全局变量left += 10;if (left >= 1000) {clearInterval(timer);}// 设置left属性box.style.left = left + 'px';}, 20);};</script>
</body></html>
三、实现动画
1.定时器
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><script>var a = 0;setInterval(function () {console.log(++a);}, 1000);</script>
</body></html>
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><h1 id="info">0</h1><button id="btn1">开始</button><button id="btn2">暂停</button><script>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>
2.延时器
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><button id="btn1">2秒后弹出你好</button><button id="btn2">取消弹出</button><script>var btn1 = document.getElementById('btn1');var btn2 = document.getElementById('btn2');var timer;btn1.onclick = function() {timer = setTimeout(function () {alert('你好');}, 2000);}btn2.onclick = function() {clearTimeout(timer);}</script>
</body></html>
3.使用定时器实现动画
<!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>#box {position: absolute;top: 100px;left: 100px;width: 100px;height: 100px;background-color: orange;}</style>
</head><body><button id="btn">开始运动</button><div id="box"></div><script>// 得到元素var btn = document.getElementById('btn');var box = document.getElementById('box');// 全局变量盒子的left值var left = 100;// 按钮监听btn.onclick = function () {var timer = setInterval(function () {// 改变全局变量left += 10;if (left >= 1000) {clearInterval(timer);}// 设置left属性box.style.left = left + 'px';}, 20);};</script>
</body></html>
4.JS和CSS3结合实现动画、函数节流公式
Demo1
<!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>#box {width: 100px;height: 100px;background-color: orange;position: absolute;top: 100px;left: 100px;}</style>
</head><body><button id="btn">按我运动</button><div id="box"></div><script>// 得到元素var btn = document.getElementById('btn');var box = document.getElementById('box');// 标识量,指示当前盒子在左边还是右边var pos = 1; // 1左边,2右边// 函数节流锁var lock = true;// 事件监听btn.onclick = function () {// 首先检查锁是否是关闭if (!lock) return;// 把过渡加上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;}// 关锁lock = false;// 指定时间后,将锁打开setTimeout(function() {lock = true;}, 2000);};</script>
</body></html>
函数节流公式
5.动画效果开发1 - 无缝连续滚动特效
<!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>* {margin: 0;padding: 0;}.box {width: 1000px;height: 130px;border: 1px solid #000;margin: 50px auto;overflow: hidden;}.box ul {list-style: none;/* 设置大一点,这样li才能浮动 */width: 5000px;position: relative;}.box ul li {float: left;margin-right: 10px;}</style>
</head><body><div id="box" class="box"><ul id="list"><li><img src="images/number/0.png" alt=""></li><li><img src="images/number/1.png" alt=""></li><li><img src="images/number/2.png" alt=""></li><li><img src="images/number/3.png" alt=""></li><li><img src="images/number/4.png" alt=""></li><li><img src="images/number/5.png" alt=""></li></ul></div><script>var box = document.getElementById('box');var list = document.getElementById('list');// 复制多一遍所有的lilist.innerHTML += list.innerHTML;// 全局变量,表示当前list的left值var left = 0;// 定时器,全局变量var timer;move();// 动画封装成函数function move() {// 设表先关,防止动画积累clearInterval(timer);timer = setInterval(function () {left -= 4;// 验收// 这个1250是每个图片的宽度是200,6个图片循环,然后有6个边距10px(最后一个边距是为了衔接自然),所以总结就是200*6+6*10=1260if (left <= - 1260) {left = 0;}list.style.left = left + 'px';}, 20);}// 鼠标进入停止定时器box.onmouseenter = function () {clearInterval(timer);};// 鼠标离开继续定时器box.onmouseleave = function () {move();};</script>
</body></html>
6.动画效果开发2 - 跑马灯轮播图特效
<!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>*{margin: 0;padding: 0;}.carousel {width: 650px;height: 360px;border: 1px solid #000;margin: 50px auto;position: relative;overflow: hidden;}.carousel ul {list-style: none;width: 6000px;position: relative;left: 0px;transition: left .5s ease 0s;}.carousel ul li {float: left;}.carousel .leftbtn {position: absolute;left: 20px;top: 50%;margin-top: -25px;width: 50px;height: 50px;background-color: rgb(28, 180, 226);border-radius: 50%;}.carousel .rightbtn {position: absolute;right: 20px;top: 50%;margin-top: -25px;width: 50px;height: 50px;background-color: rgb(28, 180, 226);border-radius: 50%;}</style>
</head>
<body><div class="carousel"><ul id="list"><li><img src="images/beijing/0.jpg" alt=""></li><li><img src="images/beijing/1.jpg" alt=""></li><li><img src="images/beijing/2.jpg" alt=""></li><li><img src="images/beijing/3.jpg" alt=""></li><li><img src="images/beijing/4.jpg" alt=""></li></ul><a href="javascript:;" class="leftbtn" id="leftbtn"></a><a href="javascript:;" class="rightbtn" id="rightbtn"></a></div><script>// 得到按钮和ul,ul整体进行运动var leftbtn = document.getElementById('leftbtn');var rightbtn = document.getElementById('rightbtn');var list = document.getElementById('list');// 克隆第一张图片var cloneli = list.firstElementChild.cloneNode(true);list.appendChild(cloneli);// 当前ul显示到第几张了,从0开始数var idx = 0;// 节流锁var lock = true;// 右边按钮监听rightbtn.onclick = function () {// 判断锁的状态if (!lock) return; lock = false;// 给list加过渡,为什么要加??css中不是已经加了么??这是因为最后一张图片会把过渡去掉list.style.transition = 'left .5s ease 0s';idx ++;if (idx > 4) {// 设置一个延时器,延时器的功能就是将ul瞬间拉回0的位置,延时器的目的就是让过渡动画结束之后setTimeout(function() {// 取消掉过渡,因为要的是瞬间移动,不是“咕噜”回去list.style.transition = 'none';list.style.left = 0;idx = 0;}, 500);}list.style.left = -idx * 650 + 'px';// 函数节流setTimeout(function() {lock = true; }, 500);}// 左边按钮监听leftbtn.onclick = function () {if (!lock) return;lock = false;// 判断是不是第0张,如果是,就要瞬间用假的替换真的if (idx == 0) {// 取消掉过渡,因为要的是瞬间移动,不是“咕噜”过去list.style.transition = 'none';// 直接瞬间移动到最后的假图片上list.style.left = -5 * 650 + 'px';// 设置一个延时器,这个延时器的延时时间可以是0毫秒,虽然是0毫秒,但是可以让我们过渡先是瞬间取消,然后再加上setTimeout(function() {// 加过渡list.style.transition = 'left .5s ease 0s';// idx改为真正的最后一张idx = 4;list.style.left = -idx * 650 + 'px';}, 0);} else {idx --;list.style.left = -idx * 650 + 'px';}// 函数节流setTimeout(function() {lock = true; }, 500);}</script>
</body>
</html>
7.动画效果开发3 - 呼吸轮播图特效
<!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>* {margin: 0;padding: 0;}.carousel {width: 650px;height: 360px;border: 1px solid #000;margin: 50px auto;position: relative;}.carousel ul {list-style: none;}.carousel ul li {position: absolute;top: 0;left: 0;/* 透明度都是0 */opacity: 0;transition: opacity 1s ease 0s;}/* 只有第一张透明度是1 */.carousel ul li:first-child {opacity: 1;}.carousel .leftbtn {position: absolute;left: 20px;top: 50%;margin-top: -25px;width: 50px;height: 50px;background-color: rgb(28, 180, 226);border-radius: 50%;}.carousel .rightbtn {position: absolute;right: 20px;top: 50%;margin-top: -25px;width: 50px;height: 50px;background-color: rgb(28, 180, 226);border-radius: 50%;}</style>
</head><body><div class="carousel"><ul id="list"><li><img src="images/beijing/0.jpg" alt=""></li><li><img src="images/beijing/1.jpg" alt=""></li><li><img src="images/beijing/2.jpg" alt=""></li><li><img src="images/beijing/3.jpg" alt=""></li><li><img src="images/beijing/4.jpg" alt=""></li></ul><a href="javascript:;" class="leftbtn" id="leftbtn"></a><a href="javascript:;" class="rightbtn" id="rightbtn"></a></div><script>// 得到按钮和ul,ul整体进行运动var leftbtn = document.getElementById('leftbtn');var rightbtn = document.getElementById('rightbtn');var list = document.getElementById('list');var lis = list.getElementsByTagName('li');// 当前是第几张图显示var idx = 0;// 节流var lock = true;// 右按钮rightbtn.onclick = function () {// 判断节流if (!lock) return;lock = false;// 还没有改idx,此时的idx这个图片就是老图,老图淡出lis[idx].style.opacity = 0;idx++;if (idx > 4) idx = 0;// 改了idx,此时的idx这个图片就是新图,新图淡入lis[idx].style.opacity = 1;// 动画结束之后,开锁setTimeout(function () {lock = true;}, 1000);}// 左按钮leftbtn.onclick = function () {// 判断节流if (!lock) return;lock = false;// 还没有改idx,此时的idx这个图片就是老图,老图淡出lis[idx].style.opacity = 0;idx--;if (idx < 0) idx = 4;// 改了idx,此时的idx这个图片就是新图,新图淡入lis[idx].style.opacity = 1;// 动画结束之后,开锁setTimeout(function () {lock = true;}, 1000);}</script>
</body></html>
二、BOM
BOM(Browser Object Model)指的是浏览器对象模型,它是一个用于描述浏览器窗口和页面文档的JavaScript对象集合。BOM主要提供了与浏览器窗口进行交互的方法和接口,使得开发者可以通过JavaScript来控制浏览器窗口的行为,例如移动、调整大小、弹出新的窗口等。
BOM的一些主要对象包括:
- window:代表浏览器窗口,同时也是全局对象,是BOM的核心对象。
- document:代表整个HTML文档,虽然通常被认为是DOM(文档对象模型)的一部分,但它也是BOM的一部分,因为它是
window
对象的属性。 - navigator:包含有关浏览器的信息,如名称、版本和系统信息。
- screen:提供有关用户屏幕分辨率、可用颜色数等信息。
- history:表示浏览器历史记录,可以通过它来前进或后退页面。
- location:包含当前URL的信息,并允许你导航到新的页面或修改当前页面的URL。
以下是一些BOM对象的简单示例:
// 使用 window 对象
window.alert("Hello, world!"); // 弹出警告框// 使用 navigator 对象
console.log(navigator.userAgent); // 输出浏览器的用户代理字符串// 使用 screen 对象
console.log(screen.width + "x" + screen.height); // 输出屏幕分辨率// 使用 history 对象
history.back(); // 回退到上一个页面
history.forward(); // 前进到下一个页面// 使用 location 对象
console.log(location.href); // 输出当前页面的URL
location.href = "https://www.example.com"; // 跳转到新的URL
需要注意的是,BOM没有统一的标准,因此不同浏览器的实现可能会有所不同。随着HTML5的推出,一些BOM的功能被标准化了,例如window
对象和location
对象的部分功能。
1.Windows对象
(1)全局变量是windows变量的属性
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><script>var a = 3;console.log(window.hasOwnProperty('a')); // trueconsole.log(window.a); // 3</script><script src="js/js1.js"></script><script src="js/js2.js"></script>
</body>
</html>
(2)内置函数普遍是window的方法
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><script>console.log(window.hasOwnProperty('setInterval')); // truewindow.setInterval(function () {window.console.log('你好');}, 1000);</script>
</body></html>
(3)窗口尺寸相关属性
(4)resize事件
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><script>// 监听窗口改变尺寸事件window.onresize = function () {var root = document.documentElement;console.log('窗口改变尺寸了', root.clientWidth, root.clientHeight);};</script>
</body>
</html>
(5)已动高度
<!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>body {height: 5000px;}</style>
</head>
<body><script>// 这两个值是相同的console.log(window.scrollY);console.log(document.documentElement.scrollTop);</script>
</body>
</html>
(6)scroll事件
<!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>body {height: 5000px;}</style>
</head>
<body><script>window.onscroll = function () {console.log('窗口卷动了', window.scrollY);};</script>
</body>
</html>
2.Navigator对象
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><script>console.log('浏览器品牌', navigator.appName);console.log('浏览器版本', navigator.appVersion);console.log('用户代理', navigator.userAgent);console.log('操作系统', navigator.platform);</script>
</body>
</html>
3.History对象
temp.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><h1>我是temp网页</h1><a href="history方法.html">去看history方法页面</a>
</body>
</html>
history.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><h1>我是history方法网页</h1><button id="btn">回退</button><a href="javascript:history.back();">回退</a><script>var btn = document.getElementById('btn');btn.onclick = function() {// history.back();//同一个意思history.go(-1);};</script>
</body>
</html>
4.Location对象
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><button id="btn1">点我去看慕课</button><button id="btn2">刷新</button><script>var btn1 = document.getElementById('btn1');var btn2 = document.getElementById('btn2');btn1.onclick = function () {window.location = 'http://www.imooc.com';};btn2.onclick = function () {window.location.reload(true);};</script>
</body>
</html>
5.BOM特效开发-01
<!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>body {height: 5000px;background-image: linear-gradient(to bottom, blue, green, yellow);}.backtotop {width: 60px;height: 60px;background-color: rgba(255, 255, 255, .6);position: fixed;bottom: 100px;right: 100px;/* 小手状 */cursor: pointer;}</style>
</head><body><div class="backtotop" id="backtotopBtn">返回顶部</div><script>var backtotopBtn = document.getElementById('backtotopBtn');var timer;backtotopBtn.onclick = function () {// 设表先关clearInterval(timer);// 设置定时器timer = setInterval(function () {// 不断让scrollTop减少document.documentElement.scrollTop -= 200;// 定时器肯定要停if (document.documentElement.scrollTop <= 0) {clearInterval(timer);}}, 20);};</script>
</body></html>
6.BOM特效开发-02
主要会用到事件委托。data-n是自定义属性。
<!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>* {margin: 0;padding: 0;}.content-part {width: 1000px;margin: 0px auto;margin-bottom: 30px;background-color: #ccc;font-size: 50px;}.floornav {position: fixed;right: 40px;top: 50%;margin-top: -100px;width: 120px;height: 200px;background-color: orange;}.floornav ul {list-style: none;}.floornav ul li {width: 120px;height: 40px;line-height: 40px;text-align: center;font-size: 26px;/* 小手指针 */cursor: pointer;}.floornav ul li.current {background: purple;color: white;}</style>
</head><body><nav class="floornav"><ul id="list"><li data-n="科技" class="current">科技</li><li data-n="体育">体育</li><li data-n="新闻">新闻</li><li data-n="娱乐">娱乐</li><li data-n="视频">视频</li></ul></nav><section class="content-part" style="height:674px;" data-n="科技">科技栏目</section><section class="content-part" style="height:567px;" data-n="体育">体育栏目</section><section class="content-part" style="height:739px;" data-n="新闻">新闻栏目</section><section class="content-part" style="height:574px;" data-n="娱乐">娱乐栏目</section><section class="content-part" style="height:1294px;" data-n="视频">视频栏目</section><script>// 使用事件委托给li添加监听var list = document.getElementById('list');var contentParts = document.querySelectorAll('.content-part');var lis = document.querySelectorAll('#list li');list.onclick = function (e) {if (e.target.tagName.toLowerCase() == 'li') {// getAttribute表示得到标签身上的某个属性值var n = e.target.getAttribute('data-n');// 可以用属性选择器(就是方括号选择器)来寻找带有相同data-n的content-partvar contentPart = document.querySelector('.content-part[data-n=' + n + ']');// 让页面的卷动自动成为这个盒子的offsetTop值document.documentElement.scrollTop = contentPart.offsetTop;}}// 在页面加载好之后,将所有的content-part盒子的offsetTop值推入数组var offsetTopArr = [];// 遍历所有的contentPart,将它们的净位置推入数组for (var i = 0; i < contentParts.length; i++) {offsetTopArr.push(contentParts[i].offsetTop);}// 为了最后一项可以方便比较,我们可以推入一个无穷大offsetTopArr.push(Infinity);console.log(offsetTopArr);// 当前所在楼层var nowfloor = -1;// 窗口的卷动window.onscroll = function () {// 得到当前的窗口卷动值var scrollTop = document.documentElement.scrollTop;// 遍历offsetTopArr数组,看看当前的scrollTop值在哪两个楼层之间for (var i = 0; i < offsetTopArr.length; i++) {if (scrollTop >= offsetTopArr[i] && scrollTop < offsetTopArr[i + 1]) {break;}}// 退出循环的时候,i是几,就表示当前楼层是几// 如果当前所在楼层,不是i,表示换楼了if (nowfloor != i) {console.log(i);// 让全局变量改变为这个楼层号nowfloor = i;// 设置下标为i的项有curfor (var j = 0; j < lis.length; j++) {if (j == i) {lis[j].className = 'current';} else {lis[j].className = '';}}}};</script>
</body></html>