写代码啦
【JS编程接口02】封装 DOM 库
回复数(0) 浏览数(113)
{{topic.upvote_count || 0}} 编辑 回复

配电脑的比喻

目标

对于BOM/DOM/Canvas/jQuery/Date/LocalStorage/RegExp/Lodash/Underscore/URL 处理的封装,先实现部分 DOM


目录

  • 术语
  • 两种风格封装DOM操作
  • 命名风格
  • 细节
  • 增删改查
  • 后续
  • 设计模式

术语

  • 库:工具代码
  • API:应用编程接口(中文翻译)(外部可使用的库的函数或属性)
  • 框架:framwork(英文翻译)
  • 封装(encapsulation)
  • 意会即可

两种风格封装DOM操作

对象风格

也叫命名空间风格

各功能接口

接口

dom.create(`<div>hi</div>`) // 用于创建节点
dom.after(node, node2) // 用于向后追加兄弟节点
// 原生的提供了一个兼容性不佳的实验性接口`ChildNode.after() MDN`
dom.before(node, node2) // 用于向前追加兄弟节点
dom.append(parent, child) // 用于创建子节点
dom.wrap(`<div></div>`) //  用于创建父节点

index.html

<body>
    示例
    <div id="dad">
        <div id="test">test</div>
    </div>
    <script src="dom.js"></script>
    <script src="main.js"></script>
</body>

dom.js

// // 相当于 dom.create = function() {}
window.dom = {
    // create: function() {} // 可简化为
    create(tagName /* 语义化 形参 */ ) {
        // return document.createElement(tagName) // 不能创建带有结构的 HTML 元素`<div><span>1</span></div>`

        // const container = document.createElement("div")
        const container = document.createElement("template")
        container.innerHTML = tagName.trim() // 除去空格
            // return container.children[0]
        return container.content.firstChild
            /* 存在 不可识别元素(<td></td>)的 bug
             ** <td</td>> 不能单独存在 只能放在<table></table> 里<tr></tr>或<tbody></tbody> 里,放在 div 里不符合 HTML 语法
             ** 可以放任意元素,不出 bug 的标签是 <template></template>
             ** <template></template> 是专门用来容纳任意标签的
             ** <template></template> 用template.content.firstChild拿到
             */
    },
    after(node, node2) { // 在后面插入节点,就相当于在此 node 后面的节点的前面插 // 必须调用父节点的 insertBefore() 方法
        console.log(node.siblings) // null ?
        node.parentNode.insertBefore(node2, node.nextSibling)
            /* 判断 排除最后一个节点 没有下一个节点 null 也符合 */
    },
    before(node, node2) {
        node.parentNode.insertBefore(node2, node)
    },
    append(parent, node) {
        parent.appendChild(node)
    },
    wrap(node, parent) {
        dom.before(node, parent) // 将要包裹的“父节点”先插到目标节点的前面
        dom.append(parent, node) // 再把目标节点用 append 移至将要包裹的父节点的下面
    }
}

main.js

// 对比 document.createElement("div") 简化代码
// const div = dom.create("div")
/* 直接写出 HTML 结构 */
const div = dom.create("<div><span>newDiv</span></div>")
const td = dom.create("<tr><td>TD</td></tr>")
console.log(div)
console.log(td)
/* after */
dom.after(test, div)
const div3 = dom.create('<div id="wrapper">DIV3</div>') // 父节点
dom.wrap(test, div3)

接口

dom.remove(node) //用于删除节点
dom.empty(parent) //用于删除后代节点

dom.js

window.dom = {
    remove(node) {
        // node.remove() // IE 不支持 兼容性不好
        node.parentNode.removeChild(node);
        return node; // 仍然需要获取此节点的引用
    },
    empty(node) {
        // 清空 node 里面的所有子元素
        // node.innerHTML = ''
        // const childNodes = node.childNodes 可以改写成以下的写法
        /*
         ** const {childNodes} = node // 解构赋值
         */
        const array = [];
        /*
         **    for (let i = 0; i < childNodes.length; i++) { // 不需要i++的循环就用 while 循环代替
         **        console.log(childNodes)
         **        console.log(childNodes.length)
         **        dom.remove(childNodes[i]) // remove( nodes) 会实时改变 nodes 的长度每次减一 导致循环的长度不固定 出现 bug
         **        array.push(childNodes[i])
         **    }
         */
        //  不需要i++的循环就用 while 循环代替for 循环

        /* 获取第一个子节点 并 push 进数组 */
        let x = node.firstChild;
        while (x) {
            // 如果 x 存在
            array.push(dom.remove(node.firstChild));
            x = node.firstChild; // 第一个子节点已经移除 原先第二节点就变为现在的第一个节点
        }
        return array; // 仍然需要获取此节点的引用
    }
}

