一、概念
1、简介
Document Object Model ,DOM,文档对象模型,是编程接口
通过这些接口可以改变网页的内容、结构和样式
2、DOM 树
① 文档:一个页面就是一个文档,DOM 中使用 document 表示
② 元素:页面中的所有标签都是元素,DOM 中使用 element 表示
③ 节点:网页中的所有内容都是节点(标签、属性、文本、注释等),DOM 中使用 node 表示
DOM 把以上内容都看作是对象 Object 类型
二、获取元素
1、获取普通元素
(1)getElementById 方法
<body>
<div id="time">2019-9-9</div>
<script>
var timer = document.getElementById('time');
console.log(timer); //<div id="time">2019-9-9</div> 获取到id为timer的元素
console.log(typeof timer); //object 页面内的文档、元素、节点等都是对象类型
console.dir(timer); //div#time dir()能查看元素对象的属性和方法
</script>
</body>
(2)getElementsByTagName 方法
<body>
<ul>
<li>知否知否1</li>
<li>知否知否2</li>
<li>知否知否3</li>
<li>知否知否4</li>
<li>知否知否5</li>
</ul>
<script>
var lis = document.getElementsByTagName('li');
console.log(lis); //HTMLCollection(5) [li, li, li, li, li] 获取以伪数组存储的元素对象
console.log(typeof lis); //object
console.log(lis[0]); //<li>知否知否1</li> 类似数组获取元素、遍历元素
</script>
</body>
如果页面内只有一个或者没有这个 TagName,获取到的也是伪数组的形式
还可以获取某个指定元素下的子元素,如下:
<body>
<ul>
<li>知否知否1</li>
<li>知否知否2</li>
</ul>
<ol id="ol">
<li>应是绿肥红瘦1</li>
<li>应是绿肥红瘦2</li>
</ol>
<script>
//获取ol下的li,不要ul下的li
//1.先通过getElementById获取指定元素
var ol = document.getElementById('ol');
//2.再通过getElementsByTagName获取指定元素的子元素
var olLi = ol.getElementsByTagName('li');
console.log(olLi); //HTMLCollection(2) 以伪数组形式存储的元素对象
</script>
</body>
(3)getElementsByClassName 方法
<body>
<div class="box">box1</div>
<div class="box">box2</div>
<script>
var boxs = document.getElementsByClassName('box');
console.log(boxs); //HTMLCollection(2)
</script>
</body>
(4)querySelecter 方法
- H5 新增获取元素的方法
- 只能获取第一个元素
- 不同选择器需要加符号来区分
<body>
<div class="box">box1</div>
<div class="box">box2</div>
<div id="nav">
<ul>
<li>首页</li>
<li>产品</li>
</ul>
</div>
<script>
var boxOne = document.querySelector('.box'); // class选择器用 .
console.log(boxOne); //<div class="box">box1</div> 只能获取第一个元素
var nav = document.querySelector('#nav'); // id选择器用 #
console.log(nav);
var liOne = document.querySelector('li'); // 标签选择器
console.log(liOne);
</script>
</body>
(5)querySelecterAll 方法
- H5 新增获取元素的方法
- 能获取所有元素
- 不同选择器需要加符号来区分
<body>
<div class="box">box1</div>
<div class="box">box2</div>
<div id="nav">
<ul>
<li>首页</li>
<li>产品</li>
</ul>
</div>
<script>
var boxOne = document.querySelectorAll('.box'); // class选择器用 .
console.log(boxOne); //NodeList(2) 获取所有元素
var nav = document.querySelectorAll('#nav'); // id选择器用 #
console.log(nav);
var liOne = document.querySelectorAll('li'); //标签选择器
console.log(liOne);
</script>
</body>
2、获取特殊元素
(1)获取 body 元素
<body>
<script>
var bodyEle = document.body;
console.log(bodyEle);
</script>
</body>
(2)获取 html 元素
<body>
<script>
var htmlEle = document.documentElement;
console.log(htmlEle);
</script>
</body>
三、事件基础
1、事件三要素
① 事件源,例如:按钮
② 事件类型,例如:点击、经过、键盘按下等
③ 事件处理程序,例如:弹出对话框
2、执行事件步骤
① 获取事件源
② 注册事件
③ 添加事件处理程序
<body>
<!-- 点击div,控制台输出我被选中了 -->
<div id="btn">盒子</div>
<script>
//1.获取事件源
var btn = document.getElementById('btn') //获取元素
//2.注册事件----元素.事件
//btn.onclick
//3.添加事件处理程序----匿名函数赋值
btn.onclick = function() {
console.log('我被选中了!');
}
</script>
</body>
四、操作元素
1、修改元素包含的内容
(1)innerText 属性
<body>
<button>显示时间</button>
<div>某个时间</div>
<script>
//点击button后,div 中的文字变为时间
var btn = document.querySelector('button');
var div = document.querySelector('div');
btn.onclick = function() {
div.innerText = '2022-04-05'; // innerText 是属性,用等号赋值
}
</script>
</body>
(2)innerHTML 属性
<body>
<div id="div1"></div>
<div id="div2"></div>
<script>
var divEle1 = document.querySelector('#div1');
var divEle2 = document.querySelector('#div2');
divEle1.innerHTML = '我是一个盒子';
divEle2.innerHTML = '<strong>我是</strong>第二个盒子'; //能识别html标签
</script>
</body>
innerText 和 innerHTML 区别:
- innerText:不识别 html 标签;去除空格和换行
- innerHTML:识别 html 标签;保留空格和换行
注意:修改表单元素中的内容和普通元素不同:
- 普通元素通过修改 innerHTML 和 innerText 属性
- 表单元素要修改 value 属性,尤其是
2、修改元素的属性
给属性重新赋值即可,案例:
<body>
<button id="btn1">有道词典</button>
<img src="img/music.png"/>
<script>
//点击button,图片切换为QQ音乐
var youdao = document.querySelector('#btn1');
var img = document.querySelector('img');
youdao.onclick = function() {
img.src = 'img/youdao.png'; //修改img元素的src属性,直接给src重新赋值即可
}
</script>
</body>
3、修改元素的样式属性
(1)通过 style 修改
<script>
var div = document.querySelector('div');
div.onclick = function () {
div.style.width = '500px'; // 元素名.style.样式属性名
div.style.backgroundColor = 'blue'; // html是-,JS是驼峰
}
</script>
注意:JS 修改 style 样式属性,产生的是行内样式
(2)通过 className 修改
<div class="box"></div>
<script type="text/javascript">
var box = document.querySelector('div');
box.onclick = function() { //1.元素名.className 来操作元素类名属性
this.className = 'box boxChange'; //2.这样修改样式属性会直接覆盖之前的类名,可以用多类名来解决
} //3.当修改样式较多时,可以使用className来修改
</script>
4、排他思想
如果有一组元素,想要某个元素实现某种样式,需要用到循环的排他算法
<script type="text/javascript">
var btns = document.getElementsByTagName('button');
//通过循环给所有按钮添加onclick点击事件
for (var i = 0; i < btns.length; i++) {
btns[i].onclick = function() {
//1.先把所有元素清除样式
for (var j = 0; j < btns.length; j++) {
btns[j].style.backgroundColor = '';
}
//2.再给某个元素设置想要的样式
this.style.backgroundColor = 'pink'; //这里不能用btn[i] 不是同一个作用域 i不起作用
}
}
</script>
5、操作元素的自定义属性
<div index="1" test="test"></div>
<script type="text/javascript">
var div = document.querySelector('div');
//1.获得自定义属性值 getAttribute
console.log(div.getAttribute('index')); //1
//2.设置自定义属性值 setAttribute
div.setAttribute('index','2');
console.log(div.getAttribute('index')); //2
//3.移除自定义属性值 removeAttribute
div.removeAttribute('test');
</script>
6、H5 自定义属性
自定义属性目的:为了保存并使用数据,有些数据不用保存到数据库,保存在页面中就可以了
但是自定义属性难以判断,所以 H5 新增了自定义属性命名方式:
- data-xxx
获取自定义属性值:
- 元素.dataset.xxx
<div data-time = "21时" data-index = "22" data-list-name = "andy"></div>
<script type="text/javascript">
var div = document.querySelector('div');
//dataset 是一个集合,里面存放了所有以 data 开头的自定义属性
console.log(div.dataset); //DOMStringMap {time: '21时', index: '22'}
console.log(div.dataset.time); //获取自定义属性 只能获取data-开头的
console.log(div.dataset.listName); // 驼峰命名法
console.log(div.dataset['time']);
</script>
五、操作节点
1、节点概述
网页中的所有内容都是节点(标签、属性、文本、注释等)
每种节点都至少拥有 nodeType(节点类型)、nodeName(节点名称)、nodeValue(节点值)这三个基本属性
- 元素节点 nodeType = 1
- 属性节点 nodeType = 2
- 文本节点 nodeType = 3 (文本节点包含文字、空格、换行等)
实际操作中,主要操作的是元素节点
2、节点层级
(1)父节点
- 获取父节点
node.parentNode
注意:
① 返回的是最近的父节点
② 没有父节点返回 null
(2)子节点
- 获取所有子节点
// 方法一:返回所有的子节点,包含元素节点、文本节点等(不常用)
node.childNodes
// 方法二:返回子节点中的元素节点(常用)
node.children
- 获取第一个子节点
// 方法一:返回第一个子节点,包含元素节点、文本节点等(不常用)
node.firstChild
// 方法二:返回子节点中的第一个元素节点 (IE9 以上才支持)
node.firstElementChild
// 方法三:返回子节点中的第一个元素节点(常用)
node.children[0]
- 获取最后一个子节点
// 方法一:返回最后一个子节点,包含元素节点、文本节点等(不常用)
node.lastChild
// 方法二:返回子节点中的最后一个元素节点 (IE9 以上才支持)
node.lastElementChild
// 方法三:返回子节点中的最后一个元素节点 (常用)
node.children[children.length - 1]
(3)兄弟节点(不常用)
- 获取上一个兄弟节点
// 方法一:返回上一个兄弟节点,包含元素节点、文本节点等(IE9 以上才支持)
node.previousSibling
// 方法二:返回兄弟节点中上一个元素节点(IE9 以上才支持)
node.previousElementSibling
- 获取下一个兄弟节点
// 方法一:返回下一个兄弟节点,包含元素节点、文本节点等(IE9 以上才支持)
node.nextSibling
// 方法二:返回兄弟节点中下一个元素节点(IE9 以上才支持)
node.nextElementSibling
(4)创建、添加子节点
<ul>
<li>123</li>
</ul>
<script type="text/javascript">
var ul = document.querySelector('ul');
// 1.创建元素节点
var li1 = document.createElement('li');
var li2 = document.createElement('li');
// 2.添加节点
// (1)在后面追加
ul.appendChild(li1);
// (2)在前面添加
ul.insertBefore(li2, ul.children[0]);
</script>
(5)删除子节点
<ul>
<li>1</li>
<li>2</li>
</ul>
<script type="text/javascript">
var ul = document.querySelector('ul');
ul.removeChild(ul.children[0]); //会把内容为1的子节点删除掉
</script>
(6)复制节点
<ul>
<li>1</li>
</ul>
<script type="text/javascript">
var ul = document.querySelector('ul');
// 1.括号为空或 false 为浅拷贝,只复制标签不复制内容
var li1 = ul.children[0].cloneNode();
// 2.括号为 true 为深拷贝,标签和内容都复制
var li2 = ul.children[0].cloneNode(true);
ul.appendChild(li1);
ul.appendChild(li2);
</script>
(7)三种动态创建元素区别
- document.write 是直接将内容写入页面的内容流,但是文档流执行完毕,则它会导致页面全部重绘
- innerHTML 是将多个内容写入某个 DOM 节点,不会导致页面重绘
- innerHTML 创建多个元素效率更高(不要拼接字符串,采取数组形式拼接),结构稍微复杂
- creatElement() 创建多个元素效率稍低一点点,但是结构更清晰
六、注册事件
1、概述
给元素添加事件,称为注册事件或绑定事件;
2、注册事件
(1)传统方式注册事件
同一个元素、多次注册同一个事件,只有一个生效,具有唯一性
(2)方法监听注册事件
同一个元素、多次注册同一个事件,都生效,不具有唯一性
IE9 以上才支持
<button type="button">事件侦听注册事件</button>
<script type="text/javascript">
var btn = document.querySelector('button');
btn.addEventListener('click', function() { //第一个参数是事件类型字符串:加引号、不带on
alert('点击了事件侦听按钮!'); //第二个参数是事件处理程序函数
})
</script>
3、删除事件
(1)传统方式注册的事件
<script type="text/javascript">
var div = document.querySelector('div');
div.onclick = function() {
alert('要开心呀!!!');
div.onclick = null; //传统方式删除事件
}
</script>
(2)方法监听注册的事件
<script type="text/javascript">
var div = document.querySelector('div');
div.addEventListener('click', fn); // 不用加小括号()
function fn(){
alert('一定要开心!');
div.removeEventListener('click',fn); // 方法监听注册事件删除事件
}
</script>
七、DOM 事件流
- DOM 事件流描述的是从页面接收事件的顺序
- 有捕获阶段、当前目标阶段、冒泡阶段
- JS 代码中只能执行捕获或冒泡其中的一个阶段
- onclick 、attachEvent 只能得到冒泡阶段
- addEventListener 第三个参数是 true,表示在事件捕获阶段调用事件处理程序;如果是 false 或省略,表示在事件冒泡阶段调用事件处理程序
- 实际开发中很少使用事件捕获,我们更关注事件冒泡
- 有些事件是没有冒泡的,比如 onblur、onfocus
八、事件委托
1、概念
事件委托也称事件代理,在 jQuery 里面称为事件委派
2、原理
不是每个子节点单独设置事件监听器,而是事件监听器设置在其父节点上,然后利用冒泡原理影响设置每个子节点
九、事件对象
- 事件对象是事件的一系列相关数据的集合,比如鼠标点击事件是鼠标事件对象(如鼠标坐标等)、键盘事件是键盘事件对象(如判断用户按下了哪个键等)
- 事件对象在事件处理程序函数的小括号里,当形参来看
- 事件对象只有有了事件才会存在,是系统给我们自动创建的,不需要我们传递参数
- 事件对象可以自己命名,比如:event、evt、e等
- 事件对象也有兼容性,ie678 通过 window.event 兼容性写法 e = e || window.event
- 事件对象的常见属性和方法:
- e.target 返回触发事件的对象(而 this 返回的是调用事件的对象,两者只在某些情况下结果相同)
- e.type 返回事件的类型,比如 click、mouseover(不带 on)
- e.preventDefault() 阻止默认事件的方法,比如让链接不跳转、让提交按钮不提交
- e.returnValue 阻止默认事件的属性,ie678使用
- 阻止事件冒泡:
- e.stopPropagation() 方法
- e.cancelBubble 属性
十、常用的鼠标事件
1、禁止右键菜单 contextmenu
<p>禁止右键菜单</p>
<script type="text/javascript">
var p = document.querySelector('p');
p.addEventListener('contextmenu', function(e) {
e.preventDefault();
})
</script>
绑定 contextmenu 事件后,用阻止默认事件的方法 e.preventDefault() 禁止掉就可以了
2、禁止选中文字 selectstart
<p>禁止选中文字</p>
<script type="text/javascript">
var p = document.querySelector('p');
p.addEventListener('selectstart', function(e) {
e.preventDefault();
})
</script>
绑定 selectstart 事件后,用阻止默认事件的方法 e.preventDefault() 禁止掉就可以了
3、鼠标事件对象的属性
e.clientX 鼠标在可视区的 x 坐标
e.clientY 鼠标在可视区的 y 坐标
e.pageX 鼠标在页面文档的 x 坐标
e.pageY 鼠标在页面文档的 y 坐标
4、鼠标移动事件 mousemove
跟随鼠标的天使案例:
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
img {
position: absolute; /* 图片要移动 而且不占位置 -> 绝对定位 absolute */
}
</style>
</head>
<body>
<img src="img/angel.gif" alt="">
<script>
var pic = document.querySelector('img');
document.addEventListener('mousemove', function(e) { //只要鼠标移动 1px 就会触发事件
var x = e.pageX;
var y = e.pageY;
pic.style.left = x + 'px'; // left 和 top 不要忘记单位px
pic.style.top = y + 'px';
})
</script>
</body>
十一、常用的键盘事件
1、按键弹起 keyup
- 弹起才触发
<script type="text/javascript">
document.addEventListener('keyup', function() {
console.log('弹起');
})
</script>
2、按键按下 keydown
- 只要按下不松开就一直触发
- keydown 能识别功能键,如:ctrl、shift、箭头等
- 相同情况下 keydown 比 keypress 先执行
<script type="text/javascript">
document.addEventListener('keydown', function() {
console.log('按下');
})
</script>
3、按键按下 keypress
- 只要按下不松开就一直触发
- keypress 不识别功能键
<script type="text/javascript">
document.addEventListener('keypress', function() {
console.log('按下');
})
</script>
4、键盘事件对象的属性
e.keycode 相应键的 ASCII 码
- keyup 事件和 keydown 事件的 keycode 属性不区分大小写
- keypress 事件的 keycode 属性区分大小写