web-dev-qa-db-fra.com

JavaScript isDOM - Comment vérifier si un objet JavaScript est un objet DOM?

J'essaie d'obtenir:

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

Dans mes propres scripts, je l'utilisais simplement car je n'avais jamais besoin de tagName comme propriété:

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

Donc, pour le deuxième objet, j'ai proposé ce qui suit comme solution rapide - ce qui fonctionne généralement. ;)

Le problème est que cela dépend des navigateurs qui appliquent des propriétés en lecture seule, ce que tous ne font pas.

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

Y a-t-il un bon substitut?

201
Jonathan Lonowski

Cela pourrait être intéressant:

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

Cela fait partie du DOM, Level2 .

Update 2: Voici comment je l'ai implémenté dans ma propre bibliothèque: (Le code précédent ne fonctionnait pas dans Chrome, car Node et HTMLElement sont des fonctions et non l'objet attendu. Ce code est testé dans FF3. , IE7, Chrome 1 et 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"
);
}
258
some

Le code suivant, super simple, compatible IE8, fonctionne parfaitement.

Le réponse acceptée ne détecte pas tous les types d'éléments HTML. Par exemple, les éléments SVG ne sont pas supportés. En revanche, cette réponse fonctionne bien pour HTML comme SVG.

Voir en action ici: https://jsfiddle.net/eLuhbu6r/

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

Toutes les solutions ci-dessus et ci-dessous (ma solution y compris) risquent d'être incorrectes, en particulier sur IE - il est tout à fait possible de (re) définir certains objets/méthodes/propriétés pour imiter un nœud DOM rendant le test invalide.

Donc, d'habitude, j'utilise le test de style de frappe de canard: je teste spécifiquement les éléments que j'utilise. Par exemple, si je veux cloner un nœud, je le teste comme suit:

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

Fondamentalement, il s’agit d’un petit test de contrôle et du test direct d’une méthode (ou d’une propriété) que je prévois d’utiliser.

Incidemment, le test ci-dessus est un bon test pour les nœuds DOM sur tous les navigateurs. Mais si vous voulez être du bon côté, vérifiez toujours la présence de méthodes et de propriétés, ainsi que leurs types.

EDIT: IE utilise des objets ActiveX pour représenter les nœuds. Par conséquent, leurs propriétés ne se comportent pas comme de vrais objets JavaScript, par exemple:

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

alors qu'il devrait renvoyer "fonction" et true respectivement. La seule façon de tester les méthodes est de voir si elles sont définies.

11
Eugene Lazutkin

Vous pouvez essayer de l'ajouter à un véritable nœud DOM ...

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

    return true;
}
7
Greg

Que diriez-vous de Le _.isElement de Lo-Dash?

$ npm install lodash.iselement

Et dans le code:

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

Ceci est extrait de la jolie bibliothèque JavaScript MooTools :

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

ancien fil, mais voici une possibilité mise à jour pour ie8 et ff3.5 users:

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

En utilisant la détection de racine trouvée ici , nous pouvons déterminer si par ex. alert est un membre de la racine de l'objet, qui sera alors probablement une fenêtre:

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

Pour déterminer si l'objet est la fenêtre en cours, c'est encore plus simple:

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

Cela semble être moins coûteux que la solution try/catch dans le thread d'ouverture.

Don P

4
Don P

Cela pourrait être 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);
}

Dans le code ci-dessus, nous utilisons l'opérateur double negation pour obtenir la valeur booléenne de l'objet passé en argument. Ainsi, nous nous assurons que chaque expression évaluée dans l'instruction conditionnelle soit booléenne, en tirant parti du . Évaluation de court-circuit, ainsi la fonction retourne true ou false

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

// En fait, je suis plus susceptible de les utiliser en ligne, mais il est parfois utile d'avoir ces raccourcis pour le code d'installation

3
Erich Horn