main.js

dom.class.add(test, "ftSize");
dom.class.remove(test, "ftSize");
/* empty test */
const nodes = dom.empty(window.empty);
console.log(nodes);

接口

dom.attr(node, 'title', ?) // 用于读写属性
dom.text(node, ?) // 用于读写文本内容
dom.html(node, ?) // 用于读写HTML内容
dom.style(node, "color", "red") // 用于修改style
dom.style(node, {color: "red"}) // 用于修改style
dom.style(node, "border") // 用于读取style
dom.class.add(node, 'blue') // 用于添加class
dom.class.remove(node, 'blue') // 用于删除class

dom.on(node, 'click', fn) // 用于添加事件监听

/* 是否能有效删除事件监听? */
dom.off(node, 'click', fn) // 用于删除事件监听
dom.toggle(node,'click',fn) // 切换开关

dom.js

window.dom = {
    /* 改 用于读写属性 */
    /* 用判断 arguments 的个数来重载函数 */
    /* 重载
     ** 有三个形参时,就是设置;
     ** 第二个形参时,就是读取
     */
    attr(node, name, value) {
        // 组合
        if (arguments.length === 3) {
            node.setAttribute(name, value); // 原生DOM setAttribute(name, value)
        } else if (arguments.length === 2) {
            return node.getAttribute(name); // 原生DOM getAttribute(name) 并返回值
        }
    },
    /* 用于读/写文本内容 */
    /* 重载
     ** 有两个形参时,就是设置;
     ** 第一个形参时,就是读取
     */
    text(node, string) {
        // 设计模式 之 适配
        // console.log('innerText' in node) //true
        if (arguments.length === 2) {
            /* 写 */
            if ("innerText" in node) {
                node.innerText = string; // IE // 会将节点原本的所有内容,包括标签全部改变
            } else {
                node.textContent = string; // Chrome/ Firefox // 会将节点原本的所有内容,包括标签全部改变
            }
        } else if (arguments.length === 1) {
            /* 读 */
            if ("innerText" in node) {
                return node.innerText; // IE // 会将节点原本的所有内容,包括标签全部改变
            } else {
                return node.textContent; // Chrome/ Firefox // 会将节点原本的所有内容,包括标签全部改变
            }
        }
    },
    /* 用于读/写HTML内容 */
    html(node, string) {
        if (arguments.length === 2) {
            node.innerHTML = string;
        } else if (arguments.length === 1) {
            return node.innerHTML;
        }
    },
    /* 用于修改style */
    /* 重载
     ** 第二个形参是对象时,就是设置;dom.style(div, {color: "red"})
     ** 有三个形参时,也是设置;dom.style(div, 'color', 'red')
     ** 第二个形参是字符串时,就是读取 dom.style(div, 'color')
     */
    style(node, name, value) {
        if (arguments.length === 3) {
            // dom.style(div, "color", "red"')
            return node.style[name] = value;
            // node.style.name = value;
        } else if (arguments.length === 2) {
            if (typeof name === "string") {
                // 读取 dom.style(div, 'color')
                return node.style[name];
            } else if (name instanceof Object /* true */ ) {
                // dom.style(div, {color:'red'})
                const object = name;
                for (let key in object) {
                    // 遍历读取所有对应的key
                    // key: border | color | ···
                    // node.style.border = ...
                    // node.style.color = ...
                    // 调用属性值 []方法 读取的时变量;点方法 读取的是字符串
                    // node.style.key; // 字符串
                    node.style[key] = object[key];
                }
                return object
            }
        }
    },
    class: {
        /* 用于添加class */
        add(node, className) {
            node.classList.add(className)
        },
        /* 用于删除class */
        remove(node, className) {
            node.classList.remove(className)
        },
        has(node, className) {
            return node.classList.contains(className)
        }
    },
    /* 事件相关 */
    on(node, eventName, fn) {
        node.addEventListener(eventName, fn)
    },
    off(node, eventName, fn) {
        console.log(`${eventName}取消事件`)
        node.removeEventListener(eventName, fn)
    },
    toggle(node, eventName, fn) {
        node.addEventListener("mousedown", function() {
            console.log("鼠标按下了");
            node.addEventListener(eventName, fn);
            node.addEventListener("mouseup", function() {
                console.log("鼠标抬起了");
                 node.removeEventListener(eventName, fn)
            })
        });
    }
}

