一.概述
1.文档对象模型(Document Object Model):是W3C组织推荐的处理可扩展标记语言(HTML,XML)的标准编程接口
2.W3C已经定义了一系列的DOM接口,通过这些接口可以改变网页的内容,结构和样式
3.DOM树:DOM树中的所有内容都可以看作是对象
(1)文档:一个页面就是一个文档,DOM中使用document表示
(2)元素:页面中的所有标签都是元素,DOM中使用element表示
(3)节点:网页中所有的内容都是节点(标签,文本,属性,注释等),DOM中使用node表示
二.获取元素
1.根据ID获取:
(1)使用 document.getElementById():返回一个元素对象
console.dir():打印返回的元素对象 可以更好地查看里面的属性和方法
//由于HTML从上到下执行,所以script要写在div标签的下边
<div id="time">2022-5-51</div>
<script>
var a = document.getElementById('time');
console.log(a);
</script>
2.根据标签名获取:
(1)使用 document.getElementsByTagName():返回带有指定标签名的对象的集合,以伪数组的形式存储
想要依次打印可以遍历
<ul>
<li>hello</li>
<li>hello</li>
<li>hello</li>
<li>hello</li>
<li>hello</li>
</ul>
<script>
var list = document.getElementsByTagName('li');
for (var i = 0;i<list.length;i++){
console.log(list[i]);
}
</script>
(2)document是获取页面所有的指定节点,想要获取某个元素的子元素指定就要用element.getElementById()
element是元素,该元素必须是指定的单个元素,不可以是伪数组
<ul>
<li>hello</li>
<li>hello</li>
<li>hello</li>
<li>hello</li>
<li>hello</li>
</ul>
<ol id="ol">
<li>world</li>
<li>world</li>
<li>world</li>
<li>world</li>
<li>world</li>
</ol>
<script>
var ol = document.getElementById('ol');
console.log(ol.getElementsByTagName('li'));
</script>
3.通过HTML5新增方法获取:
(1)使用 document.getElementsByClassName():返回指定类名的对象的集合,以伪数组的形式存储
(2)document.querySelector(‘选择器’):根据指定选择器,返回第一个元素对象(只返回第一个)
(3)document.querySelectorAll(‘选择器’):根据指定选择器,返回所有的元素对象的集合<div class="box"></div>
<div class="box"></div>
<div id="nav">
<ul>
<li>首页</li>
<li>产品</li>
</ul>
</div>
<script>
var boxs = document.getElementsByClassName('box');
console.log(boxs);
var firstbox = document.querySelector('.box');
console.log(firstbox);
var nav = document.querySelector('#nav');
console.log(nav);
var box = document.querySelectorAll('.box');
console.log(box);
</script>
4.特殊元素(body,html)获取:提供了专用方法
(1)获取body元素:document.body;
(2)获取html元素:document.documentElement;三.事件基础
1.事件:可以被js侦测的行为,可理解为触发-响应机制
2.事件组成:
(1)事件源:事件被触发的对象
(2)事件类型:如何触发 什么事件例如:鼠标点击还是鼠标经过
(3)事件处理程序:通过一个函数赋值的方式完成
(4)案例:
<button id="a">唐伯虎</button>
<script>
var btn = document.getElementById('a');
btn.onclick = function(){
alert("秋香");
}
</script>
3.执行事件的步骤:
(1)获取事件源
(2)注册事件(绑定事件)
(3)采取函数赋值形式添加事件处理程序
4.常见的鼠标事件:
鼠标事件 | 触发条件 |
---|---|
onclick | 鼠标左键点击触发 |
onmouseover | 鼠标经过触发 |
onmouseout | 鼠标离开触发 |
onfocus | 获得鼠标焦点触发 |
onblur | 失去鼠标焦点触发 |
onmousemove | 鼠标移动触发 |
onmouseup | 鼠标弹起触发 |
onmousedown | 鼠标按下触发 |
四.操作元素
1.改变元素内容:例如绑定鼠标点击事件,鼠标点击改变文字内容
(1)elements.innerText:不识别HTML标签 不保留空格和换行
(2)elements.innerHTML:识别HTML标签 保留空格和换行
(3)表单元素不能用innerHTML或innerText修改,要用value
2.修改元素属性:
(1)element.属性 = ‘属性值’;
(2)案例:点击按钮显示对应图片
<button id="ldh">刘德华</button>
<button id="zxy">张学友</button>
<img src="a.jpg">
<script>
var ldh = document.getElementById('ldh');
var zxy = document.getElementById('zxy');
ldh.onclick = function() {
img.src = 'b.jpg';
}
zxy.onclick = function() {
img.src = 'a.jpg'
}
</script>
3.修改表单属性:
(1)利用DOM可以操作如下表单属性:type,value,checked,selected,disabled
(2)例如修改内容就用element.value=””;
(3)修改为禁用就用element.disable=true;
(4)就是元素.后可以跟(1)中提到的属性
(5)案例:仿京东实现密码的显示与隐藏
<style>
.box{
position: relative;
width: 400px;
border-bottom: 1px solid #ccc;
margin:100px auto;
}
.box input {
width: 370px;
height: 30px;
border: 0;
outline: none;
}
.box img{
position: absolute;
right: 2px;
top:2px;
width: 24px;
height: 24px;
}
</style>
</head>
<body>
<div class="box">
<label for="">
<img src="close.png" id= "eye">
</label>
<input type="password" id="pwd">
</div>
<script>
var eye = document.getElementById('eye');
var pwd = document.getElementById('pwd');
var flag = 0;
eye.onclick = function(){
if (flag == 0){
pwd.type = 'text';
eye.src='open.png';
flag = 1;
}else{
pwd.type = 'password';
eye.src='close.png';
flag = 0;
}
}
</script>
</body>
4.修改元素样式:大小颜色,位置等
(1)行内样式操作:element.style
- 但是该方法只能获取和设置内联样式(行内样式)
- 如果获取行内样式没有的值会返回null
- 所以此方法可以设置多个样式,通过修改属性名或类名完成样式的修改
(2)getComputedStyle(元素/对象).属性
- 该方法可用来读取外部样式
- 但是只能读取,不能设置
(3)类名样式操作:element.className
- 通过类名可以依据js代码修改多行,弥补了行内样式操作的缺点
- 即使元素原本有类名,也可以使用,会将原有的类名覆盖,若想保留原来的类名,可以采用多类名,空格间隔那种,把原有类名也写上
(4)修改样式属性采取驼峰命名法:font-size写成FontSize
(5)用js改的样式是行内样式
(6)案例:点击隐藏盒子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box1{
width: 15px;
height: 15px;
background-color: blue;
float: left;
}
.box2{
width: 200px;
height: 200px;
background-color: aquamarine;
display: block;
float: left;
}
</style>
</head>
<body>
<div class="box1"></div>
<div class="box2"></div>
<script>
var box1 = document.querySelector('.box1');
var box2 = document.querySelector('.box2');
box1.onclick = function(){
box2.style.display = 'none';
}
</script>
</body>
</html>
(6)案例:文本框获得焦点隐藏原内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text" name="" id="" value="手机">
<script>
var text = document.querySelector('input');
//获得焦点
text.onfocus = function() {
if(this.value === '手机') {
this.value = '';
}
}
//失去焦点
text.onblur = function() {
if(this.value === '') {
this.value = '手机';
}
}
</script>
</body>
</html>
5.排他思想:
(1)同一组元素中想要某个元素实现某种样式,就用到循环的排他思想
(2)排他思想步骤:
- 首先清除元素的全部样式
- 再给当前元素配置样式
(3)注意:必须严格按照步骤执行,顺序不可颠倒
(4)案例:购物车全选和取消全选:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}
.wrap {
width: 300px;
margin: 100px auto 0;
}
table {
border-collapse: collapse;
border-spacing: 0;
border: 1px solid #c0c0c0;
width: 300px;
}
th,
td {
border: 1px solid #d0d0d0;
color: #404060;
padding: 10px;
}
th {
background-color: #09c;
font: bold 16px "微软雅黑";
color: #fff;
}
td {
font: 14px "微软雅黑";
}
tbody tr {
background-color: #f0f0f0;
}
tbody tr:hover {
cursor: pointer;
background-color: #fafafa;
}
</style>
</head>
<body>
<div class="wrap">
<table>
<thead>
<tr>
<th>
<input type="checkbox" id="j_cbAll" />
</th>
<th>商品</th>
<th>价钱</th>
</tr>
</thead>
<tbody id="j_tb">
<tr>
<td>
<input type="checkbox" />
</td>
<td>iPhone8</td>
<td>8000</td>
</tr>
<tr>
<td>
<input type="checkbox" />
</td>
<td>iPad Pro</td>
<td>5000</td>
</tr>
<tr>
<td>
<input type="checkbox" />
</td>
<td>iPad Air</td>
<td>2000</td>
</tr>
<tr>
<td>
<input type="checkbox" />
</td>
<td>Apple Watch</td>
<td>2000</td>
</tr>
</tbody>
</table>
</div>
<script>
// 由上面的复选框来控制下面复选框的全选与全不选
var j_cbAll = document.querySelector('#j_cbAll'); // 获取上面的复选框
var j_tb = document.querySelector('#j_tb').querySelectorAll('input'); // 获取下面的复选框
// 给上面的 复选框添加点击事件
j_cbAll.onclick = function() {
for (var i = 0; i < j_tb.length; i++) {
j_tb[i].checked = this.checked;
}
}
// 给下面的每个复选框绑定点击事件
for (var i = 0; i < j_tb.length; i++) {
j_tb[i].onclick = function() {
var flag = true;
// 进行循环判定 如果有一个没有被选中 上面的则不会被选中
for (var i = 0; i < j_tb.length; i++) {
if (!j_tb[i].checked) {
flag = false;
break;
}
}
j_cbAll.checked = flag;
}
}
</script>
</body>
</html>
6.获取属性值:
(1)element.属性 //获取内置属性值(元素本身自带的属性)
(2)element.getAttribute(‘属性’); //主要获得自定义的属性(程序员自己设置的,不是class,id这种自带的)
- 自定义属性的目的:是为了保存并使用数据,有些数据
- 为了区分自定义属性,H5规定自定义属性以”data-“开头
H5新增获取自定义属性方法:element.dataset.属性 或 element.dataset[‘属性’]
7.设置属性值:
(1)element.属性 = ‘值’;
(2)element.setAttribute(‘属性’,’值’); //主要针对自定义属性8.移除属性值:
(1)element.removeAttribute(‘属性’);
五.节点操作
1.简介:
(1)由于上述方法逻辑性不强且繁琐,所以有了新的方法:节点操作
(2)节点操作是利用节点层级关系获取元素,逻辑性强
(3)节点具备节点类型(nodeType),节点名称(nodeName),节点值(nodeValue)三个基本属性节点类型有:元素节点:nodeType为1,属性节点 nodeType为2,文本类型(空格,换行都算文本类型) nodeType为3
2.节点层级:
(1)父节点:node.parentNode //得到离node最近的父节点 找不到返回null
例如:li.parentNode 取得离他最近的一个父节点
(2)子节点:node.childNodes //得到指定节点的所有子节点的集合,包含空格,换行等
- 若想只获得元素节点,就用if判断他的nodeType是否为1
- 还可以用node.children,只返回元素节点
- node.firstChild //返回第一个子节点 ,包括所有节点
- node.lastChild //返回第一个子节点 ,包括所有节点
- node.firstElementChild //返回第一个元素节点
- node.lastElementChild //返回最后一个元素节点
- 开发中最常用的是:element.children[索引]
(3)兄弟节点:node.nextSibling //得到下一个节点,包括空格,换行 找不到返回null
- node.previousSibling //得到上一个节点,包括空格,换行 找不到返回null
- node.nextElementSibling //得到当前元素的下一个兄弟节点,找不到返回null
node.previousElementSibling //得到当前元素的上一个兄弟节点,找不到返回null
3.节点操作:
(1)创建节点:document.createElement(‘标签名’);
(2)添加节点:node.appendChild(创建的节点):将一个节点添加到指定父节点的子节点列表的末尾还可以在指定元素的前面添加:node.insertBefore(创建的节点,指定元素)
(3)删除节点:node.removeChild(child) //从DOM中删除一个子节点,返回删除的节点
(4)复制节点:node.cloneNode() //返回一份复制的node,也叫克隆节点
- 括号内什么都不填或者填入false就是浅拷贝,只复制节点,不复制内容
- 括号内填入true就是深拷贝,内容和节点一起复制
(5)案例:发布与删除评论:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
li{
background-color: pink;
}
</style>
</head>
<body>
<textarea name="" id="">123</textarea>
<button>发布</button>
<ul>
</ul>
<script>
var btn = document.querySelector('button');
var text = document.querySelector('textarea');
var ul = document.querySelector('ul');
// 创建留言和删除留言
btn.onclick = function(){
if(text.value == ''){
alert("你没有输入内容");
return false;
} else {
var li = document.createElement('li');
li.innerHTML = text.value + "<a href='javascript:;'>删除</a>";
ul.insertBefore(li,ul.children[0]);
var as = document.querySelectorAll('a');
for (var i=0;i<as.length;i++) {
as[i].onclick = function() {
ul.removeChild(this.parentNode);
}
}
}
}
</script>
</body>
</html>
4.三种创建元素方式的区别:
(1)document.write()
- 是将内容直接写入页面的内容流
- 如果页面文档流加载完毕,在调用这句话会导致页面重绘
(2)element.innerHTML()
- 将内容写入某个节点,不会导致页面全部重绘
- 创建多个元素效率高(不要拼接字符串,采用数组的形式拼接),结构稍微复杂
(3)document.createElement()
-
六.事件高级
1.注册事件:给元素添加事件或绑定事件
(1)传统方法:element.事件 = function(){}
例如:btn.onclick = function(){}
- 特点:注册事件的唯一性(同一个元素的同一个事件只能处理一个处理函数,最后注册的处理函数会覆盖之前的处理函数)
(2)方法监听注册方式:使用addEventListener()方法
- 语法:eventTarget.addEventListener(type,listener,useCapture)
- 该方法会将指定的监听器注册到eventTarget(目标对象)上,当该对象触发指定时间就会执行事件处理函数
- type:事件类型字符串,比如click,mouseover (注意,不能带on)
- listener:事件处理函数,事件发生时,会调用该监听函数
- useCapture:表示是否在捕获阶段触发。可省略,布尔值,默认为false;
- 同一个元素的同一个事件可以添加多个事件处理函数
-
2.删除事件:也叫解绑事件
(1)传统方法:element.onclick = null;
例如:div.onclick = null;
(2)方法监听注册方式:
语法:eventTarget.removeEventListener(type,listener,useCapture)
3.DOM事件流:
(1)事件流描述的是从页面中接收事件的顺序
(2)事件发生会在元素节点之间按照特定的顺序传播,这个传播过程即DOM事件流
(3)DOM事件流的三个阶段:捕获阶段,目标阶段,冒泡阶段比如,给元素绑定一个事件,他会从上面的元素(由DOM最顶层节点)传递过来,由于上面的元素没有绑定事件,所以不会做出反应,传递到具体元素接受的过程叫做事件捕获(捕获阶段不会触发事件),接收后逐级传到DOM最顶层的过程叫事件冒泡,也就是后代元素绑定的事件被触发,其祖先元素的相同时间也会触发
- JS代码只能执行捕获或冒泡的其中一个阶段
- onclick只能得到冒泡阶段
- 没有冒泡阶段的事件:onblur,onfocus,onmouseenter,onmouseleave
a.onclick = function(event){ //需要传入事件对象,才能取消冒泡
event = event || window.event;
alert("我是span的");
event.cancelBubble = true;
}
4.事件对象(event):
(1)绑定事件时函数括号里的就是事件对象(括号内写event或evt或e就行,兼容性有问题,可以写成window.event)
(2)事件对象有了事件才会存在,系统自动创建,不需要我们传递
(3)事件对象是我们事件的一系列相关数据的集合,跟事件相关的,比如鼠标点击就包含了鼠标的坐标等相关信息
(4)事件发生后,跟事件相关的一系列信息数据的集合都会放到这个事件对象event里面,他有很多的属性和方法
(5)event主要代表事件的状态,跟时间相关的一系列信息的集合。
- 鼠标事件对象用MouseEvent
- 键盘事件对象用KeyboardEvent
(6)事件对象常见属性和方法:
- e.target与this的区别
- e.target返回的触发时间的对象
- this返回的是绑定时间的对象
- 阻止默认行为:例如点击超链接不跳转
return false可以阻止默认行为,但是函数内后边的代码就无法执行了
5.事件委托(代理,委派)
(1)原理:不是每个子节点都设置事件监听器,而是在将事件监听器设置在其父结点上,然后利用冒泡原理影响设置每个子节点
(2)优点:减少事件绑定的次数,提高了程序的性能6.鼠标事件补充:
(1)禁止鼠标右键菜单:contextmenu
主要控制何时显示上下文菜单
document.addEventListener('contextmenu',function(e){
e.preventDefault();//取消默认事件
})
(2)禁止鼠标选中:selectstart
document.addEventListener('selectstart',function(e){
e.preventDefault();//取消默认事件
})
(3)鼠标事件对象:MouseEvent
其中x,y坐标都是针对可视窗口的上和左边缘而论
(4)鼠标移动事件:
- mousemove:鼠标移动
- mousedown:鼠标按下
- mouseup:鼠标松开
(5)鼠标滚轮事件:
- event.wheelDelta:获取滚动方向,正值为向上滚动,负值为向下滚动
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body{
height: 1000px;
}
img{
width: 200px;
position: absolute;
}
</style>
</head>
<body>
<img src="./OIP-C.jpg" alt="">
<script>
var pic = document.querySelector('img');
document.addEventListener('mousemove',function(e){
var x = e.pageX;
var y = e.pageY;
pic.style.left = x + 'px';
pic.style.top = y + 'px';
})
</script>
</body>
</html>
7.常用键盘事件:
| 键盘事件 | 触发条件 | | —- | —- | | onkeyup | 某个键盘按钮被松开时触发 | | onkeydown | 某个键盘按钮被按下时触发 | | onkeypress | 某个键盘按钮被按下时触发(不识别功能键,例如ctrl,shift等) | | keyCode | 返回该键的ASCII的值 |
(1)如果使用addEventListener不需要加on
(2)三个时间的执行顺序:keydown-keypress-keyup
(3)键盘控制盒子移动:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.bigBox {
width: 800px;
height: 600px;
border: 2px solid brown;
margin: 0 auto;
margin-top: 100px;
position: relative;
}
.smallBox {
width: 50px;
height: 50px;
background-color: khaki;
position: absolute;
left: 50px;
top: 50px;
}
</style>
</head>
<body>
<div class="bigBox">
<div class="smallBox"></div>
</div>
<script>
// 键盘按下事件 ←37 ↑38 →39 ↓40
// 获取元素节点
var smallBox = document.getElementsByClassName("smallBox")[0];
var bigBox = document.getElementsByClassName("bigBox")[0];
class Key {
constructor(smallBox, bigBox) {
this.smallBox = smallBox;
this.bigBox = bigBox;
document.onkeydown = this.keyDown.bind(this);
}
keyDown(ev) {
this.ev = ev;
// 获取小盒子的left值与top值。 注意 元素.style.属性名 只能获取行内样式。
var l = parseInt(getComputedStyle(smallBox).left);
var t = parseInt(getComputedStyle(smallBox).top);
// console.log("l="+l);
// console.log("t="+t);
// 小盒子每次移动的距离
var speed = 50;
if (ev.shiftKey) {
speed *= 2;
}
switch (ev.keyCode) {
// 左
case 37:
// 小盒子应该向左移动
l -= speed
if (l >= 0) {
smallBox.style.left = l + "px";
} else {
smallBox.style.left = 0;
}
break;
// 上
case 38:
t -= speed;
if (t >= 0) {
smallBox.style.top = t + "px";
} else {
smallBox.style.top = 0;
}
break;
// 右
case 39:
l += speed;
if (l <= (bigBox.clientWidth - smallBox.offsetWidth)) {
// 小盒子应该向右移动
smallBox.style.left = l + "px";
} else {
smallBox.style.left = (bigBox.clientWidth - smallBox.offsetWidth) + "px";
}
break;
// 下
case 40:
t += speed;
if (t <= (bigBox.clientHeight - smallBox.offsetHeight)) {
smallBox.style.top = t + "px";
} else {
smallBox.style.top = (bigBox.clientHeight - smallBox.offsetHeight) + "px";
}
break;
}
}
}
var key = new Key(smallBox, bigBox);
</script>
</body>
</html>