DOM 是 JavaScript 操作网页的接口,全称为“文档对象模型”(Document Object Model)。
它的作用是将网页转为一个 JavaScript 对象,从而可以用脚本进行各种操作(比如增删内容)。
基础 DOM 属性(常识性知识点):
方法 | 描 述 |
---|---|
document.write | 页面上输出值 |
document.title | 获取文档的 title 元素 |
document.URL | 获取文档的 URL |
document.cookie | 获取文档的 cookie |
# DOM 简介
简单是说:DOM 就是操作 html 中标签的能力。
# 1、节点(node)
DOM 的最小组成单位叫做节点(node)。节点的类型有七种 ,如下:
- Element:元素节点(1)
- Attr:属性节点(2)
- Text:文本节点(3)
- Comment:注释节点(8)
- Document:文档节点(9)
- DocumentType:文档类型节点(10)
- DocumentFragment:文档片断节点(11)
注意
浏览器提供一个原生的节点对象 Node
,上面这七种节点都继承 ✍ 了 Node,因此具有一些共同的属性和方法。
# 2、DOM 节点
首先,我们要知道关于 DOM 的节点,我们一般将其分为三大类:元素节点
、属性节点
、文本节点
。
<body>
<div id="box">
<h1>欢迎访问我的页面</h1>
<p>这是我网页上的一段!</p>
<!-- 无序列表 -->
<ul>
<li>item1</li>
<li>item2</li>
<li>item3</li>
</ul>
</div>
<script>
console.log(box.childNodes) // 打印所有节点
// 打印结果:NodeList(9) [text, h1, text, p, text, comment, text, ul, text]
console.log(box.children) // 打印 元素节点 ✔
// 打印结果:HTMLCollection(3) [h1, p, ul]
// 使用
console.log(box.children[1].innerHTML)
// "这是我网页上的一段!"
</script>
</body>
通过来说,元素节点是我们重点关注的
当然,想要详细一点的话,可以加上document节点
、注释节点
等
# 一、元素节点(1)
在学习节点操作时,有时涉及所有节点,有时只涉及元素节点。
所以关于节点操作,统一放在这里记录 😂。
# 1、获取元素
- 直接引用元素对象(不推荐)
<body>
<div id="box"></div>
<script>
console.log(box) // 🚩 <div id="box"></div>
box.innerHTML = 'lencamo'
</script>
</body>
注意
使用 document.getElementById()
方法获取元素对象与使用直接引用元素对象之间的区别在于:
- 直接引用元素的方式需要在使用它之前先找到它的位置,并且如果 HTML 结构发生更改,则可能需要更新引用。
- 通过使用 document.getElementById(),您可以确保始终获取正确的元素对象,无论其在文档中的确切位置如何。
综上,在实际开发中推荐使用 document.getElementById()
方法获取元素对象(比如后面节点的增删改查)
- 常规方式
// 1、唯一
document.getElementById()
// 2、伪数组(NodeList实例)(有length)
var items = document.getElementsByClassName()
var items = document.getElementsByTagName() // 根据标签名
var items = document.getElementsByName() // 表单中的name属性
// 转换为真数组
// var newitems = Array.from(items)
- H5 方式
CSS 获取节点的方式在 js 中进行应用
// 返回一个
document.querySelector()
// 返回所有
document.querySelectorAll()
# 2、节点的获取
html 结构
<body>
<div>
<div id="header">网页头部区域</div>
<div id="box">
<h1>欢迎访问我的页面</h1>
<p>这是我网页上的一段!</p>
<!-- 无序列表 -->
<ul>
<li id="one">item1</li>
<li>item2</li>
<li>item3</li>
</ul>
<p>这是一个简单的段落。</p>
</div>
<div id="aside">侧边栏区域</div>
<div id="footer">网页底部区域</div>
</div>
</body>
关于后代、兄弟间元素的获取,其实采用 css 选择器中的关系选择器、伪类选择器会根据灵活一些。
因此下面这部分用的很少
/* 后代(关键字:子元素) */
// 1、第一个子元素
console.log(box.firstElementChild)
// 2、最后一个子元素
console.log(box.lastElementChild)
/* 兄弟(关键字:相邻) */
// 1、上铺的第一个兄弟
console.log(box.previousElementSibling)
// 2、下铺的第一个兄弟
console.log(box.nextElementSibling)
但想较于 CSS 选择器,使用 js 方法的一个好处就是它可以操作元素的父子关系:
/* 父子(关键字:相邻)🚩 */
// 1、父节点
// 非元素节点(如文本节点、注释节点等),它们并没有parentElement属性
console.log(one.parentNode)
// 2、子节点
// 指的是第一个子节点
console.log(box.children[0])
提示
children 获取的是子元素集合对象 HTMLCollection
完整版
学习这部分内容,可以与 css 选择器中的关系选择器、伪类选择器联想、比较一下 🤔。
/* 后代(关键字:子元素) */
// 1、第一个子元素 —— :first-child……
console.log(box.firstChild) // #text
console.log(box.firstElementChild) // h1 ✔
// 2、最后一个子元素 —— :last-child……
console.log(box.lastChild) // #text
console.log(box.lastElementChild) // p ✔
/* 兄弟(关键字:相邻) */
// 1、上铺的第一个兄弟
console.log(box.previousSibling) // #text
console.log(box.previousElementSibling) // div#header ✔
// 2、下铺的第一个兄弟
console.log(box.nextSibling) // #text
console.log(box.nextElementSibling) // div#aside ✔
相较于 css,js 方法还可以操作元素的父子关系:
/* 父子(关键字:相邻)🚩 */
// 1、父节点
// 非元素节点(如文本节点、注释节点等),它们并没有parentElement属性
console.log(one.parentNode) // ul ✔ ✨
console.log(one.parentElement) // ul
// 2、子节点
// 指的是第一个子节点
console.log(box.childNodes) // NodeList []
console.log(box.children) // HTMLCollection [] ✔ ✨
# 二、节点操作
前面是节点的一些属性操作,下面要学习的是一些方法操作
# 1、创建新节点
// 1、createElement
var odiv = document.createElement('div')
// 2、模板字符串
var odiv = `<div></div>`
# 2、插入子节点 ✨
注意
如果给定的子节点是对文档中现有节点的引用,appendChild
、🤔insertBefore()
会将其从当前位置移动到新位置。
所以要重复使用同一个节点时,需要使用可以使用 Node.cloneNode() 复制节点
createElmClone = createElm.cloneNode(true)
<div id="box">
<div id="child">欢迎访问我的页面</div>
</div>
<script>
const boxElm = document.querySelector('#box')
const childElm = document.querySelector('#child')
// =====================
// 方式1 👀
var createElm = document.createElement('span')
createElm.className = 'newSpan'
createElm.style['background-color'] = 'green'
createElm.innerHTML = 'span标签的内容'
// —— 在末尾插入
boxElm.appendChild(createElm)
// —— 指定位置插入
createElmClone = createElm.cloneNode(true)
boxElm.insertBefore(createElmClone, childElm)
// =====================
// 方式2 👀
var strElm = `<span class="newSpan" style="background-color:green">span标签的内容</span>`
// —— 全局覆盖插入
boxElm.innerHTML = strElm
// —— 尾部追加
boxElm.innerHTML += strElm
</script>
思考?
如何将
strElm
字符串转换为,一个真正的 DOM 节点对象
答案
简单方法就是,在将 strElm 包裹在一个 DOM 节点中
步骤:
- 先使用 createElement 创建一个 template 节点
- 然后使用 innerHTML 向 template 节点中填充
strElm
字符串即可
# 3、删除子节点 ✨
<body>
<div id="box">
<h1>欢迎访问我的页面</h1>
<p>这是我网页上的一段!</p>
<ul>
<li id="one">item1</li>
<li>item2</li>
<li>item3</li>
</ul>
<p>这是一个简单的段落。</p>
</div>
<script>
const ulElm = document.querySelector('ul')
const item1Elm = document.querySelector('#one')
// 1、删除当前节点
ulElm.remove()
// 2、删除指定子节点
ulElm.removeChild(item1Elm)
</script>
</body>
# 4、替换子节点
<body>
<div id="box">
<p>-----</p>
<div id="child">欢迎访问我的页面</div>
<p>-----</p>
</div>
<script>
const boxElm = document.querySelector('#box')
const childElm = document.querySelector('#child')
// 创建的新节点
var newElm = document.createElement('span')
newElm.className = 'newSpan'
newElm.style['background-color'] = 'green'
newElm.innerHTML = '替换操作测试的新增span节点'
// console.log(newElm) // span.newSpan
// 1、替换子节点
boxElm.replaceChild(newElm, childElm)
</script>
</body>
# 5、克隆节点
<body>
<div id="box">
<h1>欢迎访问我的页面</h1>
<p>这是我网页上的一段!</p>
<ul id="list">
<li id="one">item1</li>
<li>item2</li>
<li>item3</li>
</ul>
<p>这是一个简单的段落。</p>
</div>
<script>
const ulElm = document.querySelector('ul')
// 1、克隆当前节点
const cloneElm1 = ulElm.cloneNode()
console.log(cloneElm1) // <ul id="list"></ul>
// 2、克隆完整节点(含子节点)
const cloneElm2 = ulElm.cloneNode(true)
console.log(cloneElm2) // <ul id="list">...</ul>
</script>
</body>
应用:
// 1、待办事项
// 2、评论的动态删除
# 三、属性节点(2)
# 1、属性使用
- 常规使用
<input type="checkbox" checked id="rember" />
<img src="" alt="" id="photo" />
<script>
// 原生属性是可以直接在DOM对象上获取的
rember.checked = false
photo.src = 'https://www.gov.cn/govweb/xhtml/2016gov/images/public/logo.jpg'
</script>
<!--
应用:
// 1、密码的显示与隐藏
// 2、全选和全不选的切换
-->
- 自定义属性
h5 上明确规矩:自定义属性前添加
data-
来与其他属性进行区分
<div id="box1">采用js方法</div>
<div id="box2">采用H5挂载</div>
<script>
// 1、使用js方法
box1.setAttribute('effect', 'copy')
console.log(box1) // <div id="box1" effect="copy">采用js方法</div>
box1.getAttribute('effect')
box1.removeAttribute('effect')
// 2、使用H5🎈挂载
box2.dataset.effect = 'copy'
console.log(box2) // <div id="box2" data-effect="copy">采用H5挂载</div>
box2.dataset.effect
delete box.dataset.effect
// 其他
Element.hasAttribute('effect')
</script>
- 应用
// 批量操作 CSS 样式
box.setAttribute('style', 'width:100px;' + 'background-color:skyblue;')
# 2、元素样式 ✨
// 1、行内样式
// 获取
console.log(box.style['background-color'])
console.log(box.style.backgroundColor)
// 修改/添加
box.style.width = '60px'
// 2、css样式
var styleObj = window.getComputedStyle(box) // // CSSStyleDeclaration 实例
styleObj.backgroundColor
window.getComputedStyle() 方法
其中,Element.style
返回的只是行内样式,并不是该元素的全部样式。通过 css 文件设置的样式、父元素继承的样式,Element.style
是无法获得的,因为它操作的只是行内样式
但我们可以通过window.getComputedStyle()
来获取元素的全部样式了。(只读的)
# 3、class 类名 🎉
- Element.className
可以联想一下前面的
innerHTML
<div>
<span id="icon" class="iconfont icon-yijieshu"></span>
</div>
<script>
// 1、覆盖
icon.className = 'iconfont icon-yibaoming'
// console.log(icon.className)
// 2、追加
icon.className += ' personLook' // 注意加一个👀空格
</script>
- Element.classList
通过打印box.classList
,我们可以发现:获取的是一个DOMTokenList
集合(一个 class 类名集合对象)
DOMTokenList 集合
<div>
<div id="box" class="item item1 item2"></div>
</div>
<script>
console.log(box.classList)
// [object DOMTokenList]
// {
// "0":"item",
// "1":"item1",
// "2":"item2"
// }
</script>
提示
使用 classList 比上面的 className 操作 class 要 NB 的一点是已经存在于元素的 class 属性不会被重复添加,并且不存在覆盖问题。
// 1、常规
box.classList.add('item3')
box.classList.remove('item3')
// 2、切换
box.classList.toggle('itme3') // 将某个 class 移入或移出当前元素
// 3、其他
box.classList.contains() // 检查当前元素是否包含某个 class
- 应用
// 1、导航栏切换(action样式切换)
// 2、选项卡组件(样式、内容同时变化)😂
通过下面原生 js 的编写方式,我们就可以感受到 🤔 jQuery 的
链式编程
、隐式迭代
的强大。
答案
- 原生方式
let tabNodeList = document.querySelectorAll('.tabs > li')
let containNodeList = document.querySelectorAll('#content > li')
// 事件绑定(for一层)
for (let i = 0; i < tabNodeList.length; i++) {
tabNodeList[i].onclick = function () {
// 排它思想(for二层)
for (let j = 0; j < tabNodeList.length; j++) {
tabNodeList[j].classList.remove('active')
containNodeList[j].classList.remove('active')
}
tabNodeList[i].classList.add('active')
containNodeList[i].classList.add('active')
}
}
- jQuery 方式
// 事件绑定
$('.tabs').on('click', 'li', function () {
// 排它思想
$(this).addClass('active').siblings().removeClass('active')
const index = $(this).index()
$('#content > li').eq(index).addClass('active').siblings().removeClass('active')
})
# 四、文本节点(3)
# 1、文本内容
注意
不管是 innerText、还是 innerHTML,都其打印/传入的内容必须是字符串。
<body>
<div id="box">
hello world
<div>lencamo</div>
</div>
<script>
// 1、打印
console.log(box.innerHTML) // 获取所有
console.log(box.innerText) // 不解析 html
// 2、修改(全部覆盖)
box.innerHTML = '呵呵'
</script>
</body>
# 2、value 属性
<body>
<select name="" id="select">
<option value="1">one</option>
<option value="2" selected>two</option>
<option value="3">three</option>
</select>
<script>
// 更改默认值 🤔
// console.log(select.value)
select.value = '3' // 注意匹配字符串
</script>
</body>