การจัดรูปแบบตามเนื้อหา

ฟีเจอร์บนเว็บ: :has() :not()

ในอดีต การใช้เลย์เอาต์ที่แตกต่างกันกับคอมโพเนนต์ตามเนื้อหาต้องใช้ JavaScript หรือตรรกะแบบมีเงื่อนไขในภาษาเทมเพลต HTML เพื่อแทรกคลาสตัวแก้ไข (เช่น .card--has-image หรือ .card--text-only)

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

เมื่อใช้ :has() คุณจะกำหนดรูปแบบเลย์เอาต์ที่แตกต่างกันทั้งหมดใน CSS ได้อย่างง่ายดายโดยอิงตามเนื้อหา DOM จริงของคอมโพเนนต์ นอกจากนี้ คุณยังเลือกที่จะรวมเข้ากับ :not() เพื่อกำหนดเป้าหมายการไม่มีเนื้อหาอย่างชัดเจนเพื่อกำหนดเลย์เอาต์เริ่มต้นได้ด้วย

การใช้การจัดรูปแบบคอนเทนเนอร์ตามเนื้อหา

บังคับ: คุณต้องใช้ตัวเลือก :has() ในองค์ประกอบคอนเทนเนอร์เพื่อตรวจหาเนื้อหาย่อยที่เฉพาะเจาะจง

วิธีสร้างคอมโพเนนต์ที่เปลี่ยนเลย์เอาต์ตามเนื้อหา

  1. กำหนดการจัดรูปแบบเริ่มต้น: ใช้รูปแบบเลย์เอาต์พื้นฐานกับองค์ประกอบคอนเทนเนอร์ (เช่น สแต็กคอลัมน์เดียวแบบง่าย)
  2. ใช้การลบล้างตามเนื้อหา: กำหนดเป้าหมายคอนเทนเนอร์ด้วย :has([child-selector]) และใช้รูปแบบเลย์เอาต์ใหม่เมื่อมีเนื้อหานั้น (เช่น ตารางกริดแบบหลายคอลัมน์)

ตัวอย่าง: คอมโพเนนต์การ์ดที่เปลี่ยนเป็นเลย์เอาต์แบบเคียงข้างกันหากมีรูปภาพ

/* 1. Define the default state on the component container */
/* This applies when there is NO image */
.article-card {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: 1.5rem;
  border: 1px solid #ccc;
  border-radius: 8px;
}

/* 2. Apply styles when a specific child element is present */
/* MANDATORY: Target the container and use :has() to check for the descendant */
.article-card:has(img) {
  /* Change the layout to a row if an image exists */
  flex-direction: row;
  align-items: center;
}

/* Optional: Style the image itself (no :has() needed here) */
.article-card img {
  width: 150px;
  height: auto;
  border-radius: 4px;
}

/* You can also combine :has() and :not() to explicitly target the ABSENCE of content */
/* This selects a card that DOES NOT have an image */
.article-card:not(:has(img)) {
  /* E.g., apply a different background color for text-only cards */
  background-color: #f9f9f9;
}
<!-- Assume an <h1> precedes these components in the document layout -->
<!-- A card WITH an image (will use row layout) -->
<article class="article-card">
  <img src="thumbnail.jpg" alt="Article thumbnail" />
  <div class="content">
    <h2>Card With Image</h2>
    <p>This card lays out its content horizontally.</p>
  </div>
</article>

<!-- A card WITHOUT an image (will use default column layout) -->
<article class="article-card">
  <div class="content">
    <h2>Text-Only Card</h2>
    <p>This card lays out its content vertically, and gets its background color from the :not(:has()) rule.</p>
  </div>
</article>

เคล็ดลับด้านประสิทธิภาพ: เมื่อใช้ :has() ให้กำหนดขอบเขตของตัวเลือกไปยังคอนเทนเนอร์คอมโพเนนต์ที่เฉพาะเจาะจงที่สุดเท่าที่จะเป็นไปได้ (เช่น .article-card) หลีกเลี่ยงการยึดกับองค์ประกอบระดับสูงมาก เช่น body:has(img) หากการเปลี่ยนแปลงการจัดรูปแบบมีการแปลเป็นภาษาท้องถิ่นสูง เนื่องจากการค้นหา :has() แบบกว้างอาจทำให้เกิดการคำนวณเลย์เอาต์ใหม่มากขึ้น

กลยุทธ์สำรอง

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

บังคับ: เมื่อใช้การสำรองสำหรับเลย์เอาต์ที่สำคัญ คุณต้องใช้ @supports not selector(:has(*)) ใน CSS เพื่อกำหนดการสำรองตามคลาสแบบดั้งเดิม (เช่น .has-image)

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

หากไม่สามารถใช้การแสดงผลฝั่งเซิร์ฟเวอร์ได้ คุณต้องใช้สคริปต์ขนาดเล็กที่มี CSS.supports() เพื่อตรวจหาเนื้อหาและเพิ่มคลาสเมื่อโหลดหรือหลังจากการแทรกเนื้อหาแบบไดนามิก

/* Fallback CSS for older browsers */
/* We check if the browser DOES NOT support the :has() selector */
@supports not selector(:has(*)) {
  /* Define a traditional modifier class that applies the exact same layout overrides */
  .article-card.has-image {
    flex-direction: row;
    align-items: center;
  }
  
  .article-card:not(.has-image) {
    background-color: #f9f9f9;
  }
}
/* Fallback JavaScript for older browsers (if not using SSR to add the class) */
/* Check for support before running the script to avoid unnecessary work in modern browsers */
if (!CSS.supports('selector(:has(*))')) {
  // Find all components that need checking
  const cards = document.querySelectorAll('.article-card');
  
  cards.forEach(card => {
    // If the critical content exists, manually add the fallback class
    if (card.querySelector('img')) {
      card.classList.add('has-image');
    }
  });
}