【问题标题】:How do you check if a JavaScript Object is a DOM Object?如何检查 JavaScript 对象是否为 DOM 对象?
【发布时间】:2010-09-27 21:51:06
【问题描述】:

我想得到:

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

有没有好的替代品?

【问题讨论】:

  • 我是否迟钝地想知道“DOM 对象”是否不仅应涵盖元素,还应涵盖所有节点(文本节点、属性等)?您提出问题的所有答案和方式往往表明这个问题专门针对元素......

标签: javascript dom object


【解决方案1】:

这是来自可爱的 JavaScript 库MooTools

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

【讨论】:

  • 这段代码没有断言对象是一个DOM元素;只是它看起来有点像。任何对象都可以被赋予 nodeName 和 nodeType 属性并满足此代码。
  • 此答案未检测到所有类型的 HTML 元素。例如,不支持 SVG 元素。请参阅下面的答案。
  • 并非真正适用于所有元素,例如 SVG。请参阅下面的答案,stackoverflow.com/a/36894871/1204556
【解决方案2】:

我认为你需要做的是彻底检查一些属性,这些属性总是在一个 dom 元素中,但它们的组合不会很可能在另一个对象中,就像这样:

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

【讨论】:

    【解决方案3】:

    您可以尝试将其附加到真实的 DOM 节点...

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

    【讨论】:

    • 这行得通吗?它仍然是一个解决方案。很有创意。
    • +1 为它提供的创造力和确定性。但是,如果节点恰好已经是 DOM 的一部分,那么您刚刚删除了它!所以......这个答案是不完整的,如果必要的话,如果没有做将元素重新添加到 DOM 的工作。
    • 我读了将近 5 年了,我认为这是最酷的一本。它只需要细化。例如,您可以尝试将节点的 clone 附加到分离的元素。如果那不是 DOM 对象。肯定会出问题的。不过,这仍然是一个相当昂贵的解决方案。
    • 或者不要尝试附加元素的克隆只是尝试克隆它就足够了:obj.cloneNode(false)AND 没有副作用。
    • 这真的很昂贵而且不必要地复杂。请参阅下面的答案,stackoverflow.com/a/36894871/1204556
    【解决方案4】:

    这可能很有趣:

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

    【讨论】:

    • 值得注意的是,这不适用于属于其他窗口/框架的元素。鸭子打字是推荐的方法
    • 你可以用:function Fake() {}; Fake.prototype=document.createElement("div"); alert(new Fake() instanceof HTMLElement);
    • WTF 事实:Firefox 5 及更早版本返回 true[] instanceof HTMLElement
    • 顺便说一句,HTMLElement 始终是function,所以typeof 会让你偏离轨道并执行语句的第二部分。如果您愿意,可以尝试instanceof Object,因为该函数将是Object 的实例,或者直接检查typeof === "function",因为NodeHTMLElement 都是本机对象函数。
    • 当您调用 isElement(0) 时,它返回 0,而不是 false...这是为什么,我该如何防止呢?
    【解决方案5】:

    在 Firefox 中,您可以使用instanceof NodeNodeDOM1 中定义。

    但这在 IE 中并不容易。

    1. “instanceof ActiveXObject”只能判断它是本机对象。
    2. "typeof document.body.appendChild=='object'" 告诉它可能是DOM对象,也可以是其他具有相同功能的东西。

    您只能通过使用 DOM 函数来确保它是 DOM 元素并捕获任何异常。但是,它可能会产生副作用(例如更改对象内部状态/性能/内存泄漏)

    【讨论】:

      【解决方案6】:

      上面和下面的所有解决方案(包括我的解决方案)都存在不正确的可能性,尤其是在 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
      

      虽然它应该分别返回“function”和true。测试方法的唯一方法是查看是否已定义。

      【讨论】:

      【解决方案7】:

      旧线程,但这是 ie8 和 ff3.5 用户的更新可能性:

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

      【讨论】:

        【解决方案8】:

        也许这是另一种选择?在 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
        

        【讨论】:

        • 不错的尝试,但如果可以避免异常处理,则成本太高。此外,ES5 允许您为对象定义只读属性。
        【解决方案9】:
        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 上对此进行了测试。我找不到破解它的方法。
        理论上,它会通过在其前插入一个脚本标签来测试提议元素的每个祖先以及元素本身。
        如果它的第一个祖先追溯到一个已知元素,例如<html><head><body>,并且在此过程中没有抛出错误,那么我们就有了一个元素。
        如果第一个祖先没有附加到文档,我们创建一个元素并尝试将提议的元素放入其中,(然后将其从新元素中删除)。
        因此,它要么追溯到已知元素,要么成功附加到已知元素,要么失败。
        如果它不是元素,则返回元素或 null。

        【讨论】:

        • 您确实应该编写可读的代码。在使用之前声明变量并为它们留下非描述性的单个字符名称对我来说是一个巨大的警告信号,导致我懒得阅读您的代码。这样做并没有更“优化”。
        【解决方案10】:

        您可以查看有问题的对象或节点是否返回字符串类型。

        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
        

        【讨论】:

        • typeof ({innerHTML: ""}).innerHTML === "string"
        • 热!这个反应应该是游戏的赢家。 if(typeof obj.innerHTML!=='string') //不是 dom 元素。
        • 我最初反对@Qtax 和thomasrutter's critique on an earlier answer,但我开始购买它。虽然我以前没有遇到过像鸭子一样嘎嘎叫的狗,但我可以看到有人不检查某个节点是否是一个节点,运行notANode.innerHTML = "<b>Whoops</b>";,然后让该代码将其受污染的 obj 传递给 this代码。防御性代码 === 更好的代码,所有其他条件都相同,这最终不是防御性的。
        【解决方案11】:

        这是我想出来的:

        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 语句中测试它的原因。例如 IE7 失败了,因为 (document.createElement("a")).constructor 在 IE7 中是 undefined

        这种方法的问题是document.createElement 确实不是最快的函数,如果你用它测试很多元素,它很容易减慢你的应用程序。为了解决这个问题,我决定缓存构造函数。对象ElementConstructors 将 nodeNames 作为键,并将其对应的构造函数作为值。如果构造函数已被缓存,则从缓存中使用它,否则创建元素,缓存其构造函数以供将来访问,然后对其进行测试。

        第三个测试是令人不快的回退。它测试 el 是否为object,将nodeType 属性设置为1,并将字符串设置为nodeName。这当然不是很可靠,但绝大多数用户甚至不应该退缩到现在。

        这是我想出的最可靠的方法,同时仍保持尽可能高的性能。

        【讨论】:

          【解决方案12】:

          检测元素是否属于 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 中测试。

          【讨论】:

          • 如果myelement 不是 DOM 对象,这很可能会崩溃。
          【解决方案13】:

          这里有一个使用 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));
          
          }
          

          【讨论】:

          • jQuery 在内部执行 elem.nodeType === 1 那么为什么不保存调用开销和 jQuery 依赖项并让您的 isElement 函数自己完成呢?
          • 现在是 2016 年,就说“不”吧。
          【解决方案14】:

          使用root检测发现here,我们可以判断是否例如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 解决方案便宜。

          唐P

          【讨论】:

          • 我已经在最新的 Chrome 和 FF 以及 IE11 中对此进行了测试,它适用于任何地方,也适用于通过 document.createElement() 创建但也未插​​入 DOM 的文本节点和对象。太棒了(:谢谢
          • 这看起来是一个不错的答案,尽管我的答案很多,而且不那么复杂。 stackoverflow.com/a/36894871/1204556
          【解决方案15】:
          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; },
          

          //实际上我更可能不会使用这些内联,但有时最好有这些快捷方式来设置代码

          【讨论】:

          • obj.constructor.name 在 IE 中不起作用,因为在 IE 中,函数没有 name 属性。替换为 obj.constructor != Object.
          【解决方案16】:

          测试obj 是否继承自Node

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

          Node 是一个基本的Interface,HTMLElement 和 Text 都从该Interface 继承。

          【讨论】:

            【解决方案17】:

            不要对这个或任何东西进行抨击,但对于符合 ES5 的浏览器,为什么不只是:

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

            不能在 TextNodes 上工作,也不确定 Shadow DOM 或 DocumentFragments 等,但 可以在几乎所有 HTML 标记元素上工作。

            【讨论】:

              【解决方案18】:

              这可能会有所帮助: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);
              }
              

              在上面的代码中,我们使用 双重否定 运算符来获取作为参数传递的对象的布尔值,这样我们确保在条件语句中评估的每个表达式都是布尔值,利用Short-Circuit Evaluation,因此该函数返回truefalse

              【讨论】:

              • 任何虚假都应该使您的布尔值短路。例如,undefined &amp;&amp; window.spam("should bork") 从不评估伪造的 spam 函数。所以不需要!!,我不相信。那,你能提供一个 [非学术] 边缘案例,它的使用很重要吗?
              • 感谢您的夸奖。我使用 *!!* 双重否定 将所有表达式转换为布尔值,而不是真值或假值。
              • 对,但没有实际理由这样做,我不认为 -- 请参阅 here。当然也没有必要在这里利用 Short-Cut eval。即使您没有购买“永远不需要!!”参数(如果您不购买,我很好奇为什么不购买),您可以将该行编辑为return !!(obj &amp;&amp; typeof obj === "object" &amp;&amp; obj.nodeType === 1 &amp;&amp; obj.nodeName); 和让它运行一样。
              • 这就是我所做的;)更干净,效果相同。谢谢。
              【解决方案19】:

              Lo-Dash's _.isElement怎么样?

              $ npm install lodash.iselement
              

              在代码中:

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

              【讨论】:

              • 我喜欢这个解决方案。它很简单,并且适用于 Edge 和 IE,甚至适用于单独 iframe 中的元素,这与这里大多数投票率最高的解决方案不同。
              • 这个答案很有帮助,尽管需要 Webpack 在浏览器中运行 NPM 模块。
              【解决方案20】:

              对于那些使用 Angular 的人:

              angular.isElement
              

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

              【讨论】:

              • 包含Angular's code 更有用:function isElement(node) { return !!(node &amp;&amp; (node.nodeName || (node.prop &amp;&amp; node.attr &amp;&amp; node.find))); } 看起来有点像@finpingvin's。请注意,它正在确定“引用是否为 DOM 元素(或包装的 jQuery 元素)。
              【解决方案21】:

              这几乎适用于任何浏览器。 (这里不区分元素和节点)

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

              【讨论】:

              • 首先,不需要返回true或者返回false,返回if语句即可。其次,这将为 {nodeType:1} 返回 true
              • 我倾向于使用过于冗长的代码(在这个网站上),以使代码的意图更清晰;)
              【解决方案22】:

              我认为原型设计不是一个很好的解决方案,但也许这是最快的解决方案: 定义这个代码块;

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

              检查你的对象 isDomElement 属性:

              if(a.isDomElement){}
              

              我希望这会有所帮助。

              【讨论】:

              • 1) 不建议更改您不拥有的对象。 2) 这不会检测不属于同一文档的元素。
              • 这仍然不会被检测到js var fake = Object.create(Element.prototype)
              【解决方案23】:

              将原始 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

              【讨论】:

              • 有趣的解决方案。了解更多关于为什么/如何工作的细节会很有帮助。方法到底比什么?
              • js 认为一切都是对象,ojects 通过原型扩展,标准 dom 元素都共享“toString”方法,返回 4ex“HTMLDivElement”
              • 感谢您提供详细信息。我刚刚自己测试过,看到document.body.toString() 返回字符串"[object HTMLBodyElement]"。如果我做对了,另一种写 isDOM 的方式可能是return -1 !== x.toString().indexOf("[object HTML")
              【解决方案24】:

              以下 IE8 兼容,超级简单的代码完美运行。

              accepted answer 不会检测所有类型的 HTML 元素。例如,不支持 SVG 元素。相比之下,这个答案适用于 HTML 和 SVG。

              在此处查看实际操作:https://jsfiddle.net/eLuhbu6r/

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

              【讨论】:

              • 'Element' 在 IE7 上未定义
              • 我认为任何仍在使用 IE7 的人都应该花 5 秒钟时间下载更好的浏览器,而不是让我们花费数天或数周的时间来解决他们拒绝与时俱进的问题。
              • 请注意,这不适用于多个窗口。 IE。 window.open().document.body instanceof Element 返回false
              • @nik10110 您正在检查 NEW 窗口的 body 实例是否是 OLD 窗口的 Element 的实例。如果您将代码调整如下:-) win = window.open(); win.document.body instanceof win.Element; // true
              • 我可以理解在一定程度上存在限制。然而,这种限制的目的是限制安全责任。在这一点上,为了安全起见限制为 IE7 就像试图在一个防水布翻盖的门上放一个锁舌。
              【解决方案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);
              

              【讨论】:

                【解决方案26】:

                大多数答案使用某种鸭子类型,例如检查对象是否具有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;
                  }
                }
                

                【讨论】:

                • 你的 isElement 可能会为其他具有 'value' 属性的对象返回 true,你的 isHTMLElement 会发出点击
                • @kofifus 你能提供一个破坏isElement的例子吗?关于点击,我认为它并没有太大的危害。 HTMLElement.prototype 包含的方法不多,但当然使用属性getter(如隐藏的sn-p所示)会更安全,但不太清楚。
                【解决方案27】:

                我有一种特殊的方法可以做到这一点,但答案中尚未提及。

                我的解决方案基于四个测试。如果对象通过了所有四个,那么它就是一个元素:

                1. 对象不为空。

                2. 该对象有一个名为“appendChild”的方法。

                3. 方法“appendChild”继承自 Node 类,而不仅仅是一个冒名顶替的方法(用户创建的同名属性)。

                4. 对象属于节点类型 1(元素)。从 Node 类继承方法的对象始终是 Nodes,但不一定是 Elements。

                问:我如何检查给定的属性是否是继承的并且不仅仅是冒名顶替者?

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

                【讨论】:

                  【解决方案28】:

                  每个 DOMElement.constructor 返回 function 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;
                  }
                  

                  【讨论】:

                    【解决方案29】:

                    不需要hack,你可以问一个元素是否是DOM Element的一个实例:

                    const isDOM = el => el instanceof Element
                    

                    【讨论】:

                    • 太棒了!有效!
                    • 几乎完全兼容(caniuse.com/mdn-javascript_operators_instanceof),这应该是公认的答案
                    • 附注:如果有人也使用它来检查元素是否仍然是 DOM 的一部分(这有点像这个问题引起的),那么这个检查就会失败。它只检查元素的类型,而不是它的真实存在。
                    【解决方案30】:
                    (element instanceof $ && element.get(0) instanceof Element) || element instanceof Element
                    

                    这将检查它是否是 jQuery 或 JavaScript DOM 元素

                    【讨论】:

                      猜你喜欢
                      • 2012-06-26
                      • 1970-01-01
                      • 2021-10-29
                      • 1970-01-01
                      • 2010-12-23
                      • 2011-06-12
                      • 1970-01-01
                      • 1970-01-01
                      • 2020-06-18
                      相关资源
                      最近更新 更多