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 ответов
Это может быть интересно:
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"
);
}
Следующий супер-простой код, совместимый с IE8, работает отлично.
Принятый ответ не обнаруживает все типы элементов HTML. Например, элементы SVG не поддерживаются. В отличие от этого, этот ответ работает для HTML хорошо, как SVG.
Смотрите это в действии здесь: https://jsfiddle.net/eLuhbu6r/
function isElement(element) {
return element instanceof Element || element instanceof HTMLDocument;
}
Нет необходимости во взломах, вы можете просто спросить, является ли элемент экземпляром элемента:
const isElement = el => el instanceof Element
Все решения выше и ниже (включая мое решение) страдают от возможности быть некорректными, особенно в IE - вполне возможно (пере) определить некоторые объекты / методы / свойства для имитации узла DOM, делающего тест недействительным.
Поэтому обычно я использую тестирование в стиле утиной типографии: я тестирую специально для того, что использую. Например, если я хочу клонировать узел, я проверяю его следующим образом:
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
в то время как он должен вернуть "функцию" и true
соответственно. Единственный способ проверить методы - это определить, определены ли они.
Я предлагаю простой способ проверки, является ли переменная элементом DOM
function isDomEntity(entity) {
if( typeof entity === 'object' && entity.nodeType != undefined){
return true;
}
else{
return false;
}
}
Вы можете попробовать добавить его к реальному DOM-узлу...
function isDom(obj)
{
var elm = document.createElement('div');
try
{
elm.appendChild(obj);
}
catch (e)
{
return false;
}
return true;
}
Как насчет Ло-Даш_.isElement
?
$ npm install lodash.iselement
И в коде:
var isElement = require("lodash.iselement");
isElement(document.body);
Это из прекрасной библиотеки JavaScript MooTools:
if (obj.nodeName){
switch (obj.nodeType){
case 1: return 'element';
case 3: return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace';
}
}
Старый поток, но вот обновленная возможность для пользователей ie8 и ff3.5:
function isHTMLElement(o) {
return (o.constructor.toString().search(/\object HTML.+Element/) > -1);
}
Используя обнаружение корня, найденное здесь, мы можем определить, является ли, например, 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 в начальном потоке.
Дон П
Я думаю, что прототипирование не очень хорошее решение, но, возможно, оно самое быстрое: определите этот блок кода;
Element.prototype.isDomElement = true;
HTMLElement.prototype.isDomElement = true;
чем проверить ваши объекты isDomElement свойство:
if(a.isDomElement){}
Надеюсь, это поможет.
Это может быть полезно: 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);
}
В приведенном выше коде мы используем оператор двойного отрицания, чтобы получить логическое значение объекта, переданного в качестве аргумента. Таким образом, мы гарантируем, что каждое выражение, вычисленное в условном выражении, будет логическим, используя преимущества оценки короткого замыкания, таким образом, функцию возвращается true
или же false
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; },
// На самом деле, я с большей вероятностью использую эти строки, но иногда полезно иметь эти ярлыки для кода установки
Вы можете увидеть, возвращает ли рассматриваемый объект или узел строковый тип.
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
Для тех, кто использует Angular:
angular.isElement
https://docs.angularjs.org/api/ng/function/angular.isElement
Я думаю, что вам нужно сделать тщательную проверку некоторых свойств, которые всегда будут в элементе dom, но их комбинация, скорее всего, не будет в другом объекте, например так:
var isDom = function (inp) {
return inp && inp.tagName && inp.nodeName && inp.ownerDocument && inp.removeAttribute;
};
По словам МДН
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
Это будет работать практически для любого браузера. (Здесь нет различия между элементами и узлами)
function dom_element_check(element){
if (typeof element.nodeType !== 'undefined'){
return true;
}
return false;
}
Отличить необработанный объект 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;
}
});
использовать:
o={}; o.is("HTML") // false
o=document.body; o.is("HTML") // true
Вот моя версия. Имеет поддержку элементов из 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'
)
}
Возможно, это альтернатива? Протестировано в 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
Вот что я понял:
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, например, терпит неудачу это, потому что (document.createElement("a")).constructor
является undefined
в IE7.
Проблема с этим подходом заключается в том, что document.createElement
на самом деле это не самая быстрая функция, которая может легко замедлить работу вашего приложения, если вы тестируете с его помощью множество элементов. Чтобы решить эту проблему, я решил кешировать конструкторы. Предмет ElementConstructors
имеет nodeNames в качестве ключей с соответствующими конструкторами в качестве значений. Если конструктор уже кэширован, он использует его из кэша, в противном случае он создает элемент, кэширует его конструктор для будущего доступа, а затем проверяет его.
Третий тест - неприятный запасной вариант. Это проверяет, является ли эль object
, имеет nodeType
свойство установлено в 1
и строка как nodeName
, Конечно, это не очень надежно, но подавляющее большинство пользователей не должно даже отступать.
Это самый надежный подход, который я придумал, сохраняя при этом производительность как можно выше.
В Firefox вы можете использовать instanceof Node
, Тот Node
определяется в DOM1.
Но это не так просто в IE.
- Только "instanceof ActiveXObject" может сказать, что это нативный объект.
- "typeof document.body.appendChild == 'object'" говорит, что это может быть объект DOM, но также может быть что-то еще, имеющее ту же функцию.
Вы можете только гарантировать, что это элемент DOM, используя функцию DOM и перехватывать, если есть какие-либо исключения. Тем не менее, это может иметь побочный эффект (например, изменить внутреннее состояние объекта / производительность / утечка памяти)
Каждый 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;
}
(element instanceof $ && element.get(0) instanceof Element) || element instanceof Element
Это будет проверять, даже если это элемент DOM jQuery или JavaScript
Вот трюк с использованием 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));
}
Вот простое решение этой задачи, которое проверяет самые основные важные критерии HTML-элемента DOM:
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
Абсолютно правильный метод, check target - это настоящий первичный код 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);
Не для того, чтобы разбираться с этим или чем-то, кроме браузеров, соответствующих ES5, почему бы просто:
function isDOM(e) {
return (/HTML(?:.*)Element/).test(Object.prototype.toString.call(e).slice(8, -1));
}
Не будет работать с TextNodes и не уверен в Shadow DOM, DocumentFragments и т. Д., Но будет работать почти со всеми элементами HTML-тегов.