ในอดีต การใช้เลย์เอาต์ที่แตกต่างกันกับคอมโพเนนต์ตามเนื้อหาต้องใช้ JavaScript หรือตรรกะแบบมีเงื่อนไขในภาษาเทมเพลต HTML เพื่อแทรกคลาสตัวแก้ไข (เช่น .card--has-image หรือ .card--text-only)
คลาสเทียม :has() ช่วยขจัดความจำเป็นนี้โดยทำหน้าที่เป็นตัวเลือกหลัก ซึ่งช่วยให้คุณจัดรูปแบบองค์ประกอบคอนเทนเนอร์ตามเงื่อนไขได้โดยอิงตามการมีหรือไม่มีองค์ประกอบสืบทอดที่เฉพาะเจาะจง
เมื่อใช้ :has() คุณจะกำหนดรูปแบบเลย์เอาต์ที่แตกต่างกันทั้งหมดใน CSS ได้อย่างง่ายดายโดยอิงตามเนื้อหา DOM จริงของคอมโพเนนต์ นอกจากนี้ คุณยังเลือกที่จะรวมเข้ากับ :not() เพื่อกำหนดเป้าหมายการไม่มีเนื้อหาอย่างชัดเจนเพื่อกำหนดเลย์เอาต์เริ่มต้นได้ด้วย
การใช้การจัดรูปแบบคอนเทนเนอร์ตามเนื้อหา
บังคับ: คุณต้องใช้ตัวเลือก :has() ในองค์ประกอบคอนเทนเนอร์เพื่อตรวจหาเนื้อหาย่อยที่เฉพาะเจาะจง
วิธีสร้างคอมโพเนนต์ที่เปลี่ยนเลย์เอาต์ตามเนื้อหา
- กำหนดการจัดรูปแบบเริ่มต้น: ใช้รูปแบบเลย์เอาต์พื้นฐานกับองค์ประกอบคอนเทนเนอร์ (เช่น สแต็กคอลัมน์เดียวแบบง่าย)
- ใช้การลบล้างตามเนื้อหา: กำหนดเป้าหมายคอนเทนเนอร์ด้วย
: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');
}
});
}