DOM

以下内容总结自《js高级程序设计 第三版》

今天来说说JS中的DOM以及DOM操作。

DOM是什么,做了什么?
    DOM是针对HTML和XML文档的一个API,也叫文档对象模型。
    DOM描绘了一个层次化的节点树,允许开发人员添加,移除,修改页面的某一部分。

DOM并不难,但需要各位同学记忆和了解的部分居多,所以本章少说废话,API部分尽量简单明了,供各位同学参阅。

一、节点层次

刚才说过,DOM是一个节点树,既然是树,那肯定就会分层级。
以HTML为例,它的结构如下

<html>
  <head>
    <title>ATitle</title>
  </head>
  <body>
    <p>Hello World :)</p>
  </body>
</html>

是不是感觉根节点是最外层的<html>标签?诶,并不是
文档节点才是每个文档(这里指这个HTML页面)的根节点。而文档节点的子节点,才是<html>元素。
我们也叫<html>元素为文档元素,它是元素中的老大,所有的其他元素都包含在<html>中。
每一个文档只能有一个文档元素。在HTML中,始终就是<html>;而在XML中,据随便了,谁都可能成为文档元素。

了解了大概的层级,接下来就得看看每层都是什么类型的节点。毕竟,类型不同,干的活也不一样。

1 Node类型

一个文档中,总会有多种类型的节点配合使用,这样做才会分工明确,逻辑清晰。
在JS中,所有的节点类型都继承自Node类型,所以它们共享着一些相同的基本属性和方法。
我们先来说说都有哪些节点类型,再来讲讲共同属性,方法。

Node.ELEMENT_NODE(1)
Node.ATTRIBUTE_NODE(2)
Node.TEXT_NODE(3)
Node.CDATA_SECTION_NODE(4)
Node.ENTITY_REFERENCE_NODE(5)
Node.ENTITY_NODE(6)
Node.PROCESSING_INSTRUCTION_NODE(7)
Node.COMMENT_NODE(8)
Node.DOCUMENT_NODE(9)
Node.DOCUMENT_TYPE_NODE(10)
Node.DOCUMENT_FRAGMENT_NODE(11)
Node.NOTATION_NODE(12)

每个节点的类型必是其中之一,虽然这里12个类型看着密密麻麻的有点慌,但是不用怕,我们Web浏览器也不是全部都支持的,而且比较常用的就是元素文本节点

接下来就说一下节点的属性和方法。

共同属性:

nodeType、nodeName、nodeValue、
childNode、parentNode、
perviousSibling、nextSibling、
firstChild、lastChild、
ownerDocument


nodeType

nodeType 是用来查看节点类型的,我们可以这样做

if(nodeType === Node.ELEMENT_NODE){
      console.log('Node is a element')
}

但是由于IE没有公开Node类型的构造函数,所以我们想跨浏览器兼容的话,就得这样做

//  所有浏览器适用
if(nodeType === 1){  
      console.log('Node is a element')
}

虽然写法更简单了,但是IE坑还是非常多的,珍爱秀发,远离IE。


nodeName和nodeValue

这两个属性的值取决于及节点的类型。
常用的元素节点(ndeoType == 1),它的nodeName就是标签名,而nodeValue是null。
其他类型的话,见到再说吧。


childNodes

在这个属性存放着一个NodeList类对象数组
这里需要知道的两点:
1 NodeList是一个像数组却不是数组的对象,按顺序存放着一个节点的所有子元素;
2 NodeList是‘活’的,DOM结构变化它就跟着变。

我们可以像使用数组那样使用它

// 使用[]
const firstChild = some.childNodes[0]
// 使用item(n)
const secondChild = some.childNodes.item(1)
// 获取长度
const count = some.childNodes.length

parentNode

父节点,谁包着当前节点,parentNode就指向谁。


perviousSibling、nextSibling

perviousSibling:同级的前面一个节点
nextSibling: 同级的后面一个节点
当前节点是第一个或者最后一个时,perviousSibling或nextSibling为null


firstChild、lastChild

firstChild: 一个节点的第一个子节点
lastChild:一个节点的最后一个子节点
没有子节点firstChild和lastChild都为null


ownerDocument

该属性指向整个文档的文档节点。


共同方法:

适用于有子节点的节点:appendChild()、insertBefore()、replaceChild()、removeChild()
适用于所有节点:cloneChild()、normalize()


appendChild()

向父节点内最后的位置添加一个节点,返回值为插入后的该节点

const returnedNode = someNode.appendChild(someNode.firstChild)
alert(returnedNode === someNode.firstNode)          // false
alert(returnedNode === someNode.lastNode)          // true

insertBefore()

