Jonathan Lonowski
Jonathan Lonowski

Reputation: 123563

How do you check if a JavaScript Object is a DOM Object?

I'm trying to get:

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

In my own scripts, I used to just use this since I never needed tagName as a property:

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

So for the second object, I came up with the following as a quick solution -- which mostly works. ;)

The problem is, it depends on browsers enforcing read-only properties, which not all do.

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

Is there a good substitute?

Upvotes: 333

Views: 286750

Answers (30)

PHP Guru
PHP Guru

Reputation: 1564

You can test for a DOM node with document.cloneNode.call(obj) which throws only when passed a value that isn't a DOM node. So it's just a matter of using try/catch. It even works with nodes from a different frame or window.

In Internet Explorer 8 and below one must use a different approach. Thankfully it is just a matter of trying to write to obj.nodeType as that throws only if it's a DOM node. So again it's just a matter of using try/catch.

So here is the complete function:

// Cross browser solution that works with high accuracy
function isDOMNode(obj) {
    if (obj!=null && typeof obj.nodeType=="number") {//nodeType is always a number
        var testNodeObj = document.cloneNode
        ,   success = false;
        if (typeof testNodeObj=="function") {// Test for non-IE8- browsers
            try {
                testNodeObj.call(obj);// Throws if obj is not a DOM node
                success = true;
            }
            catch(e) {
            }
        }
        else if ({}.hasOwnProperty.call(obj, 'nodeType')) {//Possible IE8- node
            try {
                obj.nodeType = obj.nodeType// throws in IE8- on DOM nodes
            }
            catch(e) {
                success = true;
            }
        }
    }
    return !!success
}

Upvotes: 0

Mahmoud Magdy
Mahmoud Magdy

Reputation: 941

for specific element type like my case i think this solution enough to detect if it valid audio element.

const elm = document.querySelector("#demo");
if (elm.nodeName && elm.nodeName.toLowerCase() === 'audio'){
   alert(elm.nodeName);
}

Said By google element.nodeName is a DOM Level 1 (1998) feature. It is fully supported in all browsers: Chrome, Edge, Firefox, Safari, Opera, IE. Yes, Yes ... so by logic if it has nodeName and it equal to audio it sure element and should work on all browser, if you need div or other element change the string note toLowerCase because elm.nodeName returns 'AUDIO'

Upvotes: 0

Christiyan
Christiyan

Reputation: 534

Here's a simple solution to this task that checks the most basic important criterias of a DOM HTML element:

const isHTMLEl = o => typeof o === 'object' && !!o.tagName && o instanceof HTMLElement && !!o.nodeType;

const obj = document.createElement('iframe');
console.log( isHTMLEl(obj) ); // returns true
console.log( isHTMLEl([]) ); // return false
console.log( isHTMLEl('<div />') ); // returns false

Upvotes: 1

Monarch Wadia
Monarch Wadia

Reputation: 4996

The accepted answer is a bit complicated, and does not detect all types of HTML elements. For example, SVG elements are not supported. In contrast, this answer works for HTML as well as SVG, etc.

See it in action here: https://jsfiddle.net/eLuhbu6r/

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

Cherry on top: the above code is IE8 compatible.

Upvotes: 125

Roman
Roman

Reputation: 21873

A simple way to test if a variable is a DOM element (verbose, but more traditional syntax :-)

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

Or as HTMLGuy suggested (short and clean syntax):

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

Upvotes: 10

Rannie Aguilar Peralta
Rannie Aguilar Peralta

Reputation: 1752

Here's my version. It has support for elements from 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'
  )
}

Upvotes: 0

anonymous
anonymous

Reputation: 1

I use this function:

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/

Upvotes: -1

William Leung
William Leung

Reputation: 1653

if you are using jQuery, try this

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

Upvotes: -1

The only way to guarentee you're checking an actual HTMLEement, and not just an object with the same properties as an HTML Element, is to determine if it inherits from Node, since its impossible to make a new Node() in JavaScript. (unless the native Node function is overwritten, but then you're out of luck). So:

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--!>

Upvotes: 0

Damjan Pavlica
Damjan Pavlica

Reputation: 34123

No need for hacks, you can just ask if an element is an instance of the DOM Element:

const isDOM = el => el instanceof Element

Upvotes: 53

xianshenglu
xianshenglu

Reputation: 5369

According to mdn

Element is the most general base class from which all objects in a Document inherit. It only has methods and properties common to all kinds of elements.

We can implement isElement by prototype. Here is my advice:

