คุณจะตรวจสอบว่าวัตถุ JavaScript เป็นวัตถุ DOM ได้อย่างไร


248

ฉันพยายามที่จะได้รับ:

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

มีตัวแทนที่ดีหรือไม่?


3
ฉันถูกป้านในการสงสัยว่า "วัตถุ DOM" ไม่ควรครอบคลุมองค์ประกอบเท่านั้น แต่ยังรวมถึงโหนดทั้งหมด (โหนดข้อความ, แอตทริบิวต์, ฯลฯ )? คำตอบทั้งหมดและวิธีการที่คุณโพสต์คำถามมีแนวโน้มที่จะแนะนำคำถามนี้โดยเฉพาะเกี่ยวกับองค์ประกอบ ...
mike rodent

คำตอบ:


300

สิ่งนี้อาจเป็นที่สนใจ:

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 เพราะโหนดและ 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"
);
}

11
เป็นที่น่าสังเกตว่านี่จะไม่ทำงานกับองค์ประกอบที่เป็นของ windows / frames อื่น ๆ การพิมพ์เป็ดเป็นวิธีการที่แนะนำ
Andy E

2
คุณสามารถหลอกได้ด้วย:function Fake() {}; Fake.prototype=document.createElement("div"); alert(new Fake() instanceof HTMLElement);
เคอร์เนลเจมส์

11
WTF จริง: Firefox 5 และการกลับมาก่อนหน้านี้สำหรับtrue [] instanceof HTMLElement
Rob W

6
Btw HTMLElementเป็นเสมอfunctionดังนั้นtypeofจะทำให้คุณหลุดออกจากเส้นทางและจะดำเนินการส่วนที่สองของคำสั่ง คุณสามารถลองถ้าคุณต้องการinstanceof Objectเพราะฟังก์ชั่นจะเป็นตัวอย่างของObjectหรือเพียงแค่ตรวจสอบอย่างชัดเจนtypeof === "function"เพราะNodeและHTMLElementเป็นทั้งฟังก์ชั่นวัตถุพื้นเมือง
Roland

2
เมื่อคุณโทรisElement(0)มันจะคืนค่า 0 ไม่ใช่เท็จ ... ทำไมจึงเป็นเช่นนั้นและฉันจะป้องกันได้อย่างไร
เจสสิก้า

68

โค้ดที่เรียบง่ายที่ใช้งานร่วมกันได้กับ IE8 ต่อไปนี้ทำงานอย่างสมบูรณ์

คำตอบที่ได้รับการยอมรับไม่ได้ตรวจสอบทุกประเภทขององค์ประกอบ HTML ตัวอย่างเช่นองค์ประกอบ SVG ไม่ได้รับการสนับสนุน ในทางตรงกันข้ามคำตอบนี้ใช้ได้กับ HTML และ SVG

ดูการทำงานได้ที่นี่: https://jsfiddle.net/eLuhbu6r/

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

3
'Element' ไม่ได้กำหนดใน IE7
Dan

49
ฉันเห็นว่าทุกคนที่ยังใช้ IE7 ควรใช้เวลาห้าวินาทีในการดาวน์โหลดเบราว์เซอร์ที่ดีกว่าแทนที่จะปล่อยให้เราลงทุนวันหรือสัปดาห์เพื่อหลีกเลี่ยงการปฏิเสธเวลา
mopsyd

1
ฉันเห็นด้วยกับ @ mopsyd แต่ผู้เขียนของคำตอบระบุว่ามันทำงานใน IE7 ซึ่งอาจจะต้องมีการปรับปรุงเพื่อความถูกต้อง
Lajos Meszaros

1
อัปเดตเพื่อบอก IE9 ฉันไม่แน่ใจว่า IE8 รองรับหรือไม่
Monarch Wadia

1
@MonarchWadia ใช่มันรองรับใน IE8 แต่โปรดทราบว่านี่จะไม่ส่งคืนจริงสำหรับdocumentองค์ประกอบ (ในเบราว์เซอร์ทั้งหมด) หากคุณต้องการคุณควรลอง:x instanceof Element || x instanceof HTMLDocument
S.Serpooshan

11

โซลูชันทั้งหมดด้านบนและด้านล่าง (โซลูชันของฉันรวมถึง) ประสบความเป็นไปได้ที่จะไม่ถูกต้องโดยเฉพาะใน 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ตามลำดับ วิธีเดียวในการทดสอบวิธีการคือดูว่ามีการกำหนดไว้หรือไม่


"typeof document.body.cloneNode" ไม่กลับ "วัตถุ" ใน IE ของฉัน
เดนนิสซี

ดูเหมือนว่าคำตอบที่ดี ดูคำตอบของฉันด้านล่าง stackoverflow.com/a/36894871/1204556
Monarch Wadia

8

คุณสามารถลองต่อท้ายโหนดจริง ...

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

    return true;
}

11
มันใช้ได้ไหม? มันยังคงเป็นทางออก โฆษณาชิ้นหนึ่งในตอนนั้น
Justin Meyer

3
+1 สำหรับความคิดสร้างสรรค์และความมั่นใจนี้ อย่างไรก็ตามหากโหนดนั้นเป็นส่วนหนึ่งของ DOM อยู่แล้วคุณก็ลบมันได้แล้ว! ดังนั้น ... คำตอบนี้ไม่สมบูรณ์โดยไม่ต้องทำงานเพื่อเพิ่มองค์ประกอบลงใน DOM อีกครั้งหากจำเป็น
svidgen

4
ฉันอ่านบทความนี้มาเกือบ 5 ปีแล้วและฉันคิดว่ามันเป็นสิ่งที่ยอดเยี่ยมที่สุด มันแค่ต้องได้รับการขัดเกลา คุณสามารถลองผนวกโคลนของโหนดไปยังองค์ประกอบที่แยกออกมาได้ตัวอย่างเช่น หากไม่ใช่วัตถุ DOM บางสิ่งจะผิดไปแน่นอน ยังคงเป็นวิธีแก้ปัญหาที่ค่อนข้างแพง
MaxArt

หรือแทนที่จะพยายามผนวกโคลนขององค์ประกอบแค่พยายามที่จะโคลนมันobj.cloneNode(false)ควรจะเพียงพอที่: และไม่มีผลข้างเคียง
mauroc8

1
นี่แพงและซับซ้อนจริงๆโดยไม่จำเป็น ดูคำตอบของฉันด้านล่าง, stackoverflow.com/a/36894871/1204556
Monarch Wadia


6

แล้วLo-Dash_.isElementล่ะ?

$ npm install lodash.iselement

และในรหัส:

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

1
ฉันชอบวิธีนี้ มันง่ายและใช้งานได้ใน Edge และ IE แม้จะเป็นองค์ประกอบใน iframe ที่แยกจากกันซึ่งแตกต่างจากโซลูชันที่ได้รับการโหวตมากที่สุดที่นี่
Elias Zamaria

คำตอบนี้มีประโยชน์แม้ว่าจะต้องใช้ Webpack เพื่อเรียกใช้โมดูล NPM ในเบราว์เซอร์
Edwin Pratt

5

นี่คือจากไลบรารี JavaScript ที่น่ารักMooTools :

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

12
รหัสนี้ไม่ได้ยืนยันว่าวัตถุนั้นเป็นองค์ประกอบ DOM; เพียง แต่มันดูเหมือนเล็กน้อย วัตถุใด ๆ ที่สามารถได้รับคุณสมบัติ nodeName และ nodeType และตอบสนองรหัสนี้
thomasrutter

คำตอบนี้ตรวจไม่พบองค์ประกอบ HTML ทุกประเภท ตัวอย่างเช่นองค์ประกอบ SVG ไม่ได้รับการสนับสนุน ดูคำตอบของฉันด้านล่าง
Monarch Wadia

ใช้งานไม่ได้กับทุกองค์ประกอบเช่น SVG ดูคำตอบของฉันด้านล่างstackoverflow.com/a/36894871/1204556
Monarch Wadia

4

การใช้การตรวจจับรูทพบได้ที่นี่เราสามารถกำหนดได้ว่าการเตือนเช่นนั้นเป็นสมาชิกของรูทของวัตถุหรือไม่ซึ่งน่าจะเป็นหน้าต่าง:

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
}

ดูเหมือนว่าจะมีราคาถูกกว่าโซลูชันลอง / จับในเธรดการเปิด

ดอนพี


ฉันได้ทดสอบสิ่งนี้ใน Chrome และ FF ล่าสุดและใน IE11 และใช้งานได้ทุกที่รวมถึงโหนดข้อความและวัตถุที่สร้างผ่านdocument.createElement()แต่ไม่ได้แทรกใน DOM ด้วย น่าทึ่ง (: ขอบคุณ
Geradlus_RU

ดูเหมือนว่าคำตอบที่ดีแม้ว่าของฉันจะทำสิ่งเดียวกันมากและซับซ้อนน้อยกว่า stackoverflow.com/a/36894871/1204556
Monarch Wadia

4

หัวข้อเก่า แต่นี่เป็นโอกาสที่อัปเดตสำหรับผู้ใช้ie8 และ ff3.5 :

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

4

ฉันขอแนะนำวิธีง่ายๆในการทดสอบว่าตัวแปรเป็นองค์ประกอบ DOM หรือไม่

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

หรือตามที่HTMLGuyแนะนำ:

const isDomEntity = entity => {
  return typeof entity   === 'object' && entity.nodeType !== undefined
}

1
ทาง verbose เกินไป การเปรียบเทียบจะส่งคืนบูลีนแล้ว:return typeof entity === 'object' && typeof entity.nodeType !== undefined;
HTMLGuy

1
น่าสนใจมาก! บางครั้งขึ้นอยู่กับประเภทที่คุณมีในobjects และ / หรือ poperties ของคุณสิ่งนี้มีประโยชน์มาก! Tx, @Roman
Pedro Ferreira

3
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 ฟังก์ชั่นไม่มีคุณสมบัติชื่อ แทนที่โดย obj.constructor! = Object
mathheadinclouds

3

สิ่งนี้อาจเป็นประโยชน์: 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


สิ่งที่ผิดพลาดควรทำให้บูลีนลัดวงจร undefined && window.spam("should bork")ไม่เคยประเมินspamฟังก์ชั่นปลอมเช่น ดังนั้นไม่!!จำเป็นฉันไม่เชื่อ คุณสามารถระบุกรณีขอบ [ไม่ใช่ทางวิชาการ] ที่การใช้งานมีความสำคัญได้หรือไม่?
ruffin

ขอบคุณสำหรับการประกาศของคุณ ฉันใช้* !! * ลบล้างสองครั้งเพื่อแปลงนิพจน์ทั้งหมดเป็นค่าบูลีนไม่ใช่ความจริงหรือเท็จ
jherax

ถูกต้อง แต่ไม่มีเหตุผลในทางปฏิบัติที่จะทำมันฉันไม่คิดว่า - ดูที่นี่ และแน่นอนว่าไม่จำเป็นต้องใช้ประโยชน์จาก Short-Cut eval ที่นี่ แม้ว่าคุณจะไม่ได้ซื้อ!!อาร์กิวเมนต์ "ไม่จำเป็น" ( และถ้าคุณทำไม่ได้ฉันอยากรู้ว่าทำไมไม่ ) คุณสามารถแก้ไขบรรทัดนั้นreturn !!(obj && typeof obj === "object" && obj.nodeType === 1 && obj.nodeName);และให้มันทำงานเหมือนเดิม
ruffin

นั่นคือสิ่งที่ฉันทำ;) สะอาดและเอฟเฟกต์เหมือนกันมากขึ้น ขอบคุณ.
jherax

2

คุณสามารถดูว่าวัตถุหรือโหนดที่มีปัญหาส่งกลับชนิดสตริงหรือไม่

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

3
typeof ({innerHTML: ""}).innerHTML === "string"
Qtax

ร้อน! การตอบสนองนี้ควรเป็นผู้ชนะเกม if (typeof obj.innerHTML! == 'string') // ไม่ใช่องค์ประกอบของ dom
user3751385

1
ตอนแรกฉันตอบโต้กับคำติชมของ @Qtax และthomasrutter ในคำตอบก่อนหน้านี้แต่ฉันเริ่มที่จะซื้อ แต่ผมไม่ได้ทำงานเป็นสุนัข quacking เหมือนเป็ดว่าเช่นนี้มาก่อนผมสามารถเห็นคนที่ไม่ได้ตรวจสอบว่าสิ่งที่เป็นโหนดวิ่งnotANode.innerHTML = "<b>Whoops</b>";จากนั้นก็มีรหัสที่ผ่านการปนเปื้อนของ obj ไปนี้รหัส รหัสป้องกัน === รหัสที่ดีกว่าสิ่งอื่น ๆ ที่เท่าเทียมกันและในที่สุดก็ไม่ได้รับการป้องกัน
ruffin

2

สำหรับคนที่ใช้ Angular:

angular.isElement

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


มีประโยชน์มากขึ้นที่จะรวมถึงรหัสเชิงมุมของ : function isElement(node) { return !!(node && (node.nodeName || (node.prop && node.attr && node.find))); }ดูเหมือนเล็ก ๆ น้อย ๆ เช่น@ finpingvin ของ โปรดทราบว่ามันกำลังพิจารณาว่า " ถ้าการอ้างอิงเป็นองค์ประกอบ DOM (หรือองค์ประกอบ jQuery ที่ล้อมรอบ) "
ruffin

2

ฉันคิดว่าการสร้างต้นแบบไม่ใช่วิธีแก้ปัญหาที่ดีมาก แต่อาจเป็นวิธีที่เร็วที่สุด: กำหนดบล็อคโค้ดนี้

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

กว่าตรวจสอบวัตถุของคุณคุณสมบัติ isDomElement:

if(a.isDomElement){}

ฉันหวังว่านี่จะช่วยได้.


1
1) ไม่แนะนำให้เปลี่ยนวัตถุที่คุณไม่ได้เป็นเจ้าของ 2) สิ่งนี้ไม่ตรวจพบองค์ประกอบที่ไม่ได้เป็นส่วนหนึ่งของเอกสารเดียวกัน
Fregante

2

ตามmdn

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


1

ผมคิดว่าสิ่งที่คุณต้องทำคือการทำให้การตรวจสอบอย่างละเอียดของคุณสมบัติบางอย่างที่จะเป็นในองค์ประกอบ DOM แต่การรวมกันของพวกเขาจะไม่ได้มีโอกาสมากที่สุดจะอยู่ในวัตถุอื่นเช่นดังนั้น:

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

1

ใน Firefox instanceof Nodeคุณสามารถใช้ ที่Nodeกำหนดไว้ในDOM1 DOM1

แต่นั่นไม่ใช่เรื่องง่ายใน IE

  1. "instanceof ของ ActiveXObject" สามารถบอกได้ว่ามันเป็นวัตถุดั้งเดิม
  2. "typeof document.body.appendChild == 'object'" บอกว่ามันอาจเป็นวัตถุ DOM แต่อาจเป็นอย่างอื่นที่มีฟังก์ชั่นเดียวกันได้

คุณสามารถมั่นใจได้ว่ามันเป็นองค์ประกอบ DOM โดยใช้ฟังก์ชั่น DOM และจับถ้ามีข้อยกเว้น อย่างไรก็ตามอาจมีผลข้างเคียง (เช่นเปลี่ยนสถานะภายในวัตถุ / ประสิทธิภาพ / หน่วยความจำรั่ว)


1

บางทีนี่อาจเป็นทางเลือก ทดสอบใน 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

2
ลองดี แต่การจัดการข้อยกเว้นมีค่าใช้จ่ายสูงเกินไปหากสามารถหลีกเลี่ยงได้ นอกจากนี้ ES5 ยังช่วยให้คุณสามารถกำหนดคุณสมบัติแบบอ่านอย่างเดียวสำหรับวัตถุ
Andy E

1

นี่คือสิ่งที่ฉันคิดออก:

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 เป็นคีย์ที่มีตัวสร้างที่สอดคล้องกันเป็นค่า หากตัวสร้างถูกแคชไว้แล้วมันจะใช้มันจากแคชมิฉะนั้นมันจะสร้างองค์ประกอบแคชตัวสร้างของมันสำหรับการเข้าถึงในอนาคตและจากนั้นทดสอบกับมัน

การทดสอบที่สามคือทางเลือกที่ไม่พึงประสงค์ มันทดสอบว่า el เป็นobject, มีnodeTypeคุณสมบัติที่ตั้งไว้1และสตริงเป็นnodeNameและสตริงแน่นอนว่ามันไม่น่าเชื่อถือ แต่ผู้ใช้ส่วนใหญ่ไม่ควรถอยกลับไปเลย

นี่เป็นวิธีที่น่าเชื่อถือที่สุดที่ฉันคิดขึ้นมาในขณะที่ยังคงประสิทธิภาพไว้สูงที่สุดเท่าที่จะทำได้



1

แยกความแตกต่างของวัตถุ raw 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


0

นี่คือเคล็ดลับการใช้ 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));

}

2
jQuery กำลังทำภายในelem.nodeType === 1ดังนั้นทำไมไม่บันทึกค่าใช้จ่ายการโทรและการพึ่งพา jQuery และให้ฟังก์ชัน isElement ของคุณทำเช่นนั้น?
โจเซฟเลนน็อกซ์

ปี 2559 เพียงแค่พูดว่า "ไม่"
โทมัส McCabe

0

อย่าใช้ค้อนทุบสิ่งนี้หรืออะไรอื่นนอกจากเบราว์เซอร์ที่ใช้กับ ES5 ได้ทำไมไม่เพียง:

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

จะไม่ทำงานกับ TextNodes และไม่แน่ใจเกี่ยวกับ Shadow DOM หรือ DocumentFragments เป็นต้น แต่จะใช้ได้กับองค์ประกอบแท็ก HTML เกือบทั้งหมด


0

มันจะใช้ได้กับเบราว์เซอร์เกือบทุกชนิด (ไม่มีความแตกต่างระหว่างองค์ประกอบและโหนดที่นี่)

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

ก่อนอื่นคุณไม่จำเป็นต้องส่งคืนจริงหรือคืนเท็จเพียงแค่ส่งคืนคำสั่ง if ประการที่สองสิ่งนี้จะคืนค่าจริงสำหรับ {nodeType: 1}
bluejayke

ฉันมักจะใช้รหัส verbose มากเกินไป (บนเว็บไซต์นี้) เพื่อจุดประสงค์ในการทำให้รหัสชัดเจนขึ้น)
Zv_oDD

0

วิธีการที่ถูกต้องแบบสัมบูรณ์ตรวจสอบเป้าหมายคือรหัสหลักขององค์ประกอบ 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);

ทดสอบใน IE 5


0

แต่ละDOMElement.constructorส่งคืนHTML ฟังก์ชัน ... องค์ประกอบ ()หรือ[วัตถุ HTML ... องค์ประกอบ]ดังนั้น ...

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

ฉันมีวิธีพิเศษในการทำสิ่งนี้ซึ่งยังไม่ได้กล่าวถึงในคำตอบ

โซลูชันของฉันใช้การทดสอบสี่ชุด หากวัตถุผ่านทั้งสี่แสดงว่าเป็นองค์ประกอบ:

  1. วัตถุไม่เป็นโมฆะ

  2. วัตถุมีวิธีการที่เรียกว่า "appendChild"

  3. วิธีการ "appendChild" ได้รับการสืบทอดมาจากคลาสโหนดและไม่ได้เป็นเพียงวิธีการเลียนแบบ (คุณสมบัติที่ผู้ใช้สร้างขึ้นด้วยชื่อที่เหมือนกัน)

  4. วัตถุเป็นโหนดประเภท 1 (องค์ประกอบ) วัตถุที่วิธีการสืบทอดจากโหนดระดับอยู่เสมอโหนด แต่ไม่จำเป็นต้ององค์ประกอบ

ถาม: ฉันจะตรวจสอบได้อย่างไรว่าทรัพย์สินที่ให้นั้นได้รับการสืบทอดและไม่ใช่เพียงการหลอกลวง?

ตอบ: การทดสอบอย่างง่าย ๆ เพื่อดูว่าวิธีการใดสืบทอดมาจาก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;
}

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

สิ่งนี้จะตรวจสอบแม้ว่าจะเป็นองค์ประกอบ jQuery หรือ JavaScript DOM


0

วิธีเดียวที่จะรับประกันว่าคุณกำลังตรวจสอบ HTMLEement จริงและไม่ใช่แค่วัตถุที่มีคุณสมบัติเดียวกับองค์ประกอบ HTML คือการตรวจสอบว่ามันสืบทอดมาจาก Node หรือไม่เพราะมันเป็นไปไม่ได้ที่จะสร้าง Node () ใน JavaScript (ยกเว้นฟังก์ชั่น Native 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--!>

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.