Tìm phần tử gần nhất mà không có jQuery


90

Tôi đang cố gắng tìm phần tử gần nhất với tên thẻ cụ thể mà không cần jquery. Khi tôi nhấp vào một, <th>tôi muốn truy cập vào <tbody>bảng đó. Gợi ý? Tôi đã đọc về bù đắp nhưng không thực sự hiểu nó quá nhiều. Tôi có nên sử dụng:

Giả sử th đã được đặt thành phần tử thứ được nhấp

th.offsetParent.getElementsByTagName('tbody')[0]

1
Nếu bạn thấy rằng bạn phải bắt đầu duyệt qua DOM thì đây là một trường hợp mà số kbs bổ sung được phân bổ cho jquery sẽ đáng giá.
Kevin Bowersox


2
Tôi nghĩ đây là một câu hỏi rất quan trọng và hợp lệ. Không có lý do gì để phản đối.

el.closest('tbody')cho các trình duyệt không phải tức là. Xem thêm câu trả lời phức tạp + polyfill bên dưới.
oriadam

Câu trả lời:


69

Tuy nhiên, rất ít (rất) muộn đến bữa tiệc. Điều này sẽ thực hiện thủ thuật :

function closest(el, selector) {
    var matchesFn;

    // find vendor prefix
    ['matches','webkitMatchesSelector','mozMatchesSelector','msMatchesSelector','oMatchesSelector'].some(function(fn) {
        if (typeof document.body[fn] == 'function') {
            matchesFn = fn;
            return true;
        }
        return false;
    })

    var parent;

    // traverse parents
    while (el) {
        parent = el.parentElement;
        if (parent && parent[matchesFn](selector)) {
            return parent;
        }
        el = parent;
    }

    return null;
}

2
Câu trả lời tuyệt vời đó là một điều trị. MDN cũng có một polyfill cho element.closest (). Chrome bao gồm element.matches () trong bản phát hành hiện tại, vì vậy không cần tiền tố. Tôi vừa thêm điều này vào lib mà tôi đang sử dụng trong một ứng dụng mà tôi đang phát triển, Clibu.
nevf

2
Tôi đã thay đổi mã để nó cũng kiểm tra phần tử elmà jQuery.closest () và Element.closest () thực hiện. for( var parent = el ; parent !== null && !parent[matchesFn](selector) ; parent = el.parentElement ){ el = parent; } return parent;
nevf

1
Mã này là tốt, nhưng nó thiếu một dấu chấm phẩy và cũng xác định parenttrong phạm vi toàn cầu (!)
Steven Lu

1
Có thể đáng nói: developer.mozilla.org/en-US/docs/Web/API/Element/closest phương pháp gốc để hỗ trợ gần nhất, mặc dù hơi thấp: Chrome41, FF35, IE-nope, Opera28, Safari9
Michiel D

Chỉ cần một lưu ý về điều này, bạn có thể muốn làm el.parentNodehoặc điều này sẽ bị hỏng khi duyệt qua SVG trong IE.
smcka

124

Rất đơn giản:

el.closest('tbody')

Hỗ trợ trên tất cả các trình duyệt ngoại trừ IE.
CẬP NHẬT : Edge hiện cũng hỗ trợ nó.

Không cần jQuery. Hơn nữa, thay thế jQuery $(this).closest('tbody')bằng $(this.closest('tbody'))sẽ tăng hiệu suất, đáng kể khi phần tử không được tìm thấy.

Polyfill cho IE:

if (!Element.prototype.matches) Element.prototype.matches = Element.prototype.msMatchesSelector;
if (!Element.prototype.closest) Element.prototype.closest = function (selector) {
    var el = this;
    while (el) {
        if (el.matches(selector)) {
            return el;
        }
        el = el.parentElement;
    }
};

Lưu ý rằng không có returnkhi không tìm thấy phần tử, trả về hiệu quả undefinedkhi không tìm thấy phần tử gần nhất.

Để biết thêm chi tiết, hãy xem: https://developer.mozilla.org/en-US/docs/Web/API/Element/closest


4
Tại sao câu trả lời này lại ở cuối trang? Nên đứng đầu. Cảm ơn rất nhiều
Julien Le Coupanec

gần nhất là trong jQuery nhưng OP nói không có jQuery.
steve moretz

@stevemoretz. gần nhất là mẹ đẻ Javascript nay
Louise Eggleton

@LouiseEggleton Yup nó là, oops
steve Moretz

Đó được gọi là câu trả lời tốt nhất!
ChosenUser

22

Đây là cách bạn lấy phần tử gần nhất theo tên thẻ mà không cần jQuery:

function getClosest(el, tag) {
  // this is necessary since nodeName is always in upper case
  tag = tag.toUpperCase();
  do {
    if (el.nodeName === tag) {
      // tag name is found! let's return it. :)
      return el;
    }
  } while (el = el.parentNode);

  // not found :(
  return null;
}

getClosest(th, 'tbody');

2
Tôi không tin rằng điều này sẽ hiệu quả. Nó chỉ kiểm tra cây DOM. th-> thead-> bảng không bao giờ coi anh chị em
hunterc

2
Bạn nên nêu trường hợp cụ thể đó trong câu hỏi của mình.
Joon

2
Nhìn. Hàm này đi qua các Mã cha để tìm mã mẹ gần nhất (thậm chí theo phần mở rộng) sử dụng thẻ được cung cấp. Điều này sẽ không đi "xuống" cây tài liệu, chỉ "lên". Người dùng bình thường sẽ thấy nút "gần nhất" rất có thể là anh em ruột thịt nhất. Anh ta chỉ không biết thuật ngữ mà anh ta cần.

1
Công bằng với @Jhawins, định nghĩa của bạn về gần nhất là những gì thuật ngữ jQuery gần nhất. Đây không phải là một câu hỏi jQuery. Tôi không thể chứng thực tại sao jQuery quyết định gần nhất có nghĩa là tổ tiên gần nhất, nhưng một định nghĩa hợp lý hơn sẽ là phần tử gần nhất cho dù đó là cha mẹ, anh chị em trước, anh chị em kế tiếp, v.v. Bất cứ thứ gì được tìm thấy "gần nhất" với phần tử đích.
ryandlf

1
@ryandlf Nhưng nếu bạn có cả cha mẹ và anh chị em ở cùng một "khoảng cách" thì sao? Định nghĩa của jQuery rõ ràng là nó sẽ trả về tối đa một kết quả phù hợp.
mtone

9

Có một chức năng tiêu chuẩn hóa để thực hiện điều này: Element.closest . Hầu hết các trình duyệt ngoại trừ IE11 đều hỗ trợ nó ( chi tiết theo caniuse.com ). Tài liệu MDN cũng bao gồm polyfill trong trường hợp bạn phải nhắm mục tiêu các trình duyệt cũ hơn.

Để tìm tbodycha mẹ gần nhất thbạn có thể làm:

th.closest('tbody');

Trong trường hợp bạn muốn tự viết hàm - đây là những gì tôi đã nghĩ ra:

function findClosestParent (startElement, fn) {
  var parent = startElement.parentElement;
  if (!parent) return undefined;
  return fn(parent) ? parent : findClosestParent(parent, fn);
}

Để tìm cha mẹ gần nhất theo tên thẻ, bạn có thể sử dụng nó như sau:

findClosestParent(x, element => return element.tagName === "SECTION");

6
function closest(el, sel) {
    if (el != null)
        return el.matches(sel) ? el 
            : (el.querySelector(sel) 
                || closest(el.parentNode, sel));
}

Giải pháp này sử dụng một số tính năng mới hơn của thông số kỹ thuật HTML 5 và sử dụng tính năng này trên các trình duyệt cũ hơn / không tương thích (đọc: Internet Explorer) sẽ yêu cầu polyfill.

Element.prototype.matches = (Element.prototype.matches || Element.prototype.mozMatchesSelector 
    || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector 
    || Element.prototype.webkitMatchesSelector || Element.prototype.webkitMatchesSelector);

Nó luôn luôn đi đầu bảng đầu tiên. không phải bảng gần nhất .. xem liên kết này jsfiddle.net/guuy5kof
Balachandran

bắt tốt! đã sửa, xem ở đây jsfiddle.net/c2pqc2x0, hãy ủng hộ tôi như một ông chủ :)
Matthew James Davis

ya tôi ốm làm ... Bạn có kiểm tra kết quả fiddle của bạn, nó cho thấy ..matches lỗi undefined
Balachandran

hoạt động tốt cho tôi, bạn đang làm việc trên trình duyệt nào? điều này không được hỗ trợ trong các trình duyệt cũ hơn
Matthew James Davis

hey phép ra rõ ràng rằng downvote những gì bạn nói bro
Matthew James Davis

3

Để mở rộng câu trả lời @SalmanPK

nó sẽ cho phép sử dụng nút làm bộ chọn, hữu ích khi bạn làm việc với các sự kiện như di chuột qua.

function closest(el, selector) {
    if (typeof selector === 'string') {
        matches = el.webkitMatchesSelector ? 'webkitMatchesSelector' : (el.msMatchesSelector ? 'msMatchesSelector' : 'matches');
        while (el.parentElement) {
            if (el[matches](selector)) {
                return el
            };
            el = el.parentElement;
        }
    } else {
        while (el.parentElement) {
            if (el === selector) {
                return el
            };
            el = el.parentElement;
        }
    }

    return null;
}

2

Đây là chức năng đơn giản mà tôi đang sử dụng: -

function closest(el, selector) {
    var matches = el.webkitMatchesSelector ? 'webkitMatchesSelector' : (el.msMatchesSelector ? 'msMatchesSelector' : 'matches');

    while (el.parentElement) {
        if (el[matches](selector)) return el;

        el = el.parentElement;
    }

    return null;
}

2

Tóm lược:

Để tìm một tổ tiên cụ thể, chúng ta có thể sử dụng:

Element.closest();

Hàm này lấy một chuỗi bộ chọn CSS làm đối số. sau đó nó trả về tổ tiên gần nhất của phần tử hiện tại (hoặc chính phần tử đó) khớp với bộ chọn CSS đã được truyền trong các đối số. Nếu không có tổ tiên nó sẽ trở lại null.

Thí dụ:

const child = document.querySelector('.child');
// select the child

console.dir(child.closest('.parent').className);
// check if there is any ancestor called parent
<div class="parent">
  <div></div>
  <div>
    <div></div>
    <div class="child"></div>
  </div>
</div>


1

Lấy phần tử DOM gần nhất lên cây có chứa lớp, ID, thuộc tính dữ liệu hoặc thẻ. Bao gồm chính phần tử. Được hỗ trợ trở lại IE6.

var getClosest = function (elem, selector) {

    var firstChar = selector.charAt(0);

    // Get closest match
    for ( ; elem && elem !== document; elem = elem.parentNode ) {

        // If selector is a class
        if ( firstChar === '.' ) {
            if ( elem.classList.contains( selector.substr(1) ) ) {
                return elem;
            }
        }

        // If selector is an ID
        if ( firstChar === '#' ) {
            if ( elem.id === selector.substr(1) ) {
                return elem;
            }
        } 

        // If selector is a data attribute
        if ( firstChar === '[' ) {
            if ( elem.hasAttribute( selector.substr(1, selector.length - 2) ) ) {
                return elem;
            }
        }

        // If selector is a tag
        if ( elem.tagName.toLowerCase() === selector ) {
            return elem;
        }

    }

    return false;

};

var elem = document.querySelector('#some-element');
var closest = getClosest(elem, '.some-class');
var closestLink = getClosest(elem, 'a');
var closestExcludingElement = getClosest(elem.parentNode, '.some-class');

Tại sao không sử dụng một công tắc firstCharthay vì tất cả các IFđiều kiện?
Rafael Herscovici

Tại sao lại sử dụng một forvòng lặp bị che khuất ?
Rafael Herscovici

1

Tìm các Mã con của Phần tử gần nhất.

closest:function(el, selector,userMatchFn) {
var matchesFn;

// find vendor prefix
['matches','webkitMatchesSelector','mozMatchesSelector','msMatchesSelector','oMatchesSelector'].some(function(fn) {
    if (typeof document.body[fn] == 'function') {
        matchesFn = fn;
        return true;
    }
    return false;
});
function findInChilds(el){
    if(!el) return false;
    if(el && el[matchesFn] && el[matchesFn](selector)

    && userMatchFn(el) ) return [el];
    var resultAsArr=[];
    if(el.childNodes && el.childNodes.length){
        for(var i=0;i< el.childNodes.length;i++)
        {
             var child=el.childNodes[i];
             var resultForChild=findInChilds(child);
            if(resultForChild instanceof Array){
                for(var j=0;j<resultForChild.length;j++)
                {
                    resultAsArr.push(resultForChild[j]);
                }
            } 
        }

    }
    return resultAsArr.length?resultAsArr: false;
}

var parent;
if(!userMatchFn || arguments.length==2) userMatchFn=function(){return true;}
while (el) {
    parent = el.parentElement;
    result=findInChilds(parent);
    if (result)     return result;

    el = parent;
}

return null;

}


0

Đây.

function findNearest(el, tag) {
    while( el && el.tagName && el.tagName !== tag.toUpperCase()) {
        el = el.nextSibling;     
    } return el;
} 

Chỉ tìm thấy anh chị em xa hơn dưới gốc cây. Sử dụng beforeSibling để đi theo hướng khác Hoặc sử dụng các biến để đi theo cả hai cách và trả về tùy theo điều kiện nào được tìm thấy trước. Bạn có ý tưởng chung, nhưng nếu bạn muốn duyệt qua các Mã cha hoặc con nếu anh chị em không khớp, bạn cũng có thể sử dụng jQuery. Tại thời điểm đó, nó dễ dàng đáng giá.


0

Đến bữa tiệc hơi muộn, nhưng khi tôi đi ngang qua và chỉ trả lời lại một câu hỏi tương tự, tôi đưa ra giải pháp của mình ở đây - chúng ta có thể nói đó là closest()cách tiếp cận JQuery , nhưng bằng JavaScript tốt.

Nó không cần bất kỳ pollyfills nào và đó là các trình duyệt cũ hơn và thân thiện với IE (:-)): https://stackoverflow.com/a/48726873/2816279


-4

Tôi nghĩ Mã dễ bắt nhất với jquery gần nhất:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
    $(document).ready(function () {
        $(".add").on("click", function () {
            var v = $(this).closest(".division").find("input[name='roll']").val();
            alert(v);
        });
    });
</script>
<?php

for ($i = 1; $i <= 5; $i++) {
    echo'<div class = "division">'
        . '<form method="POST" action="">'
        . '<p><input type="number" name="roll" placeholder="Enter Roll"></p>'
        . '<p><input type="button" class="add" name = "submit" value = "Click"></p>'
        . '</form></div>';
}
?>

Cảm ơn nhiều.

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.