Je suggère un moyen simple de tester si une variable est un élément DOM

    function isDomEntity(entity) {

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

Pas besoin de hacks, vous pouvez simplement demander si un élément est une instance de Element :

const isElement = el => el instanceof Element
2
Damjan Pavlica

Pour ceux qui utilisent Angular:

angular.isElement

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

2
Alvis

Vous pouvez voir si l'objet ou le noeud en question renvoie un type de chaîne.

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
2
timewaster51

différencier un objet js brut d'un HTMLElement

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

utilisation: 

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

// OU

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

utilisation:

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

1
bortunac

Dans Firefox, vous pouvez utiliser le instanceof Node. Cette Node est définie dans DOM1 .

Mais ce n'est pas si facile dans IE.

  1. "instanceof ActiveXObject" peut uniquement indiquer qu'il s'agit d'un objet natif.
  2. "typeof document.body.appendChild == 'objet'" indique qu'il peut s'agir d'un objet DOM, mais que quelque chose d'autre a la même fonction.

Vous pouvez uniquement vous assurer qu'il s'agit d'un élément DOM en utilisant la fonction DOM et en captant une exception éventuelle. Cependant, cela peut avoir un effet secondaire (par exemple, changer l’état/la performance/la fuite de mémoire interne de l’objet).

1
Dennis C

C'est ce que j'ai compris:

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

Pour améliorer les performances, j'ai créé une fonction d'invocation automatique qui teste les fonctionnalités du navigateur une seule fois et attribue la fonction appropriée en conséquence. 

Le premier test devrait fonctionner dans la plupart des navigateurs modernes et a déjà été discuté ici. Il vérifie simplement si l'élément est une instance de HTMLElement. Très simple.

Le second est le plus intéressant. Voici sa fonctionnalité principale:

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

Il teste si el est une instance du constructeur qu'il prétend être. Pour ce faire, nous devons avoir accès au constructeur de l'élément. C'est pourquoi nous testons cela dans la déclaration if. IE7, par exemple, échoue, car (document.createElement("a")).constructor est undefined dans IE7.

Le problème avec cette approche est que document.createElement n’est vraiment pas la fonction la plus rapide et peut facilement ralentir votre application si vous testez beaucoup d’éléments avec. Pour résoudre ce problème, j'ai décidé de mettre en cache les constructeurs. L'objet ElementConstructors a nodeNames en tant que clés avec les constructeurs correspondants en tant que valeurs. Si un constructeur est déjà mis en cache, il l'utilise à partir du cache. Dans le cas contraire, il crée l'élément, met en cache son constructeur pour un accès ultérieur, puis le teste.

Le troisième test est le repli désagréable. Il teste si el est un object, a une propriété nodeType définie sur 1 et une chaîne en tant que nodeName. Bien sûr, ce n’est pas très fiable, mais la grande majorité des utilisateurs ne devrait même pas reculer jusqu’à présent.

C’est l’approche la plus fiable que j’ai proposée tout en maintenant des performances aussi élevées que possible.

1
Oliver Sartun

C'est peut-être une alternative? Testé sous Opera 11, FireFox 6, Internet Explorer 8, Safari 5 et 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 fonction ne sera pas dupe par exemple ce 

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

Teste si obj hérite de Node .

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

Node est une base Interface de laquelle HTMLElement et Text héritent.

1
soslan

Je pense que le prototypage n’est pas une très bonne solution mais c’est peut-être la plus rapide: Définissez ce bloc de code;

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

vérifiez que la propriété isDomElement de vos objets:

if(a.isDomElement){}

J'espère que ça aide.

1
Doğuş Atasoy

Cela fonctionnera pour presque tous les navigateurs. (Pas de distinction entre les éléments et les nœuds ici)

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

Je pense que ce que vous devez faire est de vérifier minutieusement certaines propriétés qui seront toujours dans un élément dom, mais leur combinaison ne sera probablement pas dans un autre objet, comme ceci:

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

Ne pas marteler sur ce sujet ou quoi que ce soit, sauf pour les navigateurs compatibles ES5, pourquoi ne pas simplement:

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

Ne fonctionnera pas sur les TextNodes et ne suis pas sûr du Shadow DOM ou du DocumentFragments, etc., mais will fonctionnera sur presque tous les éléments de balises HTML.

0
Travis Kaufman

Chaque DOMElement.constructor retourne function HTML ... Element () ou [Objet 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;
}
0
Paweł

voici une astuce en utilisant jQuery

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

$(obj).html() == undefined // true
$(element).html() == undefined // false

donc le mettre dans une fonction:

function isElement(obj){

   return (typeOf obj === 'object' && !($(obj).html() == undefined));

}
0
Matus

J'ai un moyen spécial de faire cela qui n'a pas encore été mentionné dans les réponses.

Ma solution est basée sur quatre tests. Si l'objet passe tous les quatre, alors c'est un élément:

  1. L'objet n'est pas nul.

  2. L'objet a une méthode appelée "appendChild".

  3. La méthode "appendChild" a été héritée de la classe Node et n'est pas simplement une méthode imposter (une propriété créée par l'utilisateur avec un nom identique).

  4. L'objet est de type de nœud 1 (élément). Les objets qui héritent des méthodes de la classe Node sont toujours des nœuds, mais pas nécessairement des éléments.

Q: Comment puis-je vérifier si une propriété donnée est héritée et n'est pas simplement un imposteur?

R: Un simple test pour voir si une méthode a vraiment été héritée de Node consiste d'abord à vérifier que la propriété a le type "objet" ou "fonction". Ensuite, convertissez la propriété en chaîne et vérifiez si le résultat contient le texte "[Code natif]". Si le résultat ressemble à ceci:

function appendChild(){
[Native Code]
}

Ensuite, la méthode a été héritée de l'objet Node. Voir https://davidwalsh.name/detect-native-function

Et finalement, en rassemblant tous les tests, la solution est la suivante:

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

Une méthode tout à fait correcte, check target est un élément html real html Primary code:

    (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

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

Ceci vérifiera même s'il s'agit d'un élément jQuery ou JavaScript DOM 

0
Arjun Kakkar

Selon mdn

Element est la classe de base la plus générale à partir de laquelle tous les objets d'une Document héritent. Il n'a que des méthodes et des propriétés communes à toutes sortes d'éléments. 

Nous pouvons implémenter isElement par prototype. Voici mon conseil:

/**
 * @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

0
xianshenglu