วิธีที่ดีที่สุดในการรับโหนดย่อย


233

ฉันสงสัยว่า JavaScript มีวิธีการมากมายเพื่อให้ได้องค์ประกอบลูกแรกจากองค์ประกอบใด ๆ แต่สิ่งใดที่ดีที่สุด โดยที่ดีที่สุดฉันหมายถึง: เบราว์เซอร์ที่เข้ากันได้มากที่สุดเร็วที่สุดครอบคลุมที่สุดและคาดเดาได้เมื่อมีพฤติกรรม รายการของวิธีการ / คุณสมบัติที่ฉันใช้เป็นนามแฝง:

var elem = document.getElementById('container');
var child = elem.children[0];
var child = elem.firstElementChild; // == children[0]

ใช้ได้กับทั้งสองกรณี:

var child = elem.childNodes[0]; // or childNodes[1], see below

นั่นคือในกรณีของรูปแบบหรือ<div>การทำซ้ำ หากฉันอาจพบองค์ประกอบข้อความ:

var child = elem.childNodes; // treat as NodeList
var child = elem.firstChild;

เท่าที่ผมสามารถทำงานออกfirstChildใช้ NodeList จากchildNodesและการใช้งานfirstElementChild childrenฉันใช้ข้อสมมติฐานนี้อ้างอิง MDN:

childNodeเป็นการอ้างอิงถึงองค์ประกอบลูกแรกของโหนดองค์ประกอบหรือnullถ้าไม่มี

ฉันเดาว่าในแง่ของความเร็วความแตกต่างถ้ามีจะไม่เป็นอะไรเลยเนื่องจากfirstElementChildมีการอ้างอิงอย่างมีประสิทธิภาพchildren[0]และchildrenวัตถุนั้นอยู่ในความทรงจำอยู่แล้ว

สิ่งที่ทำให้ฉันเป็นchildNodesวัตถุ ฉันใช้มันเพื่อดูฟอร์มในองค์ประกอบตาราง ในขณะที่childrenรายการองค์ประกอบของรูปแบบทั้งหมดchildNodesก็ดูเหมือนว่าจะมีช่องว่างจากรหัส HTML:

console.log(elem.childNodes[0]);
console.log(elem.firstChild);

บันทึกทั้งคู่ <TextNode textContent="\n ">

console.log(elem.childNodes[1]);
console.log(elem.children[0]);
console.log(elem.firstElementChild);

บันทึกทั้งหมด<input type="text">. มาทำไม ฉันเข้าใจว่าวัตถุหนึ่งจะอนุญาตให้ฉันทำงานกับรหัส HTML "แบบดิบ" ในขณะที่อีกชิ้นหนึ่งยึดติดกับ DOM แต่childNodesองค์ประกอบดูเหมือนว่าจะใช้ได้ทั้งสองระดับ

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

ใครสามารถอธิบายความแตกต่างระหว่างวัตถุที่อยู่ในมือ? หากมีความแตกต่างของความเร็วอย่างไรก็ตามเล็กน้อยฉันก็รู้เช่นกัน หากฉันเห็นสิ่งผิดทั้งหมดรู้สึกฟรีเพื่อให้ความรู้แก่ฉัน


PS: ได้โปรดได้โปรดฉันชอบ JavaScript ดังนั้นใช่ฉันต้องการจัดการกับสิ่งนี้ คำตอบเช่น“ jQuery เกี่ยวข้องกับสิ่งนี้เพื่อคุณ” ไม่ใช่สิ่งที่ฉันกำลังมองหา แท็ก


50
>> ได้โปรดได้โปรดฉันชอบ JavaScript ดังนั้นใช่ฉันต้องการจัดการกับสิ่งนี้ คำตอบเช่น 'jQuery เกี่ยวข้องกับสิ่งนี้เพื่อคุณ' ไม่ใช่สิ่งที่ฉันกำลังมองหา << upvotes สำหรับสิ่งนี้
david.barkhuizen

คำตอบ:


211

ดูเหมือนว่าคุณกำลังคิดมากอยู่ คุณสังเกตเห็นความแตกต่างระหว่างchildNodesและchildrenซึ่งchildNodesประกอบด้วยโหนดทั้งหมดรวมถึงโหนดข้อความที่ประกอบด้วยช่องว่างทั้งหมดในขณะที่childrenเป็นคอลเลกชันของโหนดลูกที่เป็นองค์ประกอบเท่านั้น นั่นคือทั้งหมดที่มีให้มัน

ไม่มีอะไรที่คาดเดาไม่ได้เกี่ยวกับคอลเล็กชั่นทั้งสองแม้ว่าจะมีปัญหาสองสามข้อที่ต้องระวัง:

  • IE <= 8 ไม่รวมถึงโหนดข้อความ white-only space ในchildNodesขณะที่เบราว์เซอร์อื่นทำ
  • IE <= 8 รวมโหนดความคิดเห็นไว้childrenในขณะที่เบราว์เซอร์อื่นมีองค์ประกอบเท่านั้น

