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

我想要:

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

有好的替代品吗?

246894 次浏览

这是来自可爱的JavaScript库MooTools:

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

我认为你必须做的是彻底检查一些属性,它们总是在dom元素中,但它们的组合不会最有可能的在另一个对象中,如下所示:

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

您可以尝试将它附加到一个真正的DOM节点…

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


return true;
}

你可能会感兴趣:

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,二级的一部分。

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

在Firefox中,你可以使用instanceof NodeNodeDOM1中定义。

但在IE中就没那么容易了。

  1. "instanceof ActiveXObject"只能说明它是一个本机对象。
  2. “typeof document.body。appendChild=='object'"说明它可以是DOM对象,也可以是其他具有相同功能的对象。

如果有任何异常,只能使用DOM函数和catch来确保它是DOM元素。然而,它可能有副作用(例如改变对象内部状态/性能/内存泄漏)

上面和下面的所有解决方案(我的解决方案包括)遭受不正确的可能性,特别是在IE —很有可能(重新)定义一些对象/方法/属性来模拟DOM节点,使测试无效。

所以我通常使用duck-typing风格的测试:我专门测试我使用的东西。例如,如果我想克隆一个节点,我这样测试它:

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。测试方法的唯一方法是查看是否定义了方法。

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

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

也许这是另一种选择?在Opera 11, FireFox 6, Internet Explorer 8, Safari 5和谷歌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;
}

函数不会被例如这个所欺骗

isDOMNode( {'nodeName':'fake'} ); // returns false
var isElement = function(e){
try{
// if e is an element attached to the DOM, we trace its lineage and use native functions to confirm its pedigree
var a = [e], t, s, l = 0, h = document.getElementsByTagName('HEAD')[0], ht = document.getElementsByTagName('HTML')[0];
while(l!=document.body&&l!=h&&l.parentNode) l = a[a.push(l.parentNode)-1];
t = a[a.length-1];
s = document.createElement('SCRIPT');   // safe to place anywhere and it won't show up
while(a.length>1){  // assume the top node is an element for now...
var p = a.pop(),n = a[a.length-1];
p.insertBefore(s,n);
}
if(s.parentNode)s.parentNode.removeChild(s);
if(t!=document.body&&t!=h&&t!=ht)
// the top node is not attached to the document, so we don't have to worry about it resetting any dynamic media
// test the top node
document.createElement('DIV').appendChild(t).parentNode.removeChild(t);
return e;
}
catch(e){}
return null;
}
我在Firefox, Safari, Chrome, Opera和IE9上测试了这个。我找不到破解的方法。
理论上,它通过在元素之前插入一个脚本标记来测试所提议元素的每个祖先,以及元素本身 如果它的第一个祖先可以追溯到一个已知的元素,例如<html><head><body>,并且在此过程中没有抛出错误,则我们有一个元素 如果第一个祖先没有附加到文档,则创建一个元素,并尝试将建议的元素放在其中(然后从新元素中删除它) 因此,它要么追溯到一个已知的元素,成功地附加到一个已知的元素,要么失败 如果它不是一个元素,则返回该元素,否则返回null

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

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

这是我想出来的:

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-Statement中测试这个。例如,IE7就失败了,因为在IE7中(document.createElement("a")).constructorundefined

这种方法的问题是document.createElement真的不是最快的函数,如果你用它测试很多元素,它很容易减慢你的应用程序。为了解决这个问题,我决定缓存构造函数。对象ElementConstructors以nodeNames为键,其对应的构造函数为值。如果一个构造函数已经被缓存,它将从缓存中使用它,否则它将创建Element,缓存它的构造函数以供将来访问,然后对其进行测试。

第三个考验是令人不快的退路。它测试el是否为object,是否有nodeType属性设置为1和字符串nodeName。当然,这不是很可靠,但绝大多数用户甚至不应该倒退到这么远。

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

检测元素是否属于HTML DOM的最简单的跨浏览器方法如下所示:

function inHTMLDom(myelement){
if(myelement.ownerDocument.documentElement.tagName.toLowerCase()=="html"){
return true;
}else{
return false;
}
}


inHTMLDom(<your element>); // <your element>:element you are interested in checking.

在IE6,IE7,IE8,IE9,IE10,FF,Chrome,Safari,Opera中测试。

这里有一个使用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));


}

使用根检测找到在这里,我们可以确定例如警报是否是对象根的成员,然后它很可能是一个窗口:

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

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是否继承自节点

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

Node是一个基本的接口, HTMLElement和Text继承自它。

不是为了强调这一点,而是为了兼容es5的浏览器,为什么不直接:

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