向父节点内的某个节点前插入一个节点。
第一个参数:要插入的节点,
第二个参数:被插入的节点(为null时,新节点插到最后)
返回值: 插入后的该节点

// 向第一个节点前插入一个节点
someNode.insertBefore(newNode,someNode.firstChild)

replaceChild()

替换一个节点
第一个参数:要插入的节点
第二个参数:被替换的节点
返回值: 替换后的该节点

// 替换第一个节点
someNode.replaceChild(newNode,someNode.firstChild)

removeChild()

移除一个节点
第一个参数:要移除的节点
第二个参数:被替换的节点
返回值: 移除后的该节点

// 移除第一个节点
someNode.removeChild(newNode,someNode.firstChild)

cloneChild()

复制一个节点
参数:true \ false
(true时是深复制,也就是包含该节点和内部的所有子节点;false时,只复制当前节点)
返回值: 节点列表


normalize()

操作文本节点
在某个节点调用该方法时,会去其后代节点中做两件事:
1 删除空文本节点
2 合并相邻文本节点

2、Document类型

前面说过,所有的节点类型都是是继承自Node类型,所以,Document类型节点以及本节后面所有的节点,都是儿子。
Document表示文档。在浏览器中,document对象是HTMLDocument(它又继承自Document类型,所以是孙子辈的)的一个实例,表示整个HTML页面。
而且,document对象又是window对象的一个属性,所以可以将其作为全局对象来访问。
特征:

       1  nodeType 值为9
       2  nodeName 值为 "#document"
       3  nodeVlaue 值为null
       4  parentNode 值为null
       5  ownerDocument 值为null
       6  其子节点可能是一个DocumentType(最多一个)、Element(最多一个)、ProcessingInstruction或Comment

Document类型的属性

documentElement、body、doctype、title、
URL、domain、referrer、
anchors、applets、forms、images、links、
implementation


>1、documentElement、body、doctype、title

      documentElement:  对<html>的引用
                 body:  对<body>的引用
              doctype: 对<!DOCTYPE>的引用、
                title: 对<title>的引用

>2、URL、domain、referrer

      URL: 当前页面的网址
      domain: 当前页面的域名
      referrer: 来源页面的URL(当前页面是在哪打开的)

其中domain可以设置,但乱改是不行的,只能是当前URL的子域名。


>3、anchors、applets、forms、images、links

      anchors:所有带name特性的<a>元素
      applets:所有<applets>元素(不推荐使用,了解即可)
        forms:所有<form>元素
       images:所有<img>元素
        links:所有带href特性的<a>元素

>4、implementation
这个属性是用来提供相关信息和功能的对象,来查看浏览器实现了对应的哪些功能。
它有一个特定的方法hasFeature(),接收两个参数
第一个参数:要检测的DOM功能的名称
第二个参数:要检测的DOM功能的版本号
使用方法如下

//  返回true或false
cosnt result = document.implementation.hasFeature('XML','1.0')

下面是可以检测的值,看看就好。


;).jpg

Document类型的方法

getElementById()、 getElementsByTagName()、getElementsByName()、
write()、writeln()、open()、close()


>1、getElementById()
根据节点id值获取节点,只能获取一个节点
这应该是大家最常用的方法了


>2、getElementsByTagName()
根据标签名获取节点列表(是一个HTMLCollection 类数组对象)

const images = document.getElementByTagName('img')
alert(images.length) 
alert(images[0].src) 
alert(images.item(0).src)
//  特有方法,根据name属性获取节点
images.namedItem('aName')
// 或者这样写
images['aName']
//-----------------------------------
// 获取全部
document.getElementByTagName('*')

我想聪明的你都能看懂。


>3、getElementsByName()
根据节点的name属性的值获取节点列表。(HTMLDocument类型特有方法)


>4、write()、writeln()、open()、close()

         write(): 写入,接受一个字符串参数
       writeln(): 在write的基础上在结尾加个 '\n'
          open(): 打开网页输入流  // 不怎么用
         close(): 关闭网页输入流  // 不怎么用

3、Element类型

又是一个常见的节点类型,Element类型常用于表示XML或HTML元素。
特征:

       1  nodeType 值为1
       2  nodeName 值为元素的标签名
       3  nodeVlaue 值为null
       4  parentNode 值可能为Document或Element
       5  ownerDocument 值为null
       6  其子节点可能是Element、Text、Comment、ProcessingInstruction、CDATASection或EntryReference
Element类型的属性

nodeName、tagName、
id、title、lang、dir、className、
attributes


>1、nodeName、tagName
两个属性等价,返回值为元素的标签名。


