如何检查JavaScript对象是否为DOM对象?


247

我试图得到:

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;
  }
}

有没有好的替代品?


3
我是否想知道“ DOM对象”是否不仅应涵盖元素,还应涵盖所有节点(文本节点,属性等)?所有的答案以及您提出问题的方式都倾向于暗示这个问题与元素有关……
mike rodent

Answers:


299

这可能很有趣:

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"
);
}

11
值得注意的是,这不适用于属于其他窗口/框架的元素。推荐使用鸭子打字
Andy E

2
您可以使用以下方法来欺骗它:function Fake() {}; Fake.prototype=document.createElement("div"); alert(new Fake() instanceof HTMLElement);
内核詹姆斯

11
WTF事实:火狐5和早期恢复true[] instanceof HTMLElement
Rob W

6
顺便说一句,HTMLElement它始终是一个function,因此typeof将使您脱离轨道,并执行语句的第二部分。您可以尝试是否愿意instanceof Object,因为函数将是的实例Object,或者只是显式检查typeof === "function",因为NodeHTMLElement都是本机对象函数。
罗兰

2
当您调用时isElement(0),它返回0,而不是false ...为什么会这样,如何防止这种情况?
杰西卡

68

以下与IE8兼容的超简单代码可完美运行。

接受的答案没有检测到所有类型的HTML元素。例如,不支持SVG元素。相反,此答案与SVG一样适用于HTML。

在此处查看其运行情况:https : //jsfiddle.net/eLuhbu6r/

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

3
在IE7上未定义“元素”
丹丹,

49
我认为,任何仍在使用IE7的人都应该花5秒钟时间下载更好的浏览器,而不是让我们花几天或几周的时间来努力避免与时俱进。
mopsyd '18年

1
我确实同意@mopsyd,但答案的作者指出,它在IE7中有效,为了正确起见,可能需要对其进行更新。
拉霍斯·梅萨罗斯

1
更新为说IE9。我不确定IE8是否支持它。
君主瓦迪亚

1
@MonarchWadia是的,它在IE8中受支持。但是请注意,这不会对document元素返回true (在所有浏览器中)。如果需要,您应该尝试:x instanceof Element || x instanceof HTMLDocument
S.Serpooshan

11

上面和下面的所有解决方案(包括我的解决方案)都有可能出现错误的情况,尤其是在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

而应true分别返回“功能”和。测试方法的唯一方法是查看是否已定义。


“ typeof document.body.cloneNode”确实在我的IE中返回了“ object”
Dennis

这看起来像是一个不错的答案。不过请参阅下面的答案, stackoverflow.com/a
36894871/1204556

8

您可以尝试将其附加到真实的DOM节点...

function isDom(obj)
{
    var elm = document.createElement('div');
    try
    {
        elm.appendChild(obj);
    }
    catch (e)
    {
        return false;
    }

    return true;
}

11
这样行吗?仍然是解决方案。一个有创造力的人。
贾斯汀·迈尔

3
+1为您提供的创造力和确定性。但是,如果该节点已经恰好是DOM的一部分,则只需将其删除!因此...如果没有进行必要的将工作元素重新添加到DOM的工作,此答案是不完整的。
svidgen

4
我读了将近5年后,我认为这是最酷的之一。它只是需要完善。例如,您可以尝试将节点的克隆附加到分离的元素上。如果那不是DOM对象。肯定会出问题。不过,这仍然是一个昂贵的解决方案。
MaxArt 2013年

或者,而不是试图追加克隆的元素,只是想克隆它应该足够:obj.cloneNode(false)有没有副作用。
mauroc8

1
这确实是昂贵的并且不必要地复杂。请参阅下面的答案,stackoverflow.com/a
36894871/1204556


6

如何螺Dash的_.isElement

$ npm install lodash.iselement

并在代码中:

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

1
我喜欢这个解决方案。它很简单,并且可以在Edge和IE中使用,甚至适用于单独iframe中的元素,这与此处大多数投票率最高的解决方案不同。
伊莱亚斯·扎马里亚

这个答案很有帮助,尽管可能需要Webpack在浏览器中运行NPM模块。
Edwin Pratt

5

