1

Node.prototype.appendChildPlus()

写于:2017-1-2 | 更新于:2017-1-3

概述

appendChild方法的缺点有两个:

  • 不能传多个参数

  • 参数只能是节点对象

这里给Node.prototype添加一个方法,名称为:appendChildPlus,可以认为是appendChild的加强版,特点如下:

  • 参数可以是html字符串和节点对象

  • 可以传多个参数

  • script标签字符串和新建的script节点对象插入文档以后可以正常运行

  • 所有参数是一起插入指定节点的,而不是一个一个插入。

源代码

// appendChildPlus.js
/**
 * Created by jszhou on 2017/1/2.
 * updated by jszhou on 2017/1/3.
 */
(function () {
    /**
     * 把类数组对象转换为数组。
     */
    var toArray = Array.from || function (arg) {
            return Array.prototype.slice.call(arg);
        };

    /**
     * 判断参数是否为节点对象。
     * @param {?} arg
     * @return {boolean}
     */
    function isNode(arg) {
        return arg instanceof Node;
    }

    /**
     * 判断参数是否为script节点对象。
     * @param {?} arg
     * @return {boolean}
     */
    function isScript(arg) {
        return isNode(arg) && arg.nodeName.toLowerCase() === 'script';
    }

    /**
     * 复制script节点对象,复制以后script标签的内容可以运行。
     * @param {object} script script节点对象
     * @return {object} 返回新的script节点对象
     */
    function copyScript(script) {
        var newScript = document.createElement('script');

        // 复制属性
        toArray(script.attributes).forEach(function (item) {
            newScript.setAttribute(item.nodeName, item.nodeValue);
        });

        newScript.innerHTML = script.innerHTML;

        return newScript;
    }

    /**
     * 替代script节点对象,替代以后script标签的内容可以运行。
     * @param {object} node 节点对象
     * @return {object} 返回script被替代的节点对象(script节点对象被替代成可以运行的)
     */
    function replaceAllScript(node) {
        Array.prototype.forEach.call(node.childNodes, function (item) {
            // 如果是script节点对象,则替代为可运行的script节点对象
            if(isScript(item)) {
                item.parentNode.replaceChild(copyScript(item), item);
                return;
            }
            // 递归
            if(item.childNodes.length > 0){
                replaceAllScript(item);
            }
        });

        return node;
    }

    /**
     * 字符串转成文档片段节点对象。
     * @param {String} str 字符串,可以包含script标签,转换完插入文档以后可以运行。
     * @return {Object} 返回文档片段节点对象
     */
    function strToNode(str) {
        var docFrag = document.createDocumentFragment(),
            div = document.createElement('div');

        div.innerHTML = str;

        toArray(replaceAllScript(div).childNodes).forEach(function (item) {
            docFrag.appendChild(item);
        });

        return docFrag;
    }

    /**
     * 用于扩展appendChild方法。可以传入html字符串和节点对象,可以传多个参数。
     * 如果html字符串中有script标签,插入以后可以正常运行。
     * 注意,所有参数是一起插入指定节点的,而不是一个一个插入。
     */
    Node.prototype.appendChildPlus = function () {
        var docFrag = document.createDocumentFragment();

        toArray(arguments).forEach(function (arg) {
            docFrag.appendChild(isNode(arg) ? arg : strToNode(String(arg)));
        });

        this.appendChild(docFrag);
    };
})();

示例代码

// extend.js
var wrap = document.querySelector('.js-wrap'),
    deepScript = '<div><script>wrap.append("deepScript");</script></div>',
    newScriptNode = document.createElement('script'),
    outerScript = '<script src="./outerScript.js"></script>';

newScriptNode.textContent = 'wrap.append("newScriptNode");';

wrap.appendChildPlus(deepScript, newScriptNode, outerScript, '<div>nihao</div>');

// outerScript.js
wrap.append('outerScript');
<!--extend.html-->
<!--运行前-->
<div class="js-wrap"></div>
<script src="../appendChildPlus.js"></script>
<script src="./extend.js"></script>

<!--运行后-->
<div class="js-wrap">
    <div><script>wrap.append("deepScript");</script></div>
    <script>wrap.append("newScriptNode");</script>
    <script src="./outerScript.js"></script>
    <div>nihao</div>
    deepScript
    newScriptNode
    outerScript
</div>
<script src="../appendChildPlus.js"></script>
<script src="./extend.js"></script>

更多

本扩展的markdown和demo源文件在github上,点击这里查看


jszhou
53 声望1 粉丝

front-end developer