ตารางที่ปรับเปลี่ยนตามอุปกรณ์

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

หัวใจสำคัญของตารางที่ปรับเปลี่ยนตามอุปกรณ์คือการรักษาความสัมพันธ์ระหว่างเซลล์ข้อมูลและส่วนหัว

  1. พื้นฐานเชิงความหมาย: ใช้เอลิเมนต์ <table> มาตรฐานกับเอลิเมนต์ <thead>, <tbody> และ <th>
  2. บริบทแบบติดหนึบ: ใช้ position: sticky กับทั้งส่วนหัวของคอลัมน์และส่วนหัวของแถว วิธีนี้ช่วยให้มั่นใจได้ว่าไม่ว่าผู้ใช้จะเลื่อนไปไกลแค่ไหนในทิศทางใดก็ตาม ผู้ใช้จะไม่สูญเสียบริบทของสิ่งที่ข้อมูลแสดง
  3. การเปลี่ยนรูปแบบที่ปรับเปลี่ยนได้: ใช้การค้นหา @container แทนการค้นหา @media วิธีนี้ช่วยให้ตารางปรับตามความกว้างของตัวเอง (เช่น เมื่อวางในแถบด้านข้างหรือวิดเจ็ตแดชบอร์ดแคบๆ) แทนที่จะเป็นวิวพอร์ตทั้งหมด
  4. การแทรกป้ายกำกับ: ในเลย์เอาต์แบบซ้อน <thead> จะถูกซ่อน และส่วนหัวที่เข้าถึงได้จะถูกแทรกลงในแต่ละเซลล์โดยใช้องค์ประกอบจำลอง ::before และตัวแปร CSS

ขั้นตอนการติดตั้งใช้งาน

1. มาร์กอัปและเลย์เอาต์

จัดโครงสร้างตารางด้วยส่วนหัวเชิงความหมายมาตรฐาน ใช้ .table-wrapper เพื่อจัดการเนื้อหาที่ล้นและจัดเตรียมคอนเทนเนอร์สำหรับการค้นหา เพิ่มแอตทริบิวต์ tabindex="0" ลงในคอนเทนเนอร์เพื่อให้ผู้ที่ใช้เฉพาะแป้นพิมพ์สามารถเลื่อนตารางได้อย่างสม่ำเสมอ

ระบุแอตทริบิวต์ role="region" ให้ Wrapper เพื่อสื่อสารวัตถุประสงค์ ระบุชื่อที่เข้าถึงได้โดยใช้ aria-labelledby (อ้างอิงแอตทริบิวต์ id ขององค์ประกอบที่มีอยู่ซึ่งใส่คำอธิบายภาพ/ชื่อตาราง) หรือแอตทริบิวต์ aria-label

<div tabindex="0" role="region" aria-labelledby="employee-table-caption" class="table-wrapper">
  <table>
    <caption id="employee-table-caption">Current employee directory</caption>
    <thead>
      <tr>
        <th>Employee</th>
        <th>Role</th>
        <th>Status</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <!-- Row header remains sticky horizontally on desktop, 
             and becomes a sticky card header on mobile -->
        <th>Alex Rivera</th>
        <td>Engineer</td>
        <td>Active</td>
      </tr>
    </tbody>
  </table>
</div>

2. กำหนดค่าส่วนหัวแบบติดหนึบ

เปิดใช้การเลื่อนและทำให้ส่วนหัวติดหนึบ ใช้พร็อพเพอร์ตี้เชิงตรรกะและค่าดัชนีลำดับ Z ที่ชัดเจนเพื่อจัดการลำดับการซ้อนกัน

.table-wrapper {
  overflow: auto;
  max-inline-size: 100%;
  max-block-size: min(500px, 80vh);
  container-type: inline-size;
}

table {
  border-collapse: separate;
  border-spacing: 0;
}

/* Sticky column headers (desktop) */
thead {
  position: sticky;
  inset-block-start: 0;
  z-index: 1; /* Above sticky row headers */
}

/* Sticky row headers (desktop) */
tbody th {
  position: sticky;
  inset-inline-start: 0;
  background: white; /* Required to cover background during scroll */
}

3. เลย์เอาต์แบบซ้อนกันที่ปรับเปลี่ยนตามอุปกรณ์

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

@container (width < 600px) {
  table, thead, tbody, tr, th, td {
    display: block;
  }

  thead {
    /* MANDATORY: Hide column header from layout and screen readers. */
    display: none
  }

  tr {
    margin-block-end: 1.5rem;
    border: 1px solid #ccc;
  }

  /* Sticky row headers at the top of each block/card */
  tbody th {
    position: sticky;
    inset-block-start: 0;
    z-index: 2;
    background: #eee;
  }

  /* Define column labels as CSS variables (often set via JS) */
  table {
    --label-1: "Employee";
    --label-2: "Role";
    --label-3: "Status";
  }

  /* Inject labels */
  td:nth-child(2)::before {
    /* MANDATORY: If using non-text content, provide the accessible name (after the /) which ensures screen readers hear the text alternative. */
    content: var(--label-2) ": ";
  }
  /* MANDATORY: Map the label for each column */
  td:nth-child(3)::before {
    content: var(--label-3) ": ";
  }
}