ค้นหาเนื้อหาที่ซ่อนอยู่

อินเทอร์เฟซเว็บมักจะซ่อนเนื้อหาที่ซ่อนจากมุมมองเพื่อปรับปรุงประสบการณ์ของผู้ใช้ ประหยัดพื้นที่หน้าจอ หรือเพิ่มประสิทธิภาพของหน้าเว็บ วิธีการแบบเดิมๆ เช่น display: none หรือ visibility: hidden จะซ่อนเนื้อหาที่ซ่อนไม่ให้มองเห็นได้ แต่ก็ทำให้โปรแกรมอ่านหน้าจอและฟีเจอร์ของเบราว์เซอร์ เช่น "ค้นหาในหน้า" เข้าถึงเนื้อหานั้นไม่ได้เลย

หากต้องการซ่อนเนื้อหาไม่ให้มองเห็นได้ แต่ยังคงอนุญาตให้ผู้ใช้ค้นหาได้ และเปิดใช้การทำ Deep Link ผ่านส่วนย่อย URL และลิงก์ "เลื่อนไปยังส่วนย่อยข้อความ" คุณสามารถใช้องค์ประกอบ HTML <details> หรือแอตทริบิวต์ hidden="until-found" ก็ได้ โดยทั่วไปเราขอแนะนำให้ใช้องค์ประกอบ <details> เนื่องจากติดตั้งใช้งานและดูแลรักษาได้ง่ายกว่า แต่ก็มีบางกรณีที่ซับซ้อนกว่าซึ่ง <details> ไม่เพียงพอและจำเป็นต้องใช้ hidden="until-found"

เช่น

  • หากต้องการควบคุมสไตล์ของกลไกการแสดง/ซ่อนได้อย่างเต็มที่
  • หากตัวควบคุม UI สำหรับแสดง/ซ่อนเนื้อหาอยู่ในส่วนอื่นของ DOM
  • หากคุณไม่ต้องการรองรับการซ่อนเนื้อหาหลังจากที่แสดงแล้ว

วิธีการนำไปใช้งาน

องค์ประกอบ <details> มีข้อความที่ค้นหาและเข้าถึงได้โดยค่าเริ่มต้น และไม่จำเป็นต้องมีการติดตั้งใช้งานพิเศษ แนะนำให้ใช้ <details> แทน hidden="until-found" หากเป็นไปได้

หากต้องการใช้ hidden="until-found" แทน ให้ทำตามวิธีการต่อไปนี้

  1. ใช้แอตทริบิวต์: เพิ่มแอตทริบิวต์ HTML hidden="until-found" ลงในองค์ประกอบที่มีเนื้อหาที่ควรซ่อนจากมุมมองโดยตรง
  2. ซิงค์สถานะ UI: หากอินเทอร์เฟซมีสถานะที่เกี่ยวข้องซึ่งขึ้นอยู่กับการแสดงผลของเนื้อหา (เช่น การอัปเดตแอตทริบิวต์ ARIA, การสลับคลาส CSS แบบเปิด/ปิด หรือการหมุนไอคอน Accordion) ให้ทำดังนี้
    • คุณต้องเพิ่ม Listener เหตุการณ์สำหรับเหตุการณ์ beforematch
    • ลงทะเบียน Listener เหตุการณ์ beforematch โดยตรงในองค์ประกอบที่มีแอตทริบิวต์ hidden="until-found" เนื่องจากเหตุการณ์เป็นแบบบับเบิล คุณจึงอาจใช้การมอบสิทธิ์เหตุการณ์แทนได้โดยการลงทะเบียน Listener รายการเดียวในองค์ประกอบหลัก (เช่น คอนเทนเนอร์แท็บ) เพื่อจัดการส่วนที่ซ่อนอยู่หลายส่วนพร้อมกัน
    • ภายใน Listener เหตุการณ์ ให้เรียกใช้ตรรกะเพื่อซิงค์องค์ประกอบ UI ที่เกี่ยวข้อง (เช่น การปิดแท็บอื่นๆ ที่เปิดอยู่หรือการเปลี่ยนสถานะของปุ่มเปิด/ปิด)

โค้ดตัวอย่าง

เป้าหมาย: แสดงคอนเทนเนอร์ที่ซ่อนอยู่ซึ่งผู้ใช้จะมองไม่เห็นเนื้อหาจนกว่าจะค้นหาคำภายในส่วนนั้น

การใช้องค์ประกอบ <details>

<details>
  <summary>Click to expand</summary>
  <p>This content is visually hidden.</p>
</details>

การเปิดเผยโฆษณาที่แยกกันโดยสิ้นเชิง

เมื่อจัดการกับขอบเขตเนื้อหาที่แยกออกจากกันโดยสิ้นเชิง เช่น Accordion แบบแยกออกจากกันโดยสิ้นเชิง ให้ใช้องค์ประกอบ <details> ของ HTML แบบเนทีฟที่มีแอตทริบิวต์ name ที่ใช้ร่วมกัน

<div class="accordion-group">
  <!-- The name attribute creates an exclusive disclosure group -->
  <details class="disclosure" name="my-accordion">
    <summary>Section 1</summary>
    <p>Section 1 content</p>
  </details>

  <details class="disclosure" name="my-accordion" open>
    <summary>Section 2</summary>
    <p>Section 2 content</p>
  </details>

  <details class="disclosure" name="my-accordion">
    <summary>Section 3</summary>
    <p>Section 3 content</p>
  </details>
</div>

การใช้แอตทริบิวต์ hidden="until-found"

<!-- The browser automatically removes hidden="until-found" upon a search match -->
<div class="hidden-container" hidden="until-found">
  <p>This content is visually hidden.</p>
</div>

การเปิดเผยที่กำหนดเองซึ่งไม่รวมกัน

เมื่อจัดการขอบเขตที่แยกกันโดยสิ้นเชิงที่กำหนดเองซึ่งควบคุมโดยปุ่มภายนอก ให้ตรวจสอบว่ามีเพียงแผงที่ตรงกันเท่านั้นที่ยังคงมองเห็นได้ ในเครื่องจัดการเหตุการณ์ beforematch คุณต้องซิงค์สถานะ ARIA ที่เกี่ยวข้อง เช่น การตั้งค่า aria-expanded="true" ในปุ่มควบคุม

<div class="custom-accordion">
  <div class="controls">
    <button aria-expanded="true" aria-controls="panel-1" id="btn-1">Section 1</button>
    <button aria-expanded="false" aria-controls="panel-2" id="btn-2">Section 2</button>
  </div>

  <div id="panel-1" class="panel">
    <p>Section 1 content (visible)</p>
  </div>

  <div id="panel-2" class="panel" hidden="until-found">
    <p>Section 2 content (hidden)</p>
  </div>
</div>
const accordion = document.querySelector('.custom-accordion');

accordion.addEventListener('beforematch', (e) => {
  // Hide all panels and synchronize button states before the browser reveals the matched panel
  accordion.querySelectorAll('.panel').forEach((panel) => {
    if (panel !== e.target) {
      panel.hidden = 'until-found';
    }
  });
  accordion.querySelectorAll('button').forEach((btn) => {
    const controls = btn.getAttribute('aria-controls');
    btn.setAttribute('aria-expanded', controls === e.target.id ? 'true' : 'false');
  });
});

แนวทางปฏิบัติแนะนำสำหรับhidden="until-found"

  • ควรใช้เส้นขอบ ระยะห่างจากขอบ และพื้นหลังกับ Wrapper ย่อยที่ฝังอยู่ แทนที่จะใช้กับองค์ประกอบที่มีแอตทริบิวต์ hidden="until-found" โดยตรง ซึ่งจะช่วยป้องกันการเปลี่ยนเลย์เอาต์หรือภาพที่เหลืออยู่โดยไม่ตั้งใจขณะที่องค์ประกอบถูกซ่อนอยู่
  • อย่าใช้ display: none, visibility: hidden หรือพร็อพเพอร์ตี้ CSS display หรือ visibility ที่เกี่ยวข้องกับองค์ประกอบที่มีแอตทริบิวต์ hidden="until-found" โดยตรง ซึ่งจะทำให้ฟังก์ชันการทำงานแบบเนทีฟใช้งานไม่ได้และซ่อนเนื้อหาจากดัชนีของ Search อย่างถาวร
  • อย่าใช้ hidden="until-found" สำหรับข้อมูลที่ละเอียดอ่อน โทเค็นข้อมูลภายใน หรือข้อมูลที่ไม่เกี่ยวข้องซึ่งไม่ควรเปิดเผยผ่านการค้นหา
  • อย่าใช้ hidden="until-found" แทนข้อความ "โปรแกรมอ่านหน้าจอเท่านั้น" (.sr-only)

การรองรับเบราว์เซอร์และกลยุทธ์สำรอง

อย่าหลีกเลี่ยง hidden="until-found" เนื่องจากเบราว์เซอร์ไม่รองรับ เนื่องจากประโยชน์ด้านการช่วยเหลือพิเศษมีมากกว่าต้นทุนในการใช้ทางเลือกสำรอง

hidden="until-found" สำรอง

สำหรับองค์ประกอบ UI มาตรฐาน เช่น แถบเมนูแบบ Accordion หรือส่วน "อ่านเพิ่มเติม" ให้ใช้ JavaScript เพื่อตรวจหาฟีเจอร์และแสดงเนื้อหาทั้งหมดหากไม่รองรับฟีเจอร์ดังกล่าว

if (!('onbeforematch' in HTMLElement.prototype)) {
  // Expand all hidden content for unsupported browsers
  document.querySelectorAll('[hidden="until-found"]').forEach((el) => {
    el.removeAttribute('hidden');
    // MANDATORY: also update any aria references to this element.
  });
}

สำหรับกระบวนทัศน์ UI ที่ไม่เกิดร่วมกัน (เช่น แผงพิเศษที่กำหนดเองซึ่งเนื้อหาแชร์พื้นที่ภาพเดียวกัน) แบ็กอัปควรแยกและแสดงเนื้อหาทั้งหมดเป็นเส้นตรงใต้พื้นที่อินเทอร์แอกทีฟหลัก โดยใช้ส่วนย่อยของ Anchor ของ URL เพื่อให้ผู้ใช้ไปยังส่วนที่เกี่ยวข้องได้โดยตรง