js DOM详解

DOM(Document Object Model)文档对象模型

将整个HTML文档每个标签当做一个对象,这个对象下包含了许多属性和方法,通过操作这些属性或调用方法实现对HTML的动态更新

console.log(document); 		  // 打印整个HTML文档对象
console.log(typeof document); // object

本篇介绍DOM中的方法,以及如何对页面中的元素进行操作

js中的元素就是html标签,文本、标签、属性等都是节点

获取页面元素

想要操作页面中的标签、属性等,首先需要先获取标签

document.querySelector()

返回满足条件的第一个文档元素,未找到则返回null

可以使用css中的选择器

// <div></div>
let box = document.querySelector('div')

// <div class="box"></div>
let box = document.querySelector('.box')

// <div> <p></p> </div>
let p = document.querySelector('div p')

document.querySelectorAll()

返回满足条件的所有元素,是一个伪数组(有length,但是不能调用数组方法)

可以使用css中的选择器

// <p class="p1"></p>
// <p class="p2"></p>
let p = document.querySelectorAll('p')
console.log(p[0])  // 打印 <p class="p1"></p>
document.getElementById()根据id返回第一个匹配到的标签
document.getElementsByTagName()根据标签名,返回伪数组,包含所有匹配到的元素
document.getElementsByClassName()根据class类名,同样返回伪数组
document.getElementsByName()根据name属性值,同样返回伪数组
document.documentElement返回html根标签
document.body返回body标签

元素的属性

获取到的页面元素会自带一些属性,元素.属性就可以得到属性值

let box = querySelector('div')
console.log(box.clientWidth)    // 打印box元素的宽度
clientWidth元素宽度(width + padding)
clientHeight元素高度(height + padding)
offsetWidth元素布局宽度(width + padding + border)
offsetHeight元素布局高度(height + padding + border)
scrollWidth实际内容宽度(width + padding,包括隐藏的内容,比如overflow隐藏的宽度)
scrollHeight实际内容高度(height + padding,包括隐藏的内容,比如overflow隐藏的宽度)
offsetLeft与左侧参照物的距离 (参照物为最近的带有定位的祖先元素,没有则为body)
offsetTop与上方参照物的距离 (参照物为最近的带有定位的祖先元素,没有则为body)
scrollTop被卷去的高度(例如body在向下滚动时,它的scrollTop值会从0越来越大)

节点操作

查找元素节点

.parentNode返回父节点
.children返回所有子节点
.previousElementSibling返回上一个兄弟节点
.nextElementSibling返回下一个兄弟节点
.childNodes返回所有子节点(包括文本节点,比如文字、空格、换行等等)
.previousSibling返回上一个兄弟节点(包括文本节点)
.nextSibling返回下一个兄弟节点(包括文本节点)
// <ul><li><p>haha</p></li></ul>
const li = document.querySelector('li')
console.log(li.parentNode)         // 打印ul标签
console.log(li.children)           // 打印p标签
console.log(li.nextElementSibling) // 打印null

增加元素节点

document.createElement()在js中创建一个标签
.appendChild()将节点添加到目标节点的子元素的末尾
.prepend()将节点添加到目标节点的子元素的开头
.append()添加至末尾,可以一次添加多个,并且可以添加文本节点
const ul = document.querySelector('ul') // 获取ul标签
const li = document.createElement('li') // 创建一个li标签
ul.appendChild(li)    // ul标签内,末尾增加一个li标签
const ul = document.querySelector('ul')
const li = document.createElement('li')
ul.append(li,'aaa') // ul标签内,末尾增加一个li标签,li标签后增加aaa文字

删除元素节点

.remove()将目标节点删除
.removeChild()将目标节点的指定子元素删除
const ul = document.querySelector('ul')    // 获取ul标签
const li = document.querySelectorAll('li') // 获取所有li标签
li[0].remove()        // 删除第一个li标签
ul.removeChild(li[1]) // 删除第二个li标签

元素操作__内容

.innerText获取 / 修改元素文本内容(不包含标签)
.innerHTML获取 / 修改元素内容(包含标签、文本)
// <p><span>haha</span></p>
const p = document.querySelector('p') // 获取p标签

// 获取内容
console.log(p.innerText) // 打印 haha
// 修改内容
p.innerText = 'aaa';     // span标签将被覆盖,p标签内只有文字aaa

innerText 和 innerHTML 的区别在于,innerHTML能够解析标签,在获取时会打印标签,修改时能够解析标签,而innerText会将标签以文本形式呈现在页面中

元素操作__原生属性