main.js

/* 改 */
/* 用于读写属性  attr(node, attributeName, value) */
dom.attr(test, "title", "Hi, Jack");
const title = dom.attr(test, "title");
console.log(`title: ${title}`);
/* 用于读写文本内容 */
dom.text(test, "Hello,this is a new text");

/* 用于读写HTML内容 */
const Dad = dom.html(dad);
console.log(`Dad: ${Dad}`);

/* 用于修改style */
dom.style(test, {
    border: "1px solid cyan",
    color: "red"
});
console.log(dom.style(test, "border"));
/* 重载 修改 style */
dom.style(test, "border", "5px solid olive");
/* 重载 读取 style */
const styleBorder = dom.style(test, "border");
console.log(styleBorder);
/* 用于添加class */
dom.class.add(test, "bgColor");
dom.class.add(test, "ftSize");
dom.class.remove(test, "ftSize");
console.log(dom.class.has(test, "bgColor"));
console.log(dom.class.has(test, "ftSize"));
/* 用于添加事件监听 */
function addFn() {
    console.log('指到这里')
    console.log(`点击 ${newDiv} 取消事件`)
    console.log(newDiv)
}

// test.addEventListener('click') // TL,DR
dom.on(test, "mouseenter", addFn);
/* 用于删除事件监听 */
// test.removeEventListener(eventName, fn) // TL,DR
dom.off(test, "click", addFn);
dom.toggle(test, "click", addFn);

用到了设计模式:重载 适配

接口

/* 用 ID 来返回元素 */
dom.find('选择器') // 用于获取标签(们)
/* 无法动态地查找 因为用了 querySelector */
dom.parent(node) // 用于获取父元素
dom.childern(node) // 用于获取子元素
dom.siblings(node) // 用于获取兄弟元素
dom.next(node) // 用于获取前一个元素
dom.previous(node) // 用于获取后一个元素
dom.each(node, fn) // 用于遍历所有节点
dom.index(node) // 用于获取排列下标

dom.js

window.dom = {
    /* 查 */
    /* scope 为查找的范围 节点对象 */
    find(selector, scope) {
        /* 如果有 scope 节点 就找 scope 里的;没有就找 document 里的 */
        return (scope || document).querySelectorAll(selector)
            /* 返回的是 NodeList 伪数组 取用加 NodeList[0] */
    },
    parent(node) {
        return node.parentNode
    },
    children(node) {
        return node.children
    },
    siblings(node) {
        return Array.from(node.parentNode.children)
            .filter(n => n !== node)
    },
    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
    },
    each(nodeList, fn) {
        for (let i = 0; i < nodeList.length; i++) {
            fn.call(null, nodeList[i])
        }
    },
    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

/* 查 */
/*
<div id="siblings">
    <div id="s1"></div>
    <div id="s2"></div>
    <div id="s3"></div>
</div>
<div id="travel">
    <div id="t1">t1</div>
    <div id="t2">t2</div>
    <div id="t3">t3</div>
</div>
*/
console.log(`=== 找父、兄节点 ===`)
console.log('parent')
console.log(dom.parent(test))
console.log('children')
console.log(dom.children(test2))
console.log('siblings')
const s2 = dom.find('#s2')[0]
console.log(dom.siblings(s2))
console.log('next')
console.log(dom.next(s2))
console.log('previous')
console.log(dom.previous(s2))
console.log(`=== ===`)
    /* 遍历 */
const t = dom.find('#travel')[0]
dom.each(dom.children(t), (n) => dom.style(n, 'color', 'red'))
    /* 下标 */
console.log(dom.index(s2))

看思路,而不是源代码


·未完待续·


参考链接

2020版前端体系课【方方】之封装 DOM 库

DOM 编程.pdf

GitHub Repo源代码链接

手写 DOM 库 代码

封装DOM.pdf

相关文章

jQuery 都过时了,那我还学它干嘛?


  • 作者: Joel
  • 文章链接:
  • 版权声明
  • 非自由转载-非商用-非衍生-保持署名


