js入门(7)DOM
基本概念
- DOM是js操作html的文档接口,使文档操作变得简便
- DOM最大的优点是将文档表示为节点树
DOM节点树

绿色是元素节点
红色是属性节点
蓝色是文本节点
nodeType常用属性值
- 可以显示节点具体的类型
- 打点调用
![]()
节点访问和位置关系
访问元素节点
认识document对象
- document对象是DOM中最重要的东西,几乎所有的DOM的功能都封装在document对象中
- document对象也表示整个HTML文档,它是DOM节点树的根
- document对象的nodetype属性值是9
- 在js中是对象
![]()
访问元素节点的常用方法

getElementById()
- 通过document.getElementById()得到id元素节点
- 不用#,直接写id名即可
![]()
注意
- 如果页面上有相同的id,只能得到第一个
延迟运行
- 测试DOM节点时,js要写在html后面,否则js无法找到对应的html节点
- 可以使用window.onload=function()事件,使页面加载完毕后再执行指定的代码
![]()
getElementsByTagName()
- getElementsByTagName()方法通过标签名得到节点数组
![]()
注意
- 数组方便遍历,从而可以批量操控元素节点
- 即使页面上只有一个指定标签名的节点,也将得到长度为1的数组
- 任何一个节点元素也可以调用getElementsByTagName()方法,从而得到内部的某种类的元素节点
![]()
![]()
getElementsByClassName()
- 方法的功能是通过类名得到节点数
![]()
注意
- 从ie9开始兼容
- 可以调用再调用
querySelector()
- querySelector()方法的功能是通过选择器得到元素
- 等于标准的选择器
![]()
注意
- 只能得到一个元素,如果有多个只能得到都一个
- 从ie8开始兼容
querySelectorAll()
- querySelectorAll()方法的功能是通过选择器得到元素数组
- 即使页面上只有一个符合选择器的节点,也将得到长度为1的数组
![]()
![]()
注意
- 兼容到ie8
节点关系


注意
- 文本节点也属于节点
- 空白文本节点也应该算作节点,但是ie8之前的浏览器有一定的兼容问题,他们不把空文本节点当作节点。
排除文本节点干扰
- 从ie9开始支持只考虑元素节点的属性
![]()
例子:
![]()
没有特别标记应该是7段
![]()
书写常见的节点关系函数
ie6需要使用封装达到寻找所有元素子节点,类似childern

ie6需要使用封装达到返回前一个元素兄弟节点,类似previousElementSibling

ie6需要使用封装达到返回所有元素兄弟节点
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);
}
看不明白
节点操作
改变节点中的内容
- innerHTML
可以用html语法设置节点中的内容
![]()
书写不能出现空格
![]()
- innerText
只能用纯文本的形式设置节点中的内容
![]()
![]()
改变元素节点的css样式

改变元素节点的HTML属性
- 标准的W3C属性,直接打点更新即可
![]()
- 不是标准的属性
要使用setAttribute()和getAttribu()来设置读取
![]()
![]()
。
![]()
节点的创建,操作,克隆
节点的创建
- document.createElement()方法用于创建一个指定tagname的HTML元素
![]()
注意
- 创建出来的节点是孤儿节点
- 因为它没有被挂载再DOM树上,所以我们无法看见它
- 必须继续使用appendChild()【追加子节点】或insert Before()【在。。。之前插入】方法将孤儿节点插入到DoM树上
appendChild()
- 任何已经在DOM树上的节点,都可以调用appendChild()方法,它可以将孤儿节点挂载到它的内部,成为它最后一个子节点
![]()
insertBefore()
- 任何已经在DOM树上的节点,都可以调用insertBefore()方法,它可以将孤儿节点挂载到它的内部,成为它的‘标杆子节点’之前的节点
![]()
![]()
![]()
这里使用的是父节点插入
![]()
这里使用的是标杆节点插入
小练习 创建一个20行12列的表格

小练习 创建九九乘法表

