DOM👀
🌈 什么是DOM
DOM(Document Object Model,文档对象模型)是JavaScript操作HTML文档的接口,使文档操作变得非常优雅、简便。
🔔nodeType属性值
节点的nodeType属性可以显示这个节点具体的类型。
| nodeType值 |
节点类型 |
| 1 |
元素节点 |
| 3 |
文本节点 |
| 8 |
注释节点 |
| 9 |
document节点 |
| 10 |
DTD节点(文档类型声明) |
⭕DOM节点操作
📍访问元素节点的常用方法
| 方法 |
功能 |
兼容性 |
| document.getElementById() |
通过id获得元素 |
IE6 |
| document.getElementsByTagName() |
通过标签名获得元素数组 |
IE6 |
| document.getElementsByClassName() |
通过类名获得元素数组 |
IE9 |
| document.querySelect() |
通过选择器获得元素 |
IE8部分兼容、IE9完全兼容 |
| document.querySelectAll() |
通过选择器获得元素数组 |
IE8部分兼容、IE9完全兼容 |
❗拓展:动态获取元素
1.getElementsByClassName()方法和getElementsByClassName()方法可以动态获取元素,可以理解为,页面上增加或者删除元素时,获取的元素个数可以改变;
2.而querySelectorAll()方法做不到此效果。
📍节点的关系
| 关系 |
考虑所有节点 |
只考虑元素节点 |
| 子节点 |
childNode |
children |
| 父节点 |
parentNode |
parentNode |
| 第一个子节点 |
firstChild |
firstElementChild |
| 最后一个子节点 |
lastChild |
lastElementChild |
| 前一个兄弟节点 |
previousSibling |
previousElementSibling |
| 后一个兄弟节点 |
nextSibling |
nextElementSibling |
❗注意:文本节点也属于节点
在标准的W3C规范中,空白文本节点也应该算作节点,但是在IE8及以前的浏览器中会有一定的兼容问题,它们不把空文本节点当做节点
💡书写IE6也能兼容的“寻找所有元素子节点”函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| <!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>我是一段落1</p> <p>我是一段落2</p> 我是文本文本文本 <p>我是一段落3</p> <p>我是一段落4</p> <p>我是一段落5</p> </div> <script>
var oBox = document.getElementById('box') var arr_p = oBox.childNodes console.log(arr_p) function getElementChild(obj) { var arrChild = obj.childNodes var result = [] for (var i = 0 ; i < arrChild.length ; i ++) { arrChild[i].nodeType === 1 ? result.push(arrChild[i]) : '' } return result } console.log(getElementChild(oBox)) </script> </body> </html>
|
💡封装一个的函数,这个的函数可以返回元素的前一个元素兄弟节点(兼容到IE6),类似previousE1ementsib1ing的功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function getPreviousElementSibling(bro) { var o = bro; while (o.previousSibling != null) { if (o.previousSibling.nodeType === 1) return o.previousSibling; o = o.previousSibling } return null }
console.log(getPreviousElementSibling(getElementChild(oBox)[3]))
|
💡**封装第三个函数,这个函数可以返回元素的所有元素兄弟节点 **
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| function getElementSibling(bro){ var o = bro var previous = [] var next = [] while(o.previousSibling != null) { if (o.previousSibling.nodeType === 1) { previous.unshift(o.previousSibling) } o = o.previousSibling } o = bro while(o.nextSibling != null) { if (o.nextSibling.nodeType === 1) { next.push(o.nextSibling) } o = o.nextSibling } return previous.concat(next) } console.log(getElementSibling(getElementChild(oBox)[2]))
|
📍改变元素节点中的内容
- innerHTML属性:能以HTML语法设置节点中的内容
- innerText属性:只能以纯文本的形式设置节点中的内容
📍改变元素节点的CSS样式
1 2 3 4
| oBox.style.backgroundColor ='red' oBox.style.backgroundImage ='url(images/1.jpg)' oBox.style.fontSize ='32px'
|
📍改变元素节点的HTML属性
1 2
| 标准W3C属性,如src、href等等,只需要直接打点进行更改即可 oImg.src='images/2.jpg'
|
1 2 3 4
| 不符合W3C标准的属性,要使用setAttribute()和getAttribute()来设置、读取 oBox.setAttribute('data-n', 10) var n=oBox.getAttribute('data-n') alert(n)
|
⭕节点的创建
**document.creatElement()**方法用于创还能一个指定tagname的HTML元素
var oDiv = document.creatElement(‘div’)
❗“孤儿节点”
新创建出的节点是“孤儿节点”,这意味着它并没有被挂载到DOM树上,我们无法看见它
必须继续使用**appendChild() 或 insertBefore()**方法将孤儿节点插入到DOM树上
📍appendChild()
任何已经在DOM树上的节点,都可以调用appendChild()方法,它可以将孤儿节点挂载到它的内部,成为它的最后一个子节点
📍insertBefore()
任何已经在DOM树上的节点,都可以调用insertBefore0)方法,它可以将孤儿节点挂载到它的内部,成为它的“标杆子节点”之前的节点
1
| 父节点.insertBefor(孤儿节点,标杆节点)
|
💡请动态创建出一个20行12列的表格
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <!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> table { border-collapse: collapse; } table td { width: 50px; height: 20px; border: 1px solid #000; } </style> </head> <body> <table id="table" border></table> <script> var oTable = document.getElementById('table') for (var i = 0 ; i < 20 ; i++) { var oTr = document.createElement('tr') oTable.appendChild(oTr) for(var j = 0 ; j < 12 ; j++) { var oTd = document.createElement('td') oTr.appendChild(oTd) } } </script> </body> </html>
|
💡制作九九乘法表(以上代码改动版)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| var oTable = document.getElementById('table'); for (var i = 1; i <= 9; i++) { var oTr = document.createElement('tr'); oTable.appendChild(oTr); for (var j = 1; j <= i; j++) { var oTd = document.createElement('td'); oTd.innerText = i + '乘' + j + '等于' + i * j; oTr.appendChild(oTd); } }
|
📍移动节点
如果将已经挂载到DOM树上的节点成为appendChild()或者insertBefore()的参数,这个节点将会被移动
1 2
| 新父节点.appendChild(已经有父亲的节点) 新父节点.insertBefore(已经有父亲的节点,标杆节点)
|
🔔这意味着一个节点不能同时位于DOM树的两个位置
📍删除节点
removeChild() 方法从DOM中删除一个节点
1
| 父节点.removeChild(要删除的子节点)
|
🔔节点不能主动删除自己,必须有父节点删除它
📍克隆节点
cloneNode() 方法可以克隆节点,克隆出的节点是“孤儿节点”
1 2
| var 孤儿节点 = 老节点.cloneNode() var 孤儿节点 = 老节点.cloneNode(true)
|
🔔参数是一个布尔值,表示是否采用深度克隆:如果为true,则该节点的所有后代节点也都会被克隆;如果为false,则只克隆该节点本身
⭕事件监听
📍常见的鼠标事件监听
| 事件名 |
事件描述 |
| onclick |
当鼠标单击某个对象 |
| ondblclick |
当鼠标双击某个对象 |
| onmousedown |
当鼠标按键在某个对象上被按下 |
| onmouseup |
当鼠标按键在某个对象上被松开 |
| onmousemove |
当鼠标按键在某个对象上被移动 |
| onmouseenter |
当鼠标进入某个对象(相似事件onmouseover) |
| onmouseleave |
当鼠标离开某个对象(相似事件onmouseout) |
📍常见的键盘事件监听
| 事件名 |
事件描述 |
| onkeypress |
当某个键盘的键被按下(系统按钮如箭头和功能键无法得到识别) |
| onkeydown |
当某个键盘的键被按下(系统按钮可以识别,并且会优先于onkeypress发生) |
| onkeyup |
当某个键盘的按键被松开 |
📍常见的表单事件监听
| 事件名 |
事件描述 |
| onchange |
当用户修改完了域的内容 |
| oninput |
当用户正在修改域的内容 |
| onfocus |
当某元素获得焦点(比如tab键或鼠标点击) |
| onblur |
当某元素失去焦点 |
| onsubmit |
当表单被提交 |
| onreset |
当表单被重置 |
📍常见的页面事件监听
| 事件名 |
事件描述 |
| onload |
当页面或图像被完全加载 |
| onunload |
当用户退出页面 |
🔔更多关于窗口或者页面的事件,后续在BOM章节介绍
⭕事件传播
事件的传播:先从外到内,然后再从内到外
从外到内:捕获阶段
从内到外:冒泡阶段
❗onxxx这样的写法只能监听到冒泡阶段
🔔addEventListener() 方法
DOM0级事件监听:只能监听冒泡阶段
oBox.onclick = function() { }
DOM2级事件监听:
oBox.addEventListener(‘click’ , function () {
} , true ) // ‘click’ 为事件名(注意不加on),true为监听捕获阶段,flase为监听冒泡阶段
❗注意事项
1.最内部元素不再区分捕获和冒泡阶段,会先执行写在前面的监听,然后执行后写的监听
2.如果给元素设置相同的两个或多个同名事件,则DOM0级写法后面写的会覆盖先写的;而DOM2级会按顺序执行
💡小练习:实现计算器功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| <!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="num1" /> <select id="operate"> <option value="+">+</option> <option value="-">-</option> <option value="*">*</option> <option value="/">/</option> </select> <input type="text" id="num2" /> <input type="button" value=" = " id="btn" /> <input type="text" id="result" /> <script> var num1 = document.getElementById('num1'); var num2 = document.getElementById('num2'); var result = document.getElementById('result'); var operate = document.getElementById('operate'); var btn = document.getElementById('btn'); btn.addEventListener('click', function () { switch (operate.value) { case '+': result.value = Number(num1.value) + Number(num2.value); break; case '-': result.value = num1.value - num2.value; break; case '*': result.value = num1.value * num2.value; break; case '/': result.value = num1.value / num2.value; break; } }); </script> </body> </html>
|
⭕事件对象
事件处理函数提供了一个形式参数,它是一个对象,封装了本次事件的细节
这个参数通常个用单词event或字母e来表示
oBox.onmouse = function (e) {
// 对象e就是这次事件的“事件对象”
};
📍鼠标位置
| 属性 |
属性描述 |
| clientX |
鼠标指针相对于浏览器的水平坐标 |
| clientY |
鼠标指针相对于浏览器的垂直坐标 |
| pageX |
鼠标指针相对于整张网页的水平坐标 |
| pageY |
鼠标指针相对于整张网页的垂直坐标 |
| offsetX |
鼠标指针相对于事件源元素的水平坐标 |
| offsetY |
鼠标指针相对于事件源元素的垂直坐标 |
❗注意:offsetX / offsetY 是鼠标相对事件源元素的坐标,如果监听的对象内部有元素,当鼠标移动到内部的对象时,offsetX / offsetY 的值会重新根据内部元素来计算了(此时事件源元素发生了改变,所以使用时需要避免有内部元素存在)
📍e.charCode 和 e.keyCode 属性
e.charCode属性通常用于onkeypress事件中,表示用户输入的字符的“字符码”
e.keyCode属性通常用于onkeydown 和 onkeyup事件中,表示用户按下的按键的“键码”
🔔charCode字符码
| 字符 |
字符码 |
| 数字0~数字9 |
48~57 |
| 大写字母A~Z |
65~90 |
| 小写字母a~z |
97~122 |
🔔keyCode键码
| 按键 |
键码 |
| 数字0~数字9 |
48~57(同charCode键码完全相同) |
| 字母不分大小a~z |
6590(同charCode键码的大写字母AZ,而keyCode不分大小写,一律为65~90) |
| 四个方向键 ← ↑ → ↓ |
37、38、39、40 |
| 回车键 |
13 |
| 空格键 |
32 |
💡小案例:控制盒子移动
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| <!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; } #box1 { position: absolute; top: 35%; left: 50%; margin-top: -50px; margin-left: -50px; width: 100px; height: 100px; background-color: orange; transition: all .2s linear 0s; } #box2 { position: fixed; bottom: 50px; left: 50%; margin-left: -120px; width: 240px; height: 60px; background-color: rgba(0, 0, 0, 0.5); border-radius: 20px; transition: all .2s linear 0s; } #text { text-align: center; line-height: 60px; font-size: 30px; color: white; letter-spacing: 5px; } </style> </head> <body> <div id="box1"></div> <div id="box2"><p id="text">方块向上移动</p></div> <script> var oBox1 = document.getElementById('box1'); var oBox2 = document.getElementById('box2'); var oText = document.getElementById('text') var t = 35; var l = 50; window.addEventListener('keydown', function (e) { switch (e.keyCode) { case 37: l -= 5 oText.innerText = '方块向左移动' break; case 38: t -= 5 oText.innerText = '方块向上移动' break; case 39: l += 5 oText.innerText = '方块向右移动' break; case 40: t += 5 oText.innerText = '方块向下移动' break; } oBox1.style.top = t + '%' oBox1.style.left = l + '%' }); </script> </body> </html>
|
📍e.preventDefault()方法
e.preventDefault() 方法来阻止事件产生的“默认动作”
一些特殊的业务需求,需要阻止事件的“默认动作”
💡小案例1:制作文本框,只能让用户在其中输入小写字母和数字,其他字符输入没有效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <!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.addEventListener('keypress', function (e) { console.log(e.charCode) if (!(e.charCode >= 48 && e.charCode <= 57 || e.charCode >= 97 && e.charCode <= 122)) { e.preventDefault() } }) </script> </body> </html>
|
💡小案例2:制作鼠标滚轮事件:当鼠标在盒子中向下滚动时,数字加1;反之,数字减1
鼠标滚轮事件是 onmousewheel ,它的事件对象e提供deltaY属性表示鼠标滚动方向,向下滚动时返回正值,向上滚动时返回负值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <!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: orange; } body { height: 2000px; } </style> </head> <body> <div id="box"></div> <p id="num">0</p> <script> var oBox = document.getElementById('box') var oNum = document.getElementById('num') var num = 0 oBox.addEventListener('mousewheel',function(e){ e.deltaY > 0 ? num++ : num-- oNum.innerText = num e.preventDefault() }) </script> </body> </html>
|
📍e.stopPropagation()方法
e.stopPropagation()方法用来阻止事件继续传播(捕获阶段和冒泡阶段都可以阻止)
在一些场合,非常有必要切断事件继续传播,否则会造成页面特效显示出bug
💡小案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| <!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; } #mask { position: absolute; display: none; top: 50%; left: 50%; margin-left: -300px; margin-top: -150px; width: 600px; height: 300px; background-color: rgba(0, 0, 0, 0.5); } </style> </head> <body> <input type="button" value="显示弹出层" id="btn" /> <div id="mask"></div> <script> var oMask = document.getElementById('mask'); var oBtn = document.getElementById('btn'); oBtn.addEventListener('click', function (e) { oMask.style.display = 'block'; e.stopPropagation(); }); document.addEventListener('click', function (e) { oMask.style.display = 'none'; }); oMask.addEventListener('click', function (e) { e.stopPropagation(); }); </script> </body> </html>
|
⭕事件委托
❗批量添加事件监听的性能问题
1.每一个事件监听注册都会消耗一定的系统内存,而批量添加事件会导致监听数量太多,内存消耗会非常大。
2.实际上,每一个的事件处理函数都是不同的函数,这些函数本身也会占用内存。
❗动态绑定事件的问题
1.新增元素必须分别添加事件监听,不能自动获得事件监听。
2.大量事件监听、大量事件处理函数都会产生大量消耗内存。
📍事件委托:利用事件冒泡机制,将后代元素事件委托给祖先元素
🔔e.target 和 e.currentTarget 属性
| 属性 |
属性描述 |
| target |
触发此事件的最早元素,即 “事件源元素” |
| currentTarget |
事件处理程序附加到的元素(作用和事件处理函数中的this功能类似) |
💡小案例:给列表项批量添加事件监听,并且点击按钮新增一个列表项,这个列表项也要绑定事件(点击li文字变红)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| <!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> li { cursor: pointer; } </style> </head> <body> <button id="btn">点击新增列表项</button> <ul id="list"> <li>列表项</li> <li>列表项</li> <li>列表项</li> </ul> <script> var oBtn = document.getElementById('btn'); var oList = document.getElementById('list'); oList.addEventListener('click', function (e) { e.target.style.color = 'red'; }); oBtn.addEventListener('click', function () { var oLi = document.createElement('li'); oLi.innerText = '我是新创建的li'; oList.appendChild(oLi); }); </script> </body> </html>
|
🔔事件委托的使用场景
1.当有大量类似元素需要批量添加事件监听时,使用事件委托可以减少内存开销。
2.当有动态元素节点上树时,使用事件委托可以让新上树的元素具有事件监听
❗使用事件委托时需要注意的事项
1.onmouseenter 和 onmouseover 都表示 ”鼠标移入“,但是 onmouseenter 不冒泡(使用事件委托不生效),而 onmouseover 冒泡,所以不能委托不冒泡的事件给祖先元素
2.最内层元素不能再有额外的内层元素了,否则额外的内层元素触发事件是,最内层元素不触发事件
💡页面上有一个记录待办事项的列表,在输入框中输入内容之后,点击添加按钮,可以将输入的内容添加到列表里。如果事情完成了,点击此项可以从列表里移除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| <!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> li { cursor: pointer; } </style> </head> <body> <input type="text" id="input" /> <button id="btn">添加</button> <ul id="list"></ul> <script> var oInput = document.getElementById('input'); var oBtn = document.getElementById('btn'); var oList = document.getElementById('list'); oBtn.addEventListener('click', function () { if (!oInput.value) return alert('内容不能为空'); var oLi = document.createElement('li'); oLi.innerText = oInput.value; oList.appendChild(oLi); oInput.value = ''; oInput.focus(); }); oList.addEventListener('click', function (e) { oList.removeChild(e.target); }); oInput.addEventListener('keypress', function (e) { if (e.charCode == 13) { oBtn.click(); } }); </script> </body> </html>
|
⭕定时器和延时器
📍 定时器
setInterval() 函数可以重复调用一个函数,在每次调用之间具有固定的时间间隔
1 2 3 4 5 6 7 8 9
| setInterval( function () { }, 1000);
setInterval( function (a, b) { }, 2000 , 76, 24)
|
💡具名函数也可以传入setInterval()
1 2 3 4 5
| var a = 0 function fun () { console.log(a++) } setInterval( fun, 2000)
|
📍清除定时器
clearInterval()函数可以清除一个定时器
1 2 3 4 5 6 7 8 9
|
var timer = setInterval( function () { }, 2000)
oBtn.addEventListener('click', function() { clearInterval(timer) })
|
💡小案例:计时器,从0开始,点击开始按钮每秒加1,点击暂停按钮停止加1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <!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 id="num">0</p> <button id="start">开始</button> <button id="stop">暂停</button> <script> var oNum = document.getElementById('num'); var oStart = document.getElementById('start'); var oStop = document.getElementById('stop'); var a = 0; var timer; oStart.addEventListener('click', function () { clearInterval(timer); timer = setInterval(function () { oNum.innerText = ++a; }, 1000); }); oStop.addEventListener('click', function () { clearInterval(timer); }); </script> </body> </html>
|
📍延时器
setTimeout() 函数可以设置一个延时器,当指定时间到了之后,会执行函数一次,不再重复执行。**
1 2 3 4
| setTimeout( function(){ }, 2000)
|
📍清除延时器
clearTimeout() 函数可以清除延时器,和clearInterval() 非常类似
💡小案例:创建一个延时器,2s后显示文字,点击取消按钮则取消文字显示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <!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 id="text"></p> <button id="cancel">取消</button> <script> var oText = document.getElementById('text'); var oCancel = document.getElementById('cancel'); var timer; timer = setTimeout(function () { oText.innerText = '我是2s后显示的文字!'; }, 2000); oCancel.addEventListener('click', function () { console.log('取消文字显示'); clearTimeout(timer); }); </script> </body> </html>
|
⭕异步语句
**异步:不会阻塞CPU继续执行其他语句,当异步完成时,会执行 “回调函数” ( callback) **
setInterval() 函数 和 setTimeout() 函数都是异步语句
⭕使用定时器实现动画 —— 视觉暂留原理
使用定时器实现动画较为不便:
- 不方便根据动画总时间计算步长
- 运动方向要设置正负
- 多种运动进行叠加较为困难(比如一个方形一边一移动一边变为圆形)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| <!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: 200px; height: 200px; background-color: orange; } </style> </head> <body> <button id="btn">开始移动</button> <div id="box"></div> <script> var oBtn = document.getElementById('btn'); var oBox = document.getElementById('box'); var l = 100; var timer; oBtn.addEventListener('click', function () { timer = setInterval(function(){ l += 10 if (l >= 1000) { clearInterval(timer) } oBox.style.left = l + 'px' },20) }); </script> </body> </html>
|
🔔JS和CSS3结合实现动画 —— 规避了定时器制作动画的缺点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| <!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: 200px; height: 200px; background-color: orange; } </style> </head> <body> <button id="btn">开始移动</button> <div id="box"></div> <script> var oBtn = document.getElementById('btn'); var oBox = document.getElementById('box'); var i = true; var lock = true; oBtn.addEventListener('click', function () { if (!lock) return;
oBox.style.transition = 'all 2s linear 0s'; if (i) { oBox.style.left = '1100px'; i = !i; } else { oBox.style.left = '100px'; i = !i; }
lock = false;
setTimeout(function () { lock = true; }, 2000); }); </script> </body> </html>
|
💡函数节流
函数节流:一个函数执行一次后,只有大于设定的执行周期后才允许执行第二次
函数节流非常容易实现,只需要借助setTimeout()延时器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
var lock = true;
function 需要节流的函数(){ if (!lock) return; lock = false; setTimeout(function () { lock = true; }, 2000); }
|
⭕动画效果开发
💡无缝连续滚动特效
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
| <!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 { position: relative; margin: 0 auto; margin-top: 50px; height: 100px; width: 640px; border: 1px solid #000; overflow: hidden; } #box ul { position: absolute; left: 0; width: 2000px; } #box ul li { list-style: none; float: left; height: 100px; width: 150px; margin-right: 10px; background-color: orange; text-align: center; line-height: 100px; color: white; font-size: 100px; } #box ul li.first { background-color: orange; } #box ul li.second { background-color: red; } #box ul li.third { background-color: green; } #box ul li.fourth { background-color: pink; } #box ul li.fiveth { background-color: blue; } </style> </head> <body> <div id="box"> <ul id="ul"> <li class="first">1</li> <li class="second">2</li> <li class="third">3</li> <li class="fourth">4</li> <li class="fiveth">5</li> </ul> </div> <script> var list = document.getElementById('ul'); list.innerHTML += list.innerHTML;
var left = 0; var timer;
function move() { clearInterval(timer); timer = setInterval(function () { left -= 10; if (left <= -790) { left = 0; } list.style.left = left + 'px'; }, 20); } move(); list.addEventListener('mouseenter', function () { clearInterval(timer); }); list.addEventListener('mouseleave', function () { move(); }); </script> </body> </html>
|
💡跑马灯轮播特效
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
| <!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 { position: relative; margin: 0 auto; margin-top: 50px; height: 400px; width: 640px; border: 1px solid #000; overflow: hidden; } #box ul { position: absolute; left: 0; width: 5000px; } #box ul li { list-style: none; float: left; height: 400px; width: 640px; margin-right: 10px; background-color: orange; text-align: center; line-height: 400px; color: white; font-size: 100px; } #box ul li.first { background-color: orange; } #box ul li.second { background-color: red; } #box ul li.third { background-color: green; } #box ul li.fourth { background-color: pink; } #box ul li.fiveth { background-color: blue; } #leftBtn,#rightBtn { position: absolute; top: 50%; margin-top: -25px; border-radius: 50%; width: 50px; height: 50px; background-color: rgba(0, 0, 0, 0.5); color: white; line-height: 50px; text-align: center; text-decoration: none; font-size: 30px; } #leftBtn { left: 20px; } #rightBtn { right: 20px; } </style> </head> <body> <div id="box"> <ul id="ul"> <li class="first">1</li> <li class="second">2</li> <li class="third">3</li> <li class="fourth">4</li> <li class="fiveth">5</li> </ul> <a href="javascript:;" id="leftBtn"><</a> <a href="javascript:;" id="rightBtn">></a> </div> <script> var list = document.getElementById('ul'); var leftBtn = document.getElementById('leftBtn'); var rightBtn = document.getElementById('rightBtn'); var idx = 0; var lock = true; list.style.transition = 'all .5s ease 0s'; var cloneLi = list.firstElementChild.cloneNode(true); list.appendChild(cloneLi); rightBtn.addEventListener('click', function () { if (!lock) return; lock = false;
list.style.transition = 'all .5s ease 0s'; idx++; list.style.left = idx * -650 + 'px'; if (idx == 5) { setTimeout(function () { list.style.transition = 'none'; list.style.left = 0; idx = 0; }, 500); } setTimeout(function () { lock = true; }, 500); });
leftBtn.addEventListener('click', function () { if (!lock) return; lock = false;
idx--; list.style.left = idx * -650 + 'px';
if (idx == -1) { list.style.transition = 'none'; idx = 5; list.style.left = idx * -650 + 'px'; setTimeout(function () { list.style.transition = 'all .5s ease 0s'; idx--; list.style.left = idx * -650 + 'px'; }, 0); }
setTimeout(function () { lock = true; }, 500); }); </script> </body> </html>
|
💡呼吸轮播特效
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
| <!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 { position: relative; margin: 0 auto; margin-top: 50px; height: 400px; width: 640px; border: 1px solid #000; } #box ul { position: relative; } #box ul li { position: absolute; list-style: none; height: 400px; width: 640px; text-align: center; line-height: 400px; color: white; font-size: 100px; opacity: 0; transition: all 1s ease 0s; } #box ul li:first-child{ opacity: 1; } #box ul li.first { background-color: orange; } #box ul li.second { background-color: red; } #box ul li.third { background-color: green; } #box ul li.fourth { background-color: pink; } #box ul li.fiveth { background-color: blue; } #leftBtn,#rightBtn { position: absolute; top: 50%; margin-top: -25px; border-radius: 50%; width: 50px; height: 50px; background-color: rgba(0, 0, 0, 0.5); color: white; line-height: 50px; text-align: center; text-decoration: none; font-size: 30px; } #leftBtn { left: 20px; } #rightBtn { right: 20px; } </style> </head> <body> <div id="box"> <ul id="ul"> <li class="first">1</li> <li class="second">2</li> <li class="third">3</li> <li class="fourth">4</li> <li class="fiveth">5</li> </ul> <a href="javascript:;" id="leftBtn"><</a> <a href="javascript:;" id="rightBtn">></a> </div> <script> var list = document.getElementById('ul'); var leftBtn = document.getElementById('leftBtn'); var rightBtn = document.getElementById('rightBtn'); var idx = 0; var lock = true; var arr = list.getElementsByTagName('li') rightBtn.addEventListener('click', function(){ arr[idx].style.opacity = 0 idx++ if (idx == 5) idx = 0 arr[idx].style.opacity = 1 }) leftBtn.addEventListener('click', function(){ arr[idx].style.opacity = 0 idx-- if (idx == -1) idx = 4 arr[idx].style.opacity = 1 })
</script> </body> </html>
|