ค้นหาองค์ประกอบบรรพบุรุษที่ใกล้เคียงที่สุดซึ่งมีคลาสเฉพาะ


225

ฉันสามารถหาบรรพบุรุษขององค์ประกอบที่ใกล้เคียงขึ้นไปบนต้นไม้ที่มีระดับโดยเฉพาะอย่างยิ่งใน JavaScript บริสุทธิ์ ? ตัวอย่างเช่นในต้นไม้เช่นนั้น:

<div class="far ancestor">
    <div class="near ancestor">
        <p>Where am I?</p>
    </div>
</div>

แล้วฉันต้องการdiv.near.ancestorถ้าฉันลองนี้บนและค้นหาpancestor


1
โปรดทราบว่า div ภายนอกไม่ใช่ผู้ปกครองมันเป็นบรรพบุรุษของpองค์ประกอบ ele.parentNodeถ้าคุณจริงเพียงต้องการที่จะได้รับโหนดแม่ที่คุณสามารถทำได้
เฟลิกซ์กลิง

@ FelixKling: ไม่ทราบคำศัพท์นั้น ฉันจะเปลี่ยนมัน
rvighne

3
มันเหมือนกับที่เราใช้กันจริง ๆ :) พ่อ (พ่อแม่) ของพ่อคุณ (พ่อแม่) ไม่ใช่พ่อของคุณ (พ่อแม่) มันเป็นปู่ (ปู่ย่าตายาย) หรือพูดถึงบรรพบุรุษของคุณ
เฟลิกซ์กลิง

คำตอบ:


346

อัปเดต: รองรับเบราว์เซอร์หลัก ๆ

document.querySelector("p").closest(".near.ancestor")

โปรดทราบว่าสิ่งนี้สามารถจับคู่ตัวเลือกได้ไม่เพียง แต่คลาสเท่านั้น

https://developer.mozilla.org/en-US/docs/Web/API/Element.closest


สำหรับเบราว์เซอร์รุ่นเก่าที่ไม่สนับสนุนclosest()แต่มีmatches()หนึ่งสามารถสร้างการจับคู่ตัวเลือกคล้ายกับการจับคู่คลาสของ @ rvighne:

function findAncestor (el, sel) {
    while ((el = el.parentElement) && !((el.matches || el.matchesSelector).call(el,sel)));
    return el;
}

1
ยังไม่รองรับใน Internet Explorer, Edge และ Opera Mini เวอร์ชันปัจจุบัน
kleinfreund

1
@kleinfreund - ยังไม่รองรับใน IE, Edge หรือ Opera mini caniuse.com/#search=closest
evolutionxbox

8
มิถุนายน 2559: ดูเหมือนว่าเบราว์เซอร์ทั้งหมดยังคงไม่ติด
Kunal

3
มีโปลิฟิล DOM ระดับ 4พร้อมclosestฟีเจอร์การเดินทางข้ามขั้นสูงอื่น ๆ อีกมากมาย คุณสามารถดึงการติดตั้งใช้งานด้วยตัวเองหรือรวมทั้งชุดเพื่อรับคุณลักษณะใหม่มากมายในรหัสของคุณ
Garbee

2
Edge 15 รองรับการใช้งานซึ่งควรครอบคลุมเบราว์เซอร์หลักทั้งหมด เว้นแต่ว่าคุณจะนับ IE11 แต่ไม่ได้รับการอัปเดตใด ๆ
the8472

195

นี่เป็นการหลอกลวง:

function findAncestor (el, cls) {
    while ((el = el.parentElement) && !el.classList.contains(cls));
    return el;
}

ในขณะที่รอห่วงจนelมีระดับที่ต้องการและจะกำหนดelให้กับผู้ปกครองทุกซ้ำดังนั้นในท้ายที่สุดคุณมีบรรพบุรุษที่มีระดับนั้นหรือelnull

นี่คือปริศนาหากใครต้องการที่จะปรับปรุง มันจะไม่ทำงานบนเบราว์เซอร์รุ่นเก่า (เช่น IE) เห็นนี้ตารางเข้ากันได้สำหรับ classList parentElementถูกใช้ที่นี่เพราะparentNodeจะเกี่ยวข้องกับการทำงานมากขึ้นเพื่อให้แน่ใจว่าโหนดเป็นองค์ประกอบ


1
สำหรับทางเลือกกับผู้.classListดูstackoverflow.com/q/5898656/218196
เฟลิกซ์คลิง

1
ฉันแก้ไขรหัสแล้ว แต่มันยังคงโยนข้อผิดพลาดหากไม่มีบรรพบุรุษที่มีชื่อคลาสดังกล่าว
เฟลิกซ์กลิง

2
เอ่อผมคิดว่ามันไม่ได้อยู่แต่ผมผิด อย่างไรก็ตามparentElementเป็นคุณสมบัติใหม่ (DOM ระดับ 4) และparentNodeมีอยู่ตั้งแต่ ... ตลอดไป ดังนั้นจึงparentNodeสามารถใช้งานได้ในเบราว์เซอร์รุ่นเก่าในขณะที่parentElementอาจไม่ทำงาน แน่นอนคุณสามารถสร้างอาร์กิวเมนต์เดียวกันสำหรับ / ต่อclassListแต่ไม่มีทางเลือกง่าย ๆ เช่นparentElementมี ที่จริงผมสงสัยว่าทำไมมีอยู่ทั้งหมดก็ไม่ได้ดูเหมือนจะเพิ่มมูลค่าใดparentElementparentNode
เฟลิกซ์กลิง

1
@ FelixKling: เพิ่งแก้ไขตอนนี้มันใช้ได้ดีกับกรณีที่ไม่มีบรรพบุรุษดังกล่าวอยู่ ฉันต้องได้รับ overeager กับรุ่นต้นฉบับสั้น
rvighne

3
หากคุณต้องการตรวจสอบชื่อคลาสในองค์ประกอบพารามิเตอร์ก่อนที่จะค้นหาบรรพบุรุษคุณสามารถสลับลำดับของเงื่อนไขในลูปได้:while (!el.classList.contains(cls) && (el = el.parentElement));
Nicomak

34

ใช้element.closest ()

https://developer.mozilla.org/en-US/docs/Web/API/Element/closest

ดูตัวอย่าง DOM นี้:

<article>
  <div id="div-01">Here is div-01
    <div id="div-02">Here is div-02
      <div id="div-03">Here is div-03</div>
    </div>
  </div>
</article>

นี่คือวิธีที่คุณจะใช้ element.closest:

var el = document.getElementById('div-03');

var r1 = el.closest("#div-02");  
// returns the element with the id=div-02

var r2 = el.closest("div div");  
// returns the closest ancestor which is a div in div, here is div-03 itself

var r3 = el.closest("article > div");  
// returns the closest ancestor which is a div and has a parent article, here is div-01

var r4 = el.closest(":not(div)");
// returns the closest ancestor which is not a div, here is the outmost article

14

อ้างอิงจากคำตอบ the8472และhttps://developer.mozilla.org/en-US/docs/Web/API/Element/matchesนี่เป็นโซลูชันข้ามแพลตฟอร์ม 2017:

if (!Element.prototype.matches) {
    Element.prototype.matches =
        Element.prototype.matchesSelector ||
        Element.prototype.mozMatchesSelector ||
        Element.prototype.msMatchesSelector ||
        Element.prototype.oMatchesSelector ||
        Element.prototype.webkitMatchesSelector ||
        function(s) {
            var matches = (this.document || this.ownerDocument).querySelectorAll(s),
                i = matches.length;
            while (--i >= 0 && matches.item(i) !== this) {}
            return i > -1;
        };
}

function findAncestor(el, sel) {
    if (typeof el.closest === 'function') {
        return el.closest(sel) || null;
    }
    while (el) {
        if (el.matches(sel)) {
            return el;
        }
        el = el.parentElement;
    }
    return null;
}

11

@rvighne โซลูชันทำงานได้ดี แต่ตามที่ระบุไว้ในความคิดเห็นParentElementและClassListทั้งสองมีปัญหาความเข้ากันได้ เพื่อให้เข้ากันมากขึ้นฉันได้ใช้:

function findAncestor (el, cls) {
    while ((el = el.parentNode) && el.className.indexOf(cls) < 0);
    return el;
}
  • parentNodeคุณสมบัติแทนparentElementคุณสมบัติ
  • indexOfวิธีการเกี่ยวกับclassNameทรัพย์สินแทนcontainsวิธีการในclassListทรัพย์สิน

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

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