การจัดการ DOM ใน JavaScript ล้วนๆ เทคนิค DOM: พ่อแม่ ลูก และเพื่อนบ้านแทรกเนื้อหาลงใน DOM

โดยทั่วไปแล้ว นักพัฒนาจะใช้ jQuery เมื่อต้องการทำอะไรบางอย่างกับ DOM อย่างไรก็ตาม การจัดการ DOM เกือบทั้งหมดสามารถทำได้ใน JavaScript ล้วนๆ โดยใช้ DOM API

มาดูรายละเอียดเพิ่มเติมที่ API นี้:

ในท้ายที่สุด คุณจะเขียนไลบรารี DOM แบบง่ายๆ ของคุณเองซึ่งสามารถนำไปใช้ในโปรเจ็กต์ใดก็ได้

แบบสอบถาม DOM

การสืบค้น DOM ดำเนินการโดยใช้เมธอด .querySelector() ซึ่งรับตัวเลือก CSS ที่กำหนดเองเป็นอาร์กิวเมนต์

Const myElement = document.querySelector("#foo > div.bar")

มันจะส่งคืนองค์ประกอบแรกที่ตรงกัน คุณสามารถทำสิ่งที่ตรงกันข้ามได้ - ตรวจสอบว่าองค์ประกอบตรงกับตัวเลือกหรือไม่:

MyElement.matches("div.bar") === จริง

หากคุณต้องการได้รับองค์ประกอบทั้งหมดที่ตรงกับตัวเลือก ให้ใช้โครงสร้างต่อไปนี้:

Const myElements = document.querySelectorAll(".bar")

หากคุณรู้ว่าองค์ประกอบหลักใดที่คุณต้องการอ้างอิง คุณสามารถค้นหาผ่านรายการลูกๆ แทนที่จะค้นหาผ่านโค้ดทั้งหมด:

Const myChildElemet = myElement.querySelector("input") // แทนที่จะเป็น: // document.querySelector("#foo > div.bar input")

คำถามเกิดขึ้น: เหตุใดจึงต้องใช้วิธีการอื่นที่สะดวกน้อยกว่าเช่น .getElementsByTagName() ? มีปัญหาเล็กน้อย - ผลลัพธ์ของ .querySelector() ไม่ได้รับการอัพเดต และเมื่อเราเพิ่มองค์ประกอบใหม่ (ดู ) ก็จะไม่เปลี่ยนแปลง

Const element1 = document.querySelectorAll("div") const element2 = document.getElementsByTagName("div") const newElement = document.createElement("div") document.body.appendChild(newElement) element1.length === องค์ประกอบ2.ความยาว // เท็จ

นอกจากนี้ querySelectorAll() ยังรวบรวมทุกอย่างไว้ในรายการเดียว ซึ่งทำให้ประสิทธิภาพไม่มากนัก

วิธีการทำงานกับรายการ?

ยิ่งไปกว่านั้น .querySelectorAll() ยังมีคำเตือนเล็กๆ อีก 2 ข้อ คุณไม่สามารถเรียกเมธอดกับผลลัพธ์และคาดหวังให้นำไปใช้กับแต่ละเมธอดได้ (เหมือนที่คุณอาจคุ้นเคยกับ jQuery) ไม่ว่าในกรณีใด คุณจะต้องวนซ้ำองค์ประกอบทั้งหมดในลูป ประการที่สอง วัตถุที่ส่งคืนคือรายการองค์ประกอบ ไม่ใช่อาร์เรย์ ดังนั้นวิธีการอาร์เรย์จะไม่ทำงาน แน่นอนว่ามีวิธีสำหรับรายการต่างๆ เช่น .forEach() แต่อนิจจา วิธีการเหล่านี้ไม่เหมาะกับทุกกรณี ดังนั้นจึงควรแปลงรายการเป็นอาร์เรย์จะดีกว่า:

// การใช้ Array.from() Array.from(myElements).forEach(doSomethingWithEachElement) // หรือ array ต้นแบบ (pre-ES6) Array.prototype.forEach.call(myElements, doSomethingWithEachElement) // ง่ายกว่า: .forEach.call( myElements ทำSomethingWithEachElement)

แต่ละองค์ประกอบมีคุณสมบัติบางอย่างที่อ้างถึง "ตระกูล"

MyElement.children myElement.firstElementChild myElement.lastElementChild myElement.previousElementSibling myElement.nextElementSibling

เนื่องจากอินเทอร์เฟซ Element ได้รับการสืบทอดมาจากอินเทอร์เฟซ Node จึงมีคุณสมบัติต่อไปนี้ด้วย:

