JavaScript isDOM - Как вы проверяете, является ли объект 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;
  }
}

Есть ли хорошая замена?

Ответ 1

Это может представлять интерес:

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

Ответ 2

Следующий супер-простой код, совместимый с IE8, работает отлично.

Принятый ответ не обнаруживает все типы элементов HTML. Например, элементы SVG не поддерживаются. В отличие от этого, этот ответ работает для HTML хорошо, как SVG.

Смотрите это в действии здесь: https://jsfiddle.net/eLuhbu6r/

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

Ответ 3

Все решения выше и ниже (включая мое решение) страдают от возможности быть неправильными, особенно на IE — вполне возможно (re) определить некоторые объекты/методы/свойства, чтобы имитировать DOM node, показывая, что тест недействителен.

Поэтому обычно я использую тестирование типа "утка": я тестирую именно то, что я использую. Например, если я хочу клонировать node, я тестирую его следующим образом:

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 во всех браузерах. Но если вы хотите быть в безопасности, всегда проверяйте наличие методов и свойств и проверяйте их типы.

EDIT: IE использует объекты ActiveX для представления узлов, поэтому их свойства не ведут себя как истинный объект JavaScript, например:

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

в то время как он должен возвращать "функцию" и true соответственно. Единственный способ проверить методы - проверить, определены ли они.

Ответ 4

Вы можете попробовать добавить его в реальный DOM node...

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

    return true;
}

Ответ 5

Как насчет Lo-Dash _.isElement?

$ npm install lodash.iselement

И в коде:

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

Ответ 6

Это из прекрасной библиотеки JavaScript MooTools:

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

Ответ 7

старый поток, но здесь обновлена ​​возможность для пользователей ie8 и ff3.5:

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

Ответ 8

Используя обнаружение корня найдено здесь, мы можем определить, 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 в раскрывающемся потоке.

Дон Р

Ответ 9

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

//На самом деле, я скорее всего использую эти встроенные, но иногда полезно иметь эти ярлыки для кода установки

Ответ 10

Это может быть полезно: 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);
}

В приведенном выше коде мы используем оператор double negation для получения логического значения объекта, переданного как аргумент, таким образом мы гарантируем, что каждое выражение, оцененное в условном выражении, будет логическим, используя Short-Circuit Evaluation, поэтому функция возвращает true или false

Ответ 11

Вы можете увидеть, возвращает ли объект или node строковый тип.

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

Ответ 13

Нет необходимости в хаках, вы можете просто спросить, является ли элемент экземпляром элемента:

const isElement = el => el instanceof Element

Ответ 14

Я предлагаю простой способ проверки, является ли переменная элементом DOM

    function isDomEntity(entity) {

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

Ответ 15

В Firefox вы можете использовать instanceof Node. Это значение Node определено в DOM1.

Но это не так просто в IE.

  • "instanceof ActiveXObject" может только сказать, что это родной объект.
  • "typeof document.body.appendChild == 'object'" говорит, что это может быть объект DOM, но также может быть что-то еще с той же функцией.

Вы можете убедиться, что это элемент DOM, используя функцию DOM и улавливая любое исключение. Однако он может иметь побочный эффект (например, изменить внутреннее состояние объекта/производительность/утечку памяти).

Ответ 16

Возможно, это альтернатива? Протестировано в 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;
}

Функция не будет обманута, например. это

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

Ответ 17

Вот что я понял:

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 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, к примеру, терпит неудачу, потому что (document.createElement("a")).constructor есть undefined в IE7.

Проблема с этим подходом заключается в том, что document.createElement на самом деле не самая быстрая функция и может легко замедлить ваше приложение, если вы тестируете с ним множество элементов. Чтобы решить эту проблему, я решил кэшировать конструкторы. Объект ElementConstructors имеет имена узлов как ключи с соответствующими конструкторами в качестве значений. Если конструктор уже кэширован, он использует его из кеша, в противном случае он создает элемент, кэширует его конструктор для будущего доступа, а затем проверяет его.

Третий тест - неприятный откат. Он проверяет, является ли el object, имеет свойство nodeType, установленное на 1, и строку как nodeName. Разумеется, это не очень надежно, но подавляющее большинство пользователей не должны даже отставать.

Это самый надежный подход, который я придумал, сохраняя при этом производительность как можно выше.

Ответ 18

Проверить, если obj наследуется от Node.

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

Node является базовым интерфейсом, из которого наследуются HTMLElement и Text.

Ответ 19

Я думаю, что прототипирование - не очень хорошее решение, но, возможно, это самый быстрый из них: Определите этот кодовый блок;

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

чем проверить свойства объекта isDomElement:

if(a.isDomElement){}

Надеюсь, это поможет.

Ответ 20

дифференцировать исходный js-объект из элемента HTMLElement

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

использовать:

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

//OR

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

Ответ 21

Я думаю, что вам нужно сделать тщательную проверку некоторых свойств, которые всегда будут в элементе dom, но их комбинация, скорее всего, не будет в другом объекте, например:

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

Ответ 22

здесь трюк с использованием 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));

}

Ответ 23

Не забивать это или что-то, кроме браузеров, совместимых с ES5, почему бы не просто:

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

Не будет работать с TextNodes и не будет уверен в теневых DOM или DocumentFragments и т.д., но будет работать практически со всеми элементами HTML-тегов.

Ответ 24

Это будет работать практически для любого браузера. (Здесь нет различий между элементами и узлами)

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

Ответ 25

Абсолютный правый метод, проверка цели - это реальный элемент 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

Ответ 26

У меня есть особый способ сделать это, о котором еще не упоминалось в ответах.

Мое решение основано на четырех тестах. Если объект проходит все четыре, то это элемент:

  • Объект не является нулевым.

  • У объекта есть метод под названием "appendChild".

  • Метод "appendChild" был унаследован от класса Node и не просто метод imposter (созданное пользователем свойство с идентичным именем).

  • Объект имеет значение Node Тип 1 (Элемент). Объекты, которые наследуют методы из класса Node, всегда являются узлами, но не обязательно элементами.

Q: Как проверить, унаследовано ли данное свойство и не просто ли это самозванец?

A: Простой тест, чтобы убедиться, что метод действительно унаследован от 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;
}

Ответ 27

Каждый 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;
}

Ответ 28

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

Это будет проверяться, даже если это элемент DOM jQuery или JavaScript

Ответ 29

По словам МДН

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

Ответ 30

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. Я не мог найти способ взломать его.
Теоретически, он проверяет каждого предка предлагаемого элемента, а также самого элемента, вставляя перед ним тег script.
Если его первый предок возвращается к известному элементу, например <html>, <head> или <body>, и он не выдал ошибку на этом пути, у нас есть элемент.
Если первый предок не прикреплен к документу, мы создаем элемент и пытаемся поместить в него предлагаемый элемент (а затем удалим его из нового элемента).
Таким образом, он либо возвращается к известному элементу, успешно присоединяется к известному элементу, либо терпит неудачу.
Он возвращает элемент или null, если он не является элементом.