children, firstElementChildและเพื่อน ๆ มีสิ่งอำนวยความสะดวกเพียงแค่นำเสนอมุมมองที่กรองของ DOM จำกัด องค์ประกอบเพียง


คิดได้มากถึงแม้ว่ามีสิ่งหนึ่งที่ยังคงเป็นปัญหาอยู่ฉัน: โหนดข้อความช่องว่างที่ childNode เลือกมานั้นเป็นเพียงบรรทัดใหม่และแท็บสองสามตัวในรหัส นี่เป็นเพราะประเภทของ XHTML หรือไม่ สามารถแก้ไขได้โดยใส่ช่องว่างเพื่อวางโค้ดภายในแท็กหรือไม่
Elias Van Ootegem

@EliasVanOotegem: นั่นไม่เกี่ยวข้องกับประเภทเอกสาร โหนดข้อความ white space จะรวมอยู่ใน DOM เสมอ (ยกเว้นใน IE <9) สำหรับทั้ง HTML และ XHTML ไม่ว่าจะปรากฏที่ใดในแหล่งที่มา โดยทั่วไปมันเป็นสิ่งที่ดีแม้ว่ามันจะสามารถทำให้คุณรู้ได้ว่าคุณไม่ได้เตรียมพร้อม
Tim Down

<td\n\t>content</td>ฉันหมายความว่ามันจะช่วยในการเขียนมาร์กอัปของฉันเช่นนี้ อย่างที่คุณอาจทำกับ XML เพื่อหลีกเลี่ยงช่องว่างส่วนเกินที่ถือว่าเป็นส่วนหนึ่งของข้อมูล (หรือ DOM ในกรณีนี้) เหตุใดช่องว่างภายในแท็กจึงรวมอยู่ใน DOM
Elias Van Ootegem

@EliasVanOotegem: โอ้ฉันเข้าใจแล้ว ช่องว่างหลังจากชื่อแท็กภายในแท็กจะถูกละเว้นดังนั้นคุณสามารถทำสิ่งนั้นได้ ฉันได้เห็นเทคนิคที่ใช้ในเลย์เอาต์ที่แม่นยำเพื่อป้องกันไม่ให้เบราว์เซอร์เพิ่มช่องว่างเล็ก ๆ สำหรับพื้นที่สีขาวระหว่างองค์ประกอบบางประเภท
Tim Down

2
@Christophe: อาจุดยุติธรรมขอบคุณ ฉันจะเพิ่มบันทึกลงในคำตอบของฉัน เนื่องจากที่childrenมาใน IE 4 มานานกว่าทศวรรษก่อนที่จะกลายเป็นมาตรฐานหรือถูกใช้โดยเบราว์เซอร์อื่นคุณสามารถยืนยันว่า IE <= 8 นั้นถูกต้องและเบราว์เซอร์อื่น ๆ นั้นไม่ถูกต้อง
Tim Down

23

firstElementChild อาจไม่พร้อมใช้งานใน IE <9 (เฉพาะ firstChild)

บน IE <9 firstChild เป็น firstElementChild เนื่องจาก MS DOM (IE <9) ไม่ได้เก็บโหนดข้อความว่างเปล่า แต่ถ้าคุณทำเช่นนั้นกับเบราว์เซอร์อื่นพวกเขาจะกลับมาโหนดข้อความเปล่า ...

ทางออกของฉัน

child=(elem.firstElementChild||elem.firstChild)

สิ่งนี้จะให้ลูกคนแรกแม้ใน IE <9


หากelem.firstChildไม่ใช่โหนดองค์ประกอบผลลัพธ์จะแตกต่างกันใน IE เก่าเนื่องจากelem.firstElementChildเป็นโหนดองค์ประกอบเสมอ
duri

ฉันขอโทษ แต่ฉันรู้วิธีรับลูกคนแรกในเบราว์เซอร์หลัก ๆ ทั้งหมด สิ่งที่ฉันอยากรู้ก็คือโครงสร้างของวัตถุที่แตกต่างกันอย่างไรเพื่อให้เข้าใจถึงความคล้ายคลึงและความแตกต่างระหว่างกัน ฉันจะแก้ไขคำถามของฉันในภายหลังเพื่อให้ชัดเจนยิ่งขึ้นขออภัยสำหรับการปะปน
Elias Van Ootegem

1
firstElementChildไม่จำเป็นต้องปลอดภัยในเบราว์เซอร์ที่ไม่ใช่ IE เช่น: Firefox เริ่มรองรับในเวอร์ชัน 3.5 เท่านั้น
Tim Down

สิ่งนี้ใช้ได้กับ chrome, IE9 และ IE8 เพราะหากพวกเขามี firstElementChild ดังนั้นการตรวจสอบอื่น ๆ จะไม่ถูกดำเนินการมิฉะนั้น (IE เก่า) เรามี firstChild และเป็นโหนดองค์ประกอบสำหรับเบราว์เซอร์ที่ไม่มี firstElementChild (IE <9 ) ... ฉันยังสงสัยเกี่ยวกับ FF
neu-rah