{{topic.upvote_count || 0}}

配电脑的比喻

目标

对于BOM/DOM/Canvas/jQuery/Date/LocalStorage/RegExp/Lodash/Underscore/URL 处理的封装,先实现部分 DOM


目录

  • 术语
  • 两种风格封装DOM操作
  • 命名风格
  • 细节
  • 增删改查
  • 后续
  • 设计模式

术语

  • 库:工具代码
  • API:应用编程接口(中文翻译)(外部可使用的库的函数或属性)
  • 框架:framwork(英文翻译)
  • 封装(encapsulation)
  • 意会即可

两种风格封装DOM操作

对象风格

也叫命名空间风格

各功能接口

接口

dom.create(`<div>hi</div>`) // 用于创建节点
dom.after(node, node2) // 用于向后追加兄弟节点
// 原生的提供了一个兼容性不佳的实验性接口`ChildNode.after() MDN`
dom.before(node, node2) // 用于向前追加兄弟节点
dom.append(parent, child) // 用于创建子节点
dom.wrap(`<div></div>`) //  用于创建父节点

index.html

<body>
    示例
    <div id="dad">
        <div id="test">test</div>
    </div>
    <script src="dom.js"></script>
    <script src="main.js"></script>
</body>

dom.js

// // 相当于 dom.create = function() {}
window.dom = {
    // create: function() {} // 可简化为
    create(tagName /* 语义化 形参 */ ) {
        // return document.createElement(tagName) // 不能创建带有结构的 HTML 元素`<div><span>1</span></div>`

        // const container = document.createElement("div")
        const container = document.createElement("template")
        container.innerHTML = tagName.trim() // 除去空格
            // return container.children[0]
        return container.content.firstChild
            /* 存在 不可识别元素(<td></td>)的 bug
             ** <td</td>> 不能单独存在 只能放在<table></table> 里<tr></tr>或<tbody></tbody> 里,放在 div 里不符合 HTML 语法
             ** 可以放任意元素,不出 bug 的标签是 <template></template>
             ** <template></template> 是专门用来容纳任意标签的
             ** <template></template> 用template.content.firstChild拿到
             */
    },
    after(node, node2) { // 在后面插入节点,就相当于在此 node 后面的节点的前面插 // 必须调用父节点的 insertBefore() 方法
        console.log(node.siblings) // null ?
        node.parentNode.insertBefore(node2, node.nextSibling)
            /* 判断 排除最后一个节点 没有下一个节点 null 也符合 */
    },
    before(node, node2) {
        node.parentNode.insertBefore(node2, node)
    },
    append(parent, node) {
        parent.appendChild(node)
    },
    wrap(node, parent) {
        dom.before(node, parent) // 将要包裹的“父节点”先插到目标节点的前面
        dom.append(parent, node) // 再把目标节点用 append 移至将要包裹的父节点的下面
    }
}

main.js

// 对比 document.createElement("div") 简化代码
// const div = dom.create("div")
/* 直接写出 HTML 结构 */
const div = dom.create("<div><span>newDiv</span></div>")
const td = dom.create("<tr><td>TD</td></tr>")
console.log(div)
console.log(td)
/* after */
dom.after(test, div)
const div3 = dom.create('<div id="wrapper">DIV3</div>') // 父节点
dom.wrap(test, div3)

接口

dom.remove(node) //用于删除节点
dom.empty(parent) //用于删除后代节点

dom.js

window.dom = {
    remove(node) {
        // node.remove() // IE 不支持 兼容性不好
        node.parentNode.removeChild(node);
        return node; // 仍然需要获取此节点的引用
    },
    empty(node) {
        // 清空 node 里面的所有子元素
        // node.innerHTML = ''
        // const childNodes = node.childNodes 可以改写成以下的写法
        /*
         ** const {childNodes} = node // 解构赋值
         */
        const array = [];
        /*
         **    for (let i = 0; i < childNodes.length; i++) { // 不需要i++的循环就用 while 循环代替
         **        console.log(childNodes)
         **        console.log(childNodes.length)
         **        dom.remove(childNodes[i]) // remove( nodes) 会实时改变 nodes 的长度每次减一 导致循环的长度不固定 出现 bug
         **        array.push(childNodes[i])
         **    }
         */
        //  不需要i++的循环就用 while 循环代替for 循环

        /* 获取第一个子节点 并 push 进数组 */
        let x = node.firstChild;
        while (x) {
            // 如果 x 存在
            array.push(dom.remove(node.firstChild));
            x = node.firstChild; // 第一个子节点已经移除 原先第二节点就变为现在的第一个节点
        }
        return array; // 仍然需要获取此节点的引用
    }
}