直接使用 标签.属性 进行操作(仅限于原生属性,并且此方法不可用于class类名)

// <img src="./xx.png" alt="高清图片" />
const img = document.querySelector('img')
// 获取属性值
console.log(img.alt)     // 打印'高清图片'
// 修改属性值
img.src = './aaa.png';   // 修改图片路径

元素操作__样式

使用 标签.style.样式 进行操作(样式名带有 - 连字符,需要去除 -,并将 - 后第一个字母大写)

这种方法是js直接添加行内样式,所以权重仅次于 !important

const box = document.querySelector('div')
box.style.width = '200px';            // 将宽度设置为200px
box.style.backgroundColor = '#FFF';   // 将背景色设置为白色

 元素操作__类名

.className覆盖类名
.classList.add()添加类名
.classList.remove()删除类名
.classList.toggle()

切换类名

(有就删除,没有就添加)

.classList.contains()

判断是否含有某个类名

(有就返回true,没有就返回false)

// <div class="box"></div>
const box = document.querySelector('div')

box.className = 'btn';         // 原本的所有类名被top覆盖(不推荐使用)
box.className += ' btn';       // 添加btn类名(有空格作为间隔)(即使类名重复也会添加)

box.classList.add('aa')        // 添加aa类名
box.classList.remove('bb')     // 删除bb类名
box.classList.toggle('cc')     // 原本有cc类名就删除,没有就添加
box.classList.contains('box')  // 打印true

元素操作__自定义属性

.hasAttribute()查询是否有该属性,返回 true / false
.getAttribute()获取指定属性的属性值
.setAttribute()设置指定属性的属性值
// <div aa="bb"></div>
const box = document.querySelector('div')

console.log(box.hisAttribute('aa'))  // 打印true,判断box上是否有aa属性
console.log(box.getAttribute('aa'))  // 打印bb,获取aa属性的属性值
box.setAttribute('aa','cc')          // 修改aa属性的属性值为cc
                                     //(如果没有aa属性则会添加一个aa属性)

还可以在标签中使用 data-xx="" 的方法自定义属性,js中使用 标签.dataset.xx 获取和修改

// <div data-id="haha"></div>
const box = document.querySelector('div')

// 获取属性值
console.log(box.dataset.id) // 打印 haha
// 修改属性值
box.dataset.id = 'aa';      // 设置该自定义属性的值为aa

元素操作__其他属性

.disabled按钮 / input框 的禁用状态,设为 true / '任意字符串' 为禁用,false / '' 为不禁用
.checked复选框的选中状态,设为 true / '任意字符串' 为选中,false / '' 为不选中
// <input text="checkbox" />
const input = document.querySelector('input')
input.disabled = 'true';    // 禁用该复选框
input.checked = 'true';     // 默认选中该复选框

DOM事件

对元素进行事件绑定, 由不同行为触发函数,可以获取到元素对象以及事件对象等信息

事件绑定

绑定事件就是进行点击等行为时会触发函数

绑定事件有三种方式:DOM 0级(行内绑定、js获取元素绑定),DOM 2级(事件监听)

DOM 0级事件

重复绑定事件时会覆盖,事件名前有on

// 行内绑定事件
<div onclick="fun()">点我</div>

function fun(){
    alert('haha')
}
// onclick点击事件,每当点击一次目标元素时,便执行一次函数
// js获取元素绑定事件
<div>点我<div>

const box = document.querySelector('div')
box.onclick = function(){
    console.log('haha')
}

DOM 2级事件

重复绑定事件时不会覆盖,事件名前没有on

// 事件监听
<div>点我</div>

const box = document.querySelector('div')
box.addEventListener('click',function(){
    console.log('haha')
})
// addEventListener() 添加事件监听
// 参数1:事件类型(事件名)
// 参数2:执行函数
// 参数3:可选,判断在什么阶段触发,后面会详细介绍

事件解绑

DOM 0级事件

btn.onclick = null; // 解绑(重新赋值)

DOM 2级事件

function fun(){};
btn.addEventListener('click',fun)    // 绑定事件
btn.removeEventListener('click',fun) // 解绑事件

// removeEventListener 移除事件监听
// 参数1:想要解绑的事件类型(事件名)
// 参数2:想要解绑的函数名

// 想要移除事件监听,在绑定时需要传入函数名,而不是直接定义
// 因为解绑时需要填写对应的函数名

事件类型

鼠标事件

click鼠标左键点击元素
dblclick鼠标左键双击元素
mouseenter鼠标指针移入元素
mouseleave鼠标指针移出元素
mousemove鼠标指针在元素内移动时持续触发

键盘事件

