虽然现在我们在开发中已经用不到自己操作DOM了,之前有JQ,现在更是有VUEREACT两大框架供我们使用,但是我们也有必要了解下,关于原生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元素或者节点的

思维导图

30.DOM操作之——DOM节点类型及属性 - 图1

三、需求练习

1、封装一个方法:获取指定容器CONTAINER中的所有元素子节点,需要兼容所有的浏览器

  1. function children(container) {
  2. // 获取所有的子节点,遍历这些节点,所有NODETYPE===1的就是我们想要的元素子节点
  3. var nodeList = container.childNodes,
  4. result = [];
  5. for (var i = 0; i < nodeList.length; i++) {
  6. var itemNode = nodeList[i];
  7. if (itemNode.nodeType === 1) {
  8. // 元素节点
  9. result.push(itemNode);
  10. }
  11. }
  12. return result;
  13. }
  14. var arr = children(box);
  15. console.log(arr);
  16. 复制代码

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;
}
复