DOM 编程

108 阅读4分钟

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

image.png

图片来源:饥人谷

节点的增删改查

创建一个标签节点

  • 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- 作为前缀

image.png

图片来源:饥人谷

Property v.s. Attribute

property 属性

  • JS 线程中 div1 的所有属性,叫做 div1 的 property

attribute 也是属性

  • 渲染引擎中 div1 对应标签的属性,叫做 attribute

区别

  • 大部分时候,同名的 property 和 attribute 值相等
  • 但如果不是标准属性,那么它俩只会在一开始时相等
  • 但注意 attribute 只支持字符串
  • 而 property 支持字符串、布尔等类型