节点的操作
移动节点
- 如果将已经挂载到DOM树上的节点成为appendChild()或者insertBdfore()的参数,这个节点将会被移动
![]()
![]()
- 这意味着,一个节点不能同时位于BDOM树的两个位置
![]()
![]()
删除节点
- removeChild()方法从DOM中删除一个子节点
![]()
- 节点不能自己删除自己,必须由父节点删除它
![]()
- 这个时候p就会被删除
克隆节点
- cloneNode()方法可以克隆节点,克隆的节点是“孤儿节点”
![]()
![]()
- 克隆之后因为是孤儿节点,所以还需要进行上树
- 参数是一个布尔值,表示是否采用深度克隆:如果为true,表示深度克隆,false,表示只克隆节点本身
![]()
![]()
![]()
- 当加上true之后
![]()
![]()
dom事件
事件监听
- DOM允许我们书写js代码以让html元素对事件做出反应
什么是“事件”:用户与网页的交互动作:
- 当用户点击元素时
- 当鼠标移动到元素上时
- 当文本框内容被改变的时候
- 当网页加载完毕时等等
什么是“监听”:
- 监听就是让计算机随时能过够发现这个事件发生了,从而执行程序员预先编写的一些程序
- 设置事件监听主要有 onxxx和addEventListener()两种
最简单的设置事件监听方法
- 最简单的给元素设置事件监听的方法就是设置他们的onxxx属性
![]()
![]()
- 当点击盒子,会出现一个弹框
常见的鼠标事件监听

- 鼠标抬起会优先于单机
常见的键盘事件监听

表单事件监听

- oninput表示在表单中输入
- 得到焦点就是选中输入框
![]()
常见的页面事件监听

addEventListener()方法

事件传播
当盒子嵌套时,事件监听的执行顺序




- 实际,事件的传播时:先从外到内,再从内往外
![]()
- 但实际上onxxx只能监听冒泡阶段
![]()
- 如果使用addEventListener()方法
![]()
![]()
![]()
注意事项
- 最内部元素不再区分捕获和冒泡阶段,会先执行写在前面的监听,然后执行后写的监听
- 如果给元素设置相同的两个或多个同名事件,则DOM0级写法后面的会覆盖先写的,而DOM2级会按顺序执行
事件对象
- 在本次事件处理函数提供一个形式参数,它时一个对象,封装了本次事件的细节
- 这个参数通常用单词event或者字母e来表示
![]()
鼠标位置




- 计算的是最内层的元素(如果盒子内再有盒子时,小盒子左上角也是0,0)
e.charCode和e.keyCode属性
- e.charCode属性常用于onkeypress事件中,表示用户输入的字符的“字符码”
![]()
- e.keyCode属性常用于onkeydown事件和onkeyup中,表示用户按下的按键的“健码”
![]()
- 例子:
![]()
![]()
![]()
小案例
- 制作一个特效:按方向键可以控制页面上的盒子移动
<html lang="en">
<head>
<meta charset="UTF-8">
<title>推盒子</title>
<style>
body{
margin: 0;
padding: 0;
}
#box{
position: absolute;
top: 200px;
left: 200px;
width: 100px;
height: 100px;
background-color: red;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
var oBox=document.getElementById('box');
//设置全局变量tl,表示盒子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>
方法
e.perventDefault()方法
- e.perventDefault()方法用来阻止事件产生的“默认动作”
- 一些特殊的业务需求,需要阻止事件的“默认动作”
- 案例1:制作一个文本框,只能输入小写字母和数字,其他字符输入无效
![]()
- 案例2:制作鼠标滚轮事件:当鼠标在盒子中向下滚动时,数字加1,否则减1
- 鼠标滚轮事件时onmousewheel,它的事件对象而提供deltaY属性表示鼠标滚动方向,向下滚动时返回正值,向上滚动时,返回负值
<html lang="en">
<head>
<meta charset="UTF-8">
<title>鼠标滚轮案例</title>
<style>
body{
height: 2000px;
padding: 0;
margin: 0;
}
#box{
height: 100px;
width: 200px;
background-color: black;
}
</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>
e.stopPropagation()方法
- e.stopPropagation()方法用来阻止事件继续传播
- 在一些场合,非常有必要切断事件继续传播,否则会造成页面特效显示出一些bug
![]()
- 因为是onclick会采用冒泡,所以我是盒子也会显示出来
- 小案例:制作一个弹出层:点击按钮显示弹出层,点击网页任意地方,弹出层关闭
<html lang="en">
<head>
<meta charset="UTF-8">
<title>弹出层</title>
</head>
<style>
#tcc{
height: 100px;
width: 200px;
background-color: #444444;
position: absolute;
top: 50%;
left: 50%;
margin-top: -50px;
margin-left: -100px;
display: none;
}
</style>
<body>
<!--点击按钮,出现弹出层,点击除了弹出层的任意位置,弹出层关闭-->
<button id="btn">点我</button>
<div id="tcc">弹出层</div>
<script>
var Obtn=document.getElementById('btn');
var Otcc=document.getElementById('tcc');
//点击按钮显示弹出框
Obtn.onclick=function (e) {
//因为是冒泡,所以要阻止事件传播
e.stopPropagation();
Otcc.style.display='block';
}
document.onclick=function () {
Otcc.style.display='none';
}
Otcc.onclick=function (e) {
//点击弹出框本身也要阻止事件传播
e.stopPropagation();
}
</script>
</body>
</html>
事件委托
- 利用事件冒泡机制,将后代元素事件委托给祖先元素
![]()
e.target和e.currentTarget属性
- 事件委托通常需要结合使用e.target属性
![]()
- 例子:
![]()
点击哪个,哪个变红 - 例子:
![]()
![]()
事件委托使用场景
- 有大量元素要批量添加事件监听时,使用事件委托可以减少内存消耗
- 当有动态节点上树时,使用事件委托可以让新上树的元素具有事件监听
注意

