เมื่อเร็วๆ นี้ Chris Coyier ได้เขียนบล็อกโพสต์ที่ตั้งคำถามไว้ว่า
ในตอนนี้ เครื่องมือเบราว์เซอร์ทั้งหมดรองรับการค้นหาคอนเทนเนอร์แล้ว เหตุใดนักพัฒนาซอฟต์แวร์จึงไม่ใช้คอนเทนเนอร์เหล่านี้
โพสต์ของคณิตแสดงสาเหตุที่เป็นไปได้หลายประการ (เช่น ขาดการรับรู้ นิสัยเดิมๆ ตายยาก) แต่มีเหตุผลหนึ่งข้อที่โดดเด่น
นักพัฒนาซอฟต์แวร์บางคนกล่าวว่าต้องการใช้การค้นหาคอนเทนเนอร์เลย แต่ก็คิดว่าทำไม่ได้เพราะยังต้องรองรับเบราว์เซอร์รุ่นเก่าอยู่
จากที่คุณอาจเดาได้จากชื่อแล้ว เราคิดว่านักพัฒนาส่วนใหญ่สามารถใช้คำค้นหาคอนเทนเนอร์ส่วนใหญ่ในการใช้งานจริง แม้จะต้องสนับสนุนเบราว์เซอร์รุ่นเก่าก็ตาม โพสต์นี้จะอธิบายวิธีการที่เราแนะนำ
แนวทางที่ปฏิบัติได้จริง
หากต้องการใช้การค้นหาคอนเทนเนอร์ในโค้ดตอนนี้ แต่ต้องการให้ประสบการณ์การใช้งานดูเหมือนกันในทุกเบราว์เซอร์ คุณสามารถใช้ทางเลือกสำรองแบบ JavaScript สำหรับเบราว์เซอร์ที่ไม่รองรับการค้นหาคอนเทนเนอร์
จากนั้นคำถามก็คือ วิดีโอสำรองควรมีความครอบคลุมมากน้อยเพียงใด
ความท้าทายคือการหาความสมดุลระหว่างความมีประโยชน์และประสิทธิภาพ ซึ่งก็ไม่ต่างจากวิดีโอสำรองทั่วไป สําหรับฟีเจอร์ CSS นั้นมักจะเป็นไปไม่ได้ที่จะรองรับ API เต็มรูปแบบ (ดูสาเหตุที่ไม่ใช้ polyfill) อย่างไรก็ตาม คุณอาจไปได้ไกลมากโดยการระบุชุดฟังก์ชันหลักที่นักพัฒนาซอฟต์แวร์ส่วนใหญ่ต้องการใช้ แล้วเพิ่มประสิทธิภาพสำรองสำหรับคุณลักษณะเหล่านั้นเท่านั้น
แต่ "ชุดฟังก์ชันการทำงานหลัก" คืออะไร ที่นักพัฒนาซอฟต์แวร์ส่วนใหญ่ต้องการ สำหรับการสืบค้นคอนเทนเนอร์ ในการตอบคำถามนี้ ให้พิจารณาวิธีที่นักพัฒนาซอฟต์แวร์ส่วนใหญ่สร้างเว็บไซต์ที่ปรับเปลี่ยนตามอุปกรณ์ซึ่งขณะนี้ใช้คำค้นหาสื่อ
ไลบรารีคอมโพเนนต์และระบบการออกแบบที่ทันสมัยส่วนใหญ่ได้มาตรฐานตามหลักการที่เน้นอุปกรณ์เคลื่อนที่เป็นหลัก และติดตั้งใช้งานโดยใช้ชุดเบรกพอยท์ที่กำหนดไว้ล่วงหน้า (เช่น SM
, MD
, LG
, XL
) คอมโพเนนต์ต่างๆ ได้รับการเพิ่มประสิทธิภาพให้แสดงผลได้ดีในหน้าจอขนาดเล็กโดยค่าเริ่มต้น จากนั้นรูปแบบจะวางซ้อนกันอย่างมีเงื่อนไขเพื่อรองรับชุดความกว้างของหน้าจอที่ใหญ่ขึ้น (ดูตัวอย่างเอกสาร Bootstrap และ Tailwind)
แนวทางนี้เกี่ยวข้องกับระบบการออกแบบที่ใช้คอนเทนเนอร์พอๆ กับระบบการออกแบบที่ใช้วิวพอร์ต เนื่องจากในกรณีส่วนใหญ่สิ่งที่เกี่ยวข้องกับนักออกแบบไม่ใช่ขนาดหน้าจอหรือวิวพอร์ตขนาดใหญ่ แต่นั่นก็คือปริมาณพื้นที่ที่พร้อมใช้งานสำหรับคอมโพเนนต์ในบริบทที่วาง กล่าวอีกนัยหนึ่งคือ เบรกพอยท์จะใช้กับพื้นที่เนื้อหาที่เจาะจง เช่น แถบด้านข้าง กล่องโต้ตอบแบบโมดัล หรือเนื้อหาโพสต์ แทนที่เบรกพอยท์ที่สัมพันธ์กับวิวพอร์ตทั้งหมด (และใช้ได้กับทั้งหน้าเว็บ)
หากคุณสามารถทำงานภายในข้อจำกัดของการใช้อุปกรณ์เคลื่อนที่เป็นหลักและอิงตามเบรกพอยท์ (ซึ่งนักพัฒนาซอฟต์แวร์ส่วนใหญ่ใช้อยู่ในปัจจุบัน) การใช้ทางเลือกสำรองตามคอนเทนเนอร์สำหรับแนวทางดังกล่าวจะง่ายกว่าการใช้การสนับสนุนอย่างเต็มรูปแบบสำหรับฟีเจอร์การค้นหาคอนเทนเนอร์ทุกรายการอย่างมาก
ส่วนถัดไปจะอธิบายวิธีการทำงานทั้งหมด พร้อมกับคำแนะนำทีละขั้นตอนที่จะแสดงวิธีการนำเทคโนโลยีนี้ไปใช้ในเว็บไซต์ที่มีอยู่แล้ว
วิธีการทำงาน
ขั้นตอนที่ 1: อัปเดตรูปแบบคอมโพเนนต์เพื่อใช้กฎ @container
แทนกฎ @media
ข้อ
ในขั้นตอนแรกนี้ ให้ระบุคอมโพเนนต์ในเว็บไซต์ที่คุณคิดว่าจะได้รับประโยชน์จากการปรับขนาดตามคอนเทนเนอร์แทนที่จะเป็นการปรับขนาดตามวิวพอร์ต
คุณควรเริ่มต้นด้วยคอมโพเนนต์เพียง 1-2 รายการเพื่อดูว่ากลยุทธ์นี้ทำงานอย่างไร แต่หากคุณต้องการแปลงคอมโพเนนต์ทั้ง 100% เป็นการจัดรูปแบบตามคอนเทนเนอร์ ก็ไม่มีปัญหาเช่นกัน ข้อดีของกลยุทธ์นี้คือคุณจะนำไปปรับใช้ทีละน้อยได้หากต้องการ
เมื่อระบุคอมโพเนนต์ที่ต้องการอัปเดตแล้ว คุณจะต้องเปลี่ยนกฎ @media
แต่ละข้อในคอมโพเนนต์เหล่านั้น CSS เป็นกฎ @container
ต่อไปนี้คือตัวอย่างลักษณะที่อาจปรากฏบนคอมโพเนนต์ .photo-gallery
ซึ่งโดยค่าเริ่มต้นจะเป็นคอลัมน์เดียว แล้วใช้กฎ @media
เพื่ออัปเดตเลย์เอาต์ให้เป็น 2 และ 3 คอลัมน์ในเบรกพอยท์ MD และ XL (ตามลำดับ)
.photo-gallery {
display: grid;
grid-template-columns: 1fr;
}
/* Styles for the `MD` breakpoint */
@media (min-width: 800px) {
.photo-gallery {
grid-template-columns: 1fr 1fr;
}
}
/* Styles for the `XL` breakpoint */
@media (min-width: 1200px) {
.photo-gallery {
grid-template-columns: 1fr 1fr 1fr;
}
}
หากต้องการอัปเดตคอมโพเนนต์ .photo-gallery
เพื่อใช้กฎ @container
ให้แทนที่สตริง @media
ด้วยสตริง @container
ใน CSS ก่อน ไวยากรณ์สำหรับกฎทั้งสองนี้มีความคล้ายคลึงกันมาก ซึ่งในหลายกรณีคุณอาจต้องเปลี่ยนกฎทั้งหมด
คุณอาจต้องอัปเดตเงื่อนไขขนาดด้วย โดยเฉพาะอย่างยิ่งหากกฎ @media
ของเว็บไซต์ตั้งสมมติฐานบางประการเกี่ยวกับพื้นที่ว่างที่คอมโพเนนต์ที่เฉพาะเจาะจงในวิวพอร์ตขนาดต่างๆ จะพร้อมใช้งาน ทั้งนี้ขึ้นอยู่กับการออกแบบของเว็บไซต์
ตัวอย่างเช่น หากรูปแบบสำหรับ CSS .photo-gallery
ที่เบรกพอยท์ MD
และ XL
ในตัวอย่างก่อนหน้านี้ถือว่าแถบด้านข้างที่มีความกว้าง 200 พิกเซลจะแสดงในเบรกพอยท์เหล่านั้น เงื่อนไขขนาดสำหรับกฎ @container
ควรน้อยกว่า 200 พิกเซล ซึ่งสมมติว่าเป็น "คอนเทนเนอร์" สำหรับคอมโพเนนต์ .photo-gallery
จะไม่มีแถบด้านข้าง
การแปลง CSS .photo-gallery
จากกฎ @media
เป็นกฎ @container
ประกอบด้วยชุดการเปลี่ยนแปลงทั้งหมดดังนี้
/* Before, using the original breakpoint sizes: */
@media (min-width: 800px) { /* ... */ }
@media (min-width: 1200px) { /* ... */ }
/* After, with the breakpoint sizes reduced by 200px: */
@container (min-width: 600px) { /* ... */ }
@container (min-width: 1000px) { /* ... */ }
โปรดทราบว่าคุณไม่ต้องเปลี่ยนแปลงรูปแบบใดๆ ภายในบล็อกการประกาศ เนื่องจากสไตล์เหล่านั้นจะแสดงลักษณะรูปลักษณ์ของคอมโพเนนต์ ไม่ใช่เวลาที่ควรใช้รูปแบบที่เจาะจง
เมื่อคุณอัปเดตรูปแบบคอมโพเนนต์จากกฎ @media
เป็นกฎ @container
แล้ว ขั้นตอนถัดไปคือการกำหนดค่าองค์ประกอบคอนเทนเนอร์
ขั้นตอนที่ 2: เพิ่มองค์ประกอบคอนเทนเนอร์ลงใน HTML
ขั้นตอนก่อนหน้านี้ได้กำหนดรูปแบบคอมโพเนนต์โดยอิงตามขนาดขององค์ประกอบคอนเทนเนอร์ ขั้นตอนถัดไปคือกำหนดว่าองค์ประกอบใดในหน้าเว็บควรเป็นองค์ประกอบคอนเทนเนอร์ที่ขนาดของกฎ @container
จะสัมพันธ์กับ
คุณประกาศให้องค์ประกอบใดก็ตามเป็นองค์ประกอบคอนเทนเนอร์ใน CSS ได้โดยตั้งค่าพร็อพเพอร์ตี้ container-type
ขององค์ประกอบเป็น size
หรือ inline-size
ถ้ากฎคอนเทนเนอร์เป็นแบบอิงตามความกว้าง โดยทั่วไป inline-size
จะเป็นตัวเลือกที่คุณต้องการใช้
พิจารณาเว็บไซต์ที่มีโครงสร้าง HTML พื้นฐานต่อไปนี้
<body>
<div class="sidebar">...</div>
<div class="content">...</div>
</body>
หากต้องการทำให้องค์ประกอบ .sidebar
และ .content
บนคอนเทนเนอร์ของเว็บไซต์นี้ ให้เพิ่มกฎนี้ลงใน CSS ของคุณ ดังนี้
.content, .sidebar {
container-type: inline-size;
}
สำหรับเบราว์เซอร์ที่รองรับการค้นหาคอนเทนเนอร์ CSS นี้จะช่วยคุณสร้างรูปแบบคอมโพเนนต์ที่กำหนดไว้ในขั้นตอนก่อนหน้าโดยสัมพันธ์กับพื้นที่เนื้อหาหลักหรือแถบด้านข้าง ทั้งนี้ขึ้นอยู่กับองค์ประกอบที่อยู่ภายใน
อย่างไรก็ตาม สำหรับเบราว์เซอร์ที่ไม่รองรับการค้นหาคอนเทนเนอร์ จะมีสิ่งที่ต้องทำเพิ่มเติม
คุณต้องเพิ่มโค้ดบางส่วนที่ตรวจจับเมื่อขนาดขององค์ประกอบของคอนเทนเนอร์เปลี่ยนแปลง แล้วอัปเดต DOM ตามการเปลี่ยนแปลงเหล่านั้นในลักษณะที่ CSS ของคุณเชื่อมโยงได้
โชคดีที่โค้ดที่ต้องใช้ในการดำเนินการนั้นไม่มากและอาจแยกโค้ดออกมาเป็นคอมโพเนนต์ที่แชร์ได้ซึ่งคุณนำไปใช้ในเว็บไซต์และในส่วนเนื้อหาใดก็ได้
โค้ดต่อไปนี้จะกำหนดองค์ประกอบ <responsive-container>
ที่ใช้ซ้ำได้ ซึ่งจะรับข้อมูลการเปลี่ยนแปลงขนาดโดยอัตโนมัติและเพิ่มคลาสเบรกพอยท์ที่ CSS จัดรูปแบบได้โดยอิงตามสิ่งต่อไปนี้
// A mapping of default breakpoint class names and min-width sizes.
// Redefine these (or add more) as needed based on your site's design.
const defaultBreakpoints = {SM: 400, MD: 600 LG: 800, XL: 1000};
// A resize observer that monitors size changes to all <responsive-container>
// elements and calls their `updateBreakpoints()` method with the updated size.
const ro = new ResizeObserver((entries) => {
entries.forEach((e) => e.target.updateBreakpoints(e.contentRect));
});
class ResponsiveContainer extends HTMLElement {
connectedCallback() {
const bps = this.getAttribute('breakpoints');
this.breakpoints = bps ? JSON.parse(bps) : defaultBreakpoints;
this.name = this.getAttribute('name') || '';
ro.observe(this);
}
disconnectedCallback() {
ro.unobserve(this);
}
updateBreakpoints(contentRect) {
for (const bp of Object.keys(this.breakpoints)) {
const minWidth = this.breakpoints[bp];
const className = this.name ? `${this.name}-${bp}` : bp;
this.classList.toggle(className, contentRect.width >= minWidth);
}
}
}
self.customElements.define('responsive-container', ResponsiveContainer);
โค้ดนี้ทำงานโดยการสร้าง ResizeObserver ที่จะรับข้อมูลการเปลี่ยนแปลงขนาดขององค์ประกอบ <responsive-container>
ใน DOM โดยอัตโนมัติ หากการเปลี่ยนแปลงขนาดตรงกับขนาดเบรกพอยท์ที่กำหนดไว้อย่างใดอย่างหนึ่ง คลาสที่มีชื่อเบรกพอยท์ดังกล่าวจะถูกเพิ่มลงในองค์ประกอบ (และลบออกหากเงื่อนไขไม่ตรงกันแล้ว)
ตัวอย่างเช่น หาก width
ขององค์ประกอบ <responsive-container>
อยู่ระหว่าง 600 ถึง 800 พิกเซล (ตามค่าเบรกพอยท์เริ่มต้นที่กำหนดไว้ในโค้ด) ระบบจะเพิ่มคลาส SM
และ MD
เช่นนี้
<responsive-container class="SM MD">...</responsive-container>
คลาสเหล่านี้ให้คุณกำหนดรูปแบบสำรองสำหรับเบราว์เซอร์ที่ไม่รองรับการค้นหาคอนเทนเนอร์ (ดูขั้นตอนที่ 3: เพิ่มรูปแบบสำรองลงใน CSS)
หากต้องการอัปเดตโค้ด HTML ก่อนหน้าเพื่อใช้องค์ประกอบคอนเทนเนอร์นี้ ให้เปลี่ยนแถบด้านข้างและเนื้อหาหลัก <div>
เป็นองค์ประกอบ <responsive-container>
แทน ดังนี้
<body>
<responsive-container class="sidebar">...</responsive-container>
<responsive-container class="content">...</responsive-container>
</body>
ในกรณีส่วนใหญ่ คุณสามารถใช้องค์ประกอบ <responsive-container>
โดยไม่ต้องมีการปรับแต่งใดๆ แต่ถ้าต้องการปรับแต่ง คุณสามารถใช้ตัวเลือกต่อไปนี้ได้
- ขนาดเบรกพอยท์ที่กำหนดเอง: โค้ดนี้ใช้ชุดชื่อคลาสเบรกพอยท์เริ่มต้นและขนาดความกว้างขั้นต่ำ แต่คุณเปลี่ยนค่าเริ่มต้นเหล่านี้ให้เป็นได้ตามต้องการ นอกจากนี้คุณยังลบล้างค่าเหล่านี้แบบทีละองค์ประกอบได้โดยใช้แอตทริบิวต์
breakpoints
- คอนเทนเนอร์ที่มีชื่อ: โค้ดนี้ยังรองรับคอนเทนเนอร์ที่มีชื่อโดยการส่งแอตทริบิวต์
name
ด้วย ซึ่งเป็นสิ่งสำคัญหากคุณต้องการซ้อนองค์ประกอบคอนเทนเนอร์ ดูรายละเอียดเพิ่มเติมในส่วนข้อจํากัด
นี่คือตัวอย่างที่ตั้งค่าตัวเลือกการกำหนดค่าทั้ง 2 รายการนี้
<responsive-container
name='sidebar'
breakpoints='{"bp4":400,"bp5":500,"bp6":600,"bp7":700,"bp8":800,"bp9":900,"bp10":1000}'>
</responsive-container>
สุดท้าย เมื่อรวมโค้ดนี้เข้าด้วยกันแล้ว โปรดตรวจสอบว่าคุณใช้การตรวจหาฟีเจอร์และ import()
แบบไดนามิกเพื่อโหลดโค้ดก็ต่อเมื่อเบราว์เซอร์ไม่รองรับการค้นหาคอนเทนเนอร์เท่านั้น
if (!CSS.supports('container-type: inline-size')) {
import('./path/to/responsive-container.js');
}
ขั้นตอนที่ 3: เพิ่มรูปแบบสำรองลงใน CSS
ขั้นตอนสุดท้ายในกลยุทธ์นี้คือการเพิ่มรูปแบบสำรองสำหรับเบราว์เซอร์ที่ไม่รู้จักสไตล์ที่กำหนดไว้ในกฎ @container
ซึ่งทำได้โดยการทำซ้ำกฎเหล่านั้นโดยใช้คลาสเบรกพอยท์ที่ตั้งค่าไว้ในองค์ประกอบ <responsive-container>
ต่อจากตัวอย่าง .photo-gallery
ก่อนหน้านี้ รูปแบบสำรองสำหรับกฎ @container
2 ข้ออาจมีลักษณะดังนี้
/* Container query styles for the `MD` breakpoint. */
@container (min-width: 600px) {
.photo-gallery {
grid-template-columns: 1fr 1fr;
}
}
/* Fallback styles for the `MD` breakpoint. */
@supports not (container-type: inline-size) {
:where(responsive-container.MD) .photo-gallery {
grid-template-columns: 1fr 1fr;
}
}
/* Container query styles for the `XL` breakpoint. */
@container (min-width: 1000px) {
.photo-gallery {
grid-template-columns: 1fr 1fr 1fr;
}
}
/* Fallback styles for the `XL` breakpoint. */
@supports not (container-type: inline-size) {
:where(responsive-container.XL) .photo-gallery {
grid-template-columns: 1fr 1fr 1fr;
}
}
ในโค้ดนี้ สำหรับแต่ละกฎ @container
จะมีกฎที่เทียบเท่าซึ่งจับคู่องค์ประกอบ <responsive-container>
อย่างมีเงื่อนไขหากมีคลาสเบรกพอยท์ที่เกี่ยวข้องอยู่
ส่วนของตัวเลือกที่ตรงกับองค์ประกอบ <responsive-container>
จะรวมไว้ในตัวเลือกคลาสเทียมที่มีฟังก์ชันการทำงาน :where() เพื่อรักษาความจำเพาะของตัวเลือกสำรองให้เทียบเท่ากับความจำเพาะของตัวเลือกเดิมภายในกฎ @container
กฎสำรองแต่ละข้อจะรวมอยู่ในการประกาศ @supports
ด้วย แม้ว่าวิธีนี้จะไม่จำเป็นอย่างมากเพื่อให้ตัวเลือกสำรองทำงานได้ แต่หมายความว่าเบราว์เซอร์จะไม่สนใจกฎเหล่านี้โดยสิ้นเชิงหากกฎรองรับการค้นหาคอนเทนเนอร์ ซึ่งสามารถปรับปรุงประสิทธิภาพการจับคู่รูปแบบโดยทั่วไปได้ นอกจากนี้ยังอาจอนุญาตให้เครื่องมือสร้างหรือ CDN นำการประกาศเหล่านั้นออกหากทราบว่าเบราว์เซอร์รองรับการค้นหาคอนเทนเนอร์และไม่จำเป็นต้องใช้รูปแบบสำรองเหล่านั้น
ข้อเสียหลักของกลยุทธ์ทางเลือกนี้คือคุณต้องประกาศสไตล์ซ้ำ 2 ครั้ง ซึ่งเป็นเรื่องที่น่าเบื่อหน่ายและเกิดข้อผิดพลาดได้ง่าย อย่างไรก็ตาม หากคุณใช้ CSS Preprocessor คุณอาจนำโค้ดดังกล่าวมารวมเข้าด้วยกันซึ่งจะสร้างทั้งกฎ @container
และโค้ดสำรองให้คุณ ตัวอย่างการใช้ Sass มีดังนี้
@use 'sass:map';
$breakpoints: (
'SM': 400px,
'MD': 600px,
'LG': 800px,
'XL': 1000px,
);
@mixin breakpoint($breakpoint) {
@container (min-width: #{map.get($breakpoints, $breakpoint)}) {
@content();
}
@supports not (container-type: inline-size) {
:where(responsive-container.#{$breakpoint}) & {
@content();
}
}
}
แล้วเมื่อมีส่วนผสมดังกล่าวนี้ คุณก็อัปเดตสไตล์คอมโพเนนต์ .photo-gallery
เดิมให้มีลักษณะดังนี้ ซึ่งจะกำจัดการทำซ้ำออกไปทั้งหมด
.photo-gallery {
display: grid;
grid-template-columns: 1fr;
@include breakpoint('MD') {
grid-template-columns: 1fr 1fr;
}
@include breakpoint('XL') {
grid-template-columns: 1fr 1fr 1fr;
}
}
เท่านี้ก็เรียบร้อย
สรุป
สรุปก็คือวิธีอัปเดตโค้ดเพื่อใช้การค้นหาคอนเทนเนอร์ในตอนนี้พร้อมด้วยโฆษณาสำรองข้ามเบราว์เซอร์มีดังนี้
- คอมโพเนนต์ข้อมูลประจำตัวที่คุณต้องการจัดรูปแบบโดยสัมพันธ์กับคอนเทนเนอร์ และอัปเดตกฎ
@media
ใน CSS เพื่อใช้กฎ@container
นอกจากนี้ (หากยังไม่ได้ทำ) ให้กำหนดมาตรฐานสำหรับชุดของชื่อเบรกพอยท์เพื่อให้ตรงกับเงื่อนไขของขนาดในกฎของคอนเทนเนอร์ - เพิ่ม JavaScript ที่ขับเคลื่อนองค์ประกอบ
<responsive-container>
ที่กำหนดเอง จากนั้นเพิ่มองค์ประกอบ<responsive-container>
ไปยังพื้นที่เนื้อหาใดก็ได้ในหน้าเว็บที่คุณต้องการให้คอมโพเนนต์มีความเกี่ยวข้อง - หากต้องการรองรับเบราว์เซอร์รุ่นเก่า ให้เพิ่มรูปแบบสำรองลงใน CSS ที่ตรงกับคลาสเบรกพอยท์ที่ระบบเพิ่มลงในองค์ประกอบ
<responsive-container>
ใน HTML โดยอัตโนมัติ โดยหลักการแล้วควรใช้ CSS Preprocessor Mixin เพื่อหลีกเลี่ยงการเขียนรูปแบบเดียวกัน 2 ครั้ง
ข้อดีของกลยุทธ์นี้คือมีค่าใช้จ่ายในการตั้งค่าแบบครั้งเดียว แต่หลังจากนั้นก็ไม่ต้องลงแรงเพิ่มเติมในการเพิ่มคอมโพเนนต์ใหม่และกำหนดสไตล์ที่สัมพันธ์กับคอนเทนเนอร์สำหรับคอมโพเนนต์เหล่านั้น
ดูของจริง
วิธีที่ดีที่สุดที่จะทำความเข้าใจว่าขั้นตอนทั้งหมดนี้สอดคล้องกันได้อย่างไรคือการดูตัวอย่างการใช้งานจริง
การสาธิตนี้เป็นเวอร์ชันอัปเดตของเว็บไซต์ที่สร้างขึ้นในปี 2019 (ก่อนที่จะมีการค้นหาคอนเทนเนอร์) เพื่อช่วยอธิบายว่าเหตุใดการค้นหาคอนเทนเนอร์จึงสำคัญต่อการสร้างไลบรารีคอมโพเนนต์ที่ปรับเปลี่ยนตามอุปกรณ์อย่างแท้จริง
เนื่องจากเว็บไซต์นี้มีการกำหนดสไตล์สำหรับ "คอมโพเนนต์ที่ปรับเปลี่ยนตามอุปกรณ์" จำนวนมากอยู่แล้ว จึงเป็นตัวเลือกที่เหมาะที่จะทดสอบกลยุทธ์ที่นำมาใช้ในเว็บไซต์ที่ไม่สำคัญมากนัก ปรากฏว่าการอัปเดตนั้นค่อนข้างง่ายและแทบไม่ต้องเปลี่ยนแปลงรูปแบบเว็บไซต์เดิมเลย
คุณดูซอร์สโค้ดเดโมฉบับเต็มใน GitHub และอย่าลืมดู CSS ของคอมโพเนนต์สาธิตอย่างเฉพาะเจาะจงเพื่อดูการกำหนดรูปแบบสำรอง หากต้องการทดสอบเฉพาะลักษณะการทำงานสำรอง เรามีการสาธิตเฉพาะสำรองเท่านั้นที่รวมตัวแปรนั้นเท่านั้น แม้แต่ในเบราว์เซอร์ที่รองรับการค้นหาคอนเทนเนอร์
ข้อจำกัดและการปรับปรุงที่เป็นไปได้
อย่างที่กล่าวไว้ในตอนต้นของโพสต์นี้ กลยุทธ์ที่แสดงที่นี่ทำงานได้ดีสำหรับกรณีการใช้งานส่วนใหญ่ที่นักพัฒนาซอฟต์แวร์สนใจจริงๆ เมื่อเข้าถึงคำค้นหาคอนเทนเนอร์
อย่างไรก็ตาม มีกรณีการใช้งานขั้นสูงบางส่วนที่กลยุทธ์นี้จงใจไม่พยายามให้การสนับสนุน ซึ่งจะกล่าวถึงต่อไป
หน่วยการค้นหาคอนเทนเนอร์
ข้อกำหนดสำหรับการค้นหาคอนเทนเนอร์จะกำหนดจำนวนหน่วยใหม่ ที่สัมพันธ์กับขนาดของคอนเทนเนอร์ทั้งหมด แม้ว่าอาจมีประโยชน์ในบางกรณี แต่การออกแบบที่ปรับเปลี่ยนตามอุปกรณ์ส่วนใหญ่สามารถทำได้ผ่านวิธีการที่มีอยู่ เช่น เปอร์เซ็นต์หรือการใช้เลย์เอาต์แบบตารางกริดหรือเลย์เอาต์แบบยืดหยุ่น
อย่างไรก็ตาม หากคุณจำเป็นต้องใช้หน่วยการค้นหาคอนเทนเนอร์ คุณสามารถเพิ่มการสนับสนุนให้กับหน่วยดังกล่าวได้อย่างง่ายดายโดยใช้พร็อพเพอร์ตี้ที่กำหนดเอง โดยการกำหนดพร็อพเพอร์ตี้ที่กำหนดเองสำหรับแต่ละหน่วยโฆษณาที่ใช้ในองค์ประกอบคอนเทนเนอร์ ดังนี้
responsive-container {
--cqw: 1cqw;
--cqh: 1cqh;
}
จากนั้นเมื่อใดก็ตามที่คุณต้องเข้าถึงหน่วยการค้นหาของคอนเทนเนอร์ ให้ใช้คุณสมบัติเหล่านั้นแทนที่จะใช้ตัวหน่วยดังนี้
.photo-gallery {
font-size: calc(10 * var(--cqw));
}
จากนั้นเพื่อรองรับเบราว์เซอร์เวอร์ชันเก่า ให้ตั้งค่าสำหรับพร็อพเพอร์ตี้ที่กำหนดเองเหล่านั้นในองค์ประกอบคอนเทนเนอร์ภายใน Callback ResizeObserver
class ResponsiveContainer extends HTMLElement {
// ...
updateBreakpoints(contentRect) {
this.style.setProperty('--cqw', `${contentRect.width / 100}px`);
this.style.setProperty('--cqh', `${contentRect.height / 100}px`);
// ...
}
}
ซึ่งช่วยให้คุณ "ผ่าน" ค่าเหล่านั้นตั้งแต่ JavaScript ไปจนถึง CSS จากนั้นคุณจะมีอำนาจอย่างเต็มที่ของ CSS (เช่น calc()
, min()
, max()
, clamp()
) ที่จะจัดการค่าเหล่านี้ตามต้องการ
การรองรับคุณสมบัติเชิงตรรกะและโหมดการเขียน
คุณอาจสังเกตเห็นการใช้ inline-size
แทนที่จะเป็น width
ในการประกาศ @container
ในตัวอย่าง CSS เหล่านี้บางรายการ นอกจากนี้ คุณยังอาจสังเกตเห็นหน่วยโฆษณา cqi
และ cqb
ใหม่ (สำหรับขนาดในหน้าและขนาดบล็อกตามลำดับ) ฟีเจอร์ใหม่เหล่านี้สะท้อนถึงการเปลี่ยนแปลงของ CSS ไปสู่คุณสมบัติและค่าเชิงตรรกะ แทนที่จะเป็นฟีเจอร์ทางกายภาพหรือแบบชี้นำ
น่าเสียดายที่ API อย่าง Adjust Observer จะยังคงรายงานค่าใน width
และ height
ดังนั้น หากการออกแบบของคุณต้องการความยืดหยุ่นของคุณสมบัติเชิงตรรกะ คุณก็ต้องหาคำตอบเอง
แม้ว่าคุณจะทำให้โหมดการเขียนโดยใช้บางอย่าง เช่น getComputedStyle()
ที่ส่งผ่านองค์ประกอบคอนเทนเนอร์ได้ แต่การทำเช่นนี้มีค่าใช้จ่าย และไม่ใช่วิธีที่ดีในการตรวจสอบว่าโหมดการเขียนมีการเปลี่ยนแปลงหรือไม่
ด้วยเหตุนี้ วิธีที่ดีที่สุดคือให้ตัวองค์ประกอบ <responsive-container>
ยอมรับพร็อพเพอร์ตี้โหมดการเขียนที่เจ้าของเว็บไซต์จะตั้งค่า (และอัปเดต) ได้ตามต้องการ หากต้องการใช้งาน คุณต้องทำตามแนวทางเดียวกับที่แสดงในส่วนก่อนหน้า แล้วสลับ width
และ height
ตามความจำเป็น
คอนเทนเนอร์ที่ซ้อนกัน
พร็อพเพอร์ตี้ container-name
ให้คุณตั้งชื่อคอนเทนเนอร์ซึ่งจะใช้อ้างอิงในกฎ @container
ได้ คอนเทนเนอร์ที่มีชื่อจะมีประโยชน์หากคุณมีคอนเทนเนอร์ฝังอยู่ในคอนเทนเนอร์ และคุณต้องการกฎที่จะจับคู่คอนเทนเนอร์บางรายการเท่านั้น (ไม่ใช่แค่คอนเทนเนอร์ระดับบนที่ใกล้ที่สุด)
กลยุทธ์สำรองที่แสดงที่นี่ใช้ชุดค่าผสมองค์ประกอบสืบทอดเพื่อจัดรูปแบบองค์ประกอบที่ตรงกับคลาสเบรกพอยท์บางคลาส ซึ่งอาจทำให้เสียหายได้หากคุณมีคอนเทนเนอร์ที่ซ้อนอยู่ เนื่องจากคลาสเบรกพอยท์จำนวนเท่าใดจากระดับบนขององค์ประกอบคอนเทนเนอร์หลายรายการอาจตรงกับคอมโพเนนต์ที่ระบุในเวลาเดียวกัน
ตัวอย่างเช่น มีองค์ประกอบ <responsive-container>
2 รายการที่รวมคอมโพเนนต์ .photo-gallery
แต่เนื่องจากคอนเทนเนอร์ด้านนอกมีขนาดใหญ่กว่าคอนเทนเนอร์ด้านใน จึงมีการเพิ่มคลาสเบรกพอยท์ที่แตกต่างกัน
<responsive-container class="SM MD LG">
...
<responsive-container class="SM">
...
<div class="photo-gallery">...</div class="photo-gallery">
</responsive-container>
</responsive-container>
ในตัวอย่างนี้ คลาส MD
และ LG
ในคอนเทนเนอร์ด้านนอกจะมีผลต่อกฎของรูปแบบที่ตรงกับคอมโพเนนต์ .photo-gallery
ซึ่งไม่ตรงกับการทำงานของคำค้นหาคอนเทนเนอร์ (เนื่องจากตรงกับคอนเทนเนอร์ระดับบนที่ใกล้ที่สุดเท่านั้น)
หากต้องการจัดการกับเรื่องนี้ ให้ทำอย่างใดอย่างหนึ่งต่อไปนี้
- โปรดตั้งชื่อคอนเทนเนอร์ที่จะซ้อนเสมอ และตรวจสอบว่าคลาสเบรกพอยท์มีคำนำหน้าด้วยชื่อคอนเทนเนอร์ดังกล่าวเพื่อหลีกเลี่ยงข้อขัดแย้ง
- ใช้ชุดค่าผสมย่อยแทนชุดค่าผสมองค์ประกอบสืบทอดในตัวเลือกสำรอง (ซึ่งมีข้อจำกัดมากกว่าเล็กน้อย)
ส่วนคอนเทนเนอร์ที่ซ้อนกันของเว็บไซต์สาธิตมีตัวอย่างของการทำงานนี้โดยใช้คอนเทนเนอร์ที่มีชื่อ รวมถึงส่วนผสมของ Sass ที่ใช้ในโค้ดเพื่อสร้างรูปแบบสำรองสำหรับกฎ @container
ทั้งที่มีชื่อและไม่ได้ตั้งชื่อ
จะเกิดอะไรกับเบราว์เซอร์ที่ไม่รองรับ :where()
, องค์ประกอบที่กำหนดเอง หรือตัวสังเกตการปรับขนาด
แม้ว่า API เหล่านี้อาจดูค่อนข้างใหม่ แต่ทั้งหมดมีการรองรับในทุกเบราว์เซอร์มานานกว่า 3 ปีแล้ว และทั้งหมดเป็นส่วนหนึ่งของเกณฑ์พื้นฐานที่มีให้ใช้งานในวงกว้าง
ดังนั้นหากคุณไม่มีข้อมูลที่แสดงให้เห็นว่าผู้เข้าชมเว็บไซต์ของคุณส่วนใหญ่ใช้เบราว์เซอร์ที่ไม่รองรับหนึ่งในฟีเจอร์เหล่านี้ คุณก็ต้องไม่ใช้อย่างอิสระโดยไม่มีวิดีโอสำรอง
อย่างไรก็ตาม สำหรับกรณีการใช้งานเฉพาะนี้ สิ่งที่แย่ที่สุดที่อาจเกิดขึ้นคือวิดีโอสำรองไม่ทำงานสำหรับผู้ใช้ส่วนน้อยมาก ซึ่งหมายความว่าผู้ใช้จะเห็นมุมมองเริ่มต้นแทนที่จะเป็นมุมมองที่เพิ่มประสิทธิภาพสำหรับขนาดคอนเทนเนอร์
ฟังก์ชันการทำงานของเว็บไซต์จะยังใช้งานได้อยู่และนั่นคือสิ่งสำคัญอย่างยิ่ง
ทำไมไม่ใช้ polyfill ของการค้นหาคอนเทนเนอร์
ฟีเจอร์ CSS เป็นที่กล่าวกันมากว่าใช้ Polyfill ได้ยาก และโดยทั่วไปแล้วจำเป็นต้องนำโปรแกรมแยกวิเคราะห์ CSS ทั้งหมดและตรรกะแบบ Cascade ทั้งหมดใน JavaScript มาปรับใช้ใหม่ ด้วยเหตุนี้ ผู้เขียน CSS Polyfill จึงต้องมีข้อจำกัดหลายอย่าง ซึ่งมักจะมาพร้อมกับข้อจำกัดมากมายในด้านฟีเจอร์ รวมถึงค่าใช้จ่ายในการดำเนินการที่สำคัญ
ด้วยเหตุนี้ โดยทั่วไปเราจึงไม่แนะนําให้ใช้ CSS Polyfill ในเวอร์ชันที่ใช้งานจริง รวมถึง container-query-polyfill จาก Google Chrome Labs ซึ่งไม่มีอีกต่อไปแล้ว (และมีจุดประสงค์เพื่อการสาธิตเป็นหลัก)
กลยุทธ์ทางเลือกที่กล่าวถึงในที่นี้มีข้อจำกัดน้อยกว่า ใช้โค้ดน้อยกว่ามาก และมีประสิทธิภาพสูงกว่า Polyfill ของคอนเทนเนอร์คำค้นหาใดๆ ที่เคยทำได้อย่างมีนัยสำคัญ
คุณจำเป็นต้องใช้ทางเลือกสำรองสำหรับเบราว์เซอร์รุ่นเก่าหรือไม่
หากคุณกังวลเกี่ยวกับข้อจำกัดใดๆ ที่ได้กล่าวถึงในที่นี้ ขอแนะนำให้คุณถามตัวเองว่าจริงๆ แล้วคุณต้องใช้ทางเลือกสำรองตั้งแต่แรกหรือไม่ เพราะวิธีที่ง่ายที่สุดในการหลีกเลี่ยงข้อจำกัดเหล่านี้คือการใช้ฟีเจอร์นี้โดยไม่มีทางเลือกสำรอง จริงๆ แล้ว นั่นอาจเป็นตัวเลือกที่สมเหตุสมผลจริงๆ
จากข้อมูลของ caniuse.com ผู้ใช้อินเทอร์เน็ตทั่วโลกสนับสนุนการค้นหาคอนเทนเนอร์ถึง 90% และสำหรับผู้คนจำนวนมากที่อ่านโพสต์นี้ จำนวนผู้ใช้มีแนวโน้มที่จะค่อนข้างสูงขึ้น ดังนั้นโปรดทราบว่าผู้ใช้ส่วนใหญ่จะเห็น UI เวอร์ชันคอนเทนเนอร์คำค้นหา และสำหรับผู้ใช้ 10% ที่จะไม่ดำเนินการ ก็ไม่น่าจะได้รับประสบการณ์การใช้งานที่ไม่ดี เมื่อทำตามกลยุทธ์นี้ ในกรณีที่แย่ที่สุด ผู้ใช้เหล่านี้จะเห็นค่าเริ่มต้นหรือ "อุปกรณ์เคลื่อนที่" ของคอมโพเนนต์ ซึ่งไม่ใช่จุดจบของโลก
เมื่อต้องหาข้อดีข้อเสีย ควรเพิ่มประสิทธิภาพเพื่อผู้ใช้ส่วนใหญ่ของคุณแทนที่จะกำหนดค่าเริ่มต้นให้ใช้แนวทางแบบตัวหารที่ต่ำที่สุด ซึ่งจะทำให้ผู้ใช้ทุกคนได้รับประสบการณ์ที่สอดคล้องกันแต่ต่ำกว่ามาตรฐาน
ดังนั้น ก่อนที่คุณจะคิดว่าคุณไม่สามารถใช้การค้นหาคอนเทนเนอร์ได้เนื่องจากขาดการรองรับเบราว์เซอร์ คุณควรใช้เวลาพิจารณาว่าประสบการณ์จะเป็นอย่างไรหากคุณเลือกใช้การค้นหาดังกล่าว ข้อดีข้อเสียอาจคุ้มค่าแม้จะไม่มีทางเลือกอื่นก็ตาม
เส้นทางต่อจากนี้
หวังว่าโพสต์นี้จะทำให้คุณเชื่อว่าคุณสามารถใช้การค้นหาคอนเทนเนอร์ในการใช้งานจริงได้ในตอนนี้ และคุณไม่จำเป็นต้องรอเป็นปีจนกว่าเบราว์เซอร์ที่ไม่สนับสนุนทั้งหมดจะหายไป
แม้ว่ากลยุทธ์ที่ระบุไว้ที่นี่จะต้องอาศัยการทำงานเพิ่มเติมเล็กน้อย แต่ก็ควรจะเรียบง่ายและตรงไปตรงมามากพอที่คนส่วนใหญ่สามารถนำไปใช้บนเว็บไซต์ได้ แต่ทั้งนี้ ก็ยังมีช่องทางที่คุณสามารถนำมาใช้งานได้ง่ายยิ่งขึ้น แนวคิดหนึ่งคือการรวมส่วนต่างๆ จำนวนมากเข้าด้วยกันเป็นคอมโพเนนต์เดียว เพื่อเพิ่มประสิทธิภาพสำหรับเฟรมเวิร์กหรือสแต็กที่เฉพาะเจาะจง ซึ่งช่วยจัดการงานกาวทั้งหมดให้คุณ หากคุณสร้างงานลักษณะนี้ โปรดแจ้งให้เราทราบแล้วเราจะช่วยโปรโมตได้
สุดท้ายนี้ นอกเหนือจากการค้นหาคอนเทนเนอร์แล้ว ยังมีฟีเจอร์ CSS และ UI ที่น่าทึ่งมากมายที่ตอนนี้ทำงานร่วมกันได้แล้วในเครื่องมือเบราว์เซอร์หลักๆ ทั้งหมด ในฐานะชุมชน ลองพิจารณาว่าปัจจุบันเราจะใช้ฟีเจอร์เหล่านั้นอย่างไรเพื่อให้ผู้ใช้ได้รับประโยชน์
อัปเดต (25 กรกฎาคม 2024): คำแนะนำเดิมใน "ขั้นตอนที่ 1" แนะนำว่าคำค้นหาสื่อและคำค้นหาคอนเทนเนอร์สามารถใช้เงื่อนไขขนาดเดียวกันได้ ซึ่งมักจะเป็นความจริงแต่ไม่เสมอไป (เนื่องจากบางเหตุผลก็ระบุไว้อย่างถูกต้อง) ในตอนนี้ คำแนะนำที่อัปเดตจะอธิบายเรื่องนี้ พร้อมแสดงตัวอย่างกรณีที่เงื่อนไขของขนาดอาจต้องเปลี่ยนแปลง
อัปเดต (2 กรกฎาคม 2024): เดิมตัวอย่างโค้ด CSS ทั้งหมดใช้ Sass (เพื่อให้สอดคล้องกับคำแนะนำสุดท้าย) ตามความคิดเห็นจากผู้อ่าน เราได้อัปเดต CSS 2-3 รายการแรกเป็น CSS แบบธรรมดาและใช้ Sass ในตัวอย่างโค้ดที่ต้องใช้มิกซ์อินเท่านั้น