/**
 * @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

Upvotes: 2

some
some

Reputation: 49632

This might be of interest:

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

It's part of the DOM, Level2.

Update 2: This is how I implemented it in my own library: (the previous code didn't work in Chrome, because Node and HTMLElement are functions instead of the expected object. This code is tested in FF3, IE7, Chrome 1 and 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"
);
}

Upvotes: 360

Arjun Kakkar
Arjun Kakkar

Reputation: 1

(element instanceof $ && element.get(0) instanceof Element) || element instanceof Element

This will check for even if it is a jQuery or JavaScript DOM element

Upvotes: 0

finpingvin
finpingvin

Reputation: 692

This is from the lovely JavaScript library MooTools:

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

Upvotes: 6

Cypher
Cypher

Reputation: 41

old thread, but here's an updated possibility for ie8 and ff3.5 users:

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

Upvotes: 4

user3163495
user3163495

Reputation: 3672

I have a special way to do this that has not yet been mentioned in the answers.

My solution is based on four tests. If the object passes all four, then it is an element:

  1. The object is not null.

  2. The object has a method called "appendChild".

  3. The method "appendChild" was inherited from the Node class, and isn't just an imposter method (a user-created property with an identical name).

  4. The object is of Node Type 1 (Element). Objects that inherit methods from the Node class are always Nodes, but not necessarily Elements.

Q: How do I check if a given property is inherited and isn't just an imposter?

A: A simple test to see if a method was truly inherited from Node is to first verify that the property has a type of "object" or "function". Next, convert the property to a string and check if the result contains the text "[Native Code]". If the result looks something like this:

function appendChild(){
[Native Code]
}

Then the method has been inherited from the Node object. See https://davidwalsh.name/detect-native-function

And finally, bringing all the tests together, the solution is:

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

Upvotes: 0

Paweł
Paweł

Reputation: 4536

Each DOMElement.constructor returns function HTML...Element() or [Object HTML...Element] so...

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

Upvotes: 0

Oriol
Oriol

Reputation: 288660

Most answers use some kind of duck typing, checking for example that the object has a nodeType property. But that's not enough, because non-nodes can also have node-like properties.

The other common approach is instanceof, which can produce false positives e.g. with Object.create(Node), which is not a node despite inheriting node properties.

Moreover, both approaches above call internal essential methods, which can be problematic e.g. if the tested value is a proxy.

Instead, what I recommend is borrowing a node method and calling it on our object. The browser will check that the value is a node probably by looking at internal slots not customizable in proxies, so even they won't be able to interfere with our check.

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

You can also use property getters, if you prefer.

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

Similarly, if you want to test if a value is an element, you can use

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

Upvotes: 0

dexiang
dexiang

Reputation: 1413

A absolute right method, check target is a real html element primary code:

    (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

Upvotes: 0

bortunac
bortunac

Reputation: 4828

differentiate a raw js object from a HTMLElement

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

use:

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

// OR

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

use:

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

Upvotes: 1

jherax
jherax

Reputation: 5267

This could be helpful: 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);
}

In the code above, we use the double negation operator to get the boolean value of the object passed as argument, this way we ensure that each expression evaluated in the conditional statement be boolean, taking advantage of the Short-Circuit Evaluation, thus the function returns true or false

Upvotes: 3

Doğuş Atasoy
Doğuş Atasoy

Reputation: 41

I think prototyping is not a very good solution but maybe this is the fastest one: Define this code block;

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

than check your objects isDomElement property:

if(a.isDomElement){}

I hope this helps.

Upvotes: 3

Zv_oDD
Zv_oDD

Reputation: 1878

This will work for almost any browser. (No distinction between elements and nodes here)

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

Upvotes: 1

Alvis
Alvis

Reputation: 3773

For the ones using Angular:

angular.isElement

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

Upvotes: 1

Shahar &#39;Dawn&#39; Or
Shahar &#39;Dawn&#39; Or

Reputation: 2961

How about Lo-Dash's _.isElement?

$ npm install lodash.iselement

And in the code:

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

Upvotes: 8

Travis Kaufman
Travis Kaufman

Reputation: 2937

Not to hammer on this or anything but for ES5-compliant browsers why not just:

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

Won't work on TextNodes and not sure about Shadow DOM or DocumentFragments etc. but will work on almost all HTML tag elements.

Upvotes: 0

soslan
soslan

Reputation: 78

Test if obj inherits from Node.

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

Node is a basic Interface from which HTMLElement and Text inherit.

Upvotes: 1

Matus
Matus

Reputation: 437

here's a trick using jQuery

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

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

so putting it in a function:

function isElement(obj){

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

}

Upvotes: 0

Erich Horn
Erich Horn

Reputation: 81

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

// In fact I am more likely t use these inline, but sometimes it is good to have these shortcuts for setup code

Upvotes: 3

Don P
Don P

Reputation: 171

The using the root detection found here, we can determine whether e.g. alert is a member of the object's root, which is then likely to be a window:

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

To determine whether the object is the current window is even simpler:

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

This seems to be less expensive than the try/catch solution in the opening thread.

Don P

Upvotes: 4

Related Questions