虽然现在我们在开发中已经用不到自己操作DOM
了,之前有JQ
,现在更是有VUE
、REACT
两大框架供我们使用,但是我们也有必要了解下,关于原生JS
中的DOM
操作问题。
- 这次我们介绍一下
DOM
节点类型及获取节点的方法
我们认为在页面中所有呈现的内容,都是DOM文档中的一个节点(node),例如:元素标签是元素节点、注释的内容是注释节点、文本内容是文本节点、document是文档节点…
一、节点类型
1、文档节点
document
重点记忆属性:
- nodeType(节点类型):9
- nodeName(节点名字):“#document”
- nodeValue(节点文本内容):null
2、元素节点
所有元素标签
重点记忆属性:
- nodeType(节点类型):1
- nodeName(节点名字):“大写标签名”
- nodeValue(节点文本内容):null
3、文本节点
文字、标签之间的空格和换行也被当作文本节点
重点记忆属性:
- nodeType(节点类型):3
- nodeName(节点名字):“#text”
- nodeValue(节点文本内容):文本内容
4、注释节点
注释内容
重点记忆属性:
- nodeType(节点类型):8
- nodeName(节点名字):“#comment”
- nodeValue(节点文本内容):注释内容
二、获取节点的方式
描述节点和节点之间的关系属性,基于这些属性可以获取到指定的节点
1、获取所有子节点——节点集合
- 语法:
[CONTAINER].childNodes
- 获取当前容器中所有的子节点
- 包含各种类型的节点
- 获取到的是一个节点集合,包含容器中的所有类型节点(空格换行是文本节点)
2、获取元素子节点——元素集合
- 语法:
[CONTAINER].children
- 获取当前容器中所有的元素子节点
- 获取的是一个元素集合,只有元素节点
- 只有元素标签的,在
IE
低版本浏览器中,也会把注释当作元素节点
3、获取父节点
- 语法:
[NODE].parentNode
- 获取某一个节点的父节点
4、获取一个哥哥节点
- 语法:
[NODE].previousSibling
- 获取某一个节点的上一个哥哥节点
5、获取一个哥哥元素节点
- 语法:
[NODE].previousElementSibling
- 获取某一个节点的上一个哥哥元素节点(不兼容IE低版本浏览器)
6、获取一个弟弟节点
- 语法:
[CONTAINER].nextSibling
- 获取某一个节点的下一个弟弟节点
7、获取一个弟弟元素节点
- 语法:
[CONTAINER].nextElementSibling
- 获取某一个节点的下一个弟弟元素节点(不兼容IE低版本)
8、获取第一个子节点
- 语法:
[CONTAINER].firstChild
- 获取容器中第一个子节点
9、获取第一个元素子节点
- 语法:
[CONTAINER].firstElementChild
- 获取容器中第一个元素子节点(不兼容IE低版本)
10、获取最后一个字节点
- 语法:
[CONTAINER].lastChild
- 获取容器中最后一个字节点
11、获取最后一个元素子节点
- 语法:
[CONTAINER].lastElementChild
- 获取容器中最后一个元素子节点(不兼容IE低版本)
所有方法和属性,都是为了快速获取到我们想要操作的DOM元素或者节点的
思维导图
三、需求练习
1、封装一个方法:获取指定容器CONTAINER中的所有元素子节点,需要兼容所有的浏览器
function children(container) {
// 获取所有的子节点,遍历这些节点,所有NODETYPE===1的就是我们想要的元素子节点
var nodeList = container.childNodes,
result = [];
for (var i = 0; i < nodeList.length; i++) {
var itemNode = nodeList[i];
if (itemNode.nodeType === 1) {
// 元素节点
result.push(itemNode);
}
}
return result;
}
var arr = children(box);
console.log(arr);
复制代码
2、获取当前节点的所有元素哥哥节点(兼容所有的浏览器)
JQ
中的prevAll
这个方法就是干这个的,我们自己封装一个
方法一:循环当前元素父亲的子节点方法
//=> 已知:previousElementSibling是获取上一个哥哥元素节点(兼容性) previousSibling获取上一个哥哥节点(没有兼容性的)
function prevAll(node) {
let result = [];
// 获取它爹
let parent = node.parentNode;
// 获取它爹中的儿子(自己和他所有的兄弟)
let nodeList = parent.childNodes;
// 循环所有节点,元素类型的是我们想要的,并且找到当前节点后就不在循环了
for (let i = 0; i < nodeList.length; i++) {
let item = nodeList[i];
if (item === node) {
// 找到的是自己
break;
}
// 找的不是自己,我们把元素节点存储起来
if (item.nodeType === 1) {
result.push(item);
}
}
return result;
}
复制代码
方法二:获取当前节点的哥哥节点,再获取哥哥节点的哥哥节点…一直找到没有哥哥节点为止(没有哥哥节点,结果为NULL); 再查找的过程中,把所有找到的元素节点存储起来即可;
//=> 循环不知道具体次数(不知道找多少次) =>while循环
function prevAll(node) {
let prev = node.previousSibling,
result = [];
// 循环找他的哥哥,一直到没有哥哥了为止
while (prev !== null) {
// 把找到的哥哥中是元素节点的存储起来
if (prev.nodeType === 1) {
result.unshift(prev);
}
prev = prev.previousSibling;
}
return result;
}
复制代码
3、 获取所有的弟弟元素节点(兼容所有的浏览器)
同上题一样,我们同样写两种方法
方法一:
function nextAll(node) {
// 获取其父亲中所有的儿子
let nodeList = node.parentNode.childNodes,
result = [];
// 倒着从最后一项开始循环
for (let i = nodeList.length - 1; i >= 0; i--) {
let item = nodeList[i];
if (item === node) break;
item.nodeType === 1 ? result.unshift(item) : null;
}
return result;
}
复制代码
方法二:
function nextAll(node) {
let result = [],
next = node.nextSibling;
while (next !== null) {
next.nodeType === 1 ? result.push(next) : null;
next = next.nextSibling;
}
return result;
}
复制代码
4、获取所有的兄弟元素节点:所有的哥哥+所有的弟弟
方法一:
function siblings(node) {
// 所有的儿子中一定包含了,我和我的兄弟们
let nodeList = node.parentNode.childNodes,
result = [];
// 依次遍历每一个节点,把非元素和我本身除外,其余的存储起来
for (let i = 0; i < nodeList.length; i++) {
let item = nodeList[i];
if (item.nodeType === 1 && item !== node) {
result.push(item);
}
// 上面的 if 判断条件也可改写为下面的
// if (item.nodeType !== 1 || item === node) {
// continue;
// }
// result.push(item);
}
return result;
}
复制代码
方法二:利用上面两题我们封装好的方法
function siblings(node) {
// 分别调用两个方法获取所有的哥哥和所有的弟弟,就是所有的兄弟
return prevAll(node).concat(nextAll(node));
}
复制代码
5、获取当前节点的索引:他在所有兄弟中的排行
function index(node) {
// 它有几个哥哥,那么它的索引就是几
return prevAll(node).length;
}
复