js-DOM相关知识点

前言

我们平时用的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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 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)
~~~

这个属性基本上可利用的方式就下面俩种:

if (someNode.nodeType == Node.ELEMENT_NODE){ //在 IE 中无效
alert(“这个节点是我的儿子 element”);
}

1
2
3
4
~~~
if (someNode.nodeType == 1){ //适用于所有浏览器
alert("这个节点也是我的儿子 element");
}

一般用第二种来判断,为啥?因为兼容性好呀!!! 看到这里,一般都会迷迷糊糊的。someNode是什么呀?其实就是节点,说直白点,就是我们经常用到的document.getElementById(“box”),这个js语句获取的就是someNode。

1.nodeName 和 nodeValue 属性

这俩个属性没啥好说的,不同类型的DOM节点,返回的值不一样。我只测试了三个。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let span = document.createElement("span")
span.id = "lineSpan";
span.className = "box1";
console.log(span.nodeName);//SPAN
console.log(span.nodeValue);//null
let text = document.createTextNode("Hello zhangsan");
console.log(text.nodeName);//#text
console.log(text.nodeValue);//Hello zhangsan
span.appendChild(text);
document.body.appendChild(span);
let attr = document.createAttribute("align");
console.log(attr.nodeName);//align
console.log(attr.nodeValue);//center
attr.value = "center";
span.setAttributeNode(attr);

2.关系节点的属性和方法

一图胜千言:

  • childNodes属性返回一种叫做 NodeList的类型,它是一种类数组对象,用于保存一组有序的节点,可以通过位置来访问这些节点。

  • hasChildNodes() //节点包含一或多个子节点的情况下返回 true

  • ownerDocument //该属性指向表示整个文档的文档节点。这种关系表示的是任何节点都属于它所在的文档,任何节点都不能同时存在于两个或更多个文档中。通过这个属性,我们可以不必在节点层次中通过层层回溯到达顶端,而是可以直接访问文档节点

    3.操作节点的属性和方法

  • 这节写的属性一般就是增删改查DOM节点。面试中可能会遇到这类问题。

  • appendChild() : 用于向 childNodes 列表的末尾添加一个节点

    1
    2
    3
    var returnedNode = someNode.appendChild(newNode); 
    alert(returnedNode == newNode); //true
    alert(someNode.lastChild == newNode); //true
  • insertBefore()方法。这个方法接受两个参数:要插入的节点和作为参照的节点。

    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]); //true
  • replaceChild()方法接受的两个参数是:要插入的节点和要替换的节点。

    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
2
3
4
5
6
7
8
 appendData(text):将 text 添加到节点的末尾。
 deleteData(offset, count):从 offset 指定的位置开始删除 count 个字符。
 insertData(offset, text):在 offset 指定的位置插入 text。
 replaceData(offset, count, text):用 text 替换从 offset 指定的位置开始到 offset+ count 为止处的文本。
 splitText(offset):从 offset 指定的位置将当前文本节点分成两个文本节点。
 substringData(offset, count):提取从 offset 指定的位置开始到 offset+count 为止处的字符串。

除了这些方法之外,文本节点还有一个 length 属性,保存着节点中字符的数目。而且,nodeValue.length 和 data.length 中也保存着同样的值
  • 可以使用 document.createTextNode()创建新文本节点,这个方法接受一个参数——要插入节点中的文本。

    Attr类型

    这种节点还是注意依赖于Element类型的DOM节点。
    1
    2
    3
    4
    Attr 对象有 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
    4
    if (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 元素的增删改查
    《高三第六版》