创建、插入和删除节点


DOM节点增删改查

创建节点

创建新的 Element 节点可以使用 Document 对象的 createElement() 方法,该方法需要需要传入元素标签名作为参数。

Text 节点可以通过 Document 对象的 createTextNode() 方法创建,传入文本字符串作为参数。

此外,Document 对象还提供了 createComment() 方法来创建注释节点。

另一种创建文档节点的方式是复制已存在的节点,每个节点都有一个 cloneNode() 方法来返回该节点的全新副本,给该方法传递参数 true 还可以递归复制所有的后代节点,或传入 false 只执行一个浅复制。

除此之外,在除 IE 之外的其他浏览器中,还可以通过 importNode() 方法从其他文档导入节点。

插入节点

创建完节点后,就可以通过 Node 对象的 appendChild()insertBefore() 方法将其插入到文档中。

appendChild() 方法是在需要插入的 Element 节点上调用的,它插入指定的节点使其成为该父节点的最后一个子节点。

insertBefore() 则是在新节点的父节点上调用,该方法的第一个参数是待插入的节点,第二个参数是该父节点的子节点,新节点将插入该子节点的前面,如果第二个参数为 null,则将节点插入到最后。

如果调用上面两个方法将已存在的节点再次插入,那个节点将自动从当前位置删除并在新的位置重新插入。

下面来演示下这两个方法的使用,基于表格中指定列中单元格的值进行行排序:

function sortrows(table, n, comparator) {
    var tbody = table.tBodies[0];
    var rows = tbody.getElementsByTagName("tr");
    rows = Array.prototype.slice.call(rows, 0);
    
    // 基于第n个<td>元素的值对行排序
    rows.sort(function (row1 ,row2) {
        var cell1 = row1.getElementsByTagName("td")[n];
        var cell2 = row2.getElementsByTagName("td")[n];
        var val1 = cell1.textContent || cell1.innerText;
        var val2 = cell2.textContent || cell2.innerText;
        if (comparator) return comparator(val1, val2);
        if (val1 < val2) return -1;
        else if (val1 > val2) return 1;
        else return 0;
    });
    
    // 在tbody中按照它们的顺序把行添加到最后,这将自动把它们从当前位置移走
    for (var i = 0; i < rows.length; i++) {
        tbody.appendChild(rows[i]);
    }
}
    
// 查找表格的<th>元素,让它们可以单击
function makeSortable(table) {
    var headers = table.getElementsByTagName("th");
    for (var i = 0; i < headers.length; i++) {
        (function (n) {
            headers[i].onclick = function () {
                sortrows(table, n);
            };
        }(i));
    }
}

删除和替换节点

我们可以通过 removeChild() 方法从文档树中删除一个节点,需要注意的是该方法不是在待删除的节点上调用,而是在其父节点上调用:

n.parentNode.removeChild(n);

replaceChild() 方法删除一个子节点并用一个新节点来替代,同样在父节点上调用该方法,第一个参数是新节点,第二个参数是要替代的节点:

n.parentNode.replaceChild(document.createTextNode("[ REDACTED ]"), n);

下面我们来编写一个 outerHTML 属性的自定义实现版本,以演示删除节点方法的使用:

/**
 * 为那些不支持 outerHTML 属性的浏览器实现 outerHTML 属性
 */
(function () {
    // 如果已支持则直接返回
    if (document.createElement("div").outerHTML)
        return;
    
    // 返回 this 所引用元素的外部 HTML
    function outerHTMLGetter() {
        var container = document.createElement("div");
        container.appendChild(this.cloneNode(true));
        return container.innerHTML;
    }
    
    // 用指定的值设置元素的外部 HTML
    function outerHTMLSetter(value) {
        var container = document.createElement("div");
        container.innerHTML = value;
        // 将虚拟元素的节点全部移动到文档中
        while (container.firstChild)
            this.parentNode.insertBefore(container.firstChild, this);
        // 删除所被取代的节点
        this.parentNode.removeChild(this);
    }
    
    // 为 Element 对象设置 outerHTML 属性
    if (Object.defineProperty) {
        Object.defineProperty(Element.prototype, "outerHTML", {
            get: outerHTMLGetter,
            set: outerHTMLSetter,
            enumerable: false,
            configurable: true
        });
    } else {
        Element.prototype.__defineGetter__("outerHTML", outerHTMLGetter);
        Element.prototype.__defineSetter__("outerHTML", outerHTMLSetter);
    }
}());

使用 DocumentFragment

DocumentFragment 是一种特殊的 Node,可以作为其他节点的一个临时容器,创建方式如下:

var frag = document.createDocumentFragment();

和 Document 节点一样,DocumentFragment 是独立的,而不是任何文档的一部分,它的 parentNode 总是 null。但类似 Element,它可以有多个子节点,并且可以用 appendChild()insertBefore() 等方法来操作它们。

DocumentFragment 的特殊之处在于它使得一组节点被当做一个节点看待,从而简化了对多个节点的操作。下面我们通过一段代码来演示通过 DocumentFragment 实现自定义版本的 insertAdjacentHTML() 方法:

// 本模块为不支持它的浏览器定义了 Element.insertAdjacentHTML() 方法
var Insert = (function () {
    // 如果元素支持原生的 insertAdjacentHTML
    // 则将对应调用转化为更名后的4个可读性更好的函数
    if (document.createElement("div").insertAdjacentHTML) {
        return {
            before: function (e, h) {
                e.insertAdjacentHTML("beforebegin", h);
            },
            after: function (e, h) {
                e.insertAdjacentHTML("afterend", h);
            },
            atStart: function (e, h) {
                e.insertAdjacentHTML("afterbegin", h);
            },
            atEnd: function (e, h) {
                e.insertAdjacentHTML("beforeend", h);
            }
        }
    }
    
    // 否则,自定义实现上述4个函数
    
    // 首先,定义一个工具函数,传入 HTML 字符串,返回一个 DocumentFragment,它包含了解析后的 HTML 表示
    function fragment(html) {
        var elt = document.createElement("div");
        var frag = document.createDocumentFragment();
        elt.innerHTML = html;
        while (elt.firstChild) {
            frag.appendChild(elt.firstChild);
        }
        return frag;
    }
    
    var Insert = {
        before: function (elt, html) {
            elt.parentNode.insertBefore(fragment(html), elt);
        },
        after: function (elt, html) {
            elt.parentNode.insertBefore(fragment(html), elt.nextSibling);
        },
        atStart: function (elt, html) {
            elt.parentNode.insertBefore(fragment(html), elt.firstChild);
        },
        atEnd: function (elt, html) {
            elt.appendChild(fragment(html));
        }
    };
    
    // 基于以上函数实现 insertAdjacentHTML
    Element.prototype.insertAdjacentHTML = function (pos, html) {
        switch (pos.toLowerCase()) {
            case "beforebegin":
                return Insert.before(this, html);
            case "afterend":
                return Insert.after(this, html);
            case "afterbegin":
                return Insert.atStart(this, html);
            case "beforeend":
                return Insert.atEnd(this, html);
        }
    };
    // 最后返回四个插入函数
    return Insert;
}());

点赞 取消点赞 收藏 取消收藏

<< 上一篇: 元素的内容

>> 下一篇: 文档和元素的几何形态和滚动