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

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

37 ответов

Единственный способ гарантировать, что вы проверяете реальный HTMLEement, а не просто объект с теми же свойствами, что и элемент HTML, - это определить, наследуется ли он от 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--!>

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

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

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

  2. У объекта есть метод с именем "appendChild".

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

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

В: Как я могу проверить, является ли данное свойство наследуемым, а не просто самозванцем?

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

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

Я использую эту функцию:

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

В большинстве ответов используется какая-то утка, например, проверяется, что объект имеет nodeType имущество. Но этого недостаточно, потому что неузлы также могут иметь свойства, подобные узлам.

Другой общий подход instanceof которые могут давать ложные срабатывания, например, с Object.create(Node), который не является узлом, несмотря на наследование свойств узла.

Кроме того, оба вышеуказанных подхода вызывают внутренние существенные методы, которые могут быть проблематичными, например, если проверяемое значение является прокси.

Вместо этого я рекомендую заимствовать метод узла и вызывать его для нашего объекта. Браузер проверит, что значение является узлом, вероятно, посмотрев на внутренние слоты, не настраиваемые в прокси, поэтому даже они не смогут помешать нашей проверке.

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

Вы также можете использовать собственность, если вы предпочитаете.

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;
  }
}
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>, и это не выдало ошибку по пути, у нас есть элемент.
Если первый предок не прикреплен к документу, мы создаем элемент и пытаемся поместить предложенный элемент внутри него (а затем удаляем его из нового элемента).
Таким образом, он либо прослеживается до известного элемента, либо успешно подключается к известному элементу, либо завершается неудачно.
Возвращает элемент или ноль, если это не элемент.

Если вы используете jQuery, попробуйте это

$('<div>').is('*') // true
$({tagName: 'a'}).is('*') // false
$({}).is('*') // false
$([]).is('*') // false
$(0).is('*') // false
$(NaN).is('*') // false
Другие вопросы по тегам