如何检查一个JavaScript对象是否是DOM对象?

322

我想要得到:

document.createElement('div')  //=> true
{tagName: 'foobar something'}  //=> false

在我的脚本中,我以前只使用这个,因为我从来不需要将tagName作为属性:

if (!object.tagName) throw ...;

所以对于第二个对象,我想到了以下快速解决方案——大部分情况下都有效。 ;)

问题是,它依赖于浏览器执行只读属性的规则,而并非所有浏览器都会这样做。

function isDOM(obj) {
  var tag = obj.tagName;
  try {
    obj.tagName = '';  // Read-only for DOM, should throw exception
    obj.tagName = tag; // Restore for normal objects
    return false;
  } catch (e) {
    return true;
  }
}

有没有一个好的替代品?


4
我是否过于迟钝,会想知道“DOM对象”是否不仅涵盖元素,还包括所有节点(文本节点、属性等)?所有答案和你所提出的问题都倾向于暗示这个问题特别涉及元素... - mike rodent
37个回答

353
这可能会引起兴趣:
function isElement(obj) {
  try {
    //Using W3 DOM2 (works for FF, Opera and Chrome)
    return obj instanceof HTMLElement;
  }
  catch(e){
    //Browsers not supporting W3 DOM2 don't have HTMLElement and
    //an exception is thrown and we end up here. Testing some
    //properties that all elements have (works on IE7)
    return (typeof obj==="object") &&
      (obj.nodeType===1) && (typeof obj.style === "object") &&
      (typeof obj.ownerDocument ==="object");
  }
}

这是DOM Level2的一部分。

更新2:这是我在自己的库中实现它的方式: (之前的代码在Chrome中无法正常工作,因为Node和HTMLElement是函数而不是预期的对象。此代码已在FF3、IE7、Chrome 1和Opera 9中测试过)。

//Returns true if it is a DOM node
function isNode(o){
  return (
    typeof Node === "object" ? o instanceof Node : 
    o && typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName==="string"
  );
}

//Returns true if it is a DOM element    
function isElement(o){
  return (
    typeof HTMLElement === "object" ? o instanceof HTMLElement : //DOM2
    o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName==="string"
);
}

18
值得注意的是,这不适用于属于其他窗口/框架的元素。鸭子类型是推荐的方法。 - Andy E
3
你可以用以下代码来欺骗它:function Fake() {}; Fake.prototype=document.createElement("div"); alert(new Fake() instanceof HTMLElement); - Kernel James
17
WTF事实:Firefox 5及更早版本在对[] instanceof HTMLElement进行判断时会返回true - Rob W
7
顺便说一下,“HTMLElement”始终是一个“function”,因此“typeof”会让你误入歧途并执行语句的第二部分。如果你希望尝试,可以使用“instanceof Object”,因为该函数将是“Object”的实例,或者明确检查是否为“typeof ===" function "”,因为“Node”和“HTMLElement”都是原生对象函数。 - Roland
2
当你调用isElement(0)时,它返回的是0,而不是false...为什么会这样,我该如何防止呢? - Jessica
显示剩余8条评论

110

接受的答案有点复杂,无法检测所有类型的HTML元素。例如,不支持SVG元素。相比之下,这个答案适用于HTML和SVG等。

在此处查看其实际效果:https://jsfiddle.net/eLuhbu6r/

function isElement(element) {
    return element instanceof Element || element instanceof HTMLDocument;  
}

锦上添花的是:以上代码兼容IE8。


3
"Element" 在 IE7 上未定义。 - Dan
106
我认为任何还在使用IE7的人都应该花五秒钟下载一个更好的浏览器,而不是让我们去花费数天或数周来解决他们拒绝跟上时代步伐的问题。 - mopsyd
2
请注意,它不能跨多个窗口运行。即 window.open().document.body instanceof Element 返回 false - nik10110
4
你正在检查新窗口的body实例是否属于旧窗口的Element。如果你按照下面的方式调整你的代码,它就能够工作: win = window.open(); win.document.body instanceof win.Element; // true - Monarch Wadia
7
我能理解在某种程度上有限制。然而,这些限制的目的是为了限制安全责任。现在,仅仅出于安全考虑而限制使用IE7就像试图在一扇帆布门上安装死锁一样没有意义。 - mopsyd
显示剩余7条评论

49

不需要使用任何技巧,您可以直接询问一个元素是否是DOM Element 的实例:

const isDOM = el => el instanceof Element

5
太棒了!有效! - Pedro Ferreira
几乎完全兼容(https://caniuse.com/mdn-javascript_operators_instanceof),这应该是被接受的答案。 - Ben Winding
1
@AiShiguang addEventListener("click", ({ target }) => console.log(target, target instanceof Element)); 在最新的 Firefox 和 Chrome 中始终报告元素(<input>, <div>, <body>, <html>)和true。没有任何理由不这样做。你看到的target值是什么?你使用的是哪个浏览器? - Sebastian Simon
1
啊哇~我错了。我的代码在一个iframe中,所以它不是window.Element。 - AiShiguang
1
我喜欢这个简单而有效的解决方案! - undefined
显示剩余4条评论

12

上下的所有解决方案(包括我的解决方案在内)都存在可能不正确的情况,特别是在IE浏览器上。很可能重新定义一些对象/方法/属性来模拟DOM节点从而使测试无效。

因此,通常我使用鸭子类型测试:只测试我使用的特定内容。例如,如果我想克隆一个节点,我会像这样进行测试:

if(typeof node == "object" && "nodeType" in node &&
   node.nodeType === 1 && node.cloneNode){
  // most probably this is a DOM node, we can clone it safely
  clonedNode = node.cloneNode(false);
}

基本上,这是一个小的健全性检查加上我计划使用的方法(或属性)的直接测试。

顺便提一下,上面的测试是在所有浏览器中测试DOM节点的好方法。但如果你想更加安全,始终检查方法和属性的存在并验证其类型。

编辑: IE使用ActiveX对象来表示节点,因此它们的属性不像真正的JavaScript对象那样运作,例如:

console.log(typeof node.cloneNode);              // object
console.log(node.cloneNode instanceof Function); // false

虽然它应该分别返回 "function" 和 true,但唯一测试方法的方法是查看它们是否被定义。


在我的IE中,"typeof document.body.cloneNode"返回的是"object"。 - Dennis C
这看起来是一个不错的答案。不过请看一下我的回答,链接为https://dev59.com/rXRC5IYBdhLWcg3wMd5S#36894871。 - Monarch Wadia

10

测试一个变量是否是DOM元素的简单方法(冗长但更传统的语法 :-))

