JavaScript isDOM — How do you check if a JavaScrip

2018-12-31 20:11发布

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?

30条回答
泛滥B
2楼-- · 2018-12-31 20:23

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

const isElement = el => el instanceof Element
查看更多
梦该遗忘
3楼-- · 2018-12-31 20:23

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;
}
查看更多
旧时光的记忆
4楼-- · 2018-12-31 20:24

In Firefox, you can use the instanceof Node. That Node is defined in DOM1.

But that is not that easy in IE.

  1. "instanceof ActiveXObject" only can tell that it is a native object.
  2. "typeof document.body.appendChild=='object'" tell that it may be DOM object, but also can be something else have same function.

You can only ensure it is DOM element by using DOM function and catch if any exception. However, it may have side effect (e.g. change object internal state/performance/memory leak)

查看更多
回忆,回不去的记忆
5楼-- · 2018-12-31 20:25

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

查看更多
低头抚发
6楼-- · 2018-12-31 20:28

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.

查看更多
谁念西风独自凉
7楼-- · 2018-12-31 20:29

This is what I figured out:

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

To improve performance I created a self-invoking function that tests the browser's capabilities only once and assigns the appropriate function accordingly.

The first test should work in most modern browsers and was already discussed here. It just tests if the element is an instance of HTMLElement. Very straightforward.

The second one is the most interesting one. This is its core-functionality:

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

It tests whether el is an instance of the construcor it pretends to be. To do that, we need access to an element's contructor. That's why we're testing this in the if-Statement. IE7 for example fails this, because (document.createElement("a")).constructor is undefined in IE7.

The problem with this approach is that document.createElement is really not the fastest function and could easily slow down your application if you're testing a lot of elements with it. To solve this, I decided to cache the constructors. The object ElementConstructors has nodeNames as keys with its corresponding constructors as values. If a constructor is already cached, it uses it from the cache, otherwise it creates the Element, caches its constructor for future access and then tests against it.

The third test is the unpleasant fallback. It tests whether el is an object, has a nodeType property set to 1 and a string as nodeName. This is not very reliable of course, yet the vast majority of users shouldn't even fall back so far.

This is the most reliable approach I came up with while still keeping performance as high as possible.

查看更多
登录 后发表回答