如何检查一个元素是否存在于可见的DOM中?

651

如何在不使用getElementById方法的情况下测试元素是否存在?

我已经设置了一个演示示例供参考。这里也会打印出代码:

<!DOCTYPE html>
<html>
<head>
    <script>
    var getRandomID = function (size) {
            var str = "",
                i = 0,
                chars = "0123456789abcdefghijklmnopqurstuvwxyzABCDEFGHIJKLMNOPQURSTUVWXYZ";
            while (i < size) {
                str += chars.substr(Math.floor(Math.random() * 62), 1);
                i++;
            }
            return str;
        },
        isNull = function (element) {
            var randomID = getRandomID(12),
                savedID = (element.id)? element.id : null;
            element.id = randomID;
            var foundElm = document.getElementById(randomID);
            element.removeAttribute('id');
            if (savedID !== null) {
                element.id = savedID;
            }
            return (foundElm) ? false : true;
        };
    window.onload = function () {
        var image = document.getElementById("demo");
        console.log('undefined', (typeof image === 'undefined') ? true : false); // false
        console.log('null', (image === null) ? true : false); // false
        console.log('find-by-id', isNull(image)); // false
        image.parentNode.removeChild(image);
        console.log('undefined', (typeof image === 'undefined') ? true : false); // false ~ should be true?
        console.log('null', (image === null) ? true : false); // false ~ should be true?
        console.log('find-by-id', isNull(image)); // true ~ correct but there must be a better way than this?
    };
    </script>
</head>
<body>
    <div id="demo"></div>
</body>
</html>

以上代码演示了将一个元素存储到变量中,然后从DOM中删除该元素。即使元素已从DOM中删除,变量仍保留它最初声明时的元素。换句话说,它不是对元素本身的实时引用,而是一个副本。因此,检查变量的值(元素)是否存在将提供意外的结果。

isNull函数是我尝试从变量中检查元素是否存在的方法,它有效,但我想知道是否有更简单的方法来实现同样的结果。

PS:如果有人知道一些相关的好文章,我也对为什么JavaScript变量会表现出这种行为感兴趣。


28
实际上,这是对元素本身的实时引用,只是它不再在文档中了。这种功能是必需的,因为您实际上可以从DOM中提取一个元素,然后稍后将其放回,并且所有事件处理程序/等仍然附加到它上面。至于为什么JS变量会表现出这样的行为?因为如果它们不这样做,那会非常麻烦。只有当您没有任何对变量的引用时,JS才会删除它们。语言无法知道您认为哪些引用很重要,哪些认为是无用的。 - user578895
@cwolves 很有趣。我以前遇到过很多次,但从来没有太在意。事实上,在我的当前项目中,我会在对元素进行任何更改之前将它们保存在数组中,以防我想要撤销更改。 - Justin Bull
1
垃圾回收定期运行并删除它认为可以删除的所有内容。在大多数浏览器中,它似乎相当糟糕,但随着开发人员意识到某些浏览器在重启之间运行了数天或数周,因此良好的垃圾回收对于浏览器性能至关重要,所以现在越来越好了。Web开发人员可以通过删除不再需要的属性(因此删除内存中的引用)来帮助垃圾回收。 - RobG
2
@JustinBull 注意存储元素副本以便还原时要小心。当将DOM元素存储在数组中时,存储的是对DOM元素的引用,而不是副本,因此在引用数组元素时反映对DOM元素所做的更改。这适用于javascript中的所有对象(类型为“object”的变量)。 - Anthony DiSanti
27个回答

818

好像有些人来到这里,只是想知道一个元素是否存在(与原问题稍有不同)。

这很简单,只需使用任何浏览器的选择方法,并将其检查为真值(通常情况下)。

例如,如果我的元素具有id"find-me",我可以简单地使用...

var elementExists = document.getElementById("find-me");

这个方法要么返回元素的引用,要么返回null。如果你必须得到布尔值,只需在方法调用前加上!!

此外,您可以使用许多其他查找元素的方法,例如(都在document下):

  • querySelector()/querySelectorAll()
  • getElementsByClassName()
  • getElementsByName()

其中一些方法返回一个NodeList,因此请务必检查其length属性,因为NodeList是一个对象,因此是truthy


要确定元素是否存在于可见DOM的一部分(就像最初提出的问题),Csuwldcat提供了比自己编写更好的解决方案(如此答案所含)。也就是说,使用DOM元素上的contains()方法

您可以这样使用它...

document.body.contains(someReferenceToADomElement);