function isDomEntity(entity) {
  if(typeof entity  === 'object' && entity.nodeType !== undefined){
     return true;
  }
  else{
     return false;
  }
}

或者像HTMLGuy建议的那样(简短而清晰的语法):


const isDomEntity = entity =>
  typeof entity === 'object' && entity.nodeType !== undefined

1
代码过于冗长。比较操作已经返回一个布尔值: return typeof entity === 'object' && typeof entity.nodeType !== undefined; - HTMLGuy
1
非常有趣!有时,根据您的“对象”和/或属性上的类型,这可能非常方便!谢谢,@Roman - Pedro Ferreira
具有讽刺意味的是,因为null是一个“对象”,你会看到:TypeError: null不是一个对象(在评估'entity.nodeType'时) - Dem Pilafian

9
您可以尝试将其附加到实际的DOM节点...
function isDom(obj)
{
    var elm = document.createElement('div');
    try
    {
        elm.appendChild(obj);
    }
    catch (e)
    {
        return false;
    }

    return true;
}

11
这个可以行吗?这仍然是一个解决方案。而且还是一个有创意的解决方案。 - Justin Meyer
5
赞赞赞这个创意和确定性。但是,如果该节点已经是DOM的一部分,您刚刚将其删除了!因此...如果需要,重新将元素添加到DOM中才算完整回答。 - svidgen
4
我在将近五年后重新阅读这篇文章,我认为它是最酷的之一。只需要进行一些改进。例如,您可以尝试将节点的克隆附加到一个分离的元素上。如果那不是DOM对象,那么肯定会出现问题。虽然这仍然是一个相当昂贵的解决方案。 - MaxArt
或者,不必试图附加元素的克隆,只需尝试克隆它即可:obj.cloneNode(false)AND没有副作用。 - mauroc8
1
这真的很昂贵,也非常复杂。请查看我的答案,https://dev59.com/rXRC5IYBdhLWcg3wMd5S#36894871 - Monarch Wadia
2
这个方法很有创意,本来也没什么问题,但是它有一个可怕的副作用,会改变你正在测试的对象!也就是说,它可能会从当前父元素中移除有效的DOM元素!@Greg应该明确提到这一点,因为直接使用它是很危险的。 - logidelic

8

如何使用 Lo-Dash的_.isElement

$ npm install lodash.iselement

而在代码中:

var isElement = require("lodash.iselement");
isElement(document.body);

1
我喜欢这个解决方案。它很简单,而且在Edge和IE中都能工作,甚至对于分离的iframe中的元素也是如此,这与大多数在此处得到最高票数的解决方案不同。 - Elias Zamaria
这个答案很有帮助,尽管在浏览器中运行NPM模块需要Webpack。 - Edwin Pratt

6

这是来自可爱的JavaScript库MooTools

if (obj.nodeName){
    switch (obj.nodeType){
    case 1: return 'element';
    case 3: return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace';
    }
}

15
这段代码并没有断言该对象是DOM元素;只是说它看起来有点像。任何对象都可以被赋予nodeName和nodeType属性,从而满足这段代码的要求。 - thomasrutter
此答案无法检测所有类型的HTML元素。例如,不支持SVG元素。请参见下面的我的答案。 - Monarch Wadia
并不适用于所有元素,例如SVG。请参见我的下面的答案,https://dev59.com/rXRC5IYBdhLWcg3wMd5S#36894871 - Monarch Wadia

4

虽然这是一个老的帖子,但是对于IE8和FF3.5用户来说,这里有一个更新的可能性:

function isHTMLElement(o) {
    return (o.constructor.toString().search(/\object HTML.+Element/) > -1);
}

4

利用在这里找到的根检测方式,我们可以确定例如alert是否是对象根的成员,进而推断出它很可能是一个窗口:

function isInAnyDOM(o) { 
  return (o !== null) && !!(o.ownerDocument && (o.ownerDocument.defaultView || o.ownerDocument.parentWindow).alert); // true|false
}

要确定对象是否为当前窗口,甚至更简单:
function isInCurrentDOM(o) { 
  return (o !== null) && !!o.ownerDocument && (window === (o.ownerDocument.defaultView || o.ownerDocument.parentWindow)); // true|false
}

这似乎比开头线程中的try/catch解决方案更便宜。

Don P

我已经在最新的Chrome和FF中进行了测试,也在IE11中进行了测试,它可以在任何地方工作,包括通过document.createElement()创建的文本节点和对象,但是没有插入DOM。太神奇了(:谢谢 - Geradlus_RU
这看起来是一个不错的答案,尽管我的做了很多相同的事情,但更简单。https://dev59.com/rXRC5IYBdhLWcg3wMd5S#36894871 - Monarch Wadia

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