MyElement.childNodes myElement.firstChild myElement.lastChild myElement.previousSibling myElement.nextSibling myElement.parentNode myElement.parentElement

คุณสมบัติแรกอ้างถึงองค์ประกอบ และคุณสมบัติสุดท้าย (ยกเว้น .parentElement) อาจเป็นรายการองค์ประกอบประเภทใดก็ได้ ดังนั้น คุณสามารถตรวจสอบประเภทองค์ประกอบได้:

MyElement.firstChild.nodeType === 3 // องค์ประกอบนี้จะเป็นโหนดข้อความ

การเพิ่มคลาสและคุณสมบัติ

เพิ่ม ชั้นเรียนใหม่ง่ายมาก:

MyElement.classList.add("foo") myElement.classList.remove("bar") myElement.classList.toggle("baz")

การเพิ่มคุณสมบัติให้กับองค์ประกอบจะเหมือนกับวัตถุใดๆ:

// รับค่าแอตทริบิวต์ const value = myElement.value // ตั้งค่าแอตทริบิวต์เป็นคุณสมบัติองค์ประกอบ myElement.value = "foo" // Для установки нескольких свойств используйте.Object.assign() Object.assign(myElement, { value: "foo", id: "bar" }) // Удаление атрибута myElement.value = null !}

คุณสามารถใช้เมธอด .getAttibute() , .setAttribute() และ .removeAttribute() ได้ พวกเขาจะเปลี่ยนแอตทริบิวต์ HTML ขององค์ประกอบทันที (ซึ่งตรงข้ามกับคุณสมบัติ DOM) ซึ่งจะทำให้เบราว์เซอร์แสดงผลอีกครั้ง (คุณสามารถดูการเปลี่ยนแปลงทั้งหมดได้โดยตรวจสอบองค์ประกอบโดยใช้เครื่องมือสำหรับนักพัฒนาซอฟต์แวร์ของเบราว์เซอร์) การวาดใหม่ดังกล่าวไม่เพียงแต่ต้องใช้ทรัพยากรมากกว่าการตั้งค่าคุณสมบัติ DOM เท่านั้น แต่ยังอาจทำให้เกิดข้อผิดพลาดที่ไม่คาดคิดอีกด้วย

โดยทั่วไปจะใช้สำหรับองค์ประกอบที่ไม่มีคุณสมบัติ DOM ที่สอดคล้องกัน เช่น colspan หรือหากจำเป็นจริงๆ เช่น สำหรับคุณสมบัติ HTML เมื่อสืบทอด (ดู)

การเพิ่มสไตล์ CSS

มีการเพิ่มในลักษณะเดียวกับคุณสมบัติอื่นๆ:

MyElement.style.marginLeft = "2em"

คุณสมบัติเฉพาะบางอย่างสามารถตั้งค่าได้โดยใช้ .style แต่ถ้าคุณต้องการรับค่าหลังการคำนวณ ควรใช้ window.getComputedStyle() จะดีกว่า เมธอดนี้รับองค์ประกอบและส่งกลับ CSSStyleDeclaration ที่มีสไตล์ของทั้งองค์ประกอบเองและพาเรนต์:

Window.getComputedStyle(myElement).getPropertyValue("ระยะขอบซ้าย")

การเปลี่ยน DOM

คุณสามารถย้ายองค์ประกอบ:

// ผนวก element1 เป็นลูกสุดท้ายของ element2 element1.appendChild(element2) // แทรก element2 เป็นลูกของ element1 ก่อน element3 element1.insertBefore(element2, element3)

หากคุณไม่ต้องการย้าย แต่จำเป็นต้องแทรกสำเนา ให้ใช้:

// สร้างโคลน const myElementClone = myElement.cloneNode() myParentElement.appendChild(myElementClone)

เมธอด .cloneNode() รับค่าบูลีนเป็นอาร์กิวเมนต์ และหากเป็นจริง องค์ประกอบย่อยก็จะถูกโคลนเช่นกัน

แน่นอนคุณสามารถสร้างองค์ประกอบใหม่ได้:

Const myNewElement = document.createElement("div") const myNewTextNode = document.createTextNode("ข้อความบางส่วน")

แล้วใส่ตามภาพด้านบน คุณไม่สามารถลบองค์ประกอบได้โดยตรง แต่คุณสามารถทำได้ผ่านองค์ประกอบหลัก:

MyParentElement.removeChild(myElement)

คุณสามารถติดต่อทางอ้อม:

MyElement.parentNode.removeChild (myElement)

วิธีการสำหรับองค์ประกอบ

