用 JS 去操作浏览器 和 页面中的 HTML 元素(DOM)
1. DOM的基本概念
文档对象模型,其实就是操作 html 中标签的一些能力 比如:
- 获取一个元素(标签)
- 移除一个元素
- 创建一个元素
- 向页面添加一个元素
- 给元素绑定一些事件
- 获取元素的属性
- 给元素添加一些css样式
- 。。。 DOM 的核心 就是 document 对象
document 对象是 JS 内置的一个对象, 里边存储着专门用来操作元素的各种方法
DOM: 页面中的标签, 我们通过 JS 获取到以后, 就把这个对象叫做 DOM对象(DOM节点)
2. 获取元素
- 获取非常规元素
document.documentElementdocument.headdocument.body
// 1. html 标签
console.log(document.documentElement)
// 2. head 标签
console.log(document.head)
// 3. body 标签
console.log(document.body)
- 获取常规元素
- 2.1 通过 ID 名获取标签
语法:
document.getElementById('ID名') - 2.2 通过 class 名获取标签
- 语法:
document.getElementsByClassName('class名') - 注意: 因为页面中可能会有多个元素的 class 相同, 所以获取到的是一组元素,获取到后会把元素放在一个长得很像数组一样的 数据结构内, 但它并不是数组,我们管这种数据结构叫做 伪数组
- 伪数组: 长得很像数组, 也是通过索引排列, 但是没有数组的方法
- 语法:
- 2.3 通过标签名获取
- 语法:
document.getElementsByTagName('标签名') - 注意: 获取到的也是一个长得很像数组一样的数据结构, 其实就是获取到了一个伪数组,想要准确的获取到标签元素, 我们需要通过索引来帮助我们拿到
- 语法:
- 2.4 按照选择器的形式来获取元素
- 2.4.1 querySelector 这个方法允许我们像写 css 的时候的选择器一样获取标签
- 注意: 这个方法只能获取到一个元素, 就是满足条件的第一个元素
- 语法:
document.querySelector('选择器')
- 2.4.2 querySelectorAll, 该方法与 querySelector 选择器类似, 只不过会将所有满足条件的元素都获取到,也是放在一个伪数组内
- 语法:
document.querySelectorAll('选择器')
- 语法:
- 2.4.1 querySelector 这个方法允许我们像写 css 的时候的选择器一样获取标签
<div id="box">我是 ID 名为 box 的 div 标签</div>
<div class="box_cla">我是 class 为 box_cla 的 div 标签1</div>
<div class="box_cla">我是 class 为 box_cla 的 div 标签2</div>
<div class="box_cla">我是 class 为 box_cla 的 div 标签3</div>
// 2.1 获取到页面中 ID 为 box 的标签
var box = document.getElementById('box')
// console.log(box) //<div id="box">我是 ID 名为 box 的 div 标签</div>
// 2.2 获取到页面中 class 名为 box_cla 的标签
var box_cla = document.getElementsByClassName('box_cla')
console.log(box_cla) //[div.box_cla,div.box_cla,div.box_cla]
// console.log(box_cla[0])
// console.log(box_cla[1])
// console.log(box_cla[2])
// 2.3 获取到页面中 标签名为 div 的标签
var divs = document.getElementsByTagName('div')
console.log(divs)
//[div#box, div.box_cla, div.box_cla, div.box_cla, box: div#box]
// 2.4.1 按照选择器的形式获取标签
var box2 = document.querySelector('.box_cla')
console.log(box2)
var box3 = document.querySelector('div')
console.log(box3)
// 2.4.2
var box4 = document.querySelectorAll('.box_cla')
var box5 = document.querySelectorAll('div')
console.log(box4)
console.log(box5)
3. 操作属性
我们获取到元素以后, 可以直接操作 DOM 的属性, 然后直接把效果展示在页面
元素.innerHTML
- 获取元素内部的 HTML 结构
- 我们也可以直接给这个属性重新赋值, 达到修改页面的效果
元素.innerText
- 获取元素内部的文本 (只能获取到文本, 获取不到 html 标签)
- 我们也可以直接给这个属性重新赋值, 达到修改页面的效果
4. 元素属性
- 获取元素的某些属性
- 语法:
元素.getAttribute('要查询的属性名') - 返回值: 查询到属性时返回对应的属性值, 没有查询到时直接返回 null
- 修改元素的某些属性
- 语法:
元素.setAttribute('对应的属性名', '对应的属性值') - 注意: 如果元素没有对应的属性名, 那么相当于是新增一个属性
- 删除元素的某些属性
语法:
元素.removeAttribute('要删除的属性名')
<div class="box" a="QF001"></div>
// 0. 获取元素
// var oDiv = document.querySelector('.box')
var oDiv =document.getElementsByClassName('box')[0]
//1. 获取元素的某些属性
console.log(oDiv.getAttribute('a')) //QF001
console.log(oDiv.getAttribute('class')) //box
console.log(oDiv.getAttribute('b')) //null
//2. 修改元素的某些属性
oDiv.setAttribute('a', 'QF999')
oDiv.setAttribute('b', 'qwer')
//3. 删除元素的某些属性
oDiv.removeAttribute('class')
5. h5 自定义属性
- data- 表示该属性是一个自定义属性
- data- 后边的内容才是属性名, 当前案例中属性名为a, 不是 data-a
- = 后边的内容是属性值 - 每一个 元素/DOM节点 天生自带一个属性, 叫做 dataset, 是一个类似对象的数据结构
<div data-a="100"></div>
// 0. 获取标签
var oDiv = document.querySelector('div')
// 1. 访问元素的 dataset 属性 查询
console.log(oDiv.dataset.a) // 100
// 2. 增加一个 h5 自定义属性
oDiv.dataset.age = 18 // 标签中没有这个自定义属性, 所以是新增
oDiv.dataset.a = 'QF666' // 标签中有这个自定义属性, 相当于做了修改操作
// 3. 删除
delete oDiv.dataset.a
6. 获取元素样式
- 语法:
元素.style.某个属性 - 注意:
- 我们也可以给这个语法重新赋值, 达到修改元素样式的效果 (修改的是行内的样式)
- 这种语法获取到的元素样式, 只能获取到行内样式
- 获取非行内样式
- 语法:
window.getComputedStyle(元素).要查询的css属性 - 注意: 这种方式获取到的属性 是只读的
- 只读: 能获取到, 但是不允许修改
<style>
.box {
width: 100px;
height: 100px;
/* background-color: skyblue; */
}
</style>
<div class="box" style="background-color: red;"></div>
// 0. 获取元素
var oDiv = document.querySelector('.box')
// 1. 获取元素样式
console.log(oDiv.style.width) //返回啥也没有,空白
console.log(oDiv.style.height)
// onsole.log(oDiv.style.background-color) // 直接这样写相当于写了一个 oDiv.style.background - color, 这是一个错误写法
console.log(oDiv.style['background-color']) // 中括号语法
console.log(oDiv.style.backgroundColor) // 驼峰命名
// 2. 设置元素样式
oDiv.style.width = 666 + 'px'
oDiv.style.backgroundColor = 'red'
//3. 获取非行内样式
console.log(window.getComputedStyle(oDiv).width)//100px
console.log(window.getComputedStyle(oDiv).backgroundColor)
// window.getComputedStyle(oDiv).width = 800 + 'px' // 不允许修改, 会有报错
7. className专门用来操作元素的 类名
- 语法:
元素.className我们也可以给他重新赋值, 来达到修改元素的类名
<div class="box" style="background-color: red;"></div>
oDiv.className = 'new_box'
console.log(oDiv.className)//new_box
元素.classList
// 0. 获取元素
var oDiv = document.querySelector('.box')
// console.log(oDiv.className)
// oDiv.className = 'qwer'
// 2.1 获取
console.log(oDiv.classList)
//['box', 'new_box', value: 'box new_box']
// 2.2 新增
oDiv.classList.add('qwer')
// 2.3 删除
oDiv.classList.remove('new_box')
8. DOM 节点
-
元素节点(标签) 通过 getElementBy... 获取到的都是元素节点
-
文本节点(标签内的文字) 通过 getAttribute 获取到的都是属性节点
-
属性节点(标签上的属性)
通过 getAttribute
9. 获取子节点
- 获取某一节点下 所有的 子一级 节点, 获取到的是一个伪数组
- 语法:
元素.childNodes
- 语法:
- 获取某一节点下 所有的 子一节 元素节点 获取到的是一个伪数组
- 语法:
元素.children
- 语法:
- 获取某一节点下子一级的 第一个节点
- 语法:
元素.firstChild
- 语法:
- 获取某一节点下子一级的 最`一个节点
- 语法:
元素.lastChild
- 语法:
- 获取某一节点下子一级的 第一个元素节点
- 语法:
元素.firstElementChild
- 语法:
- 获取某一节点下子一级的 最后一个元素节点
- 语法:
元素.lastElementChild
- 语法:
<div>
<p>你好</p>
<span>测试文本</span>
<h1>JS 是世界上最优美的语言</h1>
</div>
// 0. 获取元素
var oDiv = document.querySelector('div')
// 1. childNodes
console.log(oDiv.childNodes)
//[text, p, text, span, text, h1, text]
/**
* 拿到的是一个伪数组, 里边有 7 个节点
* [0]: text 从 <div> 一直到 <p> 中间有一个换行和一堆空格 这是一个文本节点
* [1]: p 这个就是 p 标签, 他是第二个节点, 这是一个 元素节点
* [2]: text 从 </p> 一直到 <span> 中间有一个换行和一堆空格 这是一个文本节点
* ....
*/
// 2. children
console.log(oDiv.children) // 这里只有 div 内部的标签[p, span, h1]
// 3. firstChild
console.log(oDiv.firstChild) // #text
// 4. lastChild
console.log(oDiv.lastChild) // #text
// 5. firstElementChild
console.log(oDiv.firstElementChild)
// 6. lastElementChild
console.log(oDiv.lastElementChild)
10. 获取兄弟节点
- 获取对应的 下一个兄弟节点
- 语法:
元素.nextSibling
- 获取对应的 上一个兄弟节点
- 语法:
元素.previousSibling
- 获取对应的 下一个兄弟元素节点
- 语法:
元素.nextElementSibling
- 获取对应的 上一个兄弟元素节点
- 语法:
元素.previousElementSibling
<div>
<p>你好</p>
<span>测试文本</span>
<h1>JS 是世界上最优美的语言</h1>
</div>
// 0. 获取元素
var oSpan = document.querySelector('span')
// 1. nextSibling
console.log(oSpan.nextSibling) // #text
// 2. previousSibling
console.log(oSpan.previousSibling) // #text
// 3. nextElementSibling
console.log(oSpan.nextElementSibling) // h1
// 4. previousElementSibling
console.log(oSpan.previousElem
entSibling) // p
11. 获取父节点与属性节点
- 父节点
- 语法:
元素.parentNode
- 获取元素的所有属性节点
- 语法:
元素.attributes
<div>
<p>你好</p>
<span>测试文本</span>
<h1 id="h1_box" class="h1_box_cla" test="QF001" num="100">JS 是世界上最优美的语言</h1>
</div>
// 0. 获取节点1
var oH = document.querySelector('h1')
// 1. parentNode
console.log(oH.parentNode)
// 2. attributes
console.log(oH.attributes)
//{0: id, 1: class, 2: test, 3: num, id: id, class: class, test: test, num: num, length: 4}
console.log(oH.attributes[0])
//id="h1_box"
12. 节点属性
- nodeType 节点类型
- 节点中的一个属性 nodeType 能够区分当前节点是什么类型
- 1 -> 元素节点
- 2 -> 属性节点
- 3 -> 文本节点
- nodeName 节点名称
- 元素节点 -> 大写的标签名 (LI / UL / DIV)
- 属性节点 -> text (属性名)
- 文本节点 -> #text
- nodeValue 节点的值
- 元素节点 -> 没有 nodeValue 也就会输出 null
- 属性节点 -> 对应的属性值
- 文本节点 -> 对应的文本内容
<ul text="我是 UL 的一个属性">
<li>你好</li>
</ul>
// 0. 获取元素
var oUl = document.querySelector('ul')
//<ul text="我是 UL 的一个属性">...</ul>
// 1. 元素节点
var eleNode = oUl.firstElementChild
//<li>...</li>
// 2. 属性节点
var attrNode = oUl.attributes[0]
//text="我是 UL 的一个属性"
// 3. 文本节点
var textNode = oUl.firstChild
//#text
// 1. nodeType
console.log('元素节点: ', eleNode.nodeType)
console.log('属性节点: ', attrNode.nodeType)
console.log('文本节点: ', textNode.nodeType)
/*元素节点: 1
属性节点: 2
文本节点: 3
*/
// 2. nodeName
console.log('元素节点: ', eleNode.nodeName)
console.log('属性节点: ', attrNode.nodeName)
console.log('文本节点: ', textNode.nodeName)
/*元素节点: LI
属性节点: text
文本节点: #text
*/
// 3. nodeValue
console.log('元素节点: ', eleNode.nodeValue)
console.log('属性节点: ', attrNode.nodeValue)
console.log('文本节点: ', textNode.nodeValue)
/*
元素节点: null
属性节点: 我是 UL 的一个属性
文本节点:
*/
13. 操作 DOM 节点(常规意义上 '增删改查')
在向页面增加一个节点, 首先, 你应该先 有一个节点
- 操作分类:
1. 在 JS 中创建一个节点
- 语法:
document.createElement('要创建的标签名')
2. 向页面增加一个节点
- 语法1:
元素.appendChild(要添加的节点)- 作用: 向元素的末尾追加一个节点
- 语法2:
元素.insertBefore(要插入的节点, 插入到那个节点的前面)- 注意: 两个参数都是必填项,第二个参数传递正常节点时, 代表插入到这个节点的前面,第二个参数传递的是 null 时, 表示插入到 "元素" 的末尾
<div class="box">
<p>我是通过 HTML 手写出来的 P 标签</p>
</div>
// 0. 获取元素
var myDiv = document.getElementsByClassName('box')[0]
var oP = document.querySelector('p')
// 1.1 创建一个元素节点
var oDiv = document.createElement('div')
var oSpan = document.createElement('span')
// 1.2 创建一个文本节点 (了解)
var oText = document.createTextNode('我是通过 JS 创建出来的文本节点')
// 1.3 将刚才创建的文本节点, 添加到元素节点内 元素.appendChild(要添加的节点)
oDiv.appendChild(oText)
oSpan.innerText = '我是通过 innerText 直接赋值的字符串'
// 2. 向页面中增加一个节点
// 2.1 元素.appendChild(要添加的节点)
myDiv.appendChild(oDiv)
myDiv.appendChild(oSpan)
// 2.2 元素.insertBefore(要插入的节点, 插入到那个节点的前面)
myDiv.insertBefore(oDiv, oP) // 将我们通过 JS 创建的 div 标签, 插入到页面的 P 标签前边
myDiv.insertBefore(oSpan, null) // 第二个参数传递 null 代表插入到 '元素' 的末尾
3. 删除页面某一个节点
- 语法1:
父节点.removeChild('要删除的子节点') - 语法2:
节点.remove()
4.修改页面的某一个节点
- 语法:
父节点.replaceChild('新节点', '旧节点/要被修改的节点') - 作用: 将页面中的某一个节点 做一个替换
5.获取页面的某一个节点
之前获取元素的方法
6.克隆一个节点 (把一个节点复制出一个一摸一样的)
- 语法:
节点.cloneNode(参数) - 参数:
- 默认是 false, 表示不克隆后代节点
- 选填是 true, 表示克隆后代节点
<div class="box">
<p>我是第一个 P 标签</p>
<p>我是第二个 P 标签</p>
</div>
// 0. 获取节点
var box = document.querySelector('.box')
var oP = document.querySelector('p') // 满足条件的第一个 p 标签
// 3. 删除页面某一个节点
box.removeChild(oP)
//box.remove()
// 4. 修改页面的某一个节点
/var oSpan = document.createElement('span') // 创建一个 span 标签
oSpan.innerText = '我是通过 JS 创建出来的 SPAN 标签' // 给 span 标签添加文字
box.replaceChild(oSpan, oP) // 用刚刚创建出来的 span 标签, 替换 原本的 p 标签
// 6. 克隆一个节点
//var cloneP = oP.cloneNode() // 克隆出来一个和 oP 这个节点 一摸一样的 新节点 不包含后代节点
var cloneP = oP.cloneNode(true) // 克隆出来一个和 oP 这个节点 一摸一样的 新节点 包含后代节点
box.appendChild(cloneP)
console.log(box)
/**
* box.appendChild(oP)
*
* 错误理解: 将 oP 这个节点, 添加到 box 的末尾, 然后页面就有 3 个 P标签了
*
* 正确理解: 将 oP 这个节点, 添加到 box 的末尾
* 因为 DOM 节点在页面中只会具有一个(核心)
* 所以这一步相当于将 原本排列在 box 内部第一个的 p 标签, 移动到了 box 内部的 最后一个
*/
14. 获取非行内样式(了解)
- 语法:
window.getComputedStyle(元素).要查询的CSS属性名 - IE 的语法: 元素.currentStyle.要查询的CSS属性名 (了解)
15.获取元素偏移量
获取元素在页面上相对参考父级的左边和上边的距离 (要求会背, 因为后续会有项目, 需要使用)
参考父级:其实就是假设 你要给一个元素 '绝对定位', 他是根据谁来进行定位, 那么这个元素的偏移量参考父级就是谁
元素.offsetParent获取元素的相对父级元素.offsetLeft获取元素距离左边的距离元素.offsetTop获取元素距离顶部的距离
//html:
<div class="box1">
<div class="box2"></div>
</div>
//css:
<style>
* {
padding: 0;
margin: 0;
}
.box1 {
width: 400px;
height: 400px;
background-color: pink;
position: relative;
top: 20px;
left: 20px;
}
.box2 {
width: 100px;
height: 100px;
background-color: skyblue;
position: absolute;
left: 100px;
top: 200px;
}
</style>
// 0. 获取元素
var box2 = document.querySelector('.box2')
// 1. 元素.offsetParent
console.log(box2.offsetParent)
// 2. 元素.offsetLeft
console.log('offsetLeft', box2.offsetLeft)
// 3. 元素.offsetTop
console.log('offsetTop', box2.offsetTop)
16. 获取元素尺寸与浏览器窗口尺寸
1. 获取元素尺寸 (元素的占地面积)
- 语法1:
元素.offsetWidth元素.offsetHeight - 语法2:
元素.clientWidth元素.clientHeight - 区别:
- offsetXXX -> 实际的宽度/高度 + padding + border
- clientXXX -> 实际的宽度/高度 + padding
<style>
* {
padding: 0;
margin: 0;
}
div {
width: 500px;
height: 6000px;
background-color: pink;
padding: 50px;
border: 10px solid black;
margin: 50px;
}
</style>
<div></div>
// 1. offsetXXX
console.log('oDiv.offsetWidth', oDiv.offsetWidth)
console.log('oDiv.offsetHeight', oDiv.offsetHeight)
console.log('手动分割线===================')
// 2. clientXXX
console.log('oDiv.clientWidht', oDiv.clientWidth)
console.log('oDiv.clientHeight', oDiv.clientHeight)
2. 获取浏览器窗口尺寸
- window.innerXXX -> 计算的时候 会包含浏览器的滚动条
- document.documentElement.clientXXX -> 计算的时候 不会计算滚动条 (只计算浏览器的可视区域)