前言
我们平时用的react,vue,jquery,lodash等等js相关的框架和库。究其原理,都是操作DOM接口。如何通过封装这些的DOM接口操作,逐渐完善成一个框架? 这时候就是体现 数据结构和算法的 的“功力”了。大部分的“程序员”都停留在使用api的层面上。如果想要做一个优秀点的程序员,是需要懂些“数据结构和算法”的。
DOM就是W3规定的一种接口(相当于我们的电脑的USB接口)。JS可以通过这个接口接上去,去操作各种DOM(相当于我们使用键盘等操作电脑)。当然DOM只是一种接口,使用其它编程语言也能操作DOM(相当于鼠标也可以连接USB操作电脑)。只不过现在基本上大家都用JS来操作,所以有人会认为DOM节点是JS体系里面的,这种观念是错误的。(相当于有些牛逼的程序员会认为键盘是操作电脑的唯一方式——全键盘操作。这种观念也是错误的)
在JS中,内置了一种Node的类型,(和Object、Array、Function类型属于同一级别的关系)。来处理DOM接口。
为了方便处理html和css,根据html文档,js基于Node类型将文档中的某些相似的类型分为了12种。称为12种节点类型。这12中新的类型,相当于是Node类型的孩子,所以他们“继承”了Node的“能力和关系”,他们共享着某些相同的方法和属性。(相当于你的大儿子和你的二儿子在大学开车撞了人之后,都可以去找你的好兄弟——李刚)
DOM节点共有的属性和方法
nodeType属性
在12中DOM节点中,有一个最牛逼的属性叫做 nodeType。和表面上翻译过来的一样,用来判断node类型。(Node共有12个儿子,通过nodeType来判断哪个是他的大儿子,哪个是二儿子)。所以nodeType是一种共有12种可能的枚举类型,分别为:
1 | Node.ELEMENT_NODE(1); |
if (someNode.nodeType == Node.ELEMENT_NODE){ //在 IE 中无效
alert(“这个节点是我的儿子 element”);
}
1 | ~~~ |
一般用第二种来判断,为啥?因为兼容性好呀!!! 看到这里,一般都会迷迷糊糊的。someNode是什么呀?其实就是节点,说直白点,就是我们经常用到的document.getElementById(“box”),这个js语句获取的就是someNode。
1.nodeName 和 nodeValue 属性
这俩个属性没啥好说的,不同类型的DOM节点,返回的值不一样。我只测试了三个。
1 | let span = document.createElement("span") |
2.关系节点的属性和方法
一图胜千言:
childNodes属性返回一种叫做 NodeList的类型,它是一种类数组对象,用于保存一组有序的节点,可以通过位置来访问这些节点。
hasChildNodes() //节点包含一或多个子节点的情况下返回 true
ownerDocument //该属性指向表示整个文档的文档节点。这种关系表示的是任何节点都属于它所在的文档,任何节点都不能同时存在于两个或更多个文档中。通过这个属性,我们可以不必在节点层次中通过层层回溯到达顶端,而是可以直接访问文档节点
3.操作节点的属性和方法
这节写的属性一般就是增删改查DOM节点。面试中可能会遇到这类问题。
appendChild() : 用于向 childNodes 列表的末尾添加一个节点
1
2
3var returnedNode = someNode.appendChild(newNode);
alert(returnedNode == newNode); //true
alert(someNode.lastChild == newNode); //trueinsertBefore()方法。这个方法接受两个参数:要插入的节点和作为参照的节点。
1
2
3
4
5
6
7
8
9
10//插入后成为最后一个子节点
returnedNode = someNode.insertBefore(newNode, null);
alert(newNode == someNode.lastChild); //true
//插入后成为第一个子节点
var returnedNode = someNode.insertBefore(newNode, someNode.firstChild);
alert(returnedNode == newNode); //true
alert(newNode == someNode.firstChild); //true
//插入到最后一个子节点前面
returnedNode = someNode.insertBefore(newNode, someNode.lastChild);
alert(newNode == someNode.childNodes[someNode.childNodes.length-2]); //truereplaceChild()方法接受的两个参数是:要插入的节点和要替换的节点。
1
2
3
4//替换第一个子节点
var returnedNode = someNode.replaceChild(newNode, someNode.firstChild);
//替换最后一个子节点
returnedNode = someNode.replaceChild(newNode, someNode.lastChild);removeChild()方法。这个方法接受一个参数,即要移除
的节点。1
2
3
4//移除第一个子节点
var formerFirstChild = someNode.removeChild(someNode.firstChild);
//移除最后一个子节点
var formerLastChild = someNode.removeChild(someNode.lastChild);4.其他方法
cloneNode()方法接受一个布尔值参数,表示是否执行深复制。
一般这个方法浅复制会脱离文档树。normalize(),这个方法唯一的作用就是处理文档树中的文本节点。合并多个连续的文本节点。
DOM各个类型详解(各个儿子的特点
Document类型
这种类型的实例就是我们常用的document对象。比如常用的方法有:
getElementById()和 getElementsByTagName()。剩下的都在《高三》里面的258页。比如document.write()会写入文档。Element类型
元素就是我们常说的“标签”,比如 body、html、div、span等等。这种类型标签有很多属性。比如id,className,title等等。对于属性的操作,元素DOM 方法主要有三个,分别是 getAttribute()、setAttribute()和 removeAttribute()。这方法的名字已经如此明了了,傻子都能看懂吧。。。
Element 类型是使用 attributes 属性的唯一一个 DOM 节点类型。attributes 属性中包含一个NamedNodeMap,与 NodeList 类似,也是一个“动态”的集合。元素的每一个特性都由一个 Attr 节点表示,每个节点都保存在 NamedNodeMap 对象中
使用 document.createElement()方法可以创建新元素。这个方法只接受一个参数,即要创建元素的标签名。
Text类型
这种类型就是我们在html中用的 文本 比如
1
<p>Some other message</p>
其中 “Some other message” 就是Text类型的DOM节点。
很明显这种节点没有子节点。
它的一些方法
1 | appendData(text):将 text 添加到节点的末尾。 |
- 可以使用 document.createTextNode()创建新文本节点,这个方法接受一个参数——要插入节点中的文本。
Attr类型
这种节点还是注意依赖于Element类型的DOM节点。1
2
3
4Attr 对象有 3 个属性:name、value 和 specified。其中,name 是特性名称(与 nodeName 的
值相同),value 是特性的值(与 nodeValue 的值相同),而 specified 是一个布尔值,用以区别特
性是在代码中指定的,还是默认的。
使用 document.createAttribute()并传入特性的名称可以创建新的特性节点。其他类型
其他类型,暂时很少用到。知道有这个东西就OK。
DOM节点增删改查的总结
添加元素
- Document.createElement() // 创建元素
语法:let element = document.createElement(tagName[, options]) - Document.createTextNode() // 创建一个新的文本节点
语法:var text = document.createTextNode() - Document.createAttribute() // 创建并返回一个新的属性节点
语法:attribute = document.createAttribute(name) - Document.createComment() // 创建并返回一个注释节点
语法:
var commentNode = document.createComment(data) - Document.createDocumentFragment() // 创建一个新的空的文档片段
语法:
var docFragment = document.createDocumentFragment() - Node.appendChild() // 将一个节点添加到指定父节点的子节点列表末尾
语法:
var child = node.appendChild(child) - Element.classList.add() // 添加指定的类值class
语法:
elementNodeReference.classList.add( String [, String] ) - Document.write()
- Document.writeIn() // 将文本字符串写入打开的文档流
语法: - document.write(markup)
- document.writeIn(line)
- document.write()和writeIn()的区别是前者没有换行,而后者有换行。
删除元素
- Element.removeAttribute() // 从元素中删除指定的属性
语法:
element.removeAttribute(attrName) - Element.removeChild()// 删除子元素
语法:
var oldChild = node.removeChild(child);
OR
element.removeChild(child); - ChildNode.remove() // 删除元素
语法:
elementNodeReference.remove() - Child.parentNode.removeChild(child) // 不确定父元素时可这样删除子元素
示例:1
2
3
4if (node.parentNode) {
//判断node是否在DOM树中
node.parentNode.removeChild(node);
} - Element.classList.remove() // 移除元素中一个或多个类名
语法:
elementNodeReference.classList.remove( String [,String] )修改元素
- Node.innerText // 修改元素文本内容
语法:
Node.innerHTML =new content - Element.innerHTML // 设置或获取描述元素后代的HTML语句
语法:
const content = element.innerHTML;
// 返回时,内容包含描述所有元素后代的序列化HTML代码 。
element.innerHTML = markup;
// 除所有元素的子节点,解析内容字符串,并将生成的节点分配给元素的子元素。 - node.cloneNode() // 拷贝元素(包括所有属性和值)
语法及示例:
var dupNode = node.cloneNode(true) - Element.setAttribute() // 设置或者改变指定属性并指定值
语法:
element.setAttribute(name, value) - style.property = new style // 修改元素CSS属性值
示例:
var title= document.querySelector(“h1”)
title.style.backgroundColor = red // h1背景色改为红色 - Node.replaceChild() // 替换子节点
语法及示例:
replacedNode = parentNode.replaceChild(newChild, oldChild)查找元素
- attribute.getAttribute() // 返回元素的指定属性值
语法:
let attribute = element.getAttribute(attributeName) - Document.getElementsByClassName() // 返回一个节点列表(数组),包含了所有拥有指定 class 的子元素
语法:
var elements = document.getElementsByClassName(names);
// or:
var elements = rootElement.getElementsByClassName(names); - Document.getElementsByName() // 返回带有指定名称的对象集合
语法:
elements = document.getElementsByName(name) - Document.getElementsByTagName() // 返回带有指定标签名的对象集合
语法:
var elements = document.getElementsByTagName(name) - Document.getElementById() // 返回对拥有指定 id 的第一个对象的引用
语法:
element = document.getElementById(id) - Document.querySelector() // 返回文档中匹配指定的CSS选择器的第一元素
语法及示例:
element = document.querySelector(selectors) // 语法
//example - document.querySelector(“#demo”) // 获取文档中ID为demo的元素
- document.querySelectorAll() // 返回与指定的选择器组匹配的文档中的元素列表
语法:
elementList = document.querySelectorAll(selectors)其它说明
其实,如果熟练操作这些API,的确不需要用jquery了。但是jquery更简单一些,同时优化了兼容性问题。至于后期要读react,vue,源码的时候,了解这些原生DOM接口是必不可少的。
DOM2中和DOM3还不是很了解,假装不知道。参考
Javascript DOM 元素的增删改查
《高三第六版》