Tìm phần tử tổ tiên gần nhất có một lớp cụ thể


225

Làm cách nào tôi có thể tìm thấy tổ tiên của một phần tử gần cây nhất có một lớp cụ thể, bằng JavaScript thuần ? Ví dụ, trong một cái cây như vậy:

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

Sau đó, tôi muốn div.near.ancestornếu tôi thử điều này trên pvà tìm kiếm ancestor.


1
xin lưu ý rằng div bên ngoài không phải là cha mẹ, nó là tổ tiên của pphần tử. Nếu bạn thực sự chỉ muốn lấy nút cha, bạn có thể làm ele.parentNode.
Felix Kling

@FelixKling: Không biết thuật ngữ đó; Tôi sẽ thay đổi điều đó.
rvighne

3
Nó thực sự giống như con người chúng ta sử dụng :) Cha (cha mẹ) của cha bạn (cha mẹ) không phải là cha của bạn (cha mẹ), đó là ông của bạn (ông bà), hay nói chung hơn là tổ tiên của bạn.
Felix Kling

Câu trả lời:


346

Cập nhật: Hiện được hỗ trợ trong hầu hết các trình duyệt chính

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

Lưu ý rằng điều này có thể phù hợp với các bộ chọn, không chỉ các lớp

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


Đối với các trình duyệt cũ không hỗ trợ closest()nhưng có matches()một trình duyệt có thể tạo kết hợp khớp chọn tương tự như đối sánh lớp của @ rvighne:

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

1
Vẫn không được hỗ trợ trong các phiên bản hiện tại của Internet Explorer, Edge và Opera Mini.
kleinfreund

1
@kleinfreund - vẫn chưa được hỗ trợ trong IE, Edge hoặc Opera mini. caniuse.com/#search=closest
Evolutionxbox

8
Tháng 6 năm 2016: Có vẻ như tất cả các trình duyệt vẫn chưa bắt kịp
Kunal

3
Có một polyfill DOM Cấp 4 cùng closestvới nhiều tính năng truyền tải DOM nâng cao hơn. Bạn có thể tự thực hiện việc triển khai đó hoặc bao gồm toàn bộ gói để có được nhiều tính năng mới trong mã của mình.
Garbee

2
Edge 15 có hỗ trợ, sẽ bao gồm tất cả các trình duyệt chính. Trừ khi bạn đếm IE11, nhưng điều đó không nhận được bất kỳ bản cập nhật nào
the8472

195

Đây là mẹo:

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

Trong khi chờ đợi vòng lặp cho đến khi elcó lớp mong muốn, và nó đặt elđể el's cha mẹ mỗi lần lặp nên cuối cùng, bạn có tổ tiên với lớp đó hoặcnull .

Đây là một câu đố , nếu bất cứ ai muốn cải thiện nó. Nó sẽ không hoạt động trên các trình duyệt cũ (ví dụ IE); xem bảng tương thích này cho classList . parentElementđược sử dụng ở đây vì parentNodesẽ liên quan đến nhiều công việc hơn để đảm bảo rằng nút là một phần tử.


1
Để thay thế .classList, hãy xem stackoverflow.com/q/5898656/218196 .
Felix Kling

1
Tôi đã sửa mã, nhưng nó vẫn sẽ báo lỗi nếu không có tổ tiên có tên lớp như vậy.
Felix Kling

2
Uh, tôi nghĩ nó không tồn tại, nhưng tôi đã sai . Dù sao, parentElementlà một tài sản khá mới (DOM cấp 4) và parentNodetồn tại kể từ ... mãi mãi. Vì vậy, parentNodecũng hoạt động trong các trình duyệt cũ hơn, trong khi parentElementcó thể không. Tất nhiên bạn có thể đưa ra lập luận tương tự cho / chống lại classList, nhưng nó không có một cách thay thế đơn giản, như parentElementcó. Tôi thực sự tự hỏi tại sao parentElementtồn tại, nó dường như không thêm bất kỳ giá trị nào parentNode.
Felix Kling

1
@FelixKling: Chỉ cần chỉnh sửa, bây giờ nó hoạt động tốt cho trường hợp khi không có tổ tiên như vậy tồn tại. Tôi đã phải vượt qua với phiên bản gốc ngắn.
rvighne

3
Nếu bạn muốn kiểm tra tên lớp trong chính phần tử tham số, trước khi tìm kiếm tổ tiên của nó, bạn chỉ cần chuyển thứ tự các điều kiện trong vòng lặp:while (!el.classList.contains(cls) && (el = el.parentElement));
Nicomak

34

Sử dụng phần tử.closest ()

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

Xem ví dụ này 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>

Đây là cách bạn sẽ sử dụng 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

Dựa trên câu trả lời8472https://developer.mozilla.org/en-US/docs/Web/API/Euity/matches ở đây là giải pháp đa nền tảng 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

Giải pháp @rvighne hoạt động tốt, nhưng như được xác định trong các nhận xét ParentElementClassListcả hai đều có vấn đề tương thích. Để làm cho nó tương thích hơn, tôi đã sử dụng:

function findAncestor (el, cls) {
    while ((el = el.parentNode) && el.className.indexOf(cls) < 0);
    return el;
}
  • parentNodetài sản thay vì parentElementtài sản
  • indexOfphương pháp trên classNametài sản thay vì containsphương pháp trên classListtài sản.

Tất nhiên, indexOf chỉ đơn giản là tìm kiếm sự hiện diện của chuỗi đó, nó không quan tâm nếu nó là toàn bộ chuỗi hay không. Vì vậy, nếu bạn có một yếu tố khác với loại 'tổ tiên', nó vẫn sẽ trở lại như đã tìm thấy 'tổ tiên', nếu đây là một vấn đề đối với bạn, có lẽ bạn có thể sử dụng regrec để tìm một kết quả khớp chính xác.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.