ค้นหาองค์ประกอบที่ใกล้เคียงที่สุดโดยไม่ต้องใช้ jQuery


90

ฉันกำลังพยายามค้นหาองค์ประกอบที่ใกล้เคียงที่สุดด้วยชื่อแท็กเฉพาะโดยไม่มี jquery เมื่อฉันคลิกที่<th>ฉันต้องการเข้าถึง<tbody>ตารางนั้น ข้อเสนอแนะ? ฉันอ่านเกี่ยวกับการชดเชย แต่ก็ไม่เข้าใจมันมากนัก ฉันควรใช้:

สมมติว่า th ถูกตั้งค่าเป็นองค์ประกอบที่คลิกแล้ว

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

1
หากคุณพบว่าคุณต้องเริ่มข้ามผ่าน DOM นี่เป็นอินสแตนซ์หนึ่งที่ kbs พิเศษที่มาจาก jquery จะคุ้มค่า
Kevin Bowersox


2
ฉันคิดว่านี่เป็นคำถามที่สำคัญและถูกต้องมาก ไม่มีเหตุผลในการโหวตลด

el.closest('tbody')สำหรับเบราว์เซอร์ที่ไม่ใช่ IE ดูคำตอบแบบละเอียดเพิ่มเติม + โพลีฟิลด้านล่าง
oriadam

คำตอบ:


69

สายไปงานปาร์ตี้น้อย (มาก) แต่กระนั้น สิ่งนี้ควรทำเคล็ดลับ :

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
คำตอบที่ดีซึ่งใช้งานได้ดี MDN ยังมี polyfill สำหรับ element.closest () Chrome มี element.matches () ในรุ่นปัจจุบันดังนั้นจึงไม่จำเป็นต้องมีคำนำหน้า ฉันเพิ่งเพิ่มสิ่งนี้ลงใน lib ที่ฉันใช้ในแอพที่ฉันกำลังพัฒนา Clibu
nevf

2
ฉันเปลี่ยนรหัสเพื่อให้ทดสอบองค์ประกอบelที่ jQuery.closest () และ Element.closest () ทำด้วย for( var parent = el ; parent !== null && !parent[matchesFn](selector) ; parent = el.parentElement ){ el = parent; } return parent;
nevf

1
รหัสนี้ใช้ได้ แต่ไม่มีเครื่องหมายอัฒภาคและกำหนดparentในขอบเขตทั่วโลก (!)
Steven Lu

1
อาจคุ้มค่าที่จะกล่าวถึง: developer.mozilla.org/en-US/docs/Web/API/Element/closestวิธีการดั้งเดิมที่ใกล้เคียงที่สุดแม้ว่าจะมีการสนับสนุนเพียงเล็กน้อย: Chrome41, FF35, IE-nope, Opera28, Safari9
Michiel D

โปรดทราบว่าคุณอาจต้องทำel.parentNodeหรือสิ่งนี้จะพังเมื่อข้าม SVG ใน IE
smcka

125

ง่ายมาก:

el.closest('tbody')

รองรับในทุกเบราว์เซอร์ยกเว้น IE
UPDATE : ตอนนี้ Edge ก็รองรับเช่นกัน

ไม่จำเป็นต้อง jQuery ยิ่งไปกว่านั้นการแทนที่ jQuery $(this).closest('tbody')ด้วย$(this.closest('tbody'))จะช่วยเพิ่มประสิทธิภาพอย่างมากเมื่อไม่พบองค์ประกอบ

Polyfill สำหรับ 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;
    }
};

โปรดทราบว่าไม่มีreturnเมื่อไม่พบองค์ประกอบซึ่งจะส่งคืนอย่างมีประสิทธิภาพundefinedเมื่อไม่พบองค์ประกอบที่ใกล้เคียงที่สุด

ดูรายละเอียดเพิ่มเติมได้ที่: https://developer.mozilla.org/en-US/docs/Web/API/Element/closest


4
ทำไมคำตอบนี้จึงอยู่ที่ด้านล่างของหน้า ควรอยู่ด้านบนสุด ขอบคุณมาก
Julien Le Coupanec