แต่ละองค์ประกอบมีคุณสมบัติ เช่น .innerHTML และ .textContent ซึ่งประกอบด้วยโค้ด HTML และตัวข้อความเองตามลำดับ ตัวอย่างต่อไปนี้จะเปลี่ยนเนื้อหาขององค์ประกอบ:

// เปลี่ยน HTML myElement.innerHTML = ` เนื้อหาใหม่ ( el.addEventListener("change", function (event) ( console.log(event.target.value) )) ))

การป้องกันการกระทำเริ่มต้น

เมื่อต้องการทำเช่นนี้ ให้ใช้เมธอด .preventDefault() ซึ่งจะบล็อกการดำเนินการมาตรฐาน ตัวอย่างเช่น มันจะบล็อกการส่งแบบฟอร์มหากการอนุญาตฝั่งไคลเอ็นต์ไม่สำเร็จ:

MyForm.addEventListener("submit", function (event) ( const name = this.querySelector("#name") if (name.value === "Donald Duck)") { alert("You gotta be kidding!") event.preventDefault() } }) !}

.stopPropagation() วิธีการจะช่วยถ้าคุณมีตัวจัดการเหตุการณ์เฉพาะที่กำหนดให้กับองค์ประกอบลูกและตัวจัดการเหตุการณ์ตัวที่สองที่กำหนดให้กับผู้ปกครองสำหรับเหตุการณ์เดียวกัน

ตามที่ระบุไว้ก่อนหน้านี้ .addEventListener() วิธีการรับอาร์กิวเมนต์ที่สามที่เป็นทางเลือกในรูปแบบของอ็อบเจ็กต์การกำหนดค่า วัตถุนี้จะต้องมีคุณสมบัติบูลีนใด ๆ ต่อไปนี้ (ทั้งหมดตั้งค่าเป็นเท็จตามค่าเริ่มต้น):

  • การจับภาพ: เหตุการณ์จะถูกแนบไปกับองค์ประกอบนี้ก่อนองค์ประกอบอื่นใดด้านล่างใน DOM
  • ครั้งเดียว: สามารถมอบหมายกิจกรรมได้เพียงครั้งเดียว
  • passive: event.preventDefault() จะถูกละเว้น (ข้อยกเว้นระหว่างเกิดข้อผิดพลาด)

คุณสมบัติที่พบบ่อยที่สุดคือ .capture และเป็นเรื่องปกติที่มีทางลัด: แทนที่จะส่งผ่านไปยังวัตถุการกำหนดค่า เพียงแค่ส่งค่าของมันที่นี่:

MyElement.addEventListener (ประเภท, ผู้ฟัง, จริง)

ตัวจัดการจะถูกลบออกโดยใช้เมธอด .removeEventListener() ซึ่งรับอาร์กิวเมนต์สองตัว: ประเภทเหตุการณ์และการอ้างอิงถึงตัวจัดการเพื่อลบ ตัวอย่างเช่น คุณสมบัติครั้งเดียวสามารถนำไปใช้ได้ดังนี้:

MyElement.addEventListener("change", function Listener (event) ( console.log(event.type + " got triggered on " + this) this.removeEventListener("change", Listener) ))

มรดก

สมมติว่าคุณมีองค์ประกอบและต้องการเพิ่มตัวจัดการเหตุการณ์สำหรับลูกทั้งหมด จากนั้นคุณจะต้องวนซ้ำโดยใช้เมธอด myForm.querySelectorAll("input") ดังที่แสดงด้านบน อย่างไรก็ตาม คุณสามารถเพิ่มองค์ประกอบลงในแบบฟอร์มและตรวจสอบเนื้อหาได้โดยใช้ event.target

MyForm.addEventListener("change", function (event) ( const target = event.target if (target.matches("input")) ( console.log(target.value) ) ))

และข้อดีอีกประการของวิธีนี้ก็คือตัวจัดการจะถูกแนบเข้ากับองค์ประกอบลูกใหม่โดยอัตโนมัติ

แอนิเมชั่น

วิธีที่ง่ายที่สุดในการเพิ่มภาพเคลื่อนไหวคือการใช้ CSS พร้อมคุณสมบัติการเปลี่ยนแปลง แต่เพื่อความยืดหยุ่นที่มากขึ้น (เช่น สำหรับเกม) JavaScript จึงเหมาะสมกว่า

การเรียกใช้เมธอด window.setTimeout() จนกว่าแอนิเมชั่นจะสิ้นสุดไม่ใช่ความคิดที่ดี เนื่องจากแอปพลิเคชันของคุณอาจค้าง โดยเฉพาะใน อุปกรณ์เคลื่อนที่. ควรใช้ window.requestAnimationFrame() เพื่อบันทึกการเปลี่ยนแปลงทั้งหมดจนกว่าจะวาดใหม่ครั้งถัดไป ใช้ฟังก์ชันเป็นอาร์กิวเมนต์ ซึ่งจะได้รับการประทับเวลา:

Const start = window.Performance.now() ระยะเวลา const = 2000 window.requestAnimationFrame (ฟังก์ชั่น fadeIn (ตอนนี้)) ( const ความคืบหน้า = ตอนนี้ - เริ่ม myElement.style.opacity = ความคืบหน้า / ระยะเวลาถ้า (ความคืบหน้า< duration) { window.requestAnimationFrame(fadeIn) } }

ด้วยวิธีนี้ ภาพเคลื่อนไหวจึงราบรื่นมาก ในบทความของเขา Mark Brown กล่าวถึงหัวข้อนี้

การเขียนห้องสมุดของเราเอง

ความจริงที่ว่าใน DOM คุณต้องวนซ้ำองค์ประกอบตลอดเวลาเพื่อดำเนินการใด ๆ กับองค์ประกอบอาจดูค่อนข้างน่าเบื่อเมื่อเทียบกับ $(".foo").css((color: "red")) ไวยากรณ์ของ jQuery แต่ทำไมไม่ลองเขียนวิธีการของคุณเองเพื่อทำให้งานนี้ง่ายขึ้นล่ะ

Const $ = function $ (ตัวเลือก, บริบท = เอกสาร) ( const element = Array.from(context.querySelectorAll(selector)) return ( element, html (newHtml) ( this.elements.forEach(element => ( element.innerHTML = newHtml )) ส่งคืนสิ่งนี้ ), css (newCss) ( this.elements.forEach(element => ( Object.assign(element.style, newCss) )) ส่งคืนสิ่งนี้ ), เปิด (เหตุการณ์, ตัวจัดการ, ตัวเลือก) ( this.elements .forEach(element => ( element.addEventListener(event, handler, options) )) ส่งคืนสิ่งนี้ ) ) )

รหัสเทียม

$(".rightArrow").click(function() ( rightArrowParents = this.dom(); //.dom(); เป็นฟังก์ชันหลอก ... ควรแสดงการแจ้งเตือนทั้งหมด (rightArrowParents); ));

ข้อความแจ้งเตือนจะเป็น:

body div.lol a.rightArrow

ฉันจะรับสิ่งนี้โดยใช้ javascript/jquery ได้อย่างไร

การใช้ jQuery เช่นนี้ (ตามด้วยโซลูชันที่ไม่ได้ใช้ jQuery ยกเว้นเหตุการณ์ การเรียกใช้ฟังก์ชันจะน้อยกว่ามากหากสิ่งนั้นสำคัญ):

ตัวอย่างเรียลไทม์:

$(".rightArrow").click(function() ( var rightArrowParents = ; $(this).parents().addBack().not("html").each(function() ( var entry = this.tagName .toLowerCase(); if (this.className) ( entry += "." + this.className.replace(/ /g, "."); ) rightArrowParents.push(entry); )); การแจ้งเตือน (rightArrowParents.join (" ")); กลับเท็จ; )); คลิกที่นี่

(ในตัวอย่างสด ฉันได้อัปเดตแอตทริบิวต์ class บน div ให้เป็น lol multi เพื่อสาธิตการจัดการหลายคลาส)

นี่คือวิธีแก้ปัญหาสำหรับการจับคู่องค์ประกอบแบบตรงทั้งหมด

สิ่งสำคัญคือต้องเข้าใจว่าตัวเลือก (ไม่จริง) ที่เครื่องมือ Chrome แสดงไม่ได้ระบุองค์ประกอบใน DOM โดยไม่ซ้ำกัน ( ตัวอย่างเช่น จะไม่แยกความแตกต่างระหว่างรายการองค์ประกอบช่วงที่ต่อเนื่องกัน ไม่มีข้อมูลตำแหน่ง/การจัดทำดัชนี)

$.fn.fullSelector = function () ( var path = this.parents().addBack(); var quickCss = path.get().map(function (item) ( var self = $(item), id = item .id ? "#" + item.id: "", clss = item.classList.length ? item.classList.toString().split(" ").map(function (c) ( return "." + c; )).join("") : "", name = item.nodeName.toLowerCase(), index = self.siblings(name).length ? ":nth-child(" + (self.index() + 1) + ")" : ""; if (name === "html" || name === "body") ( return name; ) return name + index + id + clss; )).join(" > ") ; กลับ QuickCss; );

และคุณสามารถใช้มันได้ดังนี้:

Console.log($("ตัวเลือกบางตัว").fullSelector());

ฉันย้ายชิ้นส่วนจาก T.J. Crowder ถึงเล็ก ปลั๊กอิน jQuery. ฉันใช้เวอร์ชัน jQuery แม้ว่าเขาจะพูดถูกว่ามันเป็นค่าใช้จ่ายที่ไม่จำเป็นเลย แต่ฉันใช้เพื่อวัตถุประสงค์ในการดีบั๊กเท่านั้นดังนั้นฉันจึงไม่สนใจ

การใช้งาน:

ช่วงที่ซ้อนกัน ช่วงง่าย ก่อน

// ผลลัพธ์ (อาร์เรย์): ["body", "div.sampleClass"] $("span").getDomPath(false) // ผลลัพธ์ (สตริง): body > div.sampleClass $("span").getDomPath( ) // ผลลัพธ์ (อาร์เรย์): ["body", "div#test"] $("pre").getDomPath(false) // result (string): body > div#test $("pre").getDomPath ()

Var obj = $("#show-editor-button"), path = ""; ในขณะที่ (typeof obj.prop("tagName") != "unknown")( if (obj.attr("class"))( path = "."+obj.attr("class").replace(/\s /g , ".") + path; ) if (obj.attr("id"))( path = "#"+obj.attr("id") + path; ) path = " " +obj.prop( "tagName").toLowerCase() + path; obj = obj.parent(); ) console.log(เส้นทาง);

สวัสดี ฟังก์ชั่นนี้แก้ไขข้อผิดพลาดที่เกี่ยวข้องกับองค์ประกอบปัจจุบันที่ไม่แสดงในเส้นทาง

ตรวจสอบออกตอนนี้

$j(".wrapper").click(function(event) ( SelectedElement=$j(event.target); var rightArrowParents = ; $j(event.target).parents().not("html,body") .each(function() ( var entry = this.tagName.toLowerCase(); if (this.className) ( entry += "." + this.className.replace(/ /g, "."); )else ถ้า (this.id)( entry += "#" + this.id; ) entry=replaceAll(entry,.","."); rightArrowParents.push(entry); )); rightArrowParents.reverse(); // if(event.target.nodeName.toLowerCase()=="a" || event.target.nodeName.toLowerCase()=="h1")( var entry = event.target.nodeName.toLowerCase(); ถ้า (เหตุการณ์ .target.className) ( รายการ += "." + event.target.className.replace(/ /g, "."); )else if(event.target.id)( รายการ += "#" + เหตุการณ์ target.id; ) rightArrowParents.push (รายการ); // )

โดยที่ $j = ตัวแปร jQuery

ยังแก้ปัญหาด้วย .. ในชื่อคลาสด้วย

นี่คือฟังก์ชันทดแทน:

ฟังก์ชัน EscapeRegExp(str) ( return str.replace(/([.*+?^=!:$()()|\[\]\/\\])/g, "\\$1"); ) ฟังก์ชัน แทนที่ทั้งหมด(str, ค้นหา, แทนที่) ( return str.replace(regExp ใหม่(escapeRegExp(find), "g"), แทนที่); )

นี่คือเวอร์ชัน JS ดั้งเดิมที่ส่งคืนเส้นทาง jQuery ฉันยังเพิ่ม ID สำหรับองค์ประกอบหากมีอยู่ นี่จะทำให้คุณมีตัวเลือกในการใช้เส้นทางที่สั้นที่สุดหากคุณเห็นรหัสในอาร์เรย์

Var path = getDomPath(องค์ประกอบ); console.log(path.join("> "));

เนื้อความ >section:eq(0) > div:eq(3) > Section#content > Section#firehose > div#firehoselist > article#firehose-46813651 > header > h2 > span#title-46813651

นี่คือฟังก์ชัน

ฟังก์ชัน getDomPath(el) ( var stack = ; while (el.parentNode != null) ( console.log(el.nodeName); var sibCount = 0; var sibIndex = 0; for (var i = 0; i< el.parentNode.childNodes.length; i++) { var sib = el.parentNode.childNodes[i]; if (sib.nodeName == el.nodeName) { if (sib === el) { sibIndex = sibCount; } sibCount++; } } if (el.hasAttribute("id") && el.id != "") { stack.unshift(el.nodeName.toLowerCase() + "#" + el.id); } else if (sibCount >1) ( stack.unshift(el.nodeName.toLowerCase() + ":eq(" + sibIndex + ")"); ) อื่น ๆ ( stack.unshift(el.nodeName.toLowerCase()); ) el = el.parentNode ; ) ส่งคืน stack.slice (1); // ลบองค์ประกอบ html)

แอปพลิเคชันเว็บที่ซับซ้อนและหนักหน่วงกลายเป็นเรื่องปกติในทุกวันนี้ ข้ามเบราว์เซอร์และไลบรารีที่ใช้งานง่ายเช่น jQuery พร้อมฟังก์ชันการทำงานที่หลากหลายสามารถช่วยในการจัดการ DOM ได้ทันที ดังนั้นจึงไม่น่าแปลกใจที่นักพัฒนาจำนวนมากใช้ไลบรารีดังกล่าวบ่อยกว่าการทำงานกับ DOM API ดั้งเดิมซึ่งมีปัญหามากมาย แม้ว่าความแตกต่างของเบราว์เซอร์ยังคงเป็นปัญหาอยู่ แต่ DOM ก็มีรูปร่างที่ดีขึ้นกว่าเมื่อ 5-6 ปีที่แล้วเมื่อ jQuery ได้รับความนิยม

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

การนับโหนดย่อย

สำหรับการสาธิตฉันจะใช้สิ่งต่อไปนี้ มาร์กอัป HTMLเราจะเปลี่ยนแปลงหลายครั้งตลอดทั้งบทความ:

  • ตัวอย่างที่หนึ่ง
  • ตัวอย่างที่สอง
  • ตัวอย่างที่สาม
  • ตัวอย่างที่สี่
  • ตัวอย่างที่ห้า
  • ตัวอย่างที่หก

วาร์ myList = document.getElementById("myList"); console.log(myList.children.length); // 6 console.log(myList.childElementCount); // 6

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

ในตัวอย่างที่สอง ฉันกำลังใช้เมธอด childElementCount ซึ่งฉันคิดว่าเป็นวิธีที่เรียบร้อยกว่าและอาจบำรุงรักษาได้มากกว่า (พูดคุยเรื่องนี้เพิ่มเติมในภายหลัง ฉันไม่คิดว่าคุณจะมีปัญหาในการทำความเข้าใจว่ามันทำอะไร)

ฉันสามารถลองใช้ childNodes.length (แทน children.length) แต่ดูผลลัพธ์:

วาร์ myList = document.getElementById("myList"); console.log(myList.childNodes.length); // 13

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

ตรวจสอบการมีอยู่ของโหนดลูก

หากต้องการตรวจสอบว่าองค์ประกอบมีโหนดย่อยหรือไม่ ฉันสามารถใช้เมธอด hasChildNodes() ได้ วิธีการส่งกลับค่าบูลีนที่บ่งชี้ว่ามีหรือไม่มี:

วาร์ myList = document.getElementById("myList"); console.log(myList.hasChildNodes()); // จริง

ฉันรู้ว่ารายการของฉันมีโหนดย่อย แต่ฉันสามารถเปลี่ยน HTML เพื่อไม่ให้มีโหนดย่อยได้ ตอนนี้มาร์กอัปมีลักษณะดังนี้:

และนี่คือผลลัพธ์ของการรัน hasChildNodes() อีกครั้ง:

Console.log(myList.hasChildNodes()); // จริง

วิธีการยังคงคืนค่าจริง แม้ว่ารายการจะไม่มีองค์ประกอบใดๆ แต่ก็มีช่องว่างซึ่งเป็นประเภทโหนดที่ถูกต้อง วิธีการนี้คำนึงถึงโหนดทั้งหมด ไม่ใช่แค่โหนดองค์ประกอบ เพื่อให้ hasChildNodes() คืนค่าเท็จ เราจำเป็นต้องเปลี่ยนมาร์กอัปอีกครั้ง:

และตอนนี้ผลลัพธ์ที่คาดหวังจะแสดงในคอนโซล:

Console.log(myList.hasChildNodes()); // เท็จ

แน่นอน หากฉันรู้ว่าอาจพบช่องว่าง ฉันจะตรวจสอบการมีอยู่ของโหนดย่อยก่อน จากนั้นใช้คุณสมบัติ nodeType เพื่อพิจารณาว่ามีโหนดองค์ประกอบใดๆ อยู่ในนั้นหรือไม่

การเพิ่มและการลบองค์ประกอบย่อย

มีเทคนิคต่างๆ ที่คุณสามารถใช้เพื่อเพิ่มและลบองค์ประกอบออกจาก DOM สิ่งที่มีชื่อเสียงที่สุดนั้นมาจากการผสมผสานระหว่างเมธอด createElement() และ appendChild()

Var myEl = document.createElement("div"); document.body.appendChild(myEl);

ในกรณีนี้ ฉันสร้างโดยใช้เมธอด createElement() แล้วเพิ่มลงในเนื้อหา มันง่ายมาก และคุณคงเคยใช้เทคนิคนี้มาก่อน

แต่แทนที่จะแทรกโดยเฉพาะ กำลังสร้างองค์ประกอบฉันยังสามารถใช้ appendChild() และเพียงแค่ย้ายองค์ประกอบที่มีอยู่ สมมติว่าเรามีมาร์กอัปต่อไปนี้:

  • ตัวอย่างที่หนึ่ง
  • ตัวอย่างที่สอง
  • ตัวอย่างที่สาม
  • ตัวอย่างที่สี่
  • ตัวอย่างที่ห้า
  • ตัวอย่างที่หก

ข้อความตัวอย่าง

ฉันสามารถเปลี่ยนตำแหน่งของรายการด้วยรหัสต่อไปนี้:

Var myList = document.getElementById("myList"), คอนเทนเนอร์ = document.getElementById("c"); คอนเทนเนอร์ appendChild (myList);

DOM สุดท้ายจะมีลักษณะดังนี้:

ข้อความตัวอย่าง

  • ตัวอย่างที่หนึ่ง
  • ตัวอย่างที่สอง
  • ตัวอย่างที่สาม
  • ตัวอย่างที่สี่
  • ตัวอย่างที่ห้า
  • ตัวอย่างที่หก

โปรดสังเกตว่ารายการทั้งหมดได้ถูกลบออกจากตำแหน่ง (เหนือย่อหน้า) แล้วแทรกหลังจากนั้นก่อนเนื้อหาปิด แม้ว่าโดยทั่วไปจะใช้เมธอด appendChild() เพื่อเพิ่มองค์ประกอบที่สร้างด้วย createElement() แต่ก็สามารถใช้เพื่อย้ายองค์ประกอบที่มีอยู่ได้เช่นกัน

ฉันยังสามารถลบองค์ประกอบลูกออกจาก DOM ได้อย่างสมบูรณ์โดยใช้ RemoveChild() ต่อไปนี้เป็นวิธีลบรายการของเราออกจากตัวอย่างก่อนหน้านี้:

Var myList = document.getElementById("myList"), คอนเทนเนอร์ = document.getElementById("c"); คอนเทนเนอร์removeChild(myList);

ตอนนี้องค์ประกอบได้ถูกลบออกแล้ว เมธอด RemoveChild() ส่งคืนองค์ประกอบที่ถูกลบออก เพื่อให้ฉันสามารถบันทึกได้ในกรณีที่ฉันต้องการในภายหลัง

วาร์ myOldChild = document.body.removeChild(myList); document.body.appendChild(myOldChild);

นอกจากนี้ยังมี ChildNode.remove() วิธีการที่เพิ่งเพิ่มเข้าไปในข้อกำหนด:

วาร์ myList = document.getElementById("myList"); myList.remove();

วิธีนี้ไม่ส่งคืนวัตถุระยะไกลและไม่ทำงานใน IE (Edge เท่านั้น) และทั้งสองวิธีจะลบโหนดข้อความในลักษณะเดียวกับโหนดองค์ประกอบ

การแทนที่องค์ประกอบลูก

ฉันสามารถแทนที่องค์ประกอบย่อยที่มีอยู่ด้วยองค์ประกอบใหม่ได้ ไม่ว่าฉันจะสร้างองค์ประกอบใหม่ตั้งแต่ต้นหรือไม่ก็ตาม นี่คือมาร์กอัป:

ข้อความตัวอย่าง

Var myPar = document.getElementById("par"), myDiv = document.createElement("div"); myDiv.className = "ตัวอย่าง"; myDiv.appendChild(document.createTextNode("ข้อความองค์ประกอบใหม่")); document.body.replaceChild(myDiv, myPar);

ข้อความองค์ประกอบใหม่

ดังที่คุณเห็นแล้วว่าเมธอด replacementChild() รับอาร์กิวเมนต์สองตัว: องค์ประกอบใหม่และองค์ประกอบเก่าที่แทนที่

ฉันยังสามารถใช้วิธีนี้เพื่อย้ายองค์ประกอบที่มีอยู่ได้ ลองดูที่ HTML ต่อไปนี้:

ตัวอย่างข้อความที่ 1

ตัวอย่างข้อความที่ 2

ตัวอย่างข้อความที่ 3

ฉันสามารถแทนที่ย่อหน้าที่สามด้วยย่อหน้าแรกได้โดยใช้รหัสต่อไปนี้:

Var myPar1 = document.getElementById("par1"), myPar3 = document.getElementById("par3"); document.body.replaceChild(myPar1, myPar3);

ตอนนี้ DOM ที่สร้างขึ้นมีลักษณะดังนี้:

ตัวอย่างข้อความที่ 2

ตัวอย่างข้อความที่ 1

คัดเลือกเด็กโดยเฉพาะ

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

คุณสมบัติ firstElementChild และ LastElementChild ทำตามชื่อที่แนะนำ: เลือกองค์ประกอบลูกแรกและสุดท้าย กลับไปที่มาร์กอัปของเรา:

  • ตัวอย่างที่หนึ่ง
  • ตัวอย่างที่สอง
  • ตัวอย่างที่สาม
  • ตัวอย่างที่สี่
  • ตัวอย่างที่ห้า
  • ตัวอย่างที่หก

ฉันสามารถเลือกองค์ประกอบแรกและองค์ประกอบสุดท้ายโดยใช้คุณสมบัติเหล่านี้:

วาร์ myList = document.getElementById("myList"); console.log(myList.firstElementChild.innerHTML); // "ตัวอย่างหนึ่ง" console.log(myList.lastElementChild.innerHTML); // "ตัวอย่างที่หก"

ฉันยังสามารถใช้คุณสมบัติ PreviousElementSibling และ NextElementSibling ได้ หากฉันต้องการเลือกองค์ประกอบลูกอื่นที่ไม่ใช่องค์ประกอบแรกหรือสุดท้าย ซึ่งทำได้โดยการรวมคุณสมบัติ firstElementChild และ LastElementChild:

วาร์ myList = document.getElementById("myList"); console.log(myList.firstElementChild.nextElementSibling.innerHTML); // "ตัวอย่างที่สอง" console.log(myList.lastElementChild.previousElementSibling.innerHTML); // "ตัวอย่างที่ห้า"

นอกจากนี้ยังมีคุณสมบัติที่คล้ายกัน firstChild , LastChild , PreviousSibling และ nextSibling แต่จะคำนึงถึงโหนดทุกประเภท ไม่ใช่แค่องค์ประกอบ โดยทั่วไป คุณสมบัติที่พิจารณาเฉพาะโหนดองค์ประกอบจะมีประโยชน์มากกว่าคุณสมบัติที่เลือกโหนดทั้งหมด

การแทรกเนื้อหาลงใน DOM

ฉันได้ดูวิธีแทรกองค์ประกอบลงใน DOM แล้ว เรามาต่อกันที่ หัวข้อที่คล้ายกันและดูตัวเลือกใหม่สำหรับการแทรกเนื้อหา

ประการแรก มีเมธอด insertBefore() ธรรมดาๆ เหมือนกับการแทนที่Child() โดยต้องใช้อาร์กิวเมนต์สองตัวและใช้งานได้กับทั้งองค์ประกอบใหม่และองค์ประกอบที่มีอยู่ นี่คือมาร์กอัป:

  • ตัวอย่างที่หนึ่ง
  • ตัวอย่างที่สอง
  • ตัวอย่างที่สาม
  • ตัวอย่างที่สี่
  • ตัวอย่างที่ห้า
  • ตัวอย่างที่หก

ย่อหน้าตัวอย่าง

สังเกตย่อหน้าที่ฉันจะลบออกก่อน จากนั้นจึงแทรกไว้หน้ารายการ ทั้งหมดในคราวเดียว:

Var myList = document.getElementById("myList"), คอนเทนเนอร์ = document.getElementBy("c"), myPar = document.getElementById("par"); container.insertBefore(myPar, myList);

ในผลลัพธ์ HTML ย่อหน้าจะปรากฏก่อนรายการและนี่เป็นอีกวิธีหนึ่งในการล้อมองค์ประกอบ

ย่อหน้าตัวอย่าง

  • ตัวอย่างที่หนึ่ง
  • ตัวอย่างที่สอง
  • ตัวอย่างที่สาม
  • ตัวอย่างที่สี่
  • ตัวอย่างที่ห้า
  • ตัวอย่างที่หก

เช่นเดียวกับแทนที่Child() insertBefore() รับสองอาร์กิวเมนต์: องค์ประกอบที่จะเพิ่มและองค์ประกอบก่อนที่เราต้องการแทรก

วิธีนี้ง่าย ตอนนี้เรามาลองใช้วิธีการแทรกที่มีประสิทธิภาพมากขึ้น: วิธีการ insertAdjacentHTML()