JavaScript isDOM – Come si controlla se un object JavaScript è un object DOM?

Sto cercando di ottenere:

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

Nei miei script, tagName solo questo poiché non avevo mai avuto bisogno di tagName come proprietà:

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

Quindi, per il secondo object, ho trovato il seguente come soluzione rapida, che funziona principalmente. 😉

Il problema è che dipende dai browser che impongono le proprietà di sola lettura, cosa che non tutti fanno.

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

C’è un buon sostituto?

Questo potrebbe essere di interesse:

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

Fa parte del DOM, Level2 .

Aggiornamento 2 : Ecco come l’ho implementato nella mia libreria: (il codice precedente non funzionava in Chrome, poiché Node e HTMLElement sono funzioni anziché l’object previsto. Questo codice è testato in FF3, IE7, Chrome 1 e 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" ); } 

Il seguente codice super-semplice compatibile con IE9 funziona perfettamente.

La risposta accettata non rileva tutti i tipi di elementi HTML. Ad esempio, gli elementi SVG non sono supportati. Al contrario, questa risposta funziona bene per HTML come SVG.

Guardalo in azione qui: https://jsfiddle.net/eLuhbu6r/

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

Tutte le soluzioni sopra e sotto (inclusa la mia soluzione) risentono della possibilità di essere errate, specialmente su IE – è abbastanza ansible (ri) definire alcuni oggetti / metodi / proprietà per imitare un nodo DOM che rende il test non valido.

Quindi di solito uso il test in stile dattilografia: io collaudo in particolare per le cose che uso. Ad esempio, se voglio clonare un nodo, lo collaudo in questo modo:

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

Fondamentalmente è un piccolo controllo di sanità mentale + il test diretto per un metodo (o una proprietà) che sto pensando di usare.

Per inciso, il test di cui sopra è un buon test per i nodes DOM su tutti i browser. Ma se vuoi essere al sicuro controlla sempre la presenza di metodi e proprietà e verifica i loro tipi.

EDIT: IE utilizza oggetti ActiveX per rappresentare i nodes, quindi le loro proprietà non si comportano come un vero object JavaScript, ad esempio:

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

mentre dovrebbe restituire “function” e true rispettivamente. L’unico modo per testare i metodi è vedere se sono definiti.

Potresti provare ad aggiungerlo ad un vero nodo DOM …

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

Che ne dici di _.isElement di Lo-Dash ?

 $ npm install lodash.iselement 

E nel codice:

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

Questo è dalla bella libreria JavaScript MooTools :

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

Utilizzando il rilevamento della radice trovato qui , possiamo determinare se ad es. L’ avviso è un membro della radice dell’object, che è probabile che sia una finestra:

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

Per determinare se l’object è la finestra corrente è ancora più semplice:

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

Questo sembra essere meno costoso della soluzione try / catch nel thread di apertura.

Don P

thread vecchio, ma ecco una possibilità aggiornata per gli utenti ie8 e ff3.5 :

 function isHTMLElement(o) { return (o.constructor.toString().search(/\object HTML.+Element/) > -1); } 
 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 effetti sono più propenso a usare questi inline, ma a volte è bene avere queste scorciatoie per il codice di installazione

Questo potrebbe essere utile: 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); } 

Nel codice sopra, usiamo l’operatore double negation per ottenere il valore booleano dell’object passato come argomento, in questo modo assicuriamo che ogni espressione valutata nell’istruzione condizionale sia booleana, sfruttando la valutazione di cortocircuito , quindi la funzione restituisce true o false

Puoi vedere se l’object o il nodo in questione restituisce un tipo di stringa.

 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 

Per quelli che usano Angolare:

 angular.isElement 

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

In Firefox, puoi usare l’ instanceof Node . Quel Node è definito in DOM1 .

Ma non è così facile in IE.

  1. “instanceof ActiveXObject” può solo dire che si tratta di un object nativo.
  2. “typeof document.body.appendChild == ‘object'” indica che può essere object DOM, ma può anche essere qualcos’altro con la stessa funzione.

Puoi solo assicurarti che sia un elemento DOM usando la funzione DOM e rilevi eventuali eccezioni. Tuttavia, potrebbe avere effetti collaterali (ad es. Modifica dello stato interno dell’object / prestazioni / perdita di memoria)

Forse questa è un’alternativa? Testato su Opera 11, FireFox 6, Internet Explorer 8, Safari 5 e 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; } 

La funzione non verrà ingannata, ad es

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

Questo è quello che ho capito:

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

Per migliorare le prestazioni, ho creato una funzione autoinviante che verifica le funzionalità del browser una sola volta e assegna di conseguenza la funzione appropriata.

Il primo test dovrebbe funzionare nella maggior parte dei browser moderni ed è già stato discusso qui. HTMLElement solo se l’elemento è un’istanza di HTMLElement . Molto semplice.

Il secondo è il più interessante. Questa è la sua funzionalità principale:

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

Verifica se el è un’istanza del costrutto che finge di essere. Per fare ciò, abbiamo bisogno di accedere al contructor di un elemento. Ecco perché stiamo testando questo nella dichiarazione if. Ad esempio, IE7 non riesce, perché (document.createElement("a")).constructor undefined è undefined in IE7.