这来自可爱的JavaScript库MooTools

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

12
这段代码没有断言该对象是一个DOM元素。只是看起来有点像。可以给任何对象一个nodeName和nodeType属性,并满足该代码。
thomasrutter

此答案不能检测所有类型的HTML元素。例如,不支持SVG元素。请参阅下面的答案。
君主瓦迪亚

并非真正适用于所有元素,例如SVG。请参阅下面的答案,stackoverflow.com/a
36894871/1204556

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解决方案便宜。

唐·P


我已经在最新的Chrome和FF以及IE11中对此进行了测试,它可以在任何地方使用,也适用于通过document.createElement()DOM 创建但未插入DOM的文本节点和对象。
了不起

这看起来像是一个不错的答案,尽管我的确做了很多相同的事情,而且不太复杂。stackoverflow.com/a/36894871/1204556
君主瓦迪亚

4

旧线程,但这是ie8和ff3.5用户的更新可能性:

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

4

我建议一种简单的方法来测试变量是否是DOM元素

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

或如HTMLGuy所建议:

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

1
太冗长了。比较将已经返回一个布尔值:return typeof entity === 'object' && typeof entity.nodeType !== undefined;
HTMLGuy

1
很有意思!有时,这取决于您object和/或特权的类型,这可能非常方便!Tx,@ Roman
Pedro Ferreira

3
var IsPlainObject = function ( obj ) { return obj instanceof Object && ! ( obj instanceof Function || obj.toString( ) !== '[object Object]' || obj.constructor.name !== 'Object' ); },
    IsDOMObject = function ( obj ) { return obj instanceof EventTarget; },
    IsDOMElement = function ( obj ) { return obj instanceof Node; },
    IsListObject = function ( obj ) { return obj instanceof Array || obj instanceof NodeList; },

//实际上,我更可能不使用这些内联,但是有时最好使用这些用于设置代码的快捷方式


obj.constructor.name在IE中不起作用,因为在IE中,函数没有name属性。用obj.constructor!=对象替换。
mathheadinclouds

3

这可能会有所帮助:isDOM

//-----------------------------------
// Determines if the @obj parameter is a DOM element
function isDOM (obj) {
    // DOM, Level2
    if ("HTMLElement" in window) {
        return (obj && obj instanceof HTMLElement);
    }
    // Older browsers
    return !!(obj && typeof obj === "object" && obj.nodeType === 1 && obj.nodeName);
}

在上面的代码中,我们使用了双重否定接线员给作为参数传递的对象的布尔值,这样,我们保证在条件语句评估每一个表达式是布尔,采取优势短路计算,因此功能返回truefalse


任何虚假的事情都会使您的布尔值短路。undefined && window.spam("should bork")例如,从不评估伪spam函数。所以没有!!必要,我不相信。那,您能提供一个[非学术的]边缘案例吗?
鲁芬2015年

感谢您的声明。我使用* !! *双重否定将所有表达式转换为布尔值,而不是真实的或虚假的。
jherax

是的,但是我认为没有实际的理由,请参阅此处。当然,这里没有必要利用Short-Cut评估。即使您不购买“ !!从未需要”参数(并且如果您不购买,我很好奇为什么不这样做),也可以将该行编辑为return !!(obj && typeof obj === "object" && obj.nodeType === 1 && obj.nodeName);并使其运行相同。
鲁芬,2015年

那就是我所做的;)更干净,效果相同。谢谢。
jherax

2

您可以查看所讨论的对象或节点是否返回字符串类型。

typeof (array).innerHTML === "string" => false
typeof (object).innerHTML === "string" => false
typeof (number).innerHTML === "string" => false
typeof (text).innerHTML === "string" => false

//any DOM element will test as true
typeof (HTML object).innerHTML === "string" => true
typeof (document.createElement('anything')).innerHTML === "string" => true

3
typeof ({innerHTML: ""}).innerHTML === "string"
Qtax

热!这个回应应该是游戏的赢家。if(typeof obj.innerHTML!=='string')//不是dom元素。
user3751385 2014年