>2、id、title、lang、dir、className

             id:元素id属性
          title:元素title属性(鼠标放上去显示的就是title属性)
           lang:元素lang属性(语言代码,很少使用)
            dir:元素dir属性 (文字排列方向,值为 'ltr'(left-to-right) 或 'rtl'(right-to-left))
      className:元素class属性

举个例子

<div id="myDiv" class="bd" title="Body Text" lang="en" dir="ltr"></div>

当然除了获取,也可以给这几个属性直接赋值。


>3、attributes
这个属性厉害了,Element类型专属。
返回一个NameNodeMap类数组对象。其中包含着这个元素的所有属性和值的键值对。
同时,attributes还拥有下列的方法

    getNamedItem(name):  返回属性为name的键值对
 removeNamedItem(name):  移除属性为name的键值对
    setNamedItem(node):  添加键值对,以node.nodeName属性为索引
             item(pos):  返回位于数字pos位置处的节点

// 获取
const id = element.attributes.getNamedItem('id').nodeValue
// 或者这样访问
const id = element.attributes['id'].nodeValue

// 移除
element.attributes.removeNamedItem('id')

// 添加(不常用)
element.attributes.setNamedItem(newAttr)

attributes的作用
遍历元素的属性,当然不同浏览器遍历得到的属性顺序可能不同 (远离IE,尤其是低版本)


Element类型的方法

getAttributes()、setAttributes()、removeAttributes()


>1、getAttributes()
获取元素属性的方法,标签中属性名是啥,就传入对应的字符串当作参数。
不过有两个特殊的属性名:

      1  style: 通过该方法访问返回字符串;通过属性名直接返回一个对象。
      2  事件:像onclick这样的事件。通过方法访问得到代码字符串或null;通过属性名访问得到一个函数。

>2、setAttributes()
和getAttributes()对应,设置属性值
接收两个参数

      1  属性名
      2  属性值

div.setAttribute('id','someOtherId')

>3、removeAttributes()
移除属性,不常用
根据个人经验(菜鸡水平)来说,没用过。


一个来自document的方法

document.createment()
作用:创建一个新元素
接受参数:一个,为 标签名
使用:配合Element中给新元素添加属性,最后再用插入元素的方法(忘记的同学回到上面再看看)添加到想放的地方。

4、Text类型

就是文本节点
特征:

       1  nodeType 值为3
       2  nodeName 值为"#text"
       3  nodeVlaue 值为节点所包含的文字
       4  parentNode 值是一个Element
       5  ownerDocument 值为null
       6  没有子节点

Text类型的属性

data、length


>1、data
文本节点的内容,和nodeValue的值相同。


>2、length
字符的数目。


Text类型的方法

appendData()、deleteData()、insertData()、replaceData()、splitText()、subStringData()


>1、appendData(text)
将text添加到节点末尾


>2、deleteData(offset,count)
删除从offset位置开始的的count个字符。


>3、insertData(offset,text)
从offset位置开始插入text。


>4、replaceData(offset,count,text)
用text替换从从offset位置开始的的count个字符。


>5、splitText(offset)
从offset位置分割成两个文本节点


>6、subStringData(offset,count)
提取从offset开始的count个字符组成的文本


来自document的方法

document.createTextNode()

    作用:创建新的文本节点
    参数:传一个字符串
    使用:配合插入节点的方法使用。
规范化文本节点

前面提到过document.normalize()
它的两个作用再提一遍:

  1  删除空文本节点
  2  合并相邻文本节点

5、Comment类型

注释节点类型。
特征:

       1  nodeType 值为8
       2  nodeName 值为"#comment"
       3  nodeVlaue 值为注释的内容
       4  parentNode 值可能是Document或Element
       5  没有子节点

Commit类型和Text类型继承自相同的基类,它的方法除了splitText()之外和Text类型没有区别。所以参考上面即可。

6、CDATASection类型

针对XML的文档类型,表示CDATA区域,了解即可。
特征:

       1  nodeType 值为4
       2  nodeName 值为"#cdata-section"
       3  nodeVlaue 值为CDATA区域中的内容
       4  parentNode 值可能是Document或Element
       5  没有子节点

7、DocumentType类型

不常用,甚至只有部分浏览器支持,了解即可。
特征:

       1  nodeType 值为10
       2  nodeName 值为doctype的名称
       3  nodeVlaue 值为null
       4  parentNode 值是Document
       5  没有子节点

8、DocumentFragment类型

文档片段节点类型。
特征:

       1  nodeType 值为11
       2  nodeName 值为"#document-fragment"
       3  nodeVlaue 值为null
       4  parentNode 值是null
       5  子节点可以是Element、ProcessingInstruction、Comment、Text、CDATASection、EntryReference

文档片段节点比较特殊,它就像整个文档掰碎了的碎片,能向文档节点一样操作其内的节点。
可以使用插入节点的方法将文档片段插入到文档中。