main.js

dom.class.add(test, "ftSize");
dom.class.remove(test, "ftSize");
/* empty test */
const nodes = dom.empty(window.empty);
console.log(nodes);

接口

dom.attr(node, 'title', ?) // 用于读写属性
dom.text(node, ?) // 用于读写文本内容
dom.html(node, ?) // 用于读写HTML内容
dom.style(node, "color", "red") // 用于修改style
dom.style(node, {color: "red"}) // 用于修改style
dom.style(node, "border") // 用于读取style
dom.class.add(node, 'blue') // 用于添加class
dom.class.remove(node, 'blue') // 用于删除class

dom.on(node, 'click', fn) // 用于添加事件监听

/* 是否能有效删除事件监听? */
dom.off(node, 'click', fn) // 用于删除事件监听
dom.toggle(node,'click',fn) // 切换开关

dom.js

window.dom = {
    /* 改 用于读写属性 */
    /* 用判断 arguments 的个数来重载函数 */
    /* 重载
     ** 有三个形参时,就是设置;
     ** 第二个形参时,就是读取
     */
    attr(node, name, value) {
        // 组合
        if (arguments.length === 3) {
            node.setAttribute(name, value); // 原生DOM setAttribute(name, value)
        } else if (arguments.length === 2) {
            return node.getAttribute(name); // 原生DOM getAttribute(name) 并返回值
        }
    },
    /* 用于读/写文本内容 */
    /* 重载
     ** 有两个形参时,就是设置;
     ** 第一个形参时,就是读取
     */
    text(node, string) {
        // 设计模式 之 适配
        // console.log('innerText' in node) //true
        if (arguments.length === 2) {
            /* 写 */
            if ("innerText" in node) {
                node.innerText = string; // IE // 会将节点原本的所有内容,包括标签全部改变
            } else {
                node.textContent = string; // Chrome/ Firefox // 会将节点原本的所有内容,包括标签全部改变
            }
        } else if (arguments.length === 1) {
            /* 读 */
            if ("innerText" in node) {
                return node.innerText; // IE // 会将节点原本的所有内容,包括标签全部改变
            } else {
                return node.textContent; // Chrome/ Firefox // 会将节点原本的所有内容,包括标签全部改变
            }
        }
    },
    /* 用于读/写HTML内容 */
    html(node, string) {
        if (arguments.length === 2) {
            node.innerHTML = string;
        } else if (arguments.length === 1) {
            return node.innerHTML;
        }
    },
    /* 用于修改style */
    /* 重载
     ** 第二个形参是对象时,就是设置;dom.style(div, {color: "red"})
     ** 有三个形参时,也是设置;dom.style(div, 'color', 'red')
     ** 第二个形参是字符串时,就是读取 dom.style(div, 'color')
     */
    style(node, name, value) {
        if (arguments.length === 3) {
            // dom.style(div, "color", "red"')
            return node.style[name] = value;
            // node.style.name = value;
        } else if (arguments.length === 2) {
            if (typeof name === "string") {
                // 读取 dom.style(div, 'color')
                return node.style[name];
            } else if (name instanceof Object /* true */ ) {
                // dom.style(div, {color:'red'})
                const object = name;
                for (let key in object) {
                    // 遍历读取所有对应的key
                    // key: border | color | ···
                    // node.style.border = ...
                    // node.style.color = ...
                    // 调用属性值 []方法 读取的时变量;点方法 读取的是字符串
                    // node.style.key; // 字符串
                    node.style[key] = object[key];
                }
                return object
            }
        }
    },
    class: {
        /* 用于添加class */
        add(node, className) {
            node.classList.add(className)
        },
        /* 用于删除class */
        remove(node, className) {
            node.classList.remove(className)
        },
        has(node, className) {
            return node.classList.contains(className)
        }
    },
    /* 事件相关 */
    on(node, eventName, fn) {
        node.addEventListener(eventName, fn)
    },
    off(node, eventName, fn) {
        console.log(`${eventName}取消事件`)
        node.removeEventListener(eventName, fn)
    },
    toggle(node, eventName, fn) {
        node.addEventListener("mousedown", function() {
            console.log("鼠标按下了");
            node.addEventListener(eventName, fn);
            node.addEventListener("mouseup", function() {
                console.log("鼠标抬起了");
                 node.removeEventListener(eventName, fn)
            })
        });
    }
}