keydown按下任意按键
keypress除Shift,Fn,CapsLock外任意键被按住(连续触发)
keyup释放任意按键

表单事件

input输入框内容改变时触发
change输入框内容改变并失去焦点时触发
select文本被选中时触发(input标签、textarea标签)
reset点击重置按钮时触发
submit点击提交按钮时触发

焦点事件

focus元素获得焦点时触发
blur元素失去焦点时触发

其他

copy元素内容被拷贝时触发
load一个资源及其相关资源已完成加载时触发

事件流

事件流的定义:事件完整执行过程中的流动路径

事件流的三个阶段:捕获阶段->目标阶段->冒泡阶段

一个点击事件,不仅点击到的是目标元素,也是他的父元素,所以就有了事件流,事件流就是一个事件的传播过程

比如现在给一个p标签绑定一个点击事件,点击p标签时

会从window对象开始,一级一级地向子元素传播,这个阶段就是捕获阶段,传播到p标签时,就是目标阶段,然后继续向父元素传播,就是冒泡阶段

addEventListener事件监听的第三个参数,可以声明该事件在什么阶段触发
false(默认):在冒泡阶段触发;true:在捕获阶段触发

举个例子:

给box绑定了一个点击事件,给body也绑定了一个点击事件

div.addEventListener('click',function(){
    console.log('div')
})
document.body.addEventListener('click',function(){
    console.log('body')
})

点击box时,body的点击事件也会触发,结果如下:

可以看到,先打印出box,再打印body,此时addEventListener的第三个参数为默认值,即false,表示在冒泡阶段触发,冒泡阶段在目标阶段之后,所以后打印body

此时我们将body的点击事件加上第三个参数,值为true

document.body.addEventListener('click',function(){
    console.log('body')
},true)

再次点击box,结果如下:

此时先打印body,再打印box,因为true表示在捕获阶段触发,捕获阶段在目标阶段之前,所以先打印body

事件对象

当事件发生后,浏览器会把当前事件相关的信息会封装成一个对象

获取事件对象:事件函数的第一个形参

box.addEventListener('click',function(e){
	console.log(e);  	   // 事件对象
    console.log(e.target); // 触发事件的元素
})
事件对象中的属性 
target返回触发事件的节点
currentTarget返回事件绑定的节点
eventPhase返回一个整数,表示事件流在传播阶段的位置
1:捕获阶段,2:目标阶段,3:冒泡阶段)
type返回一个字符串,表示事件类型,区分大小写
(点击事件打印:click)
timeStamp返回一个毫秒时间戳,表示事件发生的时间
(在页面加载完毕后从0开始计时)
clientX / clientY

获取鼠标事件触发时的鼠标坐标

(距离客户端边缘的距离)

pageX / pageY

获取鼠标事件触发时的鼠标坐标

(距离页面边缘的距离)

screenX / screenY

获取鼠标事件触发时的鼠标坐标

(距离屏幕边缘的距离)

key / keyCode

获取键盘事件输入的字符和按键编号

(按键a的key:a,keyCode:65)

关于target和currentTarget,在上面的box例子中,点击box时,body绑定的事件中的target是box元素,而currentTarget是body元素,box是触发事件的源头,而body是事件绑定的节点

阻止事件冒泡

不允许在冒泡阶段触发目标元素的祖先元素的绑定事件

box.addEventListener('click',function(e){
    e.stopProagation()
}
document.body.addEventListener('click',function(){
    console.log('body')
}

此时点击box元素,不会再触发body的点击事件

阻止默认行为

一些标签都有默认的行为,比如a标签点击会跳转,我们可以阻止这个跳转发生

// 第一种
e.preventDefault();
// 第二种
return false;

当然第二种会return,所以一般放在代码的最后

事件委托

又叫事件委派、事件代理

比如在ul列表中,有多个li标签,现在想给每一个li标签都绑定一个点击事件,就需要获取到所有的li标签,然后循环绑定

事件委托:获取到ul标签,给ul绑定点击事件,通过事件对象的target属性获取到触发事件的节点,也就是每一个li标签

const ul = document.querySelector('ul');
ul.addEventListener('click',function(e){
    if(e.target.tagName === 'LI'){
        // 代码块
    }
}

上面的示例中使用e.target.tagName,这个属性返回节点名称的大写字符串,通过判断可以精确每次点击的是否为li标签,也可以使用e.target.classList.contains()来判断类名等等

事件委托对动态生成的子元素也生效,如果直接给li标签绑定事件,后面js添加进去新的li标签又需要重新绑定,而事件委托则不需要。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值