JavaScript DOM 删除元素。

213

我试图测试一个DOM元素是否存在,如果存在则删除它,如果不存在则创建它。

var duskdawnkey = localStorage["duskdawnkey"];
var iframe = document.createElement("iframe");
var whereto = document.getElementById("debug");
var frameid = document.getElementById("injected_frame");
iframe.setAttribute("id", "injected_frame");
iframe.setAttribute("src", 'http://google.com');
iframe.setAttribute("width", "100%");
iframe.setAttribute("height", "400");

if (frameid) // check and see if iframe is already on page
{ //yes? Remove iframe
    iframe.removeChild(frameid.childNodes[0]);
} else // no? Inject iframe
{
    whereto.appendChild(iframe);
    // add the newly created element and it's content into the DOM
    my_div = document.getElementById("debug");
    document.body.insertBefore(iframe, my_div);
}

检查元素是否存在可以,创建元素也可以,但删除元素就不行了。基本上这段代码的功能是通过点击按钮将一个 iframe 注入到网页中。我希望的是,如果该 iframe 已经存在,则将其删除。但由于某些原因,我失败了。


可能是重复的问题:JavaScript:按ID删除元素 - Zaz
4个回答

353

removeChild 应该在父节点上被调用,即:

parent.removeChild(child);

在你的例子中,你应该像这样做:

if (frameid) {
    frameid.parentNode.removeChild(frameid);
}

谢谢,我在看到你的帖子之前就解决了。必须将其更改为whereto.removeChild(whereto.childNodes[0]); - Joshua Redfield
6
假设您的框架始终是“debug” div 的第一个子元素,那么这也可以工作。使用“parentNode”是一种更通用的解决方案,适用于任何元素。 - casablanca
1
这个解决方案可能不够。如果有人在阅读,请看一下格伦的建议。 - Sebas

98

在大多数浏览器中,从DOM中删除一个元素有一种比将.removeChild(element)调用其父级更为简洁的方法,只需调用element.remove()即可。随着时间的推移,这可能会成为从DOM中删除元素的标准惯用方式。

.remove()方法于2011年添加到DOM Living Standard中(提交记录),并由Chrome、Firefox、Safari、Opera和Edge实现。它在任何版本的Internet Explorer中都不受支持。

如果您想支持旧版浏览器,则需要进行shim。这有点烦人,因为似乎没有人制作包含这些方法的通用DOM shim,并且我们不仅要将该方法添加到单个原型中;它是ChildNode的一个方法,后者只是由规范定义的接口,并且对JavaScript不可访问,因此我们无法向其原型中添加任何内容。因此,我们需要找到所有从ChildNode继承且实际上在浏览器中定义的原型,并向它们添加.remove

下面是我想出的shim,在IE 8中经过确认可以工作。

(function () {
    var typesToPatch = ['DocumentType', 'Element', 'CharacterData'],
        remove = function () {
            // The check here seems pointless, since we're not adding this
            // method to the prototypes of any any elements that CAN be the
            // root of the DOM. However, it's required by spec (see point 1 of
            // https://dom.spec.whatwg.org/#dom-childnode-remove) and would
            // theoretically make a difference if somebody .apply()ed this
            // method to the DOM's root node, so let's roll with it.
            if (this.parentNode != null) {
                this.parentNode.removeChild(this);
            }
        };

    for (var i=0; i<typesToPatch.length; i++) {
        var type = typesToPatch[i];
        if (window[type] && !window[type].prototype.remove) {
            window[type].prototype.remove = remove;
        }
    }
})();

由于在IE 7或更低版本中无法扩展DOM原型(在IE 8之前不可能),所以这不适用于IE 7或更低版本。 我认为,虽然到了2015年的边缘,大多数人不需要关心这些事情。

一旦您已经包含了它们的shim,您就可以通过简单地调用来从DOM中删除DOM元素element

element.remove();

1
这里留下一个链接:https://polyfill.io/v2/docs/features/#Element_prototype_remove 如果你使用这个自动填充服务,你将获得对IE 7的支持。 - complistic
5
只要你不介意IE浏览器,这绝对是2017年正确的做法。参见:https://developer.mozilla.org/zh-CN/docs/Web/API/ChildNode/remove - nevf

48

似乎我没有足够的声望来发表评论,所以另一个答案将不得不做。

当您使用removeChild()或通过设置父级的innerHTML属性取消链接节点时,还需要确保没有任何其他内容引用它,否则它实际上不会被销毁并导致内存泄漏。在调用removeChild()之前,您可能已经以许多方式引用了节点,您必须确保那些尚未超出范围的引用明确地被删除。

Doug Crockford在这里写道,在IE中,事件处理程序是循环引用的一种原因,并建议在调用removeChild()之前显式地将它们删除。

function purge(d) {
    var a = d.attributes, i, l, n;
    if (a) {
        for (i = a.length - 1; i >= 0; i -= 1) {
            n = a[i].name;
            if (typeof d[n] === 'function') {
                d[n] = null;
            }
        }
    }
    a = d.childNodes;
    if (a) {
        l = a.length;
        for (i = 0; i < l; i += 1) {
            purge(d.childNodes[i]);
        }
    }
}

即使你采取了很多预防措施,在IE浏览器中仍可能会出现内存泄漏问题,就像Jens-Ingo Farley在这里所描述的一样。

最后,不要陷入认为JavaScript的delete是解决方案的误区。虽然被许多人提出,但它并不能解决问题。Kangax在这里提供了一个很好的关于理解delete的参考。


1
也许你可以展示一些 jsFiddle 来证明这个。谢谢。 - Muhaimin
1
我确认这种行为。我的框架使用JavaScript对象映射树来遍历DOM布局。每个JS对象都引用其DOM元素。即使我调用element.parentNode.removeChild来删除元素,它们仍然存在并且仍然可以被引用。它们只是在常规DOM树中不可见。 - Sebas
是的,然后删除指向该js映射对象的全局指针会神奇地解锁垃圾收集器。这是对已接受答案的重要补充。 - Sebas

15

使用Node.removeChild()可以完成任务,只需像这样使用:

var leftSection = document.getElementById('left-section');
leftSection.parentNode.removeChild(leftSection);

在DOM 4中,应用了remove方法,但根据W3C的说法,浏览器支持不佳:

节点的remove()方法已经被DOM 4规范实现。但由于浏览器支持不佳,请勿使用。

但是如果您使用jQuery,可以使用remove方法...

$('#left-section').remove(); //using remove method in jQuery

此外,在新的框架中,您可以使用条件来移除元素,例如在Angular中的*ngIf和在React中渲染不同视图取决于条件...


1
如果您正在使用createElement在js中创建模态节点,请确保在删除它之前检查该节点是否存在。_if(leftSection){..your code}_应该可以完成工作。 - Afsan Abdulali Gujarati

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接