คิวรีคอนเทนเนอร์คือฟีเจอร์ CSS ใหม่ที่ให้คุณเขียนตรรกะการจัดรูปแบบที่กำหนดเป้าหมายไปยังฟีเจอร์ขององค์ประกอบหลัก (เช่น ความกว้างหรือความสูง) เพื่อจัดรูปแบบองค์ประกอบย่อย เมื่อเร็วๆ นี้ เราได้ปล่อยการอัปเดตครั้งใหญ่สำหรับ polyfill ซึ่งสอดคล้องกับหน้าการสนับสนุนในเบราว์เซอร์
ในโพสต์นี้ คุณจะได้เห็นวิธีการทำงานของ Polyfill วิธีการทำงานของ Polyfill อุปสรรค และแนวทางปฏิบัติแนะนำในการใช้เพื่อให้ผู้เข้าชมได้รับประสบการณ์ที่ยอดเยี่ยม
กลไกภายใน
การแปลง
เมื่อโปรแกรมแยกวิเคราะห์ CSS ภายในเบราว์เซอร์พบ at-rule ที่ไม่รู้จัก เช่น กฎ @container
ใหม่ล่าสุด ระบบจะทิ้งกฎนั้นราวกับว่าไม่เคยมีมาก่อน ดังนั้นสิ่งแรกและสำคัญที่สุดที่ Polyfill ต้องทำคือแปลงคำค้นหา @container
ไปยังสิ่งที่จะไม่ทิ้ง
ขั้นตอนแรกในการแปลงภาษาคือแปลงกฎ @container
ระดับบนสุดเป็นข้อความค้นหา @media ซึ่งมักจะทำให้เนื้อหามีการจัดกลุ่มไว้ด้วยกัน เช่น เมื่อใช้ CSSOM API และเมื่อดูแหล่งที่มา CSS
@container (width > 300px) { /* content */ }
@media all { /* content */ }
ก่อนการค้นหาคอนเทนเนอร์ CSS ไม่มีวิธีที่ผู้เขียนจะเปิดหรือปิดใช้กลุ่มกฎโดยไม่มีกฎเกณฑ์ หากต้องการใช้การแปลงข้อมูลพฤติกรรมนี้ กฎภายในการค้นหาคอนเทนเนอร์จะต้องได้รับการเปลี่ยนรูปแบบด้วย @container
แต่ละรายการจะได้รับรหัสที่ไม่ซ้ำกัน (เช่น 123
) ซึ่งจะใช้เพื่อเปลี่ยนรูปแบบตัวเลือกแต่ละรายการเพื่อให้มีผลเฉพาะเมื่อองค์ประกอบมีแอตทริบิวต์ cq-XYZ
ที่มีรหัสนี้ โพลีฟิลล์จะตั้งค่าแอตทริบิวต์นี้เมื่อรันไทม์
@container (width > 300px) { .card { /* ... */ } }
@media all { .card:where([cq-XYZ~="123"]) { /* ... */ } }
สังเกตการใช้คลาสจำลอง :where(...)
โดยปกติแล้ว การรวมตัวเลือกแอตทริบิวต์เพิ่มเติมจะเพิ่มความจำเพาะของตัวเลือกดังกล่าว เมื่อใช้คลาสจำลอง คุณจะใช้เงื่อนไขเพิ่มเติมได้ในขณะที่ยังคงความเฉพาะเจาะจงเดิมไว้ ลองดูตัวอย่างต่อไปนี้เพื่อทําความเข้าใจความสำคัญของเรื่องนี้
@container (width > 300px) {
.card {
color: blue;
}
}
.card {
color: red;
}
เนื่องจาก CSS นี้ องค์ประกอบที่มีคลาส .card
ควรมี color: red
เสมอ เนื่องจากกฎภายหลังจะลบล้างกฎก่อนหน้าด้วยตัวเลือกและความเฉพาะเจาะจงเดียวกันเสมอ ดังนั้น การแปลงกฎแรกและใส่ตัวเลือกแอตทริบิวต์เพิ่มเติมโดยไม่มี :where(...)
จะเพิ่มความเฉพาะเจาะจงและทําให้ใช้ color: blue
อย่างไม่ถูกต้อง
อย่างไรก็ตาม คลาสจำลอง :where(...)
ค่อนข้างใหม่ สําหรับเบราว์เซอร์ที่ไม่รองรับ โพลีฟีลจะมอบวิธีแก้ปัญหาที่ปลอดภัยและง่ายดาย คุณสามารถจงใจเพิ่มความเฉพาะเจาะจงของกฎได้โดยการเพิ่มตัวเลือก :not(.container-query-polyfill)
จำลองลงในกฎ @container
ด้วยตนเอง ดังนี้
@container (width > 300px) { .card { color: blue; } } .card { color: red; }
@container (width > 300px) { .card:not(.container-query-polyfill) { color: blue; } } .card { color: red; }
ซึ่งมีประโยชน์หลายประการ ดังนี้
- ตัวเลือกใน CSS ต้นฉบับมีการเปลี่ยนแปลง ดังนั้นความแตกต่างของความเฉพาะเจาะจงจึงปรากฏอย่างชัดเจน ซึ่งยังทำหน้าที่เป็นเอกสารประกอบเพื่อให้คุณทราบว่าสิ่งใดได้รับผลกระทบเมื่อคุณไม่จำเป็นต้องรองรับวิธีแก้ปัญหาชั่วคราวหรือ polyfill อีกต่อไป
- ความเฉพาะเจาะจงของกฎจะเหมือนกันเสมอ เนื่องจาก polyfill จะไม่เปลี่ยนแปลงกฎ
ในระหว่างการแปลง โพลีฟีลจะแทนที่ค่าว่างนี้ด้วยตัวเลือกแอตทริบิวต์ที่มีความเฉพาะเจาะจงเดียวกัน Polyfill จะใช้ตัวเลือกทั้ง 2 รายการเพื่อหลีกเลี่ยงปัญหาที่ไม่คาดคิด โดยตัวเลือกแหล่งที่มาเดิมจะใช้เพื่อพิจารณาว่าองค์ประกอบควรได้รับแอตทริบิวต์ polyfill หรือไม่ และตัวเลือกที่แปลงแล้วจะใช้สำหรับการจัดสไตล์
องค์ประกอบจำลอง
คำถามที่คุณอาจถามตัวเองคือ หาก polyfill ตั้งค่าแอตทริบิวต์ cq-XYZ
บางรายการในองค์ประกอบเพื่อรวมรหัสคอนเทนเนอร์ที่ไม่ซ้ำกัน 123
แล้วจะรองรับองค์ประกอบจำลองซึ่งไม่สามารถตั้งค่าแอตทริบิวต์ได้อย่างไร
องค์ประกอบจำลองจะเชื่อมโยงกับองค์ประกอบจริงใน DOM เสมอ ซึ่งเรียกว่าองค์ประกอบต้นทาง ในระหว่างการแปลง ตัวเลือกแบบมีเงื่อนไขจะมีผลกับองค์ประกอบจริงนี้แทน
@container (width > 300px) { #foo::before { /* ... */ } }
@media all { #foo:where([cq-XYZ~="123"])::before { /* ... */ } }
ระบบจะย้ายตัวเลือกแบบมีเงื่อนไขไปยังท้ายองค์ประกอบต้นทาง #foo
แทนที่จะเปลี่ยนรูปแบบเป็น #foo::before:where([cq-XYZ~="123"])
(ซึ่งไม่ถูกต้อง)
แต่ยังมีสิ่งอื่นๆ ที่จำเป็นด้วย คอนเทนเนอร์ไม่ได้รับอนุญาตให้แก้ไขสิ่งที่ไม่ได้อยู่ภายในคอนเทนเนอร์ (และคอนเทนเนอร์ไม่สามารถอยู่ภายในตัวคอนเทนเนอร์เองได้) แต่ให้คิดว่าสิ่งที่จะเกิดขึ้นคือ #foo
เป็นองค์ประกอบคอนเทนเนอร์ที่ระบบค้นหา แอตทริบิวต์ #foo[cq-XYZ]
จะมีการเปลี่ยนแปลงอย่างไม่ถูกต้อง และจะใช้กฎ #foo
อย่างไม่ถูกต้อง
ในการแก้ไขปัญหานี้ Polyfill จะใช้แอตทริบิวต์ 2 รายการ โดยแอตทริบิวต์หนึ่งใช้ได้กับองค์ประกอบระดับบนสุดเท่านั้นและแอตทริบิวต์ที่องค์ประกอบนั้นใช้กับตัวเองได้ แอตทริบิวต์หลังใช้สำหรับตัวเลือกที่กำหนดเป้าหมายไปยังองค์ประกอบจำลอง
@container (width > 300px) { #foo, #foo::before { /* ... */ } }
@media all { #foo:where([cq-XYZ-A~="123"]), #foo:where([cq-XYZ-B~="123"])::before { /* ... */ } }
เนื่องจากคอนเทนเนอร์จะไม่ใช้แอตทริบิวต์แรก (cq-XYZ-A
) กับตนเอง ตัวเลือกแรกจะจับคู่ก็ต่อเมื่อคอนเทนเนอร์หลักอื่นมีคุณสมบัติตรงตามเงื่อนไขของคอนเทนเนอร์และนำแอตทริบิวต์นั้นไปใช้
หน่วยที่สัมพันธ์กับคอนเทนเนอร์
คิวรีคอนเทนเนอร์ยังมีหน่วยใหม่ 2-3 หน่วยที่คุณใช้ใน CSS ได้ เช่น cqw
และ cqh
สำหรับ 1% ของความกว้างและความสูง (ตามลำดับ) ของคอนเทนเนอร์หลักที่เหมาะสมซึ่งอยู่ใกล้ที่สุด ระบบจะเปลี่ยนรูปแบบหน่วยเป็นนิพจน์ calc(...)
โดยใช้พร็อพเพอร์ตี้ที่กำหนดเองของ CSS เพื่อรองรับรายการเหล่านี้ โพลีฟิลล์จะตั้งค่าค่าของคุณสมบัติเหล่านี้ผ่านรูปแบบอินไลน์ในองค์ประกอบคอนเทนเนอร์
.card { width: 10cqw; height: 10cqh; }
.card { width: calc(10 * --cq-XYZ-cqw); height: calc(10 * --cq-XYZ-cqh); }
นอกจากนี้ยังมีหน่วยเชิงตรรกะ เช่น cqi
และ cqb
สำหรับขนาดในบรรทัดและขนาดบล็อก (ตามลำดับ) การดำเนินการเหล่านี้มีความซับซ้อนกว่าเล็กน้อย เนื่องจากแกนแนวนอนและแกนแนวตั้งจะกำหนดโดย writing-mode
ขององค์ประกอบที่ใช้หน่วย ไม่ใช่องค์ประกอบที่ค้นหา เพื่อรองรับความต้องการนี้ Polyfill จะใช้รูปแบบอินไลน์กับองค์ประกอบที่ writing-mode
แตกต่างจากองค์ประกอบหลัก
/* Element with a horizontal writing mode */
--cq-XYZ-cqi: var(--cq-XYZ-cqw);
--cq-XYZ-cqb: var(--cq-XYZ-cqh);
/* Element with a vertical writing mode */
--cq-XYZ-cqi: var(--cq-XYZ-cqh);
--cq-XYZ-cqb: var(--cq-XYZ-cqw);
ตอนนี้คุณสามารถเปลี่ยนหน่วยเป็นพร็อพเพอร์ตี้ที่กำหนดเองของ CSS ที่เหมาะสมได้เช่นเดียวกับก่อนหน้านี้
พร็อพเพอร์ตี้
การค้นหาคอนเทนเนอร์ยังเพิ่มคุณสมบัติ CSS ใหม่ 2-3 อย่าง เช่น container-type
และ container-name
เนื่องจาก API เช่น getComputedStyle(...)
ใช้กับพร็อพเพอร์ตี้ที่ไม่รู้จักหรือไม่ถูกต้องไม่ได้ ระบบจึงเปลี่ยน API เหล่านี้เป็นพร็อพเพอร์ตี้ที่กำหนดเองของ CSS หลังจากแยกวิเคราะห์ด้วย หากแยกวิเคราะห์พร็อพเพอร์ตี้ไม่ได้ (เช่น เนื่องจากมีค่าที่ไม่ถูกต้องหรือไม่รู้จัก) ระบบจะปล่อยพร็อพเพอร์ตี้นั้นไว้เพื่อให้เบราว์เซอร์จัดการ
.card { container-name: card-container; container-type: inline-size; }
.card { --cq-XYZ-container-name: card-container; --cq-XYZ-container-type: inline-size; }
ระบบจะเปลี่ยนรูปแบบคุณสมบัติเหล่านี้ทุกครั้งที่ค้นพบ ซึ่งช่วยให้ Polyfill ทำงานกับฟีเจอร์อื่นๆ ของ CSS เช่น @supports
ได้อย่างลงตัว ฟังก์ชันการทำงานนี้เป็นพื้นฐานของแนวทางปฏิบัติแนะนำในการใช้ polyfill ตามที่อธิบายไว้ด้านล่าง
@supports (container-type: inline-size) { /* ... */ }
@supports (--cq-XYZ-container-type: inline-size) { /* ... */ }
โดยค่าเริ่มต้น ระบบจะรับค่าของพร็อพเพอร์ตี้ที่กำหนดเองของ CSS มาใช้ ซึ่งหมายความว่ารายการย่อยของ .card
จะใช้ค่าของ --cq-XYZ-container-name
และ --cq-XYZ-container-type
ซึ่งไม่ใช่ลักษณะการทำงานของพร็อพเพอร์ตี้เนทีฟ ในการแก้ปัญหานี้ โพลีฟีลจะแทรกกฎต่อไปนี้ไว้ก่อนสไตล์ของผู้ใช้ เพื่อให้แน่ใจว่าองค์ประกอบทุกรายการจะได้รับค่าเริ่มต้น เว้นแต่ว่าจะมีกฎอื่นเขียนทับไว้
* {
--cq-XYZ-container-name: none;
--cq-XYZ-container-type: normal;
}
แนวทางปฏิบัติแนะนำ
แม้ว่าจะมีการคาดหวังว่าผู้เข้าชมส่วนใหญ่จะเรียกใช้เบราว์เซอร์ที่มีการรองรับการค้นหาคอนเทนเนอร์ในตัวเร็วกว่าในภายหลัง แต่ก็ยังคงจำเป็นที่จะต้องให้ประสบการณ์ที่ดีแก่ผู้เข้าชมที่เหลือของคุณ
ระหว่างการโหลดครั้งแรก ต้องดำเนินการหลายอย่างก่อนที่ Polyfill จะจัดเลย์เอาต์หน้าเว็บได้ ดังนี้
- จำเป็นต้องโหลดและเริ่มต้น polyfill
- จำเป็นต้องแยกวิเคราะห์และแปลงไฟล์สไตล์ เนื่องจากไม่มี API ใดที่เข้าถึงซอร์สไฟล์ดิบของสไตล์ชีตภายนอก ระบบจึงอาจต้องดึงข้อมูลใหม่แบบไม่พร้อมกัน แม้ว่าควรจะทำจากแคชของเบราว์เซอร์เท่านั้น
หาก polyfill ไม่จัดการข้อกังวลเหล่านี้อย่างละเอียด อาจทำให้ Core Web Vitals ลดลง
เราได้ออกแบบ polyfill ให้ให้ความสําคัญกับ First Input Delay (FID) และ Cumulative Layout Shift (CLS) เพื่อช่วยให้คุณสามารถมอบประสบการณ์การใช้งานที่น่าพึงพอใจแก่ผู้เข้าชมได้ง่ายขึ้น ซึ่งอาจทำให้ Largest Contentful Paint (LCP) ลดลง กล่าวโดยละเอียดคือ Polyfill ไม่ได้รับประกันว่าจะมีการประเมินการค้นหาคอนเทนเนอร์ก่อน First Paint ซึ่งหมายความว่าคุณต้องซ่อนเนื้อหาที่มีขนาดหรือตําแหน่งจะได้รับผลกระทบจากการใช้การค้นหาคอนเทนเนอร์ไว้จนกว่า polyfill จะโหลดและแปลง CSS ของคุณแล้ว เพื่อให้ผู้ใช้ได้รับประสบการณ์การใช้งานที่ดีที่สุด โดยวิธีหนึ่งที่ทำได้คือการใช้กฎ @supports
ดังนี้
@supports not (container-type: inline-size) {
#content {
visibility: hidden;
}
}
เราขอแนะนำให้คุณรวมสิ่งนี้เข้ากับภาพเคลื่อนไหวการโหลด CSS ล้วนๆ ซึ่งวางไว้เหนือเนื้อหา (ที่ซ่อนอยู่) อย่างสมบูรณ์ เพื่อบอกผู้เข้าชมว่ากำลังมีบางอย่างเกิดขึ้น ดูการสาธิตวิธีการนี้แบบเต็มได้ที่นี่
เราแนะนำให้ใช้วิธีการนี้ด้วยเหตุผลหลายประการดังนี้
- ตัวโหลด CSS ล้วนช่วยลดค่าใช้จ่ายสำหรับผู้ใช้ที่มีเบราว์เซอร์รุ่นใหม่ และให้ข้อเสนอแนะเพียงเล็กน้อยแก่ผู้ใช้เบราว์เซอร์รุ่นเก่าและเครือข่ายที่ช้ากว่า
- การรวมตำแหน่งแบบสัมบูรณ์ของตัวโหลดกับ
visibility: hidden
จะช่วยให้คุณหลีกเลี่ยงการเปลี่ยนแปลงเลย์เอาต์ - หลังจากโหลด polyfill แล้ว เงื่อนไข
@supports
นี้จะหยุดทำงาน และระบบจะแสดงเนื้อหาของคุณ - ในเบราว์เซอร์ที่รองรับการค้นหาคอนเทนเนอร์ในตัว เงื่อนไขจะไม่ผ่านเลย ดังนั้นหน้าเว็บจะแสดงใน First Paint ตามปกติ
บทสรุป
หากสนใจที่จะใช้การค้นหาคอนเทนเนอร์ในเบราว์เซอร์รุ่นเก่า ให้ลองใช้ polyfill โปรดแจ้งปัญหาหากพบปัญหา
เราอดใจรอที่จะได้เห็นและสัมผัสประสบการณ์การใช้งานที่ยอดเยี่ยมที่คุณสร้างขึ้นด้วยเครื่องมือนี้