main.js

/* 改 */
/* 用于读写属性  attr(node, attributeName, value) */
dom.attr(test, "title", "Hi, Jack");
const title = dom.attr(test, "title");
console.log(`title: ${title}`);
/* 用于读写文本内容 */
dom.text(test, "Hello,this is a new text");

/* 用于读写HTML内容 */
const Dad = dom.html(dad);
console.log(`Dad: ${Dad}`);

/* 用于修改style */
dom.style(test, {
    border: "1px solid cyan",
    color: "red"
});
console.log(dom.style(test, "border"));
/* 重载 修改 style */
dom.style(test, "border", "5px solid olive");
/* 重载 读取 style */
const styleBorder = dom.style(test, "border");
console.log(styleBorder);
/* 用于添加class */
dom.class.add(test, "bgColor");
dom.class.add(test, "ftSize");
dom.class.remove(test, "ftSize");
console.log(dom.class.has(test, "bgColor"));
console.log(dom.class.has(test, "ftSize"));
/* 用于添加事件监听 */
function addFn() {
    console.log('指到这里')
    console.log(`点击 ${newDiv} 取消事件`)
    console.log(newDiv)
}

// test.addEventListener('click') // TL,DR
dom.on(test, "mouseenter", addFn);
/* 用于删除事件监听 */
// test.removeEventListener(eventName, fn) // TL,DR
dom.off(test, "click", addFn);
dom.toggle(test, "click", addFn);

用到了设计模式:重载 适配

接口

/* 用 ID 来返回元素 */
dom.find('选择器') // 用于获取标签(们)
/* 无法动态地查找 因为用了 querySelector */
dom.parent(node) // 用于获取父元素
dom.childern(node) // 用于获取子元素
dom.siblings(node) // 用于获取兄弟元素
dom.next(node) // 用于获取前一个元素
dom.previous(node) // 用于获取后一个元素
dom.each(node, fn) // 用于遍历所有节点
dom.index(node) // 用于获取排列下标

dom.js

window.dom = {
    /* 查 */
    /* scope 为查找的范围 节点对象 */
    find(selector, scope) {
        /* 如果有 scope 节点 就找 scope 里的;没有就找 document 里的 */
        return (scope || document).querySelectorAll(selector)
            /* 返回的是 NodeList 伪数组 取用加 NodeList[0] */
    },
    parent(node) {
        return node.parentNode
    },
    children(node) {
        return node.children
    },
    siblings(node) {
        return Array.from(node.parentNode.children)
            .filter(n => n !== node)
    },
    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
    },
    each(nodeList, fn) {
        for (let i = 0; i < nodeList.length; i++) {
            fn.call(null, nodeList[i])
        }
    },
    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

/* 查 */
/*
<div id="siblings">
    <div id="s1"></div>
    <div id="s2"></div>
    <div id="s3"></div>
</div>
<div id="travel">
    <div id="t1">t1</div>
    <div id="t2">t2</div>
    <div id="t3">t3</div>
</div>
*/
console.log(`=== 找父、兄节点 ===`)
console.log('parent')
console.log(dom.parent(test))
console.log('children')
console.log(dom.children(test2))
console.log('siblings')
const s2 = dom.find('#s2')[0]
console.log(dom.siblings(s2))
console.log('next')
console.log(dom.next(s2))
console.log('previous')
console.log(dom.previous(s2))
console.log(`=== ===`)
    /* 遍历 */
const t = dom.find('#travel')[0]
dom.each(dom.children(t), (n) => dom.style(n, 'color', 'red'))
    /* 下标 */
console.log(dom.index(s2))

看思路,而不是源代码


·未完待续·


参考链接

2020版前端体系课【方方】之封装 DOM 库

DOM 编程.pdf

GitHub Repo源代码链接

手写 DOM 库 代码

封装DOM.pdf

相关文章

jQuery 都过时了,那我还学它干嘛?


  • 作者: Joel
  • 文章链接:
  • 版权声明
  • 非自由转载-非商用-非衍生-保持署名


113
回复 编辑