Kodokon kodokon.com

จาก HTML สู่ DOM: การ parse และการกู้คืนจากข้อผิดพลาด

ทำความเข้าใจว่าเบราว์เซอร์เปลี่ยนสตรีม HTML ใด ๆ แม้แต่สตรีมที่ผิดรูปแบบ ให้กลายเป็นต้นไม้ DOM ที่แน่นอนได้อย่างไร

10 นาที · 3 คำถาม

เปิดบทเรียนนี้ใน Kodokon

ต่างจาก XML ตรงที่ HTML ไม่มีสิ่งที่เรียกว่าข้อผิดพลาดร้ายแรง (fatal error): ข้อกำหนดของ WHATWG ระบุทีละไบต์ว่าอินพุตสตรีมใด ๆ แม้แต่ที่ผิดรูปแบบ ต้องกลายเป็นต้นไม้ DOM อย่างไร ตัวparser ทำงานเป็นสองขั้น: tokenizer จะตัดสตรีมออกเป็น token (แท็ก ข้อความ ความคิดเห็น) จากนั้น tree builder จะแทรกพวกมันตามเครื่องสถานะ (state machine) ที่สถานะต่าง ๆ ถูกเรียกว่า insertion modes (in body, in table และอื่น ๆ) ทุก "parse error" มีการกู้คืนที่เป็นมาตรฐาน: เบราว์เซอร์ที่ทำตามข้อกำหนดสองตัวจะสร้าง DOM ที่เหมือนกันเป๊ะ

HTML
<table>
  <tr><td>Cell</td></tr>
</table>
DOM ที่ได้ออกมามี tbody ที่คุณไม่เคยเขียนเลย

ตัว parser จะแทรกแท็กโดยปริยาย: <tbody> จะปรากฏรอบ ๆ แถว และ <li>, <p> หรือ <option> จะปิดตัวเองโดยอัตโนมัติเมื่ออยู่หน้าแท็กเปิดบางตัว ที่รุนแรงยิ่งกว่านั้น: เนื้อหาใด ๆ ที่พบภายใน <table> ในตำแหน่งที่ไม่ได้รับอนุญาต จะถูก ดึงออก จากมันแล้วแทรกกลับเข้าไปใหม่ก่อนหน้าตาราง กลไกนี้เรียกว่า foster parenting ตัวเลือก CSS และสคริปต์ของคุณทำงานบน DOM ที่แก้ไขแล้วเสมอ ไม่ใช่บนซอร์สของคุณ

JAVASCRIPT
const html =
  "<table><div>out</div><tr><td>ok</td></tr></table>";
const doc = new DOMParser()
  .parseFromString(html, "text/html");
console.log(doc.body.innerHTML);
// <div>out</div><table>...</table>
การสังเกต foster parenting ด้วย DOMParser

แท็กจัดรูปแบบที่ซ้อนกันผิดจะกระตุ้น adoption agency algorithm ซึ่งเป็นหนึ่งในส่วนที่ซับซ้อนที่สุดของข้อกำหนด: ตัว parser จะโคลนและกระจายองค์ประกอบจัดรูปแบบที่กำลังทำงานอยู่ใหม่ เพื่อสร้างต้นไม้ที่ซ้อนกันอย่างถูกต้อง มาร์กอัปที่ไขว้กันด้านล่างแสดงผลลัพธ์

HTML
<p>
  <b>One <i>two</b> three</i>
</p>
DOM ที่ได้: <b>One <i>two</i></b><i> three</i> - ตัว i ถูกแยกออกเป็นสององค์ประกอบ

ตัว parser เป็น parser แบบ streaming: มันสร้างต้นไม้ขึ้นมาขณะที่ไบต์ทยอยมาถึง <script> แบบซิงโครนัสจะระงับมันไว้ - สคริปต์สามารถเรียก document.write และฉีด token เข้าไปในสตรีมได้ - แต่ preload scanner ยังคงกวาดผ่าน HTML ที่เหลือเพื่อโหลดล่วงหน้าซึ่งรูปภาพ สไตล์ชีต และสคริปต์ หากเรียกหลังจาก parsing เสร็จสิ้นแล้ว document.write จะกระตุ้น document.open และล้างทั้งหน้าทิ้ง: มันเป็นหนึ่งในกับดักที่เก่าแก่ที่สุดของแพลตฟอร์ม

ทดสอบความรู้

ตรวจสอบว่าคุณจำประเด็นสำคัญของบทเรียนนี้ได้ครบถ้วน

  1. จะเกิดอะไรขึ้นกับ <div> ใน <table><div>Out</div><tr><td>A</td></tr></table>?
    • มันถูกลบออกจาก DOM อย่างเงียบ ๆ
    • มันถูกย้ายไปไว้ก่อนหน้าตารางโดย foster parenting
    • มันถูกห่อไว้ในเซลล์ <td> ที่สร้างขึ้นมาให้
    • มันยังคงเป็นลูกโดยตรงของตาราง
  2. ข้อกำหนด HTML จัดการกับเอกสารที่ผิดรูปแบบอย่างไร?
    • มันปล่อยให้แต่ละเบราว์เซอร์เลือกกลยุทธ์การกู้คืนของตัวเอง
    • มันบังคับให้สลับไปยัง quirks mode
    • มันกำหนดอัลกอริทึมการกู้คืนที่แน่นอน เหมือนกันในทุกเบราว์เซอร์ที่ทำตามข้อกำหนด
    • มันบังคับให้หยุด parsing ที่ข้อผิดพลาดแรก
  3. ส่วนย่อย <p><div>X</div></p> สร้าง DOM แบบไหน?
    • <p></p><div>X</div><p></p>: ตัว div ปิด p ตัวแรก และ </p> ที่ไร้คู่จะสร้าง p ตัวที่สองซึ่งว่างเปล่า
    • <p><div>X</div></p> คงไว้ตามเดิม
    • <div><p>X</p></div> ตัว parser กลับด้านการซ้อน