1
ทิมถูกต้องเมื่อฉันถูก Google จับสิ่งบนวัตถุเหล่านี้ปรากฏขึ้นเว็บไซต์ที่ดีในการตรวจสอบเป็นครั้งคราว
Elias Van Ootegem

20

วิธีข้ามเบราเซอร์ที่จะทำคือการใช้childNodesที่จะได้รับNodeListแล้วให้อาร์เรย์ของโหนดทั้งหมดที่มีELEMENT_NODEnodeType

/**
 * Return direct children elements.
 *
 * @param {HTMLElement}
 * @return {Array}
 */
function elementChildren (element) {
    var childNodes = element.childNodes,
        children = [],
        i = childNodes.length;

    while (i--) {
        if (childNodes[i].nodeType == 1) {
            children.unshift(childNodes[i]);
        }
    }

    return children;
}

http://jsfiddle.net/s4kxnahu/

โดยเฉพาะอย่างยิ่งเรื่องง่ายถ้าคุณกำลังใช้ห้องสมุดยูทิลิตี้เช่นlodash :

/**
 * Return direct children elements.
 *
 * @param {HTMLElement}
 * @return {Array}
 */
function elementChildren (element) {
    return _.where(element.childNodes, {nodeType: 1});
}

อนาคต:

คุณสามารถใช้querySelectorAllร่วมกับ:scopeคลาสหลอก (ตรงกับองค์ประกอบที่เป็นจุดอ้างอิงของตัวเลือก):

parentElement.querySelectorAll(':scope > *');

ในขณะที่เขียนนี้:scopeได้รับการสนับสนุนใน Chrome, Firefox และ Safari


ขอบเขตถูกลบออกจากข้อมูลจำเพาะ เราควรลบหัวข้อในอนาคตหรือไม่
Thomas Marti

4

เพียงเพื่อเพิ่มคำตอบอื่น ๆ ยังคงมีความแตกต่างที่สำคัญที่นี่โดยเฉพาะเมื่อจัดการกับ<svg>องค์ประกอบ

ฉันได้ใช้ทั้ง.childNodesและ.childrenและชอบทำงานกับผู้HTMLCollectionส่งที่.childrenได้รับ

แต่วันนี้ฉันวิ่งเข้าไปในปัญหากับ IE / ขอบล้มเหลวเมื่อใช้บน.children <svg>ในขณะที่.childrenได้รับการสนับสนุนใน IE ในองค์ประกอบพื้นฐาน HTML, มันไม่ได้รับการสนับสนุนบนเศษเอกสาร / เอกสารหรือองค์ประกอบ SVG

สำหรับฉันฉันสามารถคว้าองค์ประกอบที่ต้องการได้.childNodes[n]เพราะฉันไม่มีโหนดข้อความที่ไม่เกี่ยวข้องที่ต้องกังวล คุณอาจทำสิ่งเดียวกันได้ แต่อย่างที่กล่าวไว้ข้างต้นอย่าลืมว่าคุณอาจพบเจอกับสิ่งที่ไม่คาดคิด

หวังว่านี่จะเป็นประโยชน์กับคนที่เกาหัวพยายามคิดว่าทำไมมันถึง.childrenทำงานที่อื่นใน js บน IE สมัยใหม่และล้มเหลวในเอกสารหรือองค์ประกอบ SVG


3

เฮ้พวกอย่าให้พื้นที่สีขาวหลอกคุณ เพียงทดสอบสิ่งนี้ในเบราว์เซอร์คอนโซล ใช้จาวาสคริปต์ นี่คือตัวอย่างของชุด ul สองชุดที่มีคลาสเดียวกัน คุณไม่จำเป็นต้องมีรายการ 'ul' ของคุณทั้งหมดในหนึ่งบรรทัดเพื่อหลีกเลี่ยงพื้นที่สีขาวเพียงแค่ใช้จำนวนแถวของคุณเพื่อข้ามพื้นที่สีขาว

วิธีการได้รับพื้นที่รอบ ๆ สีขาวมีquerySelector()แล้วchildNodes[]js ลิงค์ซอ: https://jsfiddle.net/aparadise/56njekdo/

var y = document.querySelector('.list');
var myNode = y.childNodes[11].style.backgroundColor='red';

<ul class="list">
    <li>8</li>
    <li>9</li>
    <li>100</li>
</ul>

<ul class="list">
    <li>ABC</li>
    <li>DEF</li>
    <li>XYZ</li>
</ul>

ไม่ใช่การลงคะแนนของฉัน แต่ฉันคิดว่ามีคนลงคะแนนเพราะ: ก) คำถามนี้อายุ 4 ปีdocument.querySelectorยังไม่ได้รับการสนับสนุน b) คำถามคือถามว่าสิ่งที่แตกต่างระหว่างchildrenและchildNodes ภายในซึ่งมีความน่าเชื่อถือมากขึ้นเมื่อเลือกหนึ่งกว่า และค) เพราะเป็น demonstraded ในคำถาม: childNodes ไม่รวมช่องว่างโหนดchildrenไม่ได้ ...
อีเลียสแวน Ootegem
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.