JS 用 document 操作网页
这就是 Document Object Model 文档对象模型
获取元素,也叫标签
有很多 API
- window.idxxx 或者直接 idxxx
- document.getElementById('idxxx')
- document.getElementsByTagName('idxxx')[0]
- document.getElementsByClassName('idxxx')[0]
- document.querySelector('#idxxx')
- document.querySelectorAll('.red')[0]
用哪一个
- 工作中用 querySelector 和 querySelectorAll
- 做 demo 直接用 idxxx
- 兼容IE用getElement(s)ByXXX
获取特定元素
获取 html 元素
- document.documentElement
获取 head 元素
- document.head
获取 body 元素
- document.body
获取窗口(窗口不是元素)
- window
获取所有元素
- document.all
- 这个 document.all 是第六个 falsy 值
节点、元素
节点 Node 包括以下几种
- x.nodeType 得到一个数字
- 1 表示元素 Element,也叫做标签 Tag
- 3 表示文本 Text
- 8 表示注释 Comment
- 9 表示文档 Document
- 11 表示文档片段 DocumentFragment
- 1 和 2 比较常用
console.dir(div1)看原型链
- 自身属性:className、id、style 等等
- 第一层原型:HTMLDivElement.prototype
- 这里面是所有 div 共有的属性
- 第二层原型:HTMLElement.prototype
- 这里面是所以 HTML 标签共有的属性
- 第三层原型:Element.prototype
- 这里面是所有 XML、HTML 标签的共有属性
- 第四层原型:Node.prototype
- 这里面是所有节点共有的属性,节点包括 XML 标签文本注释、HTML 标签文本注释等等
- 第五层原型:EventTarget.prototype
- 里面最重要的函数属性是 addEventListener
- 最后一层原型就是 Object.prototype
图片来源:饥人谷
节点的增删改查
增
创建一个标签节点
- let div1 = document.createElement('div')
- document.createElement('style')
- document.createElement('script')
- document.createElement('li')
创建一个文本节点
- text1 = document.createTextNode('你好')
标签里面插入文本
- div1.appendChild(text1)
- div1.innerText = '你好' 或者 div1.textContent = '你好'
- 但是不能用 div1.appendChild('你好')
插入页面中
- 创建的标签默认在 JS 线程中,不在页面里
- 必须把它插入 head 或者 body 里面,它才会生效
- document.body.appendChild(div)
- 或者 已在页面中的元素.appendChild(div)
- 一个元素不能同时出现在两个地方,除非复制一份(let div2 = div1.cloneNode(true)) // true 为深拷贝,不填为浅拷贝
删
两种方法
- 旧方法:parentNode.removeChild(childNode)
- 新方法:childNode.remove()
- 从内存里删除:childNode = null
改
写标准属性
- 改 class:div.className = 'red blue' (全覆盖)
- 改 class:div.classList.add('red')
- 改 style:div.style = 'width: 100px; color: blue;'
- 改 style的一部分:div.style.width = '200px'
- 大小写:div.style.backgroundColor = 'white'
- 改 data-* 属性:div.dataset.x = 'frank'
读标准属性
- div.classList / a.href
- div.getAttribute('class') / a.getAttribute('href')
- 两种方法都可以,但值可能稍微有些不同
改事件处理函数
div.onclick 默认为 null
- 默认点击 div 不会有任何事情发生
- 但是如果你把 div.onclick 改为一个函数 fn
- 那么点击 div 的时候,浏览器就会调用这个函数
- 并且是这样调用的 fn.call(div,event)
- div 会被当做 this
- event 则包含了点击事件的左右信息,如坐标
div.addEventListener
- 是 div.onclick 的升级版,之后的课程单独讲
改内容
改文本内容
- div.innerText = 'xxx'
- div.textContent = 'xxx'
- 两者几乎没有区别
改 HTML 内容
- div.innerHTML = '<strong>重要内容</strong>'
改标签
- div.innerHTML = '' // 先清空
- div.appendChild(div2) // 再加内容
改一个新的父节点
- newParent.appendChild(div)
查
查爸爸
- node.parentNode 或者 node.parentElement
查爷爷
- node.parentNode.parentNode
查子代
- node.childNodes 或者 node.children (优先使用)
查兄弟姐妹
- node.parentNode.childNodes
- node.parentNode.children
查看老大
- node.firstChild
查看老幺
- node.lastChild
查看上一个
- node.previousSibling
查看下一个
- node.nextSibling
遍历一个 div 里所有元素
travel = (node, fn) => {
fn(node)
if(node.children){
for(let i=0; i<node.children.length; i++){
travel(node.children[i], fn)
}
}
}
travel(div1, (node) => console.log(node))
跨线程操作
各线程各司其职
- JS 引擎不能操作页面,只能操作 JS
- 渲染引擎不能操作 JS,只能操作页面
- document.body.appendChild(div1)
跨线程通信
- 当浏览器发现 JS 在 body 里面加了一个 div1 对象
- 浏览器就会通知渲染引擎在页面里也新增一个 div 元素
- 新增的 div 元素所有属性都照抄 div1 对象
插入新标签的完整过程
在 div1 放入页面之前
- 你对 div1 所有的操作都属于 JS 线程内的操作
把 div1 放入页面之时
- 浏览器会发现 JS 的意图
- 就会通知渲染线程在页面中渲染 div1 对应的元素
把 div1 放入页面之后
- 你对 div1 的操作都有可能会触发重新渲染
- div1.id = 'newId' 可能会重新渲染,也可能不会
- div1.title = 'new' 可能会重新渲染,也可能不会
- 如果你连续对 div1 多次操作,浏览器可能会合并成一次操作,也可能不会
属性同步
标准属性
- 对 div1 的标准属性的修改,会被浏览器同步到页面中
- 比如 id、className、title 等
data-* 属性
- 同上
非标准属性
- 对非标准属性的修改,则只会停留在 JS 线程中
- 不会同步到页面里
- 如果你有自定义属性,又想被同步到页面中,请使用 data- 作为前缀
图片来源:饥人谷
Property v.s. Attribute
property 属性
- JS 线程中 div1 的所有属性,叫做 div1 的 property
attribute 也是属性
- 渲染引擎中 div1 对应标签的属性,叫做 attribute
区别
- 大部分时候,同名的 property 和 attribute 值相等
- 但如果不是标准属性,那么它俩只会在一开始时相等
- 但注意 attribute 只支持字符串
- 而 property 支持字符串、布尔等类型