DOM(document object model,文档对象模型)是JS操作HTML文档的接口,使文档操作变得非常优雅简便
特点:将文档表示为节点树
nodeType常用属性值
- 1:元素节点
- 3:文字节点
- 8:注释节点
- 9:document节点
- 10:DTD节点
访问节点元素
依靠document对象
- document.getElementById():通过Id得到元素
- document.getElementsByTagName():通过标签名得到元素数组
- document.getElementsByClassName():通过类名得到元素数组
- document.querySelector():通过选择器名得到元素
- document.querySelectorAll():通过选择器得到元素数组
getElementById
有相同id的元素,只能得到第一个
不管元素藏多深,都能通过id找到
延迟运行
在测试DOM代码时,通常JS代码一定要写道HTML节点后面,否则JS无法找到相应HTML节点
可以使用window.οnlοad=function(){}事件,使页面加载完毕后,在执行指定的代码
getElementsByTagName
数组方便遍历,从而可以批量操控元素节点
即使只有一个指定标签名的节点,也将得到长度为1的数组
任何一个节点元素也可以调用getElementsByTagName方法,从而得到其内部某种类的元素节点
getElementsByClassName
任何一个节点元素也可以调用getElementsByClassName方法,从而得到其内部某种类的元素节点
querySelector
只能得到页面上的一个元素,有多个元素符合条件,只能得到第一个
<body> <div id="box"> <p>段落</p> <p class="spec">段落</p> <p>段落</p> </div> </body>
<script type="text/javascript"> var the_p = document.querySelector('#box .spec'); console.log(the_p); </script>
document.querySelector("div[data-div]"); // 获取有 "data-div" 属性的第一个<
div
>元素
querySelectorAll
通过选择器得到元素数组
即使只有一个符合选择器的节点,也将得到长度为1的数组
节点的关系
父节点、子节点
- 子节点:childNodes
- 父节点:parentNode
- 第一个子节点:firstChild
- 最后一个子节点:lastChild
- 前一个兄弟节点:previousSibling
- 后一个兄弟节点:nextSibling
文本节点也属于节点
在标准W3C规范中,空白文本节点也应该算作节点,但因兼容问题
排除文本节点的干扰
- 子节点:children
- 父节点:parentNode
- 第一个子节点:firstElementChild
- 最后一个子节点:lastElementChild
- 前一个兄弟节点:previousElementSibling
- 后一个兄弟节点:nextElementSibling
封装节点关系函数
书写IE6也能兼容的寻找元素子节点、寻找前一个元素兄弟节点、获得某元素所有兄弟节点的函数
//寻找所有子节点 function getChildren(node){ var children = []; for(var i=0;i<node.childNodes.length;i++){ if(node.childNodes[i].nodeType==1){ children.push(node.childNodes[i]); } } return children; }
//寻找前一个兄弟节点 function getElementprevsibling(node){ var a = node; while(a.previousSibling != null){ if(a.previousSibling.nodeType==1){ return a.previousSibling; } a = a.previousSibling; } return null; }
//寻找所有兄弟节点 function getAllElementSibling(node){ var prevs = []; var nexts = []; var a = node; while(a.previousSibling != null){ if(a.previousSibling.nodeType==1){ prevs.unshift(a.previousSibling); } a = a.previousSibling; } a = node; while(a.nextSibling != null){ if(a.nextSibling.nodeType==1){ nexts.push(a.nextSibling); } a = a.nextSibling; } return prevs.concat(nexts); }
节点操作
如何改变元素节点中的内容
innerHTML:以HTML语法设置节点中的内容
innerText:以纯文本的形式设置节点中的内容
<body> <div id="box"></div> </body> <script type="text/javascript"> var oBox = document.getElementById('box'); oBox.innerHTML = '慕课网'; oBox.innerHTML = '<ul><li>1</li><li>2</li></ul>'; oBox.innerText = '慕课网'; </script>
如何改变元素节点的CSS样式
<style> .box{ width: 200px; height: 200px; border: 1px solid black; } </style> </head> <body> <div id="box" class="box"></div> </body> <script type="text/javascript"> var oBox = document.getElementById('box'); oBox.style.backgroundColor='red'; </script>
如何改变元素节点的HTML属性
只需要直接打点进行更改即可
不符合W3C标准的属性,要使用setAttribute()和getAttribute()来设置、读取
节点的创建
document.creatElement()方法用于创建一个指定的tagname的HTML元素
var op = document.createElement('p');
新创建的的节点是孤儿节点,没有挂载到DOM树上
必须使用appendChild()或inserBefore()将孤儿节点插入到DOM树上
appendChild():挂到最后,追加
父节点.appendChild(孤儿节点);
父节点.inserBefore(孤儿节点,标签节点);
动态创建20行12列的表格
<body> <table id="mytable"></table> </body> <script type="text/javascript"> var mytable = document.getElementById('mytable'); for(var i=0;i<20;i++){ var tr = document.createElement('tr'); for(var j=0;j<12;j++){ var td = document.createElement('td'); tr.appendChild(td); } mytable.appendChild(tr); } </script>
移动节点
如果已经挂载到DOM树上的节点成为appendChild()或inserBefore()的参数,这个节点将会移动
新父节点.appendChild(已有父亲的节点);
新父节点.inserBefore(已有父亲的节点,标签节点);
将会离开原来的父亲,前往新的父亲
这意味着一个节点不能同时位于DOM树的两个位置
删除节点
removeChild()从DOM中删除一个子节点
父节点.removeChild(要删除的节点);
节点不能主动删除自己,必须由父节点删除它
克隆节点
cloneNode()克隆出的节点是孤儿节点
var 孤儿节点 = 老节点.cloneNode();
var 孤儿节点 = 老节点.cloneNode(true);
true为深度克隆,则该节点的所有后代节点也都会被克隆,false为克隆节点本身不克隆内容
事件监听
最简单的设置事件监听的方法就是设置onxxx
常见的鼠标事件监听
- onclick:当鼠标单击某个对象
- ondblclick:当鼠标双击某个对象
- onmousedown:当某个鼠标按键在某个对象上被按下
- onmouseup:当某个鼠标按键在某个对象上被松开
- onmousemove:当某个鼠标按键在某个对象上被移动
- onmouseenter:当鼠标进入某个对象(相似事件onmouseover)
- onmouseleave:当鼠标离开某个对象(相似事件onmouseout)
- onmousewheel:鼠标滚轮事件,它的事件对象e提供deltaY属性,向下滚动为正值,向上滚动为负值
常见的键盘监听事件
- onkeypress:当某个键被按下(系统按钮如箭头键和功能键无法得到识别)
- onkeydown:当某个键盘的键被按下(系统按钮可以识别,并且会先于onkeypress发生)
- onkeyup:当某个键盘的键被松开
常见的表单事件监听
- onchange:当用户改变域的内容
- onfocus:当某元素获得焦点(比如tab键或鼠标监听)
- onblur:当某元素失去焦点
- onsubmit:当表单被提交
- onreset:当表单被重置
- oninput:正在输入
常见页面事件监听
onload:当页面或图像被完成加载
onunload:当用户退出页面
事件传播
当盒子嵌套时事件监听的执行顺序
先从外到内(捕获阶段),再从内到外(冒泡阶段)
onxxx只能监听冒泡阶段
addEventListener()方法:
DOM0级事件监听:只能监听冒泡阶段
DOM2级事件监听:
box.addEventListener('click', function (){ },true)//true监听捕获阶段,false监听冒泡阶段
注意事项
最内部元素不再区分捕获和冒泡阶段,先执行写在最前面的监听
如果给的元素设置相同的两个或多个同名文件,则DOM0级写法后面写的会覆盖先写的,而DOM2级会按顺序执行
事件对象
事件处理函数提供一个形式参数,它是一个对象,封装了本次事件的细节
这个参数通常用单词event或字母e来表示
box1.onclick = function (e){ console.log('我是box1'); }
对象e就是这次事件的事件对象
鼠标位置属性
- clientX:鼠标指针相对于浏览器的水平坐标
- clientY:鼠标指针相对于浏览器的垂直坐标
- pageX:鼠标指针相对于整张网页的水平坐标
- pageY:鼠标指针相对于整张网页的垂直坐标
- offsetX:鼠标指针相对于事件源元素的水平坐标
- offsetY:鼠标指针相对于事件源元素的垂直坐标
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
- a-z:65-90(不区分大小写)
- 上下左右:37-40
- 回车:13
- 空格:32
e.preventDefault()方法:阻止事件产生默认动作
<p> <input type="text" id="field"> </p> </body> <script type="text/javascript"> var field = document.getElementById('field'); field.onkeypress = function (e){ if(!(e.charCode>=48&&e.charCode<=57||e.charCode>=97&&e.charCode<=122)){ e.preventDefault();//阻止了默认事件,即输入字符 } }
<body> <div id="box"></div> <h1 id="info">0</h1> </body> <script type="text/javascript"> var box = document.getElementById('box'); var info = document.getElementById('info'); var a = 0; box.onmousewheel = function (e){ //阻止默认事件,当用户在盒子里滚动鼠标时,不会引发页面的滚动 e.preventDefault(); if(e.deltaY>0){ a--; }else{ a++; } info.innerText = a; } </script>
e.storPropagation()方法:阻止事件继续传播
制作一个弹出层:点击按钮显示弹出层,点击网页任意地方,弹出层关闭
<body> <button id="btn">按我</button> <div class="model" id="box"></div> </body> <script type="text/javascript"> var btn = document.getElementById('btn'); var box = document.getElementById('box'); btn.onclick = function (e){ //阻止事件继续传播到页面 e.stopPropagation(); box.style.display = 'block'; } document.onclick = function (){ box.style.display = 'none'; } box.onclick = function (e){ //点击弹出层内部不能关闭 e.stopPropagation(); } </script>
事件委托
批量添加事件监听
<script type="text/javascript"> var list = document.getElementById('list'); var lis = list.getElementsByTagName('li'); for(var i =0;i<lis.length;i++){ lis[i].onclick = function (){ this.style.color='red'; } } </script>
批量添加事件监听的性能问题
每一个事件监听注册都会消耗一定的系统内存,而批量添加事件会导致监听数量太多,内存消耗会非常大
实际上每个li的事件处理函数都是不同的函数,这些函数也会占用内存
新增元素动态绑定事件
点击按钮增加li元素,且每个li元素有点击事件监听
<button id="btn">按我添加新列表项</button> <ul id="list"></ul> </body> <script type="text/javascript"> var btn = document.getElementById('btn'); var list = document.getElementById('list'); //按钮点击事件 btn.onclick = function (){ //创建孤儿节点 var li = document.createElement('li'); li.innerHTML = '我是列表项'; //上树 list.appendChild(li); //给新创建的li节点添加点击事件 li.onclick = function (){ this.style.color = 'red'; } }
利用事件冒泡的机制,将后代元素的事件委托给祖先元素
e.target属性:触发此事件的最早元素,即事件源元素
e.currentTarget属性:事件处理程序附加到的元素
<body> <ul id="list"> <li>111</li> <li>111</li> <li>111</li> </ul> </body> <script type="text/javascript"> var list = document.getElementById('list'); list.onclick = function (e){ e.target.style.color='red';//li变红 e.cueerntTarget.style.color='red';//全变红,即点击函数附加在了ul所以整个ul变红 } </script>
<body> <button id="btn">按我</button> <ul id="list"></ul> </body> <script type="text/javascript"> var btn = document.getElementById('btn'); var list = document.getElementById('list'); btn.onclick = function (){ var li =document.createElement('li'); li.innerText = '111'; list.appendChild(li); } list.onclick = function (e){ e.target.style.color='red'; } </script>
事件委托的使用场景
当有大量类似元素需要批量添加事件监听时,使用事件委托可以减少内存开销
当有动态元素节点上树时,使用事件委托可以让新上树的元素具有事件监听
注意事项
onmouseenteer和onmouseover的区别:onmouseenteer不冒泡,onmouseover冒泡
不能委托不冒泡的事件给祖先元素
最内层的元素不能再有额外的内层元素了
定时器和延时器
定时器
setInterval()函数可以重复调用一个函数,在每次调用之间具有固定的时间间隔
第一个参数是函数,第二个参数是间隔时间,以毫秒为单位,1000毫秒等于1秒
具名函数也能传入setInterval,具名函数在第一个参数不能加括号
清除定时器
clearInterva()清除一个定时器
var timer = setInterval(function (){},2000); btn.onclick = function (){ clearInterval(timer); }
<body> <h1 id="info">0</h1> <button id="btn1">开始</button> <button id="btn2">暂停</button> </body> <script type="text/javascript"> var info = document.getElementById('info'); var btn1 = document.getElementById('btn1'); var btn2 = document.getElementById('btn2'); var a = 0; var timer;//设置一个全局变量 btn1.onclick = function (){ //为了防止定时器叠加,在设置定时器之前先清除定时器 clearInterval(timer); //更改全局变量timer的值为一个定时器实体 timer = setInterval(function (){ info.innerText = ++a; },1000); } //连续点击开始,会导致定时器效果叠加 btn2.onclick = function (){ clearInterval(timer); } </script>
延时器
setTimeout()函数可以设置一个延时器,当指定时间到了之后,会执行函数一次,不再重复执行
clearTimeout()函数取消延时器
初步认识异步语句
异步:不会阻塞CPU继续执行其他语句,当异步完成时,会执行回调函数(callback)
使用定时器实现动画
利用视觉暂留原理
JS和CSS3结合实现动画
<body> <button id="btn">开始</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{ box.style.left = '100px'; pos=1; } //瞬间移动,由于有过度所以是动画 } </script>
函数节流
一个函数执行一次后,只有大于设定的执行周期后才允许执行第二次
var lock = true; function 需要节流的函数名(){ //如果锁是关闭状态,则不执行 if(!lock) return; //函数核心语句 //关锁 lock = false; //指定毫秒数后将锁打开 setTimeout(function (){ lock = true; },2000); }
动画效果开发1——无缝连续滚动特效
<body> <div id="box" class="box"> <ul id="list"> <li><img src="number/0.png" alt=""></li> <li><img src="number/1.png" alt=""></li> <li><img src="number/2.png" alt=""></li> <li><img src="number/3.png" alt=""></li> <li><img src="number/4.png" alt=""></li> <li><img src="number/5.png" alt=""></li> </ul> </div> </body> <script type="text/javascript"> var box = document.getElementById('box'); var list = document.getElementById('list'); //复制多一遍所有的li list.innerHTML += list.innerHTML; //全局变量,表示当前list的left值 var left = 0; var timer; function move(){ //设表先关,防止表累积 clearInterval(timer); timer = setInterval(function (){ left -= 4; if(left<=-1260){ left = 0; } list.style.left = left + 'px'; },20); } move(); //鼠标进入停止计时器 box.onmouseenter = function (){ clearInterval(timer); } //鼠标离开继续定时器 box.onmouseleave = function (){ move(); } </script>
注意将ul设置为相对定位
动画效果开发2——跑马灯轮播图特效
<html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> *{ margin: 0; padding: 0; } .carousel{ width: 650px; height: 360px; border: 1px solid black; margin: 50px auto; overflow: hidden; position: relative; //儿子绝对定位,父亲相对定位 } .carousel ul{ list-style: none; width: 6000px; position: relative; left: 0px; //如果不加left:0会导第一张切换第二张时没有过渡 transition: left 0.5s ease 0s; } .carousel ul li{ float: left; } .carousel .leftbtn{ position: absolute; left: 20px; width: 50px; height: 50px; background-color: orange; border-radius: 50%; top: 50%; margin-top: -25px; } .carousel .rightbtn{ position: absolute; right: 20px; width: 50px; height: 50px; background-color: orange; border-radius: 50%; top: 50%; margin-top: -25px; } </style> </head> <body> <div class="carousel"> <ul id="list"> <Li><img src="beijing/0.jpg" alt=""></Li> <Li><img src="beijing/1.jpg" alt=""></Li> <Li><img src="beijing/2.jpg" alt=""></Li> <Li><img src="beijing/3.jpg" alt=""></Li> <Li><img src="beijing/4.jpg" alt=""></Li> </ul> <a href="javascript:;" class="leftbtn" id="leftbtn"></a> <a href="javascript:;" class="rightbtn" id="rightbtn"></a> </div> </body> <script type="text/javascript"> //得到按钮和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显示第几张 var idx = 0; //节流锁,使用户无法快速切换图片 var lock = true; rightbtn.onclick = function (){ //判断锁的状态 if(!lock) return; lock = false; //因为最后一张图会把过渡去掉,所以这里再加 list.style.transition = 'left 0.5s ease 0s'; idx ++; if(idx > 4){ //设置一个延时器,将ul瞬间拉回0的位置,目的是让过渡动画结束之后回去 setTimeout(function (){ //取消过渡,因为要的是瞬间移动,不是过渡回去 list.style.transition = 'none'; list.style.left = 0; idx = 0; },500); //过渡速度是0.5秒,所以这里用500毫秒 } list.style.left =-idx*650 + 'px'; setTimeout(function (){ lock = true; },500);//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 0.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> </html>
动画效果开发3——呼吸轮播图特效
<html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> *{ margin: 0; padding: 0; } .carousel{ width: 650px; height: 360px; border: 1px solid black; margin: 50px auto; position: relative; //儿子绝对定位,父亲相对定位 } .carousel ul{ list-style: none; width: 6000px; position: relative; } .carousel ul li{ position: absolute; top: 0; left: 0; opacity: 0; transition: opacity 1s ease 0s; } .carousel ul li:first-child{ opacity: 1; } .carousel .leftbtn{ position: absolute; left: 20px; width: 50px; height: 50px; background-color: orange; border-radius: 50%; top: 50%; margin-top: -25px; } .carousel .rightbtn{ position: absolute; right: 20px; width: 50px; height: 50px; background-color: orange; border-radius: 50%; top: 50%; margin-top: -25px; } </style> </head> <body> <div class="carousel"> <ul id="list"> <Li><img src="beijing/0.jpg" alt=""></Li> <Li><img src="beijing/1.jpg" alt=""></Li> <Li><img src="beijing/2.jpg" alt=""></Li> <Li><img src="beijing/3.jpg" alt=""></Li> <Li><img src="beijing/4.jpg" alt=""></Li> </ul> <a href="javascript:;" class="leftbtn" id="leftbtn"></a> <a href="javascript:;" class="rightbtn" id="rightbtn"></a> </div> </body> <script type="text/javascript"> //得到按钮和ul var leftbtn = document.getElementById('leftbtn'); var rightbtn = document.getElementById('rightbtn'); var list = document.getElementById('list'); var lis = document.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,此时是新图,新图淡入 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,此时是新图,新图淡入 lis[idx].style.opacity = 1; //动画结束后开锁 setTimeout(function (){ lock = true; },1000); } </script> </html>