วิธีการตรวจสอบใน Javascript หากองค์ประกอบหนึ่งอยู่ในองค์ประกอบอื่น


201

ฉันจะตรวจสอบได้อย่างไรว่าองค์ประกอบหนึ่งของ DOM เป็นองค์ประกอบย่อยขององค์ประกอบอื่นของ DOM หรือไม่ มีวิธีการในตัวสำหรับสิ่งนี้หรือไม่? ตัวอย่างเช่น:

if (element1.hasDescendant(element2)) 

หรือ

if (element2.hasParent(element1)) 

ถ้าไม่เช่นนั้นความคิดวิธีการทำเช่นนี้? นอกจากนี้ยังต้องมีเบราว์เซอร์ข้าม ฉันควรพูดถึงด้วยว่าเด็กสามารถซ้อนกันหลายระดับด้านล่างผู้ปกครอง


3
คุณควรใช้stackoverflow.com/a/18234150/1868545
llange

ฉันเชื่อว่าในปัจจุบัน JS เป็น "มาตรฐานสด" คำตอบที่ยอมรับควรถูกตรวจสอบอีกครั้งเพื่อระบุวิธี "conatins" เป็นคำตอบที่ยอมรับ
DavidTaubmann

คำตอบ:


219

อัปเดต:ตอนนี้มีวิธีการดั้งเดิมเพื่อให้บรรลุเป้าหมายนี้ Node.contains(). พูดถึงในความคิดเห็นและคำตอบด้านล่างเช่นกัน

คำตอบเก่า:

การใช้parentNodeคุณสมบัติควรใช้งานได้ มันค่อนข้างปลอดภัยจากมุมมองข้ามเบราว์เซอร์ หากความสัมพันธ์เป็นที่รู้จักกันในระดับหนึ่งลึกคุณสามารถตรวจสอบได้เพียง:

if (element2.parentNode == element1) { ... }

หากเด็กสามารถซ้อนกันโดยพลการภายในผู้ปกครองคุณสามารถใช้ฟังก์ชั่นที่คล้ายกับรายการต่อไปนี้เพื่อทดสอบความสัมพันธ์:

function isDescendant(parent, child) {
     var node = child.parentNode;
     while (node != null) {
         if (node == parent) {
             return true;
         }
         node = node.parentNode;
     }
     return false;
}

ขอบคุณสำหรับคำตอบของคุณปัญหาคือเด็กสามารถซ้อนหลายระดับด้านล่างผู้ปกครอง
AJ

@AJ: ฉันได้อัปเดตคำตอบของฉันเพื่อรวมโซลูชันที่ควรใช้ในการทำรังเด็กโดยพลการในแม่
Asaph

228
หากใครมาที่นี่ตอนนี้อาจเป็นไปได้ที่คุณจะใช้ Node.contain ( developer.mozilla.org/en-US/docs/DOM/Node.contain ) ซึ่งเป็นฟังก์ชันดั้งเดิมในเบราว์เซอร์สมัยใหม่
Adam Heath

2
@ JohnCarrell มีNode.contains(ไม่มีหน้า caniuse)
gcampbell

3
@ Asaph คุณช่วยกรุณาอัปเดตคำตอบเพื่อรวมคำตอบใหม่ได้Node.containsหรือไม่ :)

355

คุณควรใช้Node.containsเนื่องจากตอนนี้มันเป็นแบบมาตรฐานและพร้อมใช้งานในเบราว์เซอร์ทั้งหมด

https://developer.mozilla.org/en-US/docs/Web/API/Node.contains


ไม่จำเป็นต้องใช้วิธี Node.hasParent (parent) แต่จะเป็น Node.prototype.hasParent = function (องค์ประกอบ) {return element.contain (this);};
llange

6
รองรับเบราว์เซอร์มือถือหรือไม่ เอกสาร mdn มีเครื่องหมายคำถามสำหรับ Android, Safari, IE และ Opera
thetallweeks

1
@thallallweeks - ทำงานบน Android 7 ของฉันอย่างน้อย
Sphinxxx

5
อย่างน้อยคุณอาจเพิ่มตัวอย่างได้
Nino Škopac

2
นี่เป็นคำตอบที่ดี แต่ตัวอย่างจะดีจริง:var parent = document.getElementById('menu'); var allElements = document.getElementsByTagName('a'); if (parent.contains(allElements[i]) { alert('Link is inside meni'); }
baron_bartek

45

ฉันแค่ต้องแบ่งปัน 'ของฉัน'

แม้ว่าแนวความคิดจะเหมือนกับคำตอบของ Asaph (ได้รับประโยชน์จากการทำงานร่วมกันข้ามเบราว์เซอร์เดียวกันหรือแม้แต่ IE6) แต่ก็มีขนาดเล็กกว่ามากและมีประโยชน์เมื่อขนาดอยู่ในระดับพรีเมียมและ / หรือเมื่อไม่จำเป็นบ่อยครั้ง

function childOf(/*child node*/c, /*parent node*/p){ //returns boolean
  while((c=c.parentNode)&&c!==p); 
  return !!c; 
}

.. หรือเป็นหนึ่งซับ ( เพียง 64 ตัวอักษร !):

function childOf(c,p){while((c=c.parentNode)&&c!==p);return !!c}

และjsfiddle ที่นี่


การใช้งาน:
childOf(child, parent)คืนค่าบูลีนtrue| false.

คำอธิบาย:ประเมินตราบใดที่ในขณะที่สภาพการประเมิน
while (และ) ผู้ประกอบการผลตอบแทนนี้บูล / เท็จจริงหลังจากการประเมินด้านซ้ายมือและด้านขวามือ แต่ถ้าด้านซ้ายมือเป็นความจริง ( ) true
&&left-hand && right-hand

ด้านซ้ายมือ (ของ&&) (c=c.parentNode)เป็น:
การดำเนินการนี้จะกำหนดค่าparentNodeของcถึงcและจากนั้นตัวดำเนินการ AND จะประเมินผลลัพธ์cเป็นบูลีน
เนื่องจากparentNodeส่งคืนnullหากไม่มีพาเรนต์เหลือnullอยู่และถูกแปลงไปfalseเป็น while-loop จะหยุดอย่างถูกต้องเมื่อไม่มีพาเรนต์อีกต่อไป

ด้านขวามือ (ของ&&) c!==pเป็น:
ตัว!==ดำเนินการเปรียบเทียบคือ ' ไม่เท่ากับทั้งหมด' ดังนั้นหากผู้ปกครองของเด็กที่ไม่ได้เป็นผู้ปกครอง (คุณระบุ) จะประเมินไปtrueแต่ถ้าพ่อแม่ของเด็กเป็นfalseผู้ใหญ่แล้วก็ประเมิน
ดังนั้นหาก c!==pประเมินเป็นเท็จตัว&&ดำเนินการจะคืนค่าfalseเป็น while-condition และ while-loop หยุดทำงาน (โปรดทราบว่าไม่จำเป็นต้องใช้สักครู่ขณะหนึ่งและ;ต้องใช้เครื่องหมายอัฒภาคปิด)

ดังนั้นเมื่อ while-loop สิ้นสุดลงcเป็นทั้งโหนด (ไม่ใช่null) เมื่อพบ parent หรือเป็นnull(เมื่อวนรอบผ่านไปยังจุดสิ้นสุดโดยไม่ต้องค้นหาคู่)

ดังนั้นเราก็returnว่าจริง (แปลงเป็นค่าบูลีนแทนของโหนด) ด้วย: return !!c;ที่: !( NOTผู้ดำเนินการ) ตีความค่าบูลีน ( trueกลายเป็นfalseและในทางกลับกัน)
!cแปลงc(โหนดหรือ null) เป็นบูลีนก่อนที่จะสามารถกลับค่านั้น ดังนั้นการเพิ่มวินาที!( !!c) จะแปลงค่าเท็จกลับเป็นจริง (ซึ่งเป็นสาเหตุว่าทำไมค่า double !!จึงถูกใช้เพื่อ 'แปลงค่าอะไรเป็นบูลีน')


พิเศษ:
ร่างกาย / น้ำหนักของฟังก์ชั่นนั้นเล็กมากทั้งนี้ขึ้นอยู่กับกรณี (เช่นเมื่อมันไม่ได้ใช้บ่อยและปรากฏเพียงครั้งเดียวในรหัส) หนึ่งคนสามารถละเว้นฟังก์ชั่น (ห่อ) และเพียงแค่ใช้ห่วงขณะ:

var a=document.getElementById('child'),
    b=document.getElementById('parent'),
    c;

c=a; while((c=c.parentNode)&&c!==b); //c=!!c;

if(!!c){ //`if(c)` if `c=!!c;` was used after while-loop above
    //do stuff
}

แทน:

var a=document.getElementById('child'),
    b=document.getElementById('parent'),
    c;

function childOf(c,p){while((c=c.parentNode)&&c!==p);return !!c}

c=childOf(a, b);    

if(c){ 
    //do stuff
}

4
@SolomonUcko: การเปรียบเทียบความเท่าเทียมกันอย่างเข้มงวด ( !==) สามารถปรับปรุงความเร็วได้โดยการทำให้คอมไพเลอร์ชัดเจนว่าสามารถข้ามการตรวจสอบชนิดและขั้นตอนการแปลงทางเลือกเพิ่มเติมที่จะเกิดขึ้นในการเปรียบเทียบความเท่าเทียมกันแบบหลวม ๆ ดังนั้นการปรับปรุงความเร็ว ต้องการเกิดขึ้นซึ่งเป็นประโยชน์ต่อโปรแกรมเมอร์ด้วย) ฉันดูเหมือนจะจำได้เมื่อฉันเขียนสิ่งนี้ว่าเพียงแค่การเปรียบเทียบแบบหลวม ๆ ( !=) เป็นข้อผิดพลาดอย่างมากหรือไม่สามารถใช้งานได้ในเบราว์เซอร์รุ่นเก่าบางรุ่น (ฉันสงสัยว่ามันเป็นใน IE6 แต่ฉันลืมไปแล้ว) )
GitaarLAB

29

โซลูชันอื่นที่ไม่ได้กล่าวถึง:

ตัวอย่างที่นี่

var parent = document.querySelector('.parent');

if (parent.querySelector('.child') !== null) {
    // .. it's a child
}

มันไม่สำคัญว่าองค์ประกอบนั้นจะเป็นลูกโดยตรงหรือไม่ก็ตาม


อีกทางเลือกหนึ่งโดยใช้.contains()วิธีการ :

ตัวอย่างที่นี่

var parent = document.querySelector('.parent'),
    child = document.querySelector('.child');

if (parent.contains(child)) {
    // .. it's a child
}

19

ลองดูที่โหนด # compareDocumentPosition

function isDescendant(ancestor,descendant){
    return ancestor.compareDocumentPosition(descendant) & 
        Node.DOCUMENT_POSITION_CONTAINS;
}

function isAncestor(descendant,ancestor){
    return descendant.compareDocumentPosition(ancestor) & 
        Node.DOCUMENT_POSITION_CONTAINED_BY;
}

ความสัมพันธ์อื่น ๆ ได้แก่DOCUMENT_POSITION_DISCONNECTED, และDOCUMENT_POSITION_PRECEDINGDOCUMENT_POSITION_FOLLOWING

ไม่รองรับใน IE <= 8


! ที่น่าสนใจ สิ่งนี้ทำให้ฉันนึกถึงฟังก์ชั่น JS ที่ฉันพัฒนาเมื่อปี 2003 ... ใช้เวลาหลายวันและได้รับความช่วยเหลือจากนักพัฒนา JS บางคน ... เหลือเชื่อ (และเศร้า) ที่ทุกวันนี้ยังไม่มีการลากและวางวัตถุเต็มเบราว์เซอร์ .
DavidTaubmann


2

ฉันพบรหัสที่ยอดเยี่ยมเพื่อตรวจสอบว่าองค์ประกอบนั้นเป็นลูกขององค์ประกอบอื่นหรือไม่ ฉันต้องใช้สิ่งนี้เพราะ IE ไม่รองรับ.containsวิธีองค์ประกอบ หวังว่านี่จะช่วยผู้อื่นเช่นกัน

ด้านล่างเป็นฟังก์ชั่น:

function isChildOf(childObject, containerObject) {
  var returnValue = false;
  var currentObject;

  if (typeof containerObject === 'string') {
    containerObject = document.getElementById(containerObject);
  }
  if (typeof childObject === 'string') {
    childObject = document.getElementById(childObject);
  }

  currentObject = childObject.parentNode;

  while (currentObject !== undefined) {
    if (currentObject === document.body) {
      break;
    }

    if (currentObject.id == containerObject.id) {
      returnValue = true;
      break;
    }

    // Move up the hierarchy
    currentObject = currentObject.parentNode;
  }

  return returnValue;
}

ฉันลองใช้โปลิฟิลอื่น ๆ ทุกหน้าในหน้านี้รวมถึง. กัปตันและนี่เป็นสิ่งเดียวที่ใช้ได้ผล ขอบคุณ!
protoEvangelion

1

ลองอันนี้:

x = document.getElementById("td35");
if (x.childElementCount > 0) {
    x = document.getElementById("LastRow");
    x.style.display = "block";
}
else {
    x = document.getElementById("LastRow");
    x.style.display = "none";
}

-2

ฉันเพิ่งเจอฟังก์ชันนี้ซึ่งอาจทำ:

Node.compareDocumentPosition ()


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