应用:

  代替多次插入节点,先将所有的节点插入到文档片段中,再将文档片段插入到文档中。

9、Attr类型

元素特性类型。指HTML标签中属性。
特征:

       1  nodeType 值为2
       2  nodeName 值为特性的名称
       3  nodeVlaue 值为特性的值
       4  parentNode 值是null
       5  子节点:在HTML中没有;在XML中可以是Text或在EntryReference

虽然被定义成节点,但Attr类型被嫌弃了,它不被认可是DOM文档树的一部分。


Attr类型的属性

name、value、specified


>1、name、value、specified

  name: 属性名
  value: 属性值
  specified:布尔值,用来区分是特性是在代码中指定的还是默认的
来自document的方法

document.createAttribute()

  作用:创建一个Attr类型节点
  参数:属性名(字符串)
  使用:使用.value方式赋值,配合setAttributeNode将Attr类型节点放到元素中。
建议

使用Element节点的getAttribute()、setAttribute()、removeAttribute()代替使用Attr节点的属性。
因为更方便,不用再创建Attr类型了。

二、DOM操作技术

"DOM操作是简单的,但IE是坑的。"
所以这里我们抛开IE,拥抱主流。

1、动态脚本

有多种方法能够向页面插入js代码

  1  <script></script>内部包含js代码
  2  <script> 的src属性引入外部js
  3  使用DOM操作生成<script>标签和js代码

动态脚本讲的就是第三种方法。
我们可以这样操作:

var script = document.createElement('script')
script.type = 'text/javascript'
// client.js是一个外部js文件,这里假装有一个
script.src= 'client.js'
document.body.appendChild(script)

这就动态添加了一个脚本。
甚至可以将以上代码封装到一个函数中,在合适的时机进行调用。

2、动态样式

将css包含到页面的方法也有多种

  1  使用<link>标签引入外部css文件
  2  使用<style>包含css代码
  3  使用DOM操作动态生成css代码

举例:

var link = document.createElement('link')
link.rel = 'stylesheet'
link.type = 'text/css'
link.href = 'styles.css'
var head = document.getElementsByTagName('head')[0]
head.appendChild(link)

// 或者
var style= document.createElement('style')
style.type = 'text/css'
style.appendChild(document.createElement('body{background-color:red}'))
var head = document.getElementsByTagName('head')[0]
head.appendChild(style)

三、操作表格

首先我要赞美一下各大UI框架,让我们摆脱了又臭又长的表格操作。

首先我们来看看一个基本的表格是什么样的

<table>
  <tbody>
    <tr>
      <td>Cell 1,1</td>
      <td>Cell 2,1</td>
    </tr>
    <tr>
      <td>Cell 1,2</td>
      <td>Cell 2,2</td>
    </tr>
  </tbody>
</table>

想一想,每个元素都要使用DOM.createment()方法生成,然后再添加到其父元素中,是不是要疯了!
官方也这么觉得,所以专门为<table>、<tbody>、<tr>元素添加了一些属性和方法:

 <table>元素的属性和方法:
  1.  caption:返回表格的caption元素节点,没有则返回null;
  2.  tHead, tBodies, tFoot: 返回表格<thead>, <tbody>, <tfoot>元素;
  3.  rows: 返回元素所有行<tr>元素的HTMLCollection;
  4.  createTHead(), createTFoot(), createCaption(): 创建<thead>, <tfoot>, <caption>空元素,
      将其放到表格中,返回创建的<thead>, <tfoot>, <caption>元素节点;
  5.  deleteTHead(), deleteTFoot(), deleteCaption(): 删除<thead>, <tfoot>, <caption>空元素,
      无返回值(或返回值为undefined)
  6.  deleteRow(pos): 删除指定位置(注意参数不是索引,而是从0开始的位置)的行,返回undefined;
  7.  insertRow(pos): 向rows集合中的指定位置(不是索引)插入一行;

  <tbody>元素的属性和方法:
  1.  rows: 返回<tbody>元素下所有行<tr>元素的HTMLCollection;
  2.  deleteRow(pos): 删除指定位置(注意参数不是索引,而是从0开始的位置)的行,返回undefined;
  3.  insertRow(pos):向rows集合中的指定位置(不是索引)插入一行;

  <tr>元素的属性和方法:
  1.  cells: 返回<tr>元素中单元格的HTMLCollection;
  2.  deleteCell(pos): 删除指定位置(不是索引)的单元格;
  3.  insertCell(pos): 向cells集合中的指定位置(不是索引)插入一个单元格,返回对新插入单元格的引用;

虽然简化了表格操作,但还是很麻烦,大伙留个印象即可,用到的时候再来看看。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容