1
正是我在寻找的!显然哈哈,为什么我没想到呢。另外,你知道有没有任何好的文章解释变量为什么会像这样吗? - Justin Bull
1
我注意到只检查parentNode就足够了(如果元素被删除,它将为null)。你认为这样做也同样好吗?jsBin - Justin Bull
3
更简洁版:var elementInDom = function(el){while(el=el.parentNode)if(el===document)return true;return false;}该函数用于判断一个元素是否在文档对象(DOM)中,如果是则返回 true,如果不是返回 false。函数使用了 while 循环和 parentNode 属性来追溯元素的祖先节点,直到找到文档对象或者到达 null(也就是没有父节点了)。如果找到了文档对象,则返回 true;否则返回 false。 - bennedich
2
@ButtleButkus 请认真阅读实际问题。你使用的那个解决方案是没有意义的,因为 getElementById() 返回对 DOM 元素或 null 的引用,因此,在 RHS 上使用 typeof 是错误的(如果它没有定义,LHS 条件将抛出 ReferenceError)。 - alex
3
有没有使用这个的理由,而不是下面发布的 document.body.contains(),它似乎在浏览器支持方面非常好? - crush
显示剩余18条评论

418

如果可以使用,使用getElementById()

另外,这是使用jQuery的简单方法:

if ($('#elementId').length > 0) {
  // Exists.
}

如果您无法使用第三方库,那么只需坚持使用原生JavaScript:

var element =  document.getElementById('elementId');
if (typeof(element) != 'undefined' && element != null)
{
  // Exists.
}

2
对于我正在处理的项目,我不能使用库。只能使用传统的原始代码。我知道那个jQuery方法,但它不适用于没有包装在jQuery容器中的元素。例如,$('#elementId')[0].length将不会产生相同的结果。 - Justin Bull
16
第二个代码示例中的if语句如此复杂,有没有什么好的理由?为什么不直接使用if (element) {}?当element未定义或为null时,表达式为false。如果element是DOM元素,则表达式为true。 - kayahr
4
太过复杂了。getElementById()被规定在找不到元素时返回null,因此只需检查是否返回truthy值即可。 - alex
6
我认为那只是常识。 - Kon
9
getElementById 永远不应返回 undefined。无论如何,element != null 的检查都可以发现这一点。 - user663031
显示剩余7条评论

261

使用 Node.contains DOM API ,您可以轻松检查页面(目前在DOM中)中任何元素的存在:

document.body.contains(YOUR_ELEMENT_HERE);

跨浏览器注意事项:Internet Explorer中的document对象没有contains()方法 - 为确保跨浏览器兼容性,请改用document.body.contains()


4
如果 MDN 的说法可信,这似乎是解决这个问题的终极答案......它得到了相当好的支持。 - crush
3
这是最佳答案。请注意,只需检查 document.contains() 就足够了。 - alex
@csuwldcat,至少在Chrome中使用document.contains(document.documentElement)对我有效。据我所知,document的原型链上确实有Nodedocument->HTMLDocument->Document->Node)。 - alex
4
确实,这是最好的答案:
  • 这是一个网络标准
  • 它得到了很好的支持(有点出人意料的是,在Firefox直到9版本才出现,我猜测这是因为它是在IE中发明的非标准函数,直到后来才被标准化)
  • 它一定是最快的,因为它只使用了一次本地API的调用。
- Jon z
10
语法是document.body.contains([选择器]),即document.body.contains(document.getElementById('div')) - eduardo a
显示剩余4条评论

101

我只是这样做:

if(document.getElementById("myElementId")){
    alert("Element exists");
} else {
    alert("Element does not exist");
}

对我来说它很有效,目前还没有出现任何问题...


2
这与原问题无关。OP想知道对DOM元素的引用是否是可见DOM的一部分。 - alex
1
这个回答很好,但只有在元素具有“id”时才适用。更好的解决方案是使用document.body.contains(element)来回答问题“如何检查可见DOM中是否存在元素?” ,即使没有id的元素也可以。 - Edward
@Edward 这与 contains() 完全不同。 - alex
我明白了。我只是在评论中建议其他答案更好,更适合这个问题。 - Edward
这不会检查可见的DOM。将检查所有的DOM。 - Broda Noel
对于TypeScript应该是:if(document.getElementById("myElementId").length>0) - Andrii

40

我更喜欢使用node.isConnected属性(访问MDN)。

注意:如果元素附加到ShadowRoot中,这将返回true,这可能不是每个人都想要的行为。

示例:

const element = document.createElement('div');
console.log(element.isConnected); // Returns false
document.body.append(element);
console.log(element.isConnected); // Returns true
element.remove();
console.log(element.isConnected); // Returns false

2
最简单的解决方案... 比 document.body.contains 更容易。 - lakam99

33

最简单的方法:

const cond = document.getElementById('elem') || false
if (cond) {
    //does
} else {
    //does not
}

如果需要严格位于可见 DOM 中,即不在整个页面上,请使用类似 view-js 的东西(我的库,所以随便抨击它)。



<script src='https://view-js.glitch.me/view-main.js'></script>
<script>
elem = $sel('#myelem');
if (isVis(elem)) { //yes } else { //no }
</script>

function test() {
  pt = document.querySelector('#result')
  iv = document.querySelector('#f')
  
  cond = document.querySelector('#'+iv.value) || false
  
if (cond) {
    pt.innerText = 'Found!'
} else {
    pt.innerText = 'Not found!'
    }
}
  
Enter an id to see if it exists: <input id='f'></input>
<button onclick='test()'>Test!</button>

<br />
<p id='result'>I am a p tag. I will change depending on the result.</p>
<br />
<div id='demo'>I am a div. My id is demo.</div>


13

您可以检查parentNode属性是否为null。

也就是说,

if(!myElement.parentNode)
{
    // The node is NOT in the DOM
}
else
{
    // The element is in the DOM
}

1
我知道这是一个老问题,但这个答案正是我一直在寻找的优雅简单的解决方案。 - poby
15
虽然看起来很优雅,但它并没有真正做到所要求的。它仅检查元素是否有父元素。这并不意味着该元素在可见的 DOM 中,因为它的父元素可能未连接到它。 - kayahr
一个人必须遍历所有的父级来找出最后一个是否是文档。另一个问题是它仍然可能在可见范围之外,或者被覆盖或由于许多其他原因而不可见。 - Arek Bal
一个元素之所以可能拥有parentNode,仅仅是因为它已经被附加到了文档片段中。 - user663031
@kayahr 在什么情况下,一个元素可能在可见的DOM中,但其父节点未连接到它? - Eric
1
@Eric 如果一个元素没有父节点,那么它就不能连接到文档,这是正确的。但是另一方面,当元素有父节点时,这并不意味着该元素自动连接到文档,因为父节点可能被断开连接,这意味着所有子元素也会被断开连接。因此,这个检查是错误的,不能达到其所说的目的。 - kayahr

13

来自Mozilla 开发者网络

该函数用于检查一个元素是否在页面的主体中。由于 contains() 方法是包容性的,而确定主体是否包含自身并不是 isInPage 的意图,因此该情况明确返回 false。

function isInPage(node) {
  return (node === document.body) ? false : document.body.contains(node);
}

node 是我们想要在 <body> 中检查的节点。


+1,这对于(任意)文本节点(或注释节点)也适用吗? - Nikos M.
@NikosM。它应该在任何HTML标签中都可以工作,但我没有测试过。 - Ekramul Hoque
5
“false” 不应该改成“true” 吗? - Marc-André Lafortune
1
如果 nodedocument.body,那么这个方法肯定应该返回 true 吧?也就是说,return (node === document.body) || document.body.contains(node); - daiscog

8

最简单的解决方案是检查baseURI属性,该属性仅在元素插入DOM时设置,并在元素被移除时恢复为空字符串。

var div = document.querySelector('div');

// "div" is in the DOM, so should print a string
console.log(div.baseURI);

// Remove "div" from the DOM
document.body.removeChild(div);

// Should print an empty string
console.log(div.baseURI);
<div></div>


5
我以前甚至不知道DOM节点上有baseURI属性。我喜欢这种方法的原因是它使用了元素本身的一个属性,这意味着即使元素在不同的文档中(比如iframe),它也应该能够工作。但我不喜欢的是它似乎只在WebKit之外无法工作。 - DoctorDestructo
1
如果元素不在文档中,则需要小心,否则会抛出以下错误:Cannot read property 'baseURI' of null。例如:console.log(document.querySelector('aside').baseURI) - Ian Davis
该方法不能使用,因为它现在总是打印相同的字符串。 - Kagami Sascha Rosylight
这个方法需要一个 try catch - AGamePlayer
无法确认。只有 document.body.contains(node)node.isConnected 起作用。 - undefined

6
这段代码对我有效,没有出现任何问题。

    if(document.getElementById("mySPAN")) {
        // If the element exists, execute this code
        alert("Element exists");
    }
    else {
        // If the element does not exist execute this code
        alert("Element does not exists");
    }


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