- 不冒泡相当于,选择他的父元素,也就是全部
![]()
- target,会对最内层的元素生效
批量添加事件监听
- 页面上有一个ul无序列表,里面有20个li,实现效果,点击哪个li,哪个就会变红
<html lang="en">
<head>
<meta charset="UTF-8">
<title>批量事件监听</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 Olb= Olist.getElementsByTagName('li');
for (var i=0;i < Olb.length;i++){
Olb[i].onclick = function () {
this.style.color='red';
};
}
</script>
</body>
</html>
批量添加事件监听的性能问题
- 每一个事件监听都会消耗一定的系统内存,而批量添加事件会导致监听数量太多,内存小号会非常大
- 实际上每个li的事件处理函数都是不同的函数,这些函数本身也会占用内存
新增元素动态绑定事件
- 案例:页面上有一个无序列表ul,它内部没有li元素,请制作一个按钮,点击这个按钮就能增加一个li元素。并且要求每增加的li元素也要有点击事件监听,实现效果点击哪个li元素,哪个就会变红
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button id="btn">点我添加新的列表项</button>
<ul id="list"></ul>
<script>
var Obtn=document.getElementById('btn');
var Olist=document.getElementById('list');
/* var lis=Olist.getElementsByTagName('li');*/
Obtn.onclick=function () {
var Oli=document.createElement('li');
Oli.innerHTML='我是新来的';
Olist.appendChild(Oli);
Oli.onclick=function () {
this.style.color='red';
};
}
</script>
</body>
</html>
动态绑定事件的问题
- 新增元素必须分别添加事件监听,不能自动获得事件监听
- 大量事件监听/大量事件处理函数都会产生大量消耗内存
定时器,延时器
定时器
- setInterval()函数可以重复调用一个函数,在每次调用之前具有固定的时间间隔
![]()
函数的参数
- setInterval()函数可以接收第3,4,。。。个参数,他们将按顺序传入函数
![]()
具名函数也可以传入setLnterval()函数

- 没有括号表示传入的是函数,有的话表示函数的执行,会失去定时器的意义
清除定时器
- clearInterval()函数可以清除一个定时器
![]()
- 案例:点击开始定时器开始计数,暂停则停止
延时器
- setTimeout()函数可以设置一个延时器,当指定时间到了,会执行函数一次,不会重复执行
![]()
清除延时器
- clearTimeout()函数可以清除延时器
![]()
异步

- 举例:
![]()
实现动画
定时器实现动画

js+css3



函数节流
- 一个函数执行一次之后,只有大于设定的执行周期后才允许执行第二次
- 函数节流只需要借助延时器就可以了
![]()
- 完成一个方块点击按钮从左到右,再次点击可以返回,中途点击不会有效果
动画效果开发
无缝连续滚动特效
<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');
// 复制多一遍所有的li
list.innerHTML += list.innerHTML;
// 全局变量,表示当前list的left值
var left = 0;
// 定时器,全局变量
var timer;
move();
// 动画封装成函数
function move() {
// 设表先关,防止动画积累
clearInterval(timer);
timer = setInterval(function () {
left -= 4;
// 验收
if (left <= - 1260) {
left = 0;
}
list.style.left = left + 'px';
}, 20);
}
// 鼠标进入停止定时器
box.onmouseenter = function () {
clearInterval(timer);
};
// 鼠标离开继续定时器
box.onmouseleave = function () {
move();
};
</script>
</body>
</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>
- 延时器可以让过渡失效
呼吸轮播图
- 将所有的图片都设置在一个位置
<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>







































































浙公网安备 33010602011771号