1
最初,我在较早的答案上对@Qtax和thomasrutter的批评做出了反应,但我开始购买它。尽管以前我还没有遇到过像鸭子一样发出嘎嘎叫声的狗,但我可以看到有人没有在检查某事是否是节点,正在运行notANode.innerHTML = "<b>Whoops</b>";,然后让该代码将其受污染的obj传递给代码。防御性代码===更好的代码,其他所有条件都相同,这最终不是防御性的。
鲁芬,2015年


2

我认为原型并不是一个很好的解决方案,但是也许这是最快的解决方案:定义此代码块;

Element.prototype.isDomElement = true;
HTMLElement.prototype.isDomElement = true;

而不是检查对象的isDomElement属性:

if(a.isDomElement){}

我希望这有帮助。


1
1)不建议更改您不拥有的对象。2)这不会检测不属于同一文档的元素。
fregante

2

根据mdn

Element是从中Document继承所有对象的最通用基类。它仅具有所有元素共有的方法和属性。

我们可以isElement通过原型实现。这是我的建议:

/**
 * @description detect if obj is an element
 * @param {*} obj
 * @returns {Boolean}
 * @example
 * see below
 */
function isElement(obj) {
  if (typeof obj !== 'object') {
    return false
  }
  let prototypeStr, prototype
  do {
    prototype = Object.getPrototypeOf(obj)
    // to work in iframe
    prototypeStr = Object.prototype.toString.call(prototype)
    // '[object Document]' is used to detect document
    if (
      prototypeStr === '[object Element]' ||
      prototypeStr === '[object Document]'
    ) {
      return true
    }
    obj = prototype
    // null is the terminal of object
  } while (prototype !== null)
  return false
}
console.log(isElement(document)) // true
console.log(isElement(document.documentElement)) // true
console.log(isElement(document.body)) // true
console.log(isElement(document.getElementsByTagName('svg')[0])) // true or false, decided by whether there is svg element
console.log(isElement(document.getElementsByTagName('svg'))) // false
console.log(isElement(document.createDocumentFragment())) // false


1

我认为您需要做的是彻底检查将始终位于dom元素中的某些属性,但是它们的组合不太可能位于另一个对象中,例如:

var isDom = function (inp) {
    return inp && inp.tagName && inp.nodeName && inp.ownerDocument && inp.removeAttribute;
};

1

在Firefox中,您可以使用instanceof Node。那Node是在DOM1中定义的。

但这在IE中并不是那么容易。

  1. “ instanceof ActiveXObject”只能说出它是一个本机对象。
  2. “ typeof document.body.appendChild =='object'”告诉它可能是DOM对象,但也可能是其他具有相同功能的东西。

您只能通过使用DOM函数确保它是DOM元素,并在有任何异常的情况下进行捕获。但是,它可能会有副作用(例如,更改对象的内部状态/性能/内存泄漏)


1

也许这是另一种选择?在Opera 11,FireFox 6,Internet Explorer 8,Safari 5和Google Chrome 16中进行了测试。

function isDOMNode(v) {
  if ( v===null ) return false;
  if ( typeof v!=='object' ) return false;
  if ( !('nodeName' in v) ) return false; 

  var nn = v.nodeName;
  try {
    // DOM node property nodeName is readonly.
    // Most browsers throws an error...
    v.nodeName = 'is readonly?';
  } catch (e) {
    // ... indicating v is a DOM node ...
    return true;
  }
  // ...but others silently ignore the attempt to set the nodeName.
  if ( v.nodeName===nn ) return true;
  // Property nodeName set (and reset) - v is not a DOM node.
  v.nodeName = nn;

  return false;
}

函数不会被例如this愚弄

isDOMNode( {'nodeName':'fake'} ); // returns false

2
很好的尝试,但是如果可以避免的话,异常处理的成本太高了。此外,ES5允许您定义对象的只读属性。
Andy E

1

这是我想出的:

var isHTMLElement = (function () {
    if ("HTMLElement" in window) {
        // Voilà. Quick and easy. And reliable.
        return function (el) {return el instanceof HTMLElement;};
    } else if ((document.createElement("a")).constructor) {
        // We can access an element's constructor. So, this is not IE7
        var ElementConstructors = {}, nodeName;
        return function (el) {
            return el && typeof el.nodeName === "string" &&
                 (el instanceof ((nodeName = el.nodeName.toLowerCase()) in ElementConstructors 
                    ? ElementConstructors[nodeName] 
                    : (ElementConstructors[nodeName] = (document.createElement(nodeName)).constructor)))
        }
    } else {
        // Not that reliable, but we don't seem to have another choice. Probably IE7
        return function (el) {
            return typeof el === "object" && el.nodeType === 1 && typeof el.nodeName === "string";
        }
    }
})();

为了提高性能,我创建了一个自调用功能,该功能仅测试浏览器的功能一次,并相应地分配适当的功能。

第一次测试应该可以在大多数现代浏览器中使用,并且已经在此处进行了讨论。它只是测试元素是否是的实例HTMLElement。非常简单。

第二个是最有趣的。这是其核心功能:

return el instanceof (document.createElement(el.nodeName)).constructor

它测试el是否是其假装的构造实例。为此,我们需要访问元素的构造器。这就是为什么我们在if语句中对此进行测试。IE7例如失败这一点,因为(document.createElement("a")).constructorundefined在IE7。

这种方法的问题在于,document.createElement它实际上不是最快的功能,并且如果您正在使用它测试许多元素,可能很容易降低应用程序的速度。为了解决这个问题,我决定缓存构造函数。该对象ElementConstructors具有nodeNames作为键,其对应的构造函数作为值。如果已经缓存了构造函数,则从缓存中使用它,否则它将创建Element,缓存其构造函数以供将来访问,然后对其进行测试。

第三个测试是令人不愉快的后备。它测试el是否为objectnodeType属性设置为1,字符串为nodeName。这当然不是很可靠,但是到目前为止,绝大多数用户都不应退缩。

这是我想出的最可靠的方法,同时仍保持尽可能高的性能。


1

测试是否objNode继承。

if (obj instanceof Node){
    // obj is a DOM Object
}

Node是HTMLElement和Text继承自其中的基本接口


1

区分原始js对象和HTMLElement

function isDOM (x){
     return /HTML/.test( {}.toString.call(x) );
 }

用:

isDOM( {a:1} ) // false
isDOM( document.body ) // true

// 要么

Object.defineProperty(Object.prototype, "is",
    {
        value: function (x) {
            return {}.toString.call(this).indexOf(x) >= 0;
        }
    });

用:

o={}; o.is("HTML") // false o=document.body; o.is("HTML") // true


0

这是使用jQuery的技巧

var obj = {};
var element = document.getElementById('myId'); // or simply $("#myId")

$(obj).html() == undefined // true
$(element).html() == undefined // false

所以把它放在一个函数中:

function isElement(obj){

   return (typeOf obj === 'object' && !($(obj).html() == undefined));

}

2
jQuery在内部进行操作elem.nodeType === 1,为什么不节省调用开销和jQuery依赖关系,并让isElement函数自己执行呢?
约瑟夫·伦诺克斯

是2016年,请说“不”。
Thomas McCabe

0

除了适用于ES5的浏览器之外,别无所求,为什么不:

function isDOM(e) {
  return (/HTML(?:.*)Element/).test(Object.prototype.toString.call(e).slice(8, -1));
}

无法在TextNode上运行,并且不确定Shadow DOM或DocumentFragments等,但在几乎所有HTML标记元素上运行。


0

这几乎适用于所有浏览器。(这里没有元素和节点的区别)

function dom_element_check(element){
    if (typeof element.nodeType !== 'undefined'){
        return true;
    }
    return false;
}

首先,您不需要返回true或false,只需返回if语句即可。其次,对于{nodeType:1},它将返回true
bluejayke,

0

绝对正确的方法,检查目标是实际的 html元素主代码:

    (function (scope) {
        if (!scope.window) {//May not run in window scope
            return;
        }
        var HTMLElement = window.HTMLElement || window.Element|| function() {};

        var tempDiv = document.createElement("div");
        var isChildOf = function(target, parent) {

            if (!target) {
                return false;
            }
            if (parent == null) {
                parent = document.body;
            }
            if (target === parent) {
                return true;
            }
            var newParent = target.parentNode || target.parentElement;
            if (!newParent) {
                return false;
            }
            return isChildOf(newParent, parent);
        }
        /**
         * The dom helper
         */
        var Dom = {
            /**
             * Detect if target element is child element of parent
             * @param {} target The target html node
             * @param {} parent The the parent to check
             * @returns {} 
             */
            IsChildOf: function (target, parent) {
                return isChildOf(target, parent);
            },
            /**
             * Detect target is html element
             * @param {} target The target to check
             * @returns {} True if target is html node
             */
            IsHtmlElement: function (target) {
                if (!X.Dom.IsHtmlNode(target)) {
                    return false;
                }
                return target.nodeType === 1;
            },
            /**
             * Detect target is html node
             * @param {} target The target to check
             * @returns {} True if target is html node
             */
            IsHtmlNode:function(target) {
                if (target instanceof HTMLElement) {
                    return true;
                }
                if (target != null) {
                    if (isChildOf(target, document.documentElement)) {
                        return true;
                    }
                    try {
                        tempDiv.appendChild(target.cloneNode(false));
                        if (tempDiv.childNodes.length > 0) {
                            tempDiv.innerHTML = "";
                            return true;
                        }
                    } catch (e) {

                    }
                }
                return false;
            }
        };
        X.Dom = Dom;
    })(this);

在IE 5中测试


0

每个DOMElement.constructor返回函数HTML ... Element()[Object HTML ... Element]这样...

function isDOM(getElem){
    if(getElem===null||typeof getElem==="undefined") return false;
    var c = getElem.constructor.toString();
    var html = c.search("HTML")!==-1;
    var element = c.search("Element")!==-1;
    return html&&element;
}

0

我有一个特殊的方法来做,答案中还没有提到。

我的解决方案基于四个测试。如果对象通过所有四个,则它是一个元素:

  1. 该对象不为空。

  2. 该对象具有一个称为“ appendChild”的方法。

  3. 方法“ appendChild”是从Node类继承的,它不仅是冒名顶替的方法(用户创建的具有相同名称的属性)。

  4. 该对象属于节点类型1(元素)。从Node类继承方法的对象始终是Node,但不一定是Elements。

问:如何检查给定的属性是否继承并且不仅仅是冒名顶替者?

答:一个简单的测试可以确定该属性是否具有“对象”或“功能”类型,这是确定方法是否确实从Node继承的一种简单测试。接下来,将属性转换为字符串,并检查结果是否包含文本“ [Native Code]”。如果结果看起来像这样:

function appendChild(){
[Native Code]
}

然后,该方法已从Node对象继承。参见https://davidwalsh.name/detect-native-function

最后,将所有测试结合在一起,解决方案是:

function ObjectIsElement(obj) {
    var IsElem = true;
    if (obj == null) {
        IsElem = false;
    } else if (typeof(obj.appendChild) != "object" && typeof(obj.appendChild) != "function") {
        //IE8 and below returns "object" when getting the type of a function, IE9+ returns "function"
        IsElem = false;
    } else if ((obj.appendChild + '').replace(/[\r\n\t\b\f\v\xC2\xA0\x00-\x1F\x7F-\x9F ]/ig, '').search(/\{\[NativeCode]}$/i) == -1) {
        IsElem = false;
    } else if (obj.nodeType != 1) {
        IsElem = false;
    }
    return IsElem;
}


0

确保实际HTMLEement的唯一方法是检查其是否继承自Node,而不仅仅是检查与HTML Element具有相同属性的对象,因为它无法在JavaScript中创建新的Node()。(除非本机的Node函数被覆盖,但是您不走运)。所以:

function isHTML(obj) {
    return obj instanceof Node;
}

console.log(
  isHTML(test),
  isHTML(ok),
  isHTML(p),
  isHTML(o),
  isHTML({
    constructor: {
      name: "HTML"
    }
  }),
  isHTML({
    __proto__: {
      __proto__: {
        __proto__: {
          __proto__: {
            constructor: {
              constructor: { 
                name: "Function"
                
              },
              name: "Node"
            }
          }
        }
      }
    }
  }),
)
<div id=test></div>
<blockquote id="ok"></blockquote>
<p id=p></p>
<br id=o>
<!--think of anything else you want--!>

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.