我想要:

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

有好的替代品吗?


当前回答

确保你检查的是一个实际的HTMLEement,而不是一个与HTML Element具有相同属性的对象,唯一的方法是确定它是否继承自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--!>

其他回答

不是为了强调这一点,而是为了兼容es5的浏览器,为什么不直接:

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

不会工作在文本节点和不确定的影子DOM或DocumentFragments等,但将工作在几乎所有的HTML标签元素。

你可能会感兴趣:

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的一部分,第2层。

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

上面和下面的所有解决方案(包括我的解决方案)都有可能是不正确的,特别是在IE上——(重新)定义一些对象/方法/属性来模拟DOM节点,使测试无效是很可能的。

所以我通常使用duck-typing风格的测试:我专门测试我使用的东西。例如,如果我想克隆一个节点,我这样测试它:

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。测试方法的唯一方法是查看是否定义了方法。

不需要黑客,你可以只问一个元素是否是DOM元素的一个实例:

const isDOM = el => el instanceof Element

也许这是另一种选择?在Opera 11, FireFox 6, Internet Explorer 8, Safari 5和谷歌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