ใกล้เคียงที่สุดอยู่ใน jQuery แต่ OP บอกว่าไม่มี jQuery
steve moretz

@stevemoretz. ใกล้เคียงที่สุดคือ JavaScript ดั้งเดิมในขณะนี้
Louise Eggleton

@LouiseEggleton ใช่แล้วอ๊ะ
สตีฟมอเรตซ์

เรียกว่าตอบโจทย์ที่สุด!
ChosenUser

22

นี่คือวิธีที่คุณได้รับองค์ประกอบที่ใกล้เคียงที่สุดโดยใช้ชื่อแท็กโดยไม่มี 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
ฉันไม่เชื่อว่ามันจะได้ผล ตรวจสอบโครงสร้าง DOM เท่านั้น th-> thead-> table ไม่เคยคำนึงถึงพี่น้อง
hunterc

2
คุณควรระบุกรณีเฉพาะนั้นในคำถามของคุณ
จุน

2
ดู. ฟังก์ชันนี้ข้าม parentNodes เพื่อค้นหาพาเรนต์ที่ใกล้เคียงที่สุด (แม้โดยส่วนขยาย) ที่ใช้แท็กที่ให้มา การดำเนินการนี้จะไม่ "ลง" โครงสร้างเอกสารเพียง แต่ "ขึ้น" ผู้ใช้โดยเฉลี่ยจะเห็นว่าโหนด "ใกล้เคียงที่สุด" น่าจะเป็นพี่น้องที่สนิทที่สุด เขาไม่รู้คำศัพท์ที่เขาต้องการ

1
ในความเป็นธรรมกับ @Jhawins คำจำกัดความของคุณที่ใกล้เคียงที่สุดคือสิ่งที่ jQuery ใกล้เคียงที่สุด นี่ไม่ใช่คำถาม jQuery ฉันไม่สามารถยืนยันได้ว่าเหตุใด jQuery จึงตัดสินใจที่ใกล้เคียงที่สุดจึงหมายถึงบรรพบุรุษที่ใกล้เคียงที่สุด แต่คำจำกัดความที่สมเหตุสมผลกว่าจะเป็นองค์ประกอบที่ใกล้เคียงที่สุดไม่ว่าจะเป็นพ่อแม่พี่น้องคนก่อนพี่น้องคนต่อไป ฯลฯ สิ่งใดก็ตามที่พบ "ใกล้เคียงที่สุด" กับองค์ประกอบเป้าหมาย
ryandlf

1
@ryandlf แต่ถ้าคุณมีทั้งพ่อและแม่และพี่น้องที่ "ห่างกัน" เท่ากันล่ะ? คำจำกัดความของ jQuery นั้นชัดเจนว่าจะส่งคืนรายการที่ตรงกันมากที่สุดหนึ่งรายการ
mtone

9

มีฟังก์ชั่นมาตรฐานในการทำเช่นนี้มีอยู่: Element.closest เบราว์เซอร์ส่วนใหญ่ยกเว้น IE11 รองรับ ( รายละเอียดโดย caniuse.com ) เอกสาร MDNได้แก่ polyfill ในกรณีที่คุณต้องกำหนดเป้าหมายเบราว์เซอร์ที่มีอายุมากกว่า

ในการค้นหาtbodyผู้ปกครองที่ใกล้เคียงที่สุดที่thคุณสามารถทำได้:

th.closest('tbody');

ในกรณีที่คุณต้องการเขียนฟังก์ชันด้วยตัวเองนี่คือสิ่งที่ฉันคิดขึ้นมา:

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

หากต้องการค้นหาพาเรนต์ที่ใกล้เคียงที่สุดโดยใช้ชื่อแท็กคุณสามารถใช้มันได้ดังนี้:

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

โซลูชันนี้ใช้คุณลักษณะล่าสุดบางอย่างของข้อมูลจำเพาะ HTML 5 และการใช้สิ่งนี้กับเบราว์เซอร์รุ่นเก่า / ที่เข้ากันไม่ได้ (อ่าน: Internet Explorer) จะต้องใช้ polyfill

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

