WebValid
WebValid Team

DOM Hallucinations: 5 บั๊กโครงสร้างสุดประหลาดจาก AI

AI React Frontend Bugs HTML

ขอบเขตการเนื้อหา: ตัวอย่างด้านล่างใช้ React และ Next.js App Router (SSR) แต่ปัญหาเชิงโครงสร้างเหล่านี้สามารถเกิดขึ้นได้กับทุกเฟรมเวิร์กสมัยใหม่ที่จัดการกับ DOM hydration และ HTML payloads

Cursor และ Copilot สามารถสร้างคอมโพเนนต์ที่ซับซ้อนได้ในไม่กี่วินาที โครงสร้างเชิงตรรกะดูเป็นระเบียบ TypeScript compiler ไม่มีข้อผิดพลาด และ Tailwind utility classes ก็ดูสมบูรณ์แบบ แต่เมื่อคุณนำไปใช้งานจริงบน staging คุณอาจสังเกตเห็นว่า console แจ้งเตือนข้อผิดพลาด React hydration error, รายละเอียดเลย์เอาต์พังอย่างไม่คาดคิดใน Safari หรือ screen readers ข้ามบล็อกเนื้อหาไปอย่างน่าประหลาด ยินดีต้อนรับสู่โลกที่น่าหงุดหงิดของ “อาการหลอนของโครงสร้าง DOM” (DOM tree hallucinations)

AI code assistants มีความเชี่ยวชาญในการสร้าง markup ที่ดูสมบูรณ์ในเชิงสายตา แต่พวกเขามักมองข้ามข้อกำหนด HTML5 DOM specifications ในขณะที่เรากำลัง “vibe-coding” เพื่อสร้างฟีเจอร์ MVP อย่างรวดเร็ว การเชื่อมั่นในโครงสร้างที่ AI สร้างขึ้นเป็นเรื่องง่ายเพราะมันดูถูกต้องเมื่อมองผ่านหน้าจอ อย่างไรก็ตาม ข้อผิดพลาดในการจับคู่ DOM (DOM mapping errors) ที่เงียบเชียบเหล่านี้มักไม่กระตุ้นคำเตือนทางไวยากรณ์หรือความผิดพลาดของ ESLint มาตรฐาน พวกมันซ่อนอยู่ใน codebase จนกระทั่งเอนจินการวิเคราะห์ของเบราว์เซอร์สมัยใหม่พยายามแก้ไขพวกมัน ส่งผลให้เกิดโครงสร้างเลย์เอาต์ที่รวนและการไม่ตรงกันของ hydration (hydration mismatches)

นี่คือ 5 บั๊กเชิงโครงสร้างที่ AI generator ของคุณอาจจะกำลังเขียนอยู่ตอนนี้ และสาเหตุที่การหวังพึ่งแค่ linting แบบธรรมดาจึงไม่เพียงพอ

ระเบิดเวลา Paragraph Hydration

Critical - ข้อมูลแอปพลิเคชันไม่ตรงกัน - React Hydration Failure

AI assistants มักจะจัดกลุ่มข้อความและองค์ประกอบต่างๆ เข้าด้วยกันในเชิงตรรกะเพื่อสร้างคอมโพเนนต์ เมื่อถูกขอให้จัดสไตล์ย่อหน้าที่ซับซ้อนด้วย blockquote หรือกล่องจัดสไตล์ด้านใน พวกเขามักจะนำแท็กระดับบล็อก (block-level) อย่าง <div> ไปใส่ไว้ข้างในแท็กอินไลน์ (inline) อย่าง <p>

Bad AI Code:

<p>
  นี่คือเนื้อหาหลักของบทความ
  <div className="highlight-box">นี่คือกล่องข้อความสำคัญที่ถูกเรียกใช้งาน</div>
  ดำเนินการต่อกับเนื้อหา...
</p>

ความเป็นจริง: ข้อกำหนด HTML5 ไม่อนุญาตให้องค์ประกอบระดับบล็อก (<div>) อยู่ภายในองค์ประกอบเนื้อหาที่เป็นวลี (phrasing content elements เช่น <p>) เมื่อ HTML นี้ถูกส่งมาจากเซิร์ฟเวอร์ของคุณผ่าน SSR เบราว์เซอร์จะพยายามแก้ไข DOM ที่ไม่ถูกต้องโดยอัตโนมัติด้วยการปิดแท็ก <p> ก่อนที่ <div> จะเริ่มต้นขึ้น

เมื่อ React พยายามทำ hydration บนฝั่งไคลเอนต์ มันจะมองหา <p> ที่มี <div> อยู่ข้างใน แต่แทนที่จะเป็นอย่างนั้น มันกลับพบกับโหนดพี่น้อง (sibling nodes) ที่แยกออกจากกันโดยสิ้นเชิง การไม่ตรงกันนี้จะกระตุ้นข้อผิดพลาด React hydration error ที่รุนแรง ซึ่งอาจทำให้ฟีเจอร์การตอบสนองฝั่งไคลเอนต์ของคอมโพเนนต์ส่วนนั้นทั้งหมดหยุดทำงาน

Fixed Code:

<div>
  <p>นี่คือเนื้อหาหลักของบทความ</p>
  <div className="highlight-box">นี่คือกล่องข้อความสำคัญที่ถูกเรียกใช้งาน</div>
  <p>ดำเนินการต่อกับเนื้อหา...</p>
</div>

WebValid วิเคราะห์การละเมิดความหมาย (semantic violations) ของ HTML ที่ซ้อนกันลึกเหล่านี้บนหน้าที่ถูกเรนเดอร์ได้ทันที เพื่อให้คุณสามารถระบุระเบิดเวลา hydration ได้ก่อนที่จะส่งขึ้นใช้งานจริง

การซ้อนทับของการตอบสนอง (Inception of Interactivity)

High - บริบทการนำทางพัง - ละเมิดมาตรฐาน HTML5

หากคุณขอ “คอมโพเนนต์การ์ดที่คลิกได้พร้อมปุ่มสั่งการภายใน” ปฏิกิริยาโต้ตอบทันทีของ AI คือการนำการ์ดทั้งหมดไปใส่ไว้ในแท็ก <a> หรือ <Link> แล้วนำปุ่มสั่งการไปวางไว้ข้างในลิงก์นั้นโดยตรง

Bad AI Code:

<a href="/dashboard/metrics">
  <div className="card">
    <h3>สถิติรายเดือน</h3>
    <p>ดูสถิติของคุณ</p>
    <button onClick={handleExport}>ส่งออกข้อมูล</button>
  </div>
</a>

มาตรฐาน HTML ห้ามการนำองค์ประกอบที่โต้ตอบได้ (interactive elements) ไปซ้อนไว้ในองค์ประกอบที่โต้ตอบได้อื่นๆ อย่างเด็ดขาด (เช่น <button> ใน <a>) แม้ว่าบ่อยครั้งมันจะดูปกติเมื่อมองด้วยตา แต่ในเชิงโครงสร้างมันทำลายการทำงานของเทคโนโลยีช่วยเหลือ (assistive technologies) ทำให้ screen readers สับสนในการประกาศการกระทำนั้นๆ นอกจากนี้ click event bubbling จะกลายเป็นเรื่องยุ่งเหยิง การคลิกปุ่มส่งออกของคุณอาจจะทำทั้งการดาวน์โหลดข้อมูลและพากันย้ายไปหน้าถัดไปในทันที

สำหรับข้อมูลเชิงลึกเพิ่มเติมเกี่ยวกับวิธีที่เอาต์พุตจาก LLM ทั่วไปทำลายความหมายของโค้ด สามารถอ่านได้จากไกด์ของเราเกี่ยวกับ ข้อผิดพลาดด้านการเข้าถึงของ AI (AI accessibility errors)

สงครามโคลนของรหัสระบุ (ID Clone Wars)

High - CSS selectors พังและการทำ anchor routing ผิดพลาด - ความซ้ำซ้อนของ DOM ID

เมื่อ AI สร้างบล็อกการวนซ้ำ (.map()) เพื่อเรนเดอร์รายการหรือตาราง พวกเขามักจะใส่ค่า attributes ที่ควรจะเป็นค่าเฉพาะตัว (unique) ลงไปแบบคงที่ (hardcode) และเป้าหมายที่พบบ่อยที่สุดคือ attribute id

Bad AI Code:

{
  items.map((item) => (
    <article
      id="feature-card"
      key={item.id}
    >
      <h2>{item.name}</h2>
    </article>
  ));
}

ถ้าในข้อมูลอาเรย์มี 10 รายการ โค้ดจะสร้างองค์ประกอบ 10 ชิ้นที่มี id="feature-card" เหมือนกันทั้งหมด เป็นผลให้ตรรกะใดๆ ที่ใช้ document.getElementById จะล้มเหลวหรือคืนค่ามาแค่โหนดแรกเท่านั้น พฤติกรรมที่ต้องพึ่งพา ID เช่น การจัดการตำแหน่งทาง CSS (anchor positioning), การทำ routing ภายในหน้าด้วยแฮช (#), หรือการผูก aria-controls จะได้รับผลกระทบทั้งหมด บั๊กการโคลน ID นั้นตรวจจับได้ยากมากในการรีวิวโค้ด (PR reviews) เนื่องจากโค้ดของคอมโพเนนต์ชิ้นเดียวนั้นดูเหมือนจะถูกต้อง

การกลายพันธุ์ของโครงสร้างรายการ (Mutated List Children)

Medium - โครงสร้างตามความหมายถูกทำลาย - HTML5 DOM Specs

เมื่อคุณใช้ React Fragments ร่วมกับตรรกะการเรนเดอร์แบบมีเงื่อนไข โมเดล AI มักจะทำลายโครงสร้างรายการดั้งเดิม (list structures) ตัวช่วยสร้างโค้ดที่พยายามแทรกเส้นคั่น (visual dividers) มักจะใส่พวกมันเป็นอิลิเมนต์ที่อยู่ระดับเดียวกับรายการแทนที่จะอยู่ข้างใน

Bad AI Code:

<ul>
  {messages.map((msg, index) => (
    <React.Fragment key={msg.id}>
      <li>{msg.text}</li>
      {index !== messages.length - 1 && <div className="divider" />}
    </React.Fragment>
  ))}
</ul>

แท็กแม่ <ul> คาดหวังว่าลูกของมันจะต้องเป็นแท็ก <li> เท่านั้น การใส่ <div> ลงไปเป็นลูกโดยตรงจะทำลายโครงสร้างการวิเคราะห์เพื่อการเข้าถึง (accessibility tree calculation) เบราว์เซอร์อาจพยายามทำการ reflow โครงสร้าง DOM ใหม่เพื่อครอบอิลิเมนต์ที่ไม่ถูกต้อง ส่งผลให้เลย์เอาต์มีการกระโดดเล็กน้อยขณะวาดผลบนหน้าจอ

การแก้ไขคือต้องจัดสไตล์เส้นคั่นเอาไว้ ภายใน แท็ก <li> หรือเปลี่ยนจากการใช้ <ul> ไปใช้เป็น <div> ที่จัดการด้วย CSS grid แทน

ปริศนาของ Table Body ที่หายไป (The Missing Table Body Phantom)

Critical - บังคับให้เกิด layout reflows และ hydration failures - DOM Construction

ตารางมีพฤติกรรมการวิเคราะห์โค้ด (parsing behaviors) ที่ถูกกำหนดไว้อย่างเข้มงวดซึ่ง AI generator มักจะมองข้ามไปเพื่อความกระชับ รูปแบบที่พบบ่อยคือการสร้างแถวตาราง (table rows) ไว้ใต้แท็กตารางโดยตรงโดยไม่มีแท็กครอบ

Bad AI Code:

<table>
  <tr>
    <td>Status</td>
    <td>Active</td>
  </tr>
</table>

ตามมาตรฐาน HTML ผู้สร้างโค้ด สามารถ ละเว้นแท็ก <tbody> ใน HTML แบบ raw ได้ อย่างไรก็ตาม เมื่อเบราว์เซอร์สมัยใหม่ประมวลผลแท็ก <tr> ที่ไม่มีแท็กครอบ มันจะทำการแก้ไขโครงสร้าง DOM โดยอัตโนมัติด้วยการแทรกบล็อก <tbody> เข้าไปครอบแถวเหล่านั้นเพื่อความปลอดภัย

เมื่อสร้าง UI ด้วย React บนเซิร์ฟเวอร์ markup ที่ออกมาจะตรงกับโค้ดคือ <table><tr> แต่เมื่อเบราว์เซอร์ฝั่งไคลเอนต์ได้รับโค้ดนี้ parser จะแทรก <tbody> เข้าไปเป็น <table><tbody><tr> React จะตรวจพบความแตกต่างเชิงโครงสร้างทันที และจะทำการลบโหนดที่ถูกเรนเดอร์จากเซิร์ฟเวอร์ออกแล้วบังคับให้วาดใหม่ทั้งหมดบนฝั่งไคลเอนต์ ซึ่งกินทรัพยากรมากกว่า

หากคุณคิดว่าขอบเขตโครงสร้างที่หายไปนั้นแย่แล้ว ลองพิจารณาดูว่า AI ของคุณอาจจะกำลัง ทำ API keys หลุดเข้าไปใน dynamic bundles จากการเขียนตรรกะที่ข้ามไปมาระหว่างฝั่งเซิร์ฟเวอร์และไคลเอนต์

ตรวจสอบข้อเท็จจริง: Linters สามารถตรวจเจอสิ่งนี้ไหม?

ความคิดเห็น: “ถ้าฉันใช้การตั้งค่า ESLint ที่เข้มงวด ข้อผิดพลาดในการวางโครงสร้างของ AI จะถูกตรวจเจอทันที”

หลักฐาน: ไม่จริง. linter มาตรฐาน (เช่น eslint-plugin-react) ทำงานโดยการวิเคราะห์ JavaScript AST และเส้นทางการทำงานของโค้ดเท่านั้น พวกมันไม่ได้จำลองการเปลี่ยนแปลงโครงสร้างของเบราว์เซอร์หรือการปรับเปลี่ยนโครงสร้างหลังขั้นตอน hydration แม้ว่าเครื่องมืออย่าง eslint-plugin-jsx-a11y จะทำงานได้ดีเยี่ยมในการระบุความผิดพลาดแบบคงที่ (static violations) เช่น การซ้อน <button> ไว้ใน <a> แต่มันก็ไม่สามารถทำนายได้ว่าการประกอบคอมโพเนนต์หรือการแทรก fragment แบบไดนามิกจะส่งผลต่อโครงสร้าง DOM สุดท้ายอย่างไร การตรวจสอบโครงสร้างเลย์เอาต์หน้าที่ถูกต้องอย่างแท้จริงจำเป็นต้องประเมินจาก DOM สุดท้ายที่ถูกรัน เหมือนกับที่เอนจินการเรนเดอร์สร้างมันขึ้นมา

เลิกเดา แล้วเริ่มตรวจสอบอย่างจริงจัง

AI copilot ของคุณคือคนพิมพ์งานที่เก่งมาก แต่บางครั้งก็ละเลยเรื่องสถาปัตยกรรมของ HTML ไปบ้าง

ประเภทของบั๊กตรวจพบโดย Generative AI/Linter?ตรวจพบโดย WebValid DOM Scan?
Hydration mismatches (<div> ใน <p>)❌ ไม่บ่อย✅ ตรวจพบ
การใช้ id ซ้ำใน mapped arrays❌ ไม่พบ✅ ตรวจพบ
การซ้อน Interactive elements⚠️ เฉพาะ static checks เท่านั้น✅ ตรวจพบ
องค์ประกอบเชิงโครงสร้างไม่ถูกต้อง (<tbody>)❌ ไม่พบ✅ ตรวจพบ

การรีวิวโค้ดแบบ Static ไม่สามารถติดตามได้แม่นยำว่า Safari หรือ Chrome จะแก้ไขอิลิเมนต์ที่ผิดกฎโดยอัตโนมัติอย่างไร ความมั่นใจในโครงสร้างที่แท้จริงต้องมาจากการประเมินเลย์เอาต์หลังขั้นตอน hydration เท่านั้น

เช็คลิสต์ QA เชิงโครงสร้างของคุณ

ตรวจสอบการเขียนโค้ดของ AI ด้วยกฎพื้นฐานเหล่านี้:

  1. ตรวจสอบว่าไม่มีคอมโพเนนต์ระดับบล็อกวางอยู่ข้างในแท็กข้อความอินไลน์ (p, span)
  2. แยกตรรกะการนำทางภายนอกออกจากปุ่มสั่งการภายในคอมโพเนนต์
  3. ตรวจสอบการวนลูปที่ส่งค่า id ที่ซ้ำกันแบบไดนามิก
  4. กำหนดกฎการใส่ <tbody> อย่างชัดเจนในการตั้งค่าตารางทั้งหมด

เครื่องมือ AI สามารถเขียนโค้ดที่มีประสิทธิภาพสูงได้ พวกเขาแค่ยังขาดการมองเห็นภาพรวมของโครงสร้างที่ผิดเพี้ยนไป เพียงแค่ให้แผนผังข้อผิดพลาดที่แม่นยำแก่ AI แล้วมันจะจัดการแก้ไขทุกอย่างให้คุณเอง

เริ่มตรวจสอบสภาพแวดล้อม localhost และ staging ของคุณได้ฟรี และมาหยุดยั้ง “อาการหลอนของ DOM” ก่อนที่มันจะออกไปถึงลูกค้าจริง

เอกสารอย่างเป็นทางการ (Official Documentation)

บทความนี้มีประโยชน์หรือไม่?