练习:手写封装 DOM 库之获取 / 查找节点

165 阅读2分钟

1. 配置环境

在终端用 yarn 安装 parcel: yarn global add parcel 启动服务器: parcel src/index.html

2. 对象风格(命名空间风格)封装 DOM 操作

window.dom 是我们提供的全局对象,在src目录中创建index.html, dom.js 和 main.js

3. 功能:获取 / 查找节点

(1) 获取标签

  • dom.js 中调用 document.querySelectorAll 获取所有标签
    find(selector){
        return document.querySelectorAll(selector)
    }  // 不管选择器里有几个元素,全部都返回
  • main.js 中运行 dom.find ,获取所有div id为test的元素,并打印出这个div
const testDiv = dom.find('#test')[0]
console.log(testDiv)

效果图⬇️

  • 适配 在 dom.js 中设定 (scope || document) 如果有scope的话,就在scope中调用querySelectorAll,如果没有就在document中调用。
    find(selector, scope){
        return (scope || document).querySelectorAll(selector)
    }

例如在index.html中

<div id="test">
    <span>test</span>
    <p class="red">红色的test</p>
</div>
<div id="test2">
    <span>test2</span>
    <p class="red">红色的test2</p>
</div>

更新main.js,在 class='red' 的元素中,只获取id名为test2的,不获取test

const testDiv = dom.find('#test')[0]
const test2 = dom.find('#test2')[0]
dom.find('.red', test2)
console.log(dom.find('.red', test2)[0])

控制台打印内容如下 ⬇️

(2) 获取父 / 子元素(parent/children)

  • dom.js
    parent(node){
        return node.parentNode
    } // 获取父元素
    children(node){
        return node.children
    } // 获取子元素
  • main.js
console.log(dom.parent(test));

获取到了 test 的父元素

(3) 获取所有兄弟姐妹元素(siblings)

  • dom.js 中调用 .parentNode.children 查找父母的所有孩子(包括自己),然后 filter 掉自己,得到所有兄弟姐妹
    siblings(node){
        return Array.from(node.parentNode.children)
        .filter(n => n !== node)
    }
  • 在 index.html 中添加
<div id="family">
    <div id="bro">bro</div>
    <div id="sis">sis</div>
    <div id="target">target</div>
</div>
  • 在 main.js 中获取 target 的兄弟姐妹,并打印出他们如图 ⬇️
const findSib = dom.siblings(dom.find('#target')[0])
console.log(findSib)

(3) 获取同父母的上一个 / 下一个元素

  • dom.js 中使用 .nextSibling 获取节点类型为文本(nodeType === 3)的元素,如果节点存在下一个 / 存在弟弟,且类型为文本,则返回该节点,并继续找下一个弟弟;查找上一个节点同理,使用 .previousSibling
    next(node){
        let x = node.nextSibling
        while(x && x.nodeType === 3){
            x = x.nextSibling
        }
        return x
    },
    previous(node){
        let x = node.previousSibling
        while(x && x.nodeType === 3){
            x = x.previousSibling
        }
        return x
    }

其他节点类型的对应数字,详见 MDN文档-节点类型常量

  • main.js 中寻找sis的下一个元素
const preSib = dom.previous(dom.find('#sis')[0])
console.log(preSib)
const nextSib2 = dom.next(dom.find('#sis')[0])
console.log(nextSib2)

如图,控制台打印出 sis 上面的 bro 和下面的 target ⬇️

  • ⚠️ 这里不能直接用 .nextSibling,因为节点后面有回车,返回的是文本 ⬇️
// dom.js ❌ 实例
    next(node){
        return node.nextSibling
    }

此时返回的是文本 #text

(4) 遍历所有节点

  • 在 index.html 中新增 div
<div id="travel">
    <div id="t1">杭州~</div>
    <div id="t2">西雅图~</div>
    <div id="t3">慕尼黑~</div>
</div>
  • dom.js 中用 .call() 调用并返回 nodeList (this的位置用null占位)
    each(nodeList, fn){
        for(let i = 0; i< nodeList.length; i++){
            fn.call(null, nodeList[i])
        }
    }
  • main.js 中对于找到 travel 的所有子元素,对其进行 dom.each 操作遍历,将 style 属性更改为 color: blue'
const t = dom.find('#travel')[0]
dom.each(dom.children(t), (n)=> dom.style(n, 'color', 'blue'))

得到的页面效果如图 ⬇️

(5) 获取排名

  • dom.js
    index(node){
        const list = dom.children(node.parentNode)
        let i
        for(i=0; i<list.length; i++){
            if(list[i] === node){
                break
            }
        }
        return i
    }

  • main.js 中 调用 dom.find 获取 sis 的排名
const sis = dom.find('#sis')[0]
console.log(dom.index(sis));

如图⬇️ , 所以 sis 的排名是第2名 (第一名index为0,第二名为1,etc.)

手写 DOM 库系列结束,完结撒花~

Reference List | 参考资料
MDN 节点常量: developer.mozilla.org/zh-CN/docs/…
MDN .call(): developer.mozilla.org/zh-CN/docs/…