不会工作在文本节点和不确定的影子DOM或DocumentFragments等,但工作在几乎所有的HTML标签元素。

< a href = " http://jherax.github。io / # isdom-object rel =“nofollow”> 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

那么Lo-Dash的_.isElement呢?

$ npm install lodash.iselement

在代码中:

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

对于那些使用Angular的:

angular.isElement

https://docs.angularjs.org/api/ng/function/angular.isElement

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

function dom_element_check(element){
if (typeof element.nodeType !== 'undefined'){
return true;
}
return false;
}
我认为原型并不是一个很好的解决方案,但这可能是最快的方法: 定义这个代码块
Element.prototype.isDomElement = true;
HTMLElement.prototype.isDomElement = true;

然后检查对象的isDomElement属性:

if(a.isDomElement){}

我希望这能有所帮助。

区分一个原始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;
}
});

使用:

< p > <代码> o = {};o.is("HTML") // false o = document.body;o.s is("HTML") // true

下面这段兼容IE8的超级简单的代码可以完美地运行。

接受的答案不检测所有类型的HTML元素。例如,不支持SVG元素。相反,这个答案适用于HTML和SVG。

在这里可以看到它的作用:https://jsfiddle.net/eLuhbu6r/

function isElement(element) {
return element instanceof Element || element instanceof HTMLDocument;
}
一个绝对正确的方法,检查目标是真正的 html元素 主要代码:< / p >
    (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);

Test In IE 5

大多数答案使用某种鸭子类型,例如检查对象是否具有nodeType属性。但这还不够,因为非节点也可以具有类节点属性。

另一种常见的方法是instanceof,它会产生误报,例如Object.create(Node),尽管继承了节点属性,但它不是节点。

此外,上述两种方法都调用内部基本方法,这可能会有问题,例如,如果测试的值是一个代理。

相反,我建议借用一个节点方法并在我们的对象上调用它。浏览器可能会通过查看代理中不可自定义的内部槽来检查该值是否是一个节点,因此即使它们也无法干扰我们的检查。

function isNode(value) {
try {
Node.prototype.cloneNode.call(value, false);
return true;
} catch(err) {
return false;
}
}

如果您愿意,还可以使用属性getter。

function isNode(value) {
try {
Object.getOwnPropertyDescriptor(Node.prototype,'nodeType').get.call(value);
return true;
} catch(err) {
return false;
}
}

类似地,如果您想测试一个值是否是一个元素,您可以使用

function isElement(value) {
try {
Element.prototype.getAttribute.call(value, '');
return true;
} catch(err) {
return false;
}
}
function isHTMLElement(value) {
try {
HTMLElement.prototype.click.call(value);
return true;
} catch(err) {
return false;
}
}

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

我的解决方案基于四个测试。如果对象通过了这四个,那么它就是一个元素:

  1. 该对象不是空的。

  2. 对象有一个叫做“appendChild”的方法。

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

  4. 对象类型为Node Type 1 (Element)。从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;
}

每个DOMElement.constructor返回HTML…元素()函数(对象HTML元素……),因此…

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

不需要破解,你可以只问一个元素是否是DOM 元素的实例:

const isDOM = el => el instanceof Element
(element instanceof $ && element.get(0) instanceof Element) || element instanceof Element

这将检查它是否是jQuery或JavaScript DOM元素

测试变量是否是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

根据中数

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

确保你检查的是一个实际的HTMLEement,而不是一个与HTML Element具有相同属性的对象,唯一的方法是确定它是否继承自Node,因为它不可能在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--!>

如果你正在使用jQuery,试试这个

$('<div>').is('*') // true
$({tagName: 'a'}).is('*') // false
$({}).is('*') // false
$([]).is('*') // false
$(0).is('*') // false
$(NaN).is('*') // false

我使用这个函数:

function isHTMLDOMElement(obj) {
if (Object.prototype.toString.call(obj).slice(-8) === 'Element]') {
if (Object.prototype.toString.call(obj).slice(0, 12) === '[object HTML') {
return true;
}
return false;
}
return false;
}

https://jsfiddle.net/1qazxsw2/wz7e0fvj/9/

这是我的版本。它支持来自iframe的元素

/**
* @param {any} value
* @param {any} view Optional. If the value is from an iframe, provide the iframe content window here.
* @returns {boolean}
*/
function isHtmlElement(value, view) {
if (value instanceof HTMLElement) return true
if (view && value instanceof view.HTMLElement) return true


return !!(
value &&
typeof value === 'object' &&
value !== null &&
value.nodeType === 1 &&
typeof value.nodeName === 'string'
)
}