js红宝石 第十四章-DOM

文档对象模型DOM是html和xml文档的编程接口

DOM表示由多层节点构成的文档,开发者可以自行添加,删除和修改

14.1 节点层级

html可以表示为层级结构

document是每个文档的根节点

14.1.1 Node类型

Node接口是所有DOM节点类型都必须实现的

这个接口在JS中被实现为Node类型,所有节点都继承自Node类型

nodeName和nodeValue取决于节点类型

每个节点都有一个childNodes属性,返回NodeList,可以使用中括号或item()访问里面的属性

使用Array.prototype.slice()/Array.from()可以将NodeList转化为数组

ownerDocument属性是一个指向代表整个文档的文档节点指针

操作节点

appendChild() 在childNodes末尾增加子节点

如果增加的节点是原来就有的,那么会把它转移到末尾

insertBefore() 第二个参数跟参照节点,为null则和appendChild()相同

replaceChild()

removeChild()

14.1.2 Document类型

文档对象document是HTMLDocument的实例,表示整个document页面

Document类型节点的特征:

nodeType = 9

nodeName = "#document"

nodeValue parentNode ownerDocument = null

1.文档子节点

document.documentElement 始终指向<html>
document.body 指向<body>

出现在<html>元素外边的注释也是文档的子节点,类型为Comment;但是根据浏览器,这些注释不一定能被识别

2.文档信息

document.title 是个只读属性 表示<title>元素中的文本

URL获取URL

domain获取域名

referrer//取得来源

3.定位元素

getElementById() 获取首个id相同的元素,可能不区分大小写

getElementByTagName() 取得所有相同的标签名的元素,返回NodeList(HTMLCollection)对象

getElementByName() 取得所有相同的name的元素

4.特殊集合

document.anchors 返回包含所有带name属性的<a>

document.forms 返回包含所有<form>

document.images 返回包含所有<img>

document.links 返回包含所有带href属性的<a>

5.DOM对象兼容性检测

hasFeature()

6.文档写入

write() 写入一个字符串

writeln() 写入一个字符串并添加'\n'

open() 打开网页输出流

close() 关闭网页输出流

14.1.3 Element类型

Element表示XML或HTML元素,对外暴露出访问元素标签名,子节点和属性的能力

Element类型节点特征:

nodeType = 1

nodeName 为元素标签名 也可以用tagName

nodeValue = null

parentNode 为 Document或Element对象

1.HTML元素

所有HTML元素都通过HTMLElement类型表示

HTMLElement新增了一些属性:

id 元素在文档内的唯一标识符

title 元素额外信息

lang 元素的语言

dir 元素的书写方案

className 相当于class,用于指定CSS类

2.取得元素

getAttribute()

参数:

"id"  "class"  "title"  "lang"  "dir"

3.设置元素

setAttribute() 

第一个参数同上,第二个参数是新的元素值

设置的元素名会规范为小写

removeAttribute() 移除元素

4.attributes属性

Element类型是唯一一个使用attributes属性的DOM类型

attributes包含一个NamedNodeMap实例,元素的每个属性表示为Attr节点,并保存在这个对象中

NamedNodeMap包含下列方法

getNameItem(name) 返回nodeName属性等于name的节点

removeNameItem(name) 删除nodeName属性等于name的节点

setNameItem(node) 向列表中添加node节点,以其nodeName为索引

item(pos) 返回索引位置pos处的节点

5.创建元素

createElement()

参数为要设置成为的标签名

创建完后可以为其添加属性

最后在一插入节点的方法把元素添加到文档中

6.元素后代

childrenNodes属性包含元素所有的子节点

可以使用getElementByTagName()把只返回当前节点的后代

      let ul = document.getElementById("myList")
      let items = ul.getElementsByTagNameNS("li")

如果节点包含多层级,那么getElementByTagName()会把所有层级的节点都返回

14.1.4 Text类型

Text包含纯文本或者转义后的HTML字符

Text类型节点特征:

nodeType = 3

nodeName = "#text"

nodeValue 为包含的文本

parentNode 为Element对象

无子节点

包含的文本可以通过nodeValue访问,也可以通过data访问,可读写

1.创建文本节点

createTextNode()

      let element = document.createElement("div")
      element.id = 'div'

      let text = document.createTextNode("Hello World!!!")
      element.appendChild(text)

      document.body.appendChild(element)

2.规范化文本节点

normalize()

把所有同胞节点合并成为一个文本节点

3.拆分文档节点

splitText()

从指定位置拆分nodeValue,把一个文本节点拆成两个

常用于从文本节点中提取数据的DOM解析技术

14.1.5 Comment类型

Comment就是DOM中的注释

Comment类型节点特征:

nodeType = 8

nodeName = "#comment"

nodeValue 为注释的内容

parentNode 为Element或Document对象

无子节点

Comment类型和Text类型都继承自同一个基类(characterDate)

拥有除了splitText()之外的Text节点的所有方法

浏览器不承认</html>之后的注释节点,除非明确表示它们是<html>的后代

14.1.6 CDATASection类型

CDATASection表示在XML中特有的CDATA区块,继承自Text类型,基本和Text类型相同

拥有Text类型的所有方法

但是CDATA区块只在XML文档中有效

14.1.7 DocumentType类型

DocumentType包含文档的文档类型(doctype)信息

DocumentType类型节点特征:

nodeType = 10

nodeName = 文档类型名称

nodeValue 为 null

parentNode 为Document'对象

无子节点

无论如何,只有name属性是有效的,包含文档类型的名称,即紧跟在<!DOCTYPE 后面的文本

14.1.8 DocumentFragment类型

DocumentFragment类型是文档片段,被DOM定义为"轻量级文档",能够包含和操作节点,但是却没有完整文档那样的消耗

不能直接把文档片段添加到文档,文档片段的作用是充当其他要被添加到文档的节点的仓库

      let fragment = document.createDocumentFragment()
      let ul = document.getElementById("MyList")

      for(let i=0;i<3;i++){
        let li = document.createElement("li")
        li.appendChild(document.createTextNode(`Item ${i}`))
        fragment.appendChild(li)
      }

      ul.appendChild(fragment)

14.1.9 Attr类型

元素数据在DOM中通过Attr表示

Attr类型节点特征:

nodeType = 2

nodeName 为 属性名

nodeValue 为 属性值

parentNode 为 null

无子节点

属性节点虽然是节点,但是很少被引用,开发者更喜欢用getAttribute() setAttribute()等方法操作属性

14.2 DOM编程

14.2.1 动态脚本

<script>元素可以向网页中插入JS代码

动态脚本就是页面初始加载的时候不存在,之后又通过DOM包含的脚本

整个过程可以抽象为一个函数

      function loadScript(url){
        let script = document.createElement("script")
        script.src = url
        document.body.appendChild(script)
      }

      loadScript("client.js")

14.2.2 动态样式

CSS样式在HTML中可以通过两个元素加载

<link>包含外部CSS文件

<style>用于添加嵌入样式

抽象成通用函数

      function loadStyle(url){
        let link = document.createElement("link")
        link.rel = "stylesheet"
        link.type = "text/css"
        link.href = url
        let head = document.getElementsByTagName("head")[0]
        head.appendChild(link)
      }

      loadStyle(url)
      function loadStyleString(css){
        let style = document.createElement("style")
        style.type = "text/css"
        try {
          style.appendChild(document.createTextNode(css))
        } catch(ex){
          style.styleSheet.cssText = css
        }
        let head = document.getElementsByTagName("head")[0]
        head.appendChild(style)
      }

      loadStyle(css)

14.2.3 操作表格

<table>

<tbody>

<tr>

p431

14.2.4 使用NodeList

NodeList,NamedNodeMap,HTMLCollection三个集合类型都是实时的(类似v-bind直接绑定)

NodeList就是对DOM的实时查询

14.3 MutationObserver接口

MutationObserver接口可以在DOM被修改时异步执行回调

使用MutationObserver可以观察整个文档/DOM树的一部分/某个元素

或者元素属性/子节点/文本的变化

创建实例

      let observer = new MutationObserver(()=>console.log("DOM was nutated!"))

1.observe()方法

新创建的 MutationObserver对象需要通过observe()方法和DOM对象关联

接受两个参数:观察的DOM对象  MutationObserverInit对象

      let observer = new MutationObserver(() => console.log('DOM was mutated!'))
      observer.observe(document.body, { attributes: true })

      document.body.className = 'foo'
      console.log('changed body class');

回调的函数在异步队列,比同步的慢

2.回调与MutationRecord实例

每一个回调都会收到MutationRecord实例的数组

包含发生的变化以及受影响的DOM

      let observer = new MutationObserver((mutationRecords) => console.log(mutationRecords))
      observer.observe(document.body, { attributes: true })

      document.body.className = 'foo'
      console.log('changed body class');

 

连续修改会生成多个MutationRecord实例,下次执行回调就会收到半酣所有这些实例的数组

MutationRecord实例的属性:P434

3.disconnect()方法

一般来说,只要被观察的元素不被回收,MutationObserver的回调就会一直执行

要提前终止,可以调用disconnect()方法

4.复用MutationObserver

MutationObserver可以复用,以观察多个不同的目标节点

MutationRecord的target属性可以表示发生变化的目标节点

      let observer = new MutationObserver((mutationRecords) => console.log(mutationRecords.map(x=>x.target)))

      let div = document.createElement('div')
      let span = document.createElement('span')
      document.body.appendChild(div)
      document.body.appendChild(span)
      

      observer.observe(div, { attributes: true })
      observer.observe(span, { attributes: true })

      div.setAttribute('foo','bar')
      span.setAttribute('foo','bar')

4.重用MutationObserver

调用disconnect()方法不会结束MutationObserver的生命,还可以重新使用这个观察者

14.3.2 MutationObserverInit与观察范围

MutationObserverInit的属性:P437

1.观察属性

MutationObserver可以观察属性的添加,移除和修改

但是要把attributes设置为true

要观察某个或某几个属性,需要用attributeFilter设置白名单

2.观察字符数据

观察文本节点的变化

需要把characterdata设置为true

3.其他变化

chlidList  观察子节点

对子节点重新排序会调用两次回调*设计删除和添加)

subtree 观察子树

14.3.3 异步回调与记录队列

1.记录队列

每次变化的信息都会记录在MutationRecord实例中,然后又添加到记录队列中

只有当MutationRecord被添加到MutationObserver的记录队列时没有已经排期的委任为回调,才会把注册的回调放到任务队列上,避免记录队列的内容被回调处理两次

2.takeRecords()方法

takeRecords()方法可以取出记录队列中的所有MutationRecord实例并清空队列

      let observer = new MutationObserver((mutationRecords) => console.log(mutationRecords))
      

      observer.observe(document.body, { attributes: true })

      document.body.className = 'foo'
      document.body.className = 'bar'
      document.body.className = 'baz'

      console.log(observer.takeRecords());
      console.log(observer.takeRecords());

14.3.4 性能,内存与垃圾回收

MutationObserver与被观察节点的引用非对称

MutationObserver不会阻碍被观察节点的回收

被观察节点的回收却会导致MutationObserver的回收

每个MutationRecord至少包含一个对已有DOM节点的一个引用

建议提取最有用的信息后转存,然后就可以尽快释放内存

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值