มันจะไปอยู่บนโต๊ะแรกเสมอ โต๊ะไม่ใกล้เคียงที่สุด .. ดูลิงค์นี้jsfiddle.net/guuy5kof
Balachandran

รับได้สวย! แก้ไขดูที่นี่jsfiddle.net/c2pqc2x0โปรดโหวตฉันเหมือนเจ้านาย :)
Matthew James Davis

ฉันไม่ดี ... คุณตรวจสอบผลการเล่นซอของคุณแล้วมันแสดงข้อผิดพลาด .. ตรงกับที่ไม่ได้กำหนด
Balachandran

ใช้งานได้ดีสำหรับฉันคุณใช้เบราว์เซอร์ใด สิ่งนี้ไม่รองรับในเบราว์เซอร์รุ่นเก่า
Matthew James Davis

เฮ้ขอให้ชัดเจนว่าการโหวตลงคะแนนคุณพูดอะไร
Matthew James Davis

3

เพื่อขยายคำตอบ @SalmanPK

จะอนุญาตให้ใช้ node เป็นตัวเลือกซึ่งมีประโยชน์เมื่อคุณทำงานกับเหตุการณ์เช่น mouseover

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

นี่คือฟังก์ชั่นง่ายๆที่ฉันใช้: -

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

สรุป:

สำหรับการค้นหาบรรพบุรุษโดยเฉพาะเราสามารถใช้:

Element.closest();

ฟังก์ชันนี้ใช้สตริงตัวเลือก CSS เป็นอาร์กิวเมนต์ จากนั้นจะส่งคืนบรรพบุรุษที่ใกล้เคียงที่สุดขององค์ประกอบปัจจุบัน (หรือองค์ประกอบนั้นเอง) ซึ่งตรงกับตัวเลือก CSS ที่ส่งผ่านไปในอาร์กิวเมนต์ nullถ้าไม่มีปู่ย่าตายายก็จะกลับ

ตัวอย่าง:

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

รับองค์ประกอบ DOM ที่ใกล้เคียงที่สุดกับโครงสร้างที่มีคลาส ID แอตทริบิวต์ข้อมูลหรือแท็ก รวมองค์ประกอบเอง สนับสนุนกลับไปที่ 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');

ทำไมไม่ใช้สวิตช์firstCharแทนIFเงื่อนไขทั้งหมด?
Rafael Herscovici

ทำไมต้องใช้forลูปปิดบัง?
Rafael Herscovici

1

ค้นหาองค์ประกอบ childNodes ที่ใกล้ที่สุด

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

ที่นี่.

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

พบเพียงพี่น้องที่อยู่ไกลออกไปจากต้นไม้ ใช้previousSiblingเพื่อไปทางอื่นหรือใช้ตัวแปรเพื่อสำรวจทั้งสองทางและส่งกลับแล้วแต่ว่าจะพบอะไรก่อน คุณจะได้รับแนวคิดทั่วไป แต่ถ้าคุณต้องการสำรวจผ่าน parentNodes หรือลูก ๆ หากพี่น้องไม่ตรงกับคุณอาจใช้ jQuery ได้เช่นกัน เมื่อถึงจุดนั้นก็คุ้มค่าอย่างง่ายดาย


0

ไปงานปาร์ตี้สายนิดหน่อย แต่ในขณะที่ฉันเดินผ่านไปและเพียงแค่ตอบคำถามที่คล้ายกันนี้กลับมาฉันก็วางวิธีแก้ปัญหาของฉันไว้ที่นี่ - เราสามารถพูดได้ว่าเป็นclosest()แนวทางของJQuery แต่ใน JavaScript ธรรมดา ๆ

ไม่จำเป็นต้องใช้ pollyfills ใด ๆ และเป็นเบราว์เซอร์รุ่นเก่าและ IE (:-)) เป็นมิตร: https://stackoverflow.com/a/48726873/2816279


-4

ฉันคิดว่ารหัสที่ง่ายที่สุดในการจับกับ jquery ที่ใกล้เคียงที่สุด:

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

ขอบคุณมาก.

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