Il problema con questo approccio è che document.createElement è in realtà la funzione più veloce e potrebbe facilmente rallentare l’applicazione se stai testando molti elementi con esso. Per risolvere questo, ho deciso di mettere in cache i costruttori. L’object ElementConstructors ha nodeNames come chiavi con i relativi costruttori come valori. Se un costruttore è già stato memorizzato nella cache, lo utilizza dalla cache, altrimenti crea l’elemento, memorizza nella cache il suo costruttore per l’accesso futuro e poi lo mette alla prova.

Il terzo test è il ripiego sgradevole. nodeType se el è un object , ha una proprietà nodeType impostata su 1 e una stringa come nodeName . Ovviamente non è molto affidabile, tuttavia la stragrande maggioranza degli utenti non dovrebbe nemmeno tornare indietro.

Questo è l’approccio più affidabile che ho trovato mantenendo le prestazioni il più alte ansible.

Verifica se obj eredita dal nodo .

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

Il nodo è un’interfaccia di base da cui ereditano HTMLElement e Text.

Penso che la prototipazione non sia una soluzione molto buona ma forse questa è la più veloce: definire questo blocco di codice;

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

di controllare gli oggetti isDomElement property:

 if(a.isDomElement){} 

Spero che aiuti.

differenziare un object js raw da un HTMLElement

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

uso:

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

// O

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

uso:

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

Suggerisco un modo semplice per testare se una variabile è un elemento DOM

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

Non c’è bisogno di hack, puoi solo chiedere se un elemento è un’istanza dell’elemento :

 const isElement = el => el instanceof Element 

Penso che quello che devi fare è fare un controllo approfondito di alcune proprietà che saranno sempre in un elemento dom, ma la loro combinazione non sarà molto probabilmente in un altro object, in questo modo:

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

ecco un trucco usando jQuery

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

quindi metterlo in una funzione:

 function isElement(obj){ return (typeOf obj === 'object' && !($(obj).html() == undefined)); } 

Non martellare su questo o altro, ma per i browser compatibili con ES5 perché non solo:

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

Non funzionerà su TextNodes e non è sicuro su Shadow DOM o DocumentFragments ecc. Ma funzionerà su quasi tutti gli elementi di tag HTML.

Questo funzionerà per quasi tutti i browser. (Nessuna distinzione tra elementi e nodes qui)

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

Un metodo in assoluto, il target di controllo è un vero codice primario dell’elemento 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); 

Test in IE 5

Ogni DOMElement.constructor restituisce la funzione HTML … Elemento () o [Oggetto HTML … Elemento] così …

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

Ho un modo speciale per farlo che non è stato ancora menzionato nelle risposte.

La mia soluzione si basa su quattro test. Se l’object passa tutti e quattro, allora è un elemento:

  1. L’object non è nullo.

  2. L’object ha un metodo chiamato “appendChild”.

  3. Il metodo “appendChild” è stato ereditato dalla class Node e non è solo un metodo di impostore (una proprietà creata dall’utente con un nome identico).

  4. L’object è di tipo nodo 1 (elemento). Gli oggetti che ereditano i metodi dalla class Node sono sempre Nodi, ma non necessariamente Elementi.

D: Come posso verificare se una determinata proprietà è ereditata e non è solo un impostore?

A: Un semplice test per verificare se un metodo è stato realmente ereditato dal nodo è innanzitutto verificare che la proprietà abbia un tipo di “object” o “funzione”. Quindi, converti la proprietà in una stringa e controlla se il risultato contiene il testo “[Native Code]”. Se il risultato è simile a questo:

 function appendChild(){ [Native Code] } 

Quindi il metodo è stato ereditato dall’object Node. Vedi https://davidwalsh.name/detect-native-function

E infine, portando tutti i test insieme, la soluzione è:

 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; } 
 (element instanceof $ && element.get(0) instanceof Element) || element instanceof Element 

Questo verificherà anche se si tratta di un elemento DOM jQuery o JavaScript

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

Ho provato questo su Firefox, Safari, Chrome, Opera e IE9. Non sono riuscito a trovare un modo per hackerarlo.
In teoria, verifica tutti gli antenati dell’elemento proposto, oltre all’elemento stesso, inserendo un tag script prima di esso.
Se il suo primo antenato riconduce a un elemento conosciuto, come , o , e non ha generato un errore lungo il percorso, abbiamo un elemento.
Se il primo antenato non è collegato al documento, creiamo un elemento e proviamo a posizionare l’elemento proposto all’interno di esso (e quindi rimuoverlo dal nuovo elemento).
Quindi, fa riferimento a un elemento conosciuto, si collega con successo a un elemento conosciuto o fallisce.
Restituisce l’elemento o null se non è un elemento.

Il modo più semplice e cross-browser per rilevare se un elemento fa parte del DOM HTML è il seguente:

 function inHTMLDom(myelement){ if(myelement.ownerDocument.documentElement.tagName.toLowerCase()=="html"){ return true; }else{ return false; } } inHTMLDom(); // :element you are interested in checking. 

testato in IE6, IE7, IE8, IE9, IE10, FF, Chrome, Safari, Opera.