คำแนะนำสำหรับ CSS Pseudo-Class Selectors ที่ทันสมัยและรองรับใหม่
เผยแพร่แล้ว: 2022-03-10 ตัวเลือกคลาสหลอกคือตัวเลือกที่ขึ้นต้นด้วยอักขระโคลอน “ :
” และจับคู่ตาม สถานะ ขององค์ประกอบปัจจุบัน สถานะอาจสัมพันธ์กับแผนผังเอกสาร หรือเพื่อตอบสนองต่อการเปลี่ยนแปลงสถานะ เช่น :hover
หรือ :checked
:any-link
แม้ว่าจะกำหนดไว้ใน Selectors ระดับ 4 แต่คลาสหลอกนี้มีการสนับสนุนข้ามเบราว์เซอร์มาระยะหนึ่งแล้ว pseudo-class ของ any-link
จะจับคู่กับ anchor hyperlink ตราบใดที่มี href
มันจะจับคู่ในลักษณะที่เทียบเท่ากับการจับคู่ทั้ง :link
และ :visited
พร้อมกัน โดยพื้นฐานแล้ว การทำเช่นนี้อาจลดสไตล์ของคุณลงได้เพียงตัวเลือกเดียว หากคุณกำลังเพิ่มคุณสมบัติพื้นฐาน เช่น color
ที่คุณต้องการใช้กับลิงก์ทั้งหมดโดยไม่คำนึงถึงสถานะที่เข้าชม
:any-link { color: blue; text-underline-offset: 0.05em; }
หมายเหตุสำคัญเกี่ยวกับความจำเพาะคือ :any-link
จะชนะ a
เป็นตัวเลือก แม้ว่า a
จะถูกวางไว้ที่ต่ำกว่าในคาสเคด เนื่องจากมีความเฉพาะเจาะจงของคลาส ในตัวอย่างต่อไปนี้ ลิงก์จะเป็นสีม่วง:
:any-link { color: purple; } a { color: red; }
ดังนั้น หากคุณแนะนำ :any-link
โปรดทราบว่าคุณจะต้องรวมไว้ในอินสแตนซ์ของ a
เป็นตัวเลือก หากพวกเขาจะอยู่ในการแข่งขันโดยตรงสำหรับความเฉพาะเจาะจง
:focus-visible
ฉันพนันได้เลยว่าการละเมิดการช่วยสำหรับการเข้าถึงที่พบบ่อยที่สุดอย่างหนึ่งในเว็บคือการลบ outline
ขององค์ประกอบเชิงโต้ตอบ เช่น ลิงก์ ปุ่ม และอินพุตของแบบฟอร์มสำหรับสถานะ :focus
จุดประสงค์หลักประการหนึ่งของ outline
นั้นคือเพื่อใช้เป็นตัวบ่งชี้ที่มองเห็นได้สำหรับผู้ใช้ที่ใช้แป้นพิมพ์เป็นหลักในการนำทาง สถานะการโฟกัสที่มองเห็นได้นั้นมีความสำคัญ ในฐานะเครื่องมือในการค้นหาเส้นทาง เนื่องจากผู้ใช้เหล่านั้นแท็บผ่านอินเทอร์เฟซ และเพื่อช่วยเสริมว่าองค์ประกอบแบบโต้ตอบคืออะไร โดยเฉพาะอย่างยิ่ง การโฟกัสที่มองเห็นได้ครอบคลุมอยู่ในเกณฑ์ความสำเร็จ WCAG 2.4.11: ลักษณะที่ปรากฏของโฟกัส (ขั้นต่ำ)
คลาสหลอก :focus-visible
มีวัตถุประสงค์เพื่อแสดงเฉพาะวงแหวนโฟกัสเมื่อตัวแทนผู้ใช้กำหนดผ่านฮิวริสติกว่าควรมองเห็นได้ กล่าวอีกนัยหนึ่ง: เบราว์เซอร์จะกำหนดว่า เมื่อใดควรใช้ :focus-visible
ตามวิธีการป้อนข้อมูล ประเภทขององค์ประกอบ และบริบทของการโต้ตอบ สำหรับวัตถุประสงค์ในการทดสอบผ่านคอมพิวเตอร์เดสก์ท็อปที่มีการป้อนข้อมูลด้วยแป้นพิมพ์และเมาส์ คุณควรเห็น :focus-visible
styles แนบมาเมื่อคุณแท็บในองค์ประกอบแบบโต้ตอบ แต่ไม่ใช่เมื่อคุณคลิก ยกเว้นการป้อนข้อความและ textareas ซึ่งควรแสดง :focus-visible
สำหรับอินพุตโฟกัสทุกประเภท
หมายเหตุ : สำหรับรายละเอียดเพิ่มเติม ตรวจสอบร่างการทำงานของ :focus-visible
spec
เวอร์ชันล่าสุดของเบราว์เซอร์ Firefox และ Chromium ดูเหมือนจะจัดการ :focus-visible
บนอินพุตของฟอร์มตามข้อกำหนดที่ระบุว่า UA ควรลบ :focus
styles เมื่อ :focus-visible
ตรงกัน Safari ยังไม่รองรับ :focus-visible
ดังนั้นเราจำเป็นต้องตรวจสอบให้แน่ใจว่าสไตล์ :focus
ถูกรวมไว้เป็นทางเลือกเพื่อหลีกเลี่ยงการลบ outline
สำหรับการช่วยสำหรับการเข้าถึง
ด้วยปุ่มและการป้อนข้อความที่มีชุดสไตล์ต่อไปนี้ มาดูกันว่าจะเกิดอะไรขึ้น:
input:focus, button:focus { outline: 2px solid blue; outline-offset: 0.25em; } input:focus-visible { outline: 2px solid transparent; border-color: blue; } button:focus:not(:focus-visible) { outline: none; } button:focus-visible { outline: 2px solid transparent; box-shadow: 0 0 0 2px #fff, 0 0 0 4px blue; }
Chromium และ Firefox
-
input
ลบ:focus
styles อย่างถูกต้องเมื่อองค์ประกอบถูกโฟกัสผ่านการป้อนข้อมูลด้วยเมาส์:focus-visible
ส่งผลให้เปลี่ยนborder-color
และซ่อนoutline
ในการป้อนข้อมูลด้วยแป้นพิมพ์ -
button
ไม่เพียงใช้:focus-visible
โดยไม่มีกฎเพิ่มเติมสำหรับbutton:focus:not(:focus-visible)
ที่จะลบโครงร่างบน:focus
แต่จะอนุญาตให้มองเห็นbox-shadow
เฉพาะเมื่อป้อนข้อมูลด้วยแป้นพิมพ์
ซาฟารี
-
input
ใช้เฉพาะ:focus
styles . เท่านั้น -
button
ดูเหมือนว่า ตอนนี้จะเคารพจุดประสงค์ของ:focus-visible
บนปุ่มบางส่วนโดยการซ่อน:focus
ลักษณะเมื่อคลิก แต่ยังคงแสดง:focus
ลักษณะในการโต้ตอบของแป้นพิมพ์
ดังนั้น สำหรับตอนนี้ คำแนะนำคือให้ดำเนินการต่อโดยใส่ :focus
styles แล้วค่อยๆ ปรับปรุงเป็น :focus-visible
ซึ่งโค้ดสาธิตอนุญาต นี่คือ CodePen สำหรับคุณในการทดสอบต่อ:
:focus-within
:focus-within
pseudo-class รองรับเบราว์เซอร์สมัยใหม่ทั้งหมด และทำหน้าที่ เกือบจะ เหมือนกับตัวเลือกพาเรนต์ แต่สำหรับเงื่อนไขที่เฉพาะเจาะจงมากเท่านั้น เมื่อแนบกับองค์ประกอบที่มีและองค์ประกอบย่อยตรงกับ :focus
คุณสามารถเพิ่มสไตล์ไปยังองค์ประกอบที่มี และ/หรือ องค์ประกอบอื่นๆ ภายในคอนเทนเนอร์ได้
การปรับปรุงที่ใช้งานได้จริงเพื่อใช้ลักษณะการทำงานนี้คือ การกำหนดสไตล์ป้ายชื่อแบบฟอร์ม เมื่ออินพุตที่เกี่ยวข้องมีโฟกัส เพื่อให้ใช้งานได้ เราห่อป้ายกำกับและป้อนข้อมูลในคอนเทนเนอร์ จากนั้นแนบ :focus-within
คอนเทนเนอร์นั้นรวมถึงการเลือกป้ายกำกับ:
.form-group:focus-within label { color: blue; }
ส่งผลให้ป้ายกำกับเปลี่ยนเป็นสีน้ำเงินเมื่ออินพุตมีโฟกัส
การสาธิต CodePen นี้ยังรวมถึงการเพิ่มโครงร่างโดยตรงไปยัง .form-group
:
:is()
ยังเป็นที่รู้จักกันในนาม "ตรงกับคลาสหลอก" ใด ๆ :is()
สามารถใช้รายการตัวเลือกเพื่อพยายามจับคู่ ตัวอย่างเช่น แทนที่จะแสดงรายการสไตล์ส่วนหัวทีละรายการ คุณสามารถจัดกลุ่มไว้ใต้ตัวเลือกของ :is(h1, h2, h3)
พฤติกรรมเฉพาะสองสามอย่างเกี่ยวกับรายการตัวเลือก :is()
:
- หากตัวเลือกที่ระบุไม่ถูกต้อง กฎจะยังคงตรงกับตัวเลือกที่ถูกต้อง ให้
:is(-ua-invalid, article, p)
กฎจะจับคู่article
และp
- ความจำเพาะที่คำนวณได้จะเท่ากับตัวเลือกที่ผ่านด้วยความจำเพาะสูงสุด ตัวอย่างเช่น
:is(#id, p)
จะมีความจำเพาะของ#id
— 1.0.0 — ในขณะที่:is(p, a)
จะมีความจำเพาะที่ 0.0.1
พฤติกรรมแรกของการละเว้นตัวเลือกที่ไม่ถูกต้องคือข้อดีหลัก เมื่อใช้ตัวเลือกอื่นในกลุ่มที่ตัวเลือกหนึ่งไม่ถูกต้อง เบราว์เซอร์จะลบกฎทั้งหมดออก สิ่งนี้มีผลบังคับใช้ในบางกรณีที่คำนำหน้าผู้ขายยังคงจำเป็น และการจัดกลุ่มตัวเลือกคำนำหน้าและตัวเลือกที่ไม่ใช่คำนำหน้าจะทำให้กฎล้มเหลวในทุกเบราว์เซอร์ ด้วย :is()
คุณสามารถจัดกลุ่มสไตล์เหล่านั้นได้อย่างปลอดภัย และจะนำไปใช้เมื่อจับคู่และจะถูกละเว้นเมื่อไม่ตรงกัน
สำหรับฉัน การจัดกลุ่มสไตล์ส่วนหัว ตามที่กล่าวไว้ก่อนหน้านี้ถือเป็นชัยชนะครั้งใหญ่สำหรับตัวเลือกนี้ นอกจากนี้ยังเป็นประเภทของกฎที่ฉันรู้สึกสบายใจที่จะใช้โดยไม่มีทางเลือกเมื่อใช้รูปแบบที่ไม่สำคัญ เช่น:
:is(h1, h2, h3) { line-height: 1.2; } :is(h2, h3):not(:first-child) { margin-top: 2em; }
ในตัวอย่างนี้ (ซึ่งมาจากรูปแบบเอกสารในโปรเจ็กต์ SmolCSS ของฉัน) การมีความสูงของ line-height
สืบทอดมาจากสไตล์พื้นฐานหรือการไม่มี margin-top
นั้นไม่ใช่ปัญหาสำหรับเบราว์เซอร์ที่ไม่รองรับ มันน้อยกว่าอุดมคติ สิ่งที่คุณไม่ต้องการใช้ :is()
สำหรับค่อนข้างจะเป็น สไตล์เลย์เอาต์ที่สำคัญ เช่น Grid หรือ Flex ที่ควบคุมอินเทอร์เฟซของคุณอย่างมาก
นอกจากนี้ เมื่อเชื่อมโยงกับตัวเลือกอื่น คุณสามารถทดสอบว่าตัวเลือกฐานตรงกับตัวเลือกสืบทอดภายใน :is()
หรือไม่ ตัวอย่างเช่น กฎต่อไปนี้จะเลือกเฉพาะย่อหน้าที่เป็นทายาทโดยตรงของบทความ ใช้ตัวเลือกสากลเพื่ออ้างอิงถึงตัวเลือกฐาน p
p:is(article > *)
สำหรับการสนับสนุนในปัจจุบันที่ดีที่สุด หากคุณต้องการเริ่มใช้งาน คุณจะต้อง เพิ่มสไตล์เป็นสองเท่า โดยใส่กฎที่ซ้ำกันโดยใช้ :-webkit-any()
และ :matches()
อย่าลืมสร้างกฎแต่ละข้อเหล่านี้ หรือแม้แต่เบราว์เซอร์ที่รองรับก็ปล่อยมันไป! กล่าวอีกนัยหนึ่ง ให้รวมสิ่งต่อไปนี้ทั้งหมด:
:matches(h1, h2, h3) { } :-webkit-any(h1, h2, h3) { } :is(h1, h2, h3) { }
มูลค่าการกล่าวขวัญ ณ จุดนี้ก็คือว่าพร้อมกับตัวเลือกที่ใหม่กว่านั้นคือรูปแบบที่อัปเดตของ @supports
ซึ่งก็คือ @supports selector
สิ่งนี้มีให้เช่นกันเนื่องจาก @supports not selector
หมายเหตุ : ในปัจจุบัน (ของเบราว์เซอร์รุ่นใหม่) มีเพียง Safari เท่านั้นที่ไม่รองรับ at-rule
คุณสามารถตรวจสอบ :is()
support ด้วยสิ่งต่อไปนี้ แต่จริง ๆ แล้วคุณจะสูญเสียการสนับสนุน Safari เนื่องจาก Safari รองรับ :is()
แต่ไม่รองรับ @supports selector
@supports selector(:is(h1)) { :is(h1, h2, h3) { line-height: 1.1; } }
:where()
pseudo-class :where()
เกือบจะเหมือนกับ :is()
ยกเว้นข้อแตกต่างที่สำคัญอย่างหนึ่ง: มันจะมีความเฉพาะเจาะจงเป็นศูนย์ เสมอ สิ่งนี้มีความหมายที่เหลือเชื่อสำหรับ ผู้ที่กำลังสร้างเฟรมเวิร์ก ธีม และระบบการออกแบบ การใช้ :where()
ผู้เขียนสามารถตั้งค่าเริ่มต้น และนักพัฒนาดาวน์สตรีมสามารถรวมการแทนที่หรือส่วนขยายได้โดยไม่ขัดแย้งกันเฉพาะเจาะจง
พิจารณาชุดสไตล์ img
ต่อไปนี้ การใช้ :where()
แม้จะมีตัวเลือกความจำเพาะสูง ความจำเพาะก็ยังเป็นศูนย์ ในตัวอย่างต่อไปนี้ คุณคิดว่าภาพจะมีเส้นขอบสีใด
:where(article img:not(:first-child)) { border: 5px solid red; } :where(article) img { border: 5px solid green; } img { border: 5px solid orange; }
กฎข้อแรกมีความเฉพาะเจาะจงเป็นศูนย์ เนื่องจากมีทั้งหมดอยู่ภายใน :where()
ดังนั้นตรงข้ามกฎข้อที่สองโดยตรง กฎข้อที่สองชนะ ขอแนะนำตัวเลือกเฉพาะองค์ประกอบ img
ตามกฎสุดท้าย มันจะชนะเนื่องจากการเรียงซ้อน นี่เป็นเพราะมันจะคำนวณให้มีความจำเพาะเช่นเดียวกับกฎ :where(article) img
เนื่องจากส่วน :where()
ไม่เพิ่มความเฉพาะเจาะจง
การใช้ :where()
ควบคู่ไปกับทางเลือกจะยากขึ้นเล็กน้อยเนื่องจากคุณลักษณะที่ไม่เฉพาะเจาะจงเป็นศูนย์ เนื่องจากคุณลักษณะดังกล่าวมีแนวโน้มว่าเหตุใดคุณจึง ต้องการ ใช้แทน :is()
และหากคุณเพิ่มกฎทางเลือก กฎเหล่านั้นมีแนวโน้มที่จะเอาชนะ :where()
เนื่องจากธรรมชาติของมันเอง และมี การสนับสนุนโดยรวมที่ดี กว่า @supports selector
ดังนั้นการพยายามใช้สิ่งนั้นเพื่อสร้างทางเลือกสำรองไม่น่าจะให้ผลกำไรมากนัก (ถ้ามี) โดยพื้นฐานแล้ว ให้ระวังการไม่สามารถสร้างทางเลือกสำหรับ :where()
ได้อย่างถูกต้อง และตรวจสอบข้อมูลของคุณเองอย่างถี่ถ้วนเพื่อพิจารณาว่าการเริ่มใช้สำหรับผู้ชมเฉพาะของคุณนั้นปลอดภัยหรือไม่
คุณสามารถทดสอบเพิ่มเติม :where()
ด้วย CodePen ต่อไปนี้ซึ่งใช้ตัวเลือก img
จากด้านบน:
ปรับปรุง :not()
ตัวเลือกพื้นฐาน :not()
ได้รับการสนับสนุนตั้งแต่ Internet Explorer 9 แต่ตัวเลือกระดับ 4 ปรับปรุง :not()
โดยอนุญาตให้ใช้รายการตัวเลือก เช่นเดียวกับ :is()
และ :where()
กฎต่อไปนี้ให้ผลลัพธ์เดียวกันในการสนับสนุนเบราว์เซอร์:
article :not(h2):not(h3):not(h4) { margin-bottom: 1.5em; } article :not(h2, h3, h4) { margin-bottom: 1.5em; }
ความสามารถของ :not()
ในการยอมรับรายการตัวเลือกมีการสนับสนุนเบราว์เซอร์ที่ทันสมัยอย่างมาก
ตามที่เราเห็นด้วย :is()
ที่ปรับปรุงแล้ว :not()
สามารถมีการอ้างอิงไปยังตัวเลือกฐานในฐานะผู้สืบทอดโดยใช้ *
CodePen นี้แสดงให้เห็นถึงความสามารถนี้โดยการเลือกลิงก์ที่ ไม่ใช่ ลูกหลานของ nav
โบนัส : ตัวอย่างก่อนหน้านี้ยังรวมถึงตัวอย่างของการเชื่อมโยง :not()
และ :is()
เพื่อเลือกรูปภาพที่ไม่ใช่พี่น้องที่อยู่ติดกันขององค์ประกอบ h2
หรือ h3
เสนอแต่ “เสี่ยง” — :has()
pseudo-class สุดท้ายที่เป็นข้อเสนอที่น่าตื่นเต้นมาก แต่ไม่มีเบราว์เซอร์ปัจจุบันที่ใช้งานแม้ในวิธีทดลองคือ :has()
อันที่จริง มันถูกระบุไว้ในตัวเลือกระดับ 4 Editor's Draft ว่า "มีความเสี่ยง" ซึ่งหมายความว่าเป็นที่ยอมรับว่ามีปัญหาในการดำเนินการให้เสร็จสิ้น ดังนั้นจึง อาจ ถูกละทิ้งจากคำแนะนำ
หากนำไปใช้งาน :has()
จะเป็น "ตัวเลือกหลัก" ที่ CSS หลายคนปรารถนาจะมีให้ใช้งาน มันจะทำงานด้วยตรรกะที่คล้ายกับการรวมกันของทั้ง :focus-within
และ :is()
กับตัวเลือกการสืบทอด ซึ่งคุณกำลังมองหาการ ปรากฏตัวของผู้สืบทอด แต่สไตล์ที่ปรับใช้จะเป็นองค์ประกอบหลัก
จากกฎต่อไปนี้ หากการนำทางมีปุ่ม การนำทางจะลดช่องว่างด้านบนและด้านล่าง:
nav { padding: 0.75rem 0.25rem; nav:has(button) { padding-top: 0.25rem; padding-bottom: 0.25rem; }
ขอย้ำอีกครั้งว่า ฟีเจอร์นี้ยัง ไม่ได้ ใช้งานในเบราว์เซอร์ใด ๆ แม้แต่ในการทดลอง — แต่ก็สนุกที่จะลองคิดดู! Robin Rendle ให้ข้อมูลเชิงลึกเพิ่มเติมเกี่ยวกับตัวเลือกในอนาคตนี้ใน CSS-Tricks
รางวัลชมเชยจากระดับ 3: :empty
คลาสหลอกที่มีประโยชน์ที่คุณอาจพลาดจากตัวเลือกระดับ 3 คือ :empty
ซึ่งตรงกับองค์ประกอบเมื่อ ไม่มี องค์ประกอบย่อย ซึ่งรวมถึงโหนดข้อความ
กฎ p:empty
จะจับคู่ <p></p>
แต่ไม่ตรงกับ <p>Hello</p>
วิธีหนึ่งที่คุณสามารถใช้ :empty
คือการซ่อนองค์ประกอบที่อาจเป็นตัวยึดตำแหน่งสำหรับเนื้อหาแบบไดนามิกที่เติมด้วย JavaScript บางทีคุณอาจมี div ที่จะได้รับผลการค้นหา และเมื่อมีการเติมข้อมูลแล้ว ก็จะมีเส้นขอบและช่องว่างภายในบางส่วน แต่หากยังไม่มีผลลัพธ์ คุณคงไม่ต้องการให้ใช้พื้นที่บนหน้า การใช้ :empty
คุณสามารถซ่อนด้วย:
.search-results:empty { display: none; }
คุณอาจกำลังคิดที่จะเพิ่มข้อความในสถานะว่างและต้องการเพิ่มข้อความด้วยองค์ประกอบหลอกและ content
ข้อผิดพลาดในที่นี้คือ ข้อความอาจไม่พร้อมใช้งานสำหรับผู้ใช้เทคโนโลยีอำนวยความสะดวก ซึ่งไม่สอดคล้องกับว่าพวกเขาสามารถเข้าถึง content
ได้หรือไม่ กล่าวอีกนัยหนึ่ง เพื่อ ให้แน่ใจว่าข้อความประเภท "ไม่มีผลลัพธ์" สามารถเข้าถึงได้ คุณจะต้องเพิ่มสิ่งนั้นเป็นองค์ประกอบจริง เช่น ย่อหน้า (จะไม่สามารถเข้าถึง aria-label
สำหรับ div ที่ซ่อนอยู่ได้อีกต่อไป)
แหล่งข้อมูลสำหรับการเรียนรู้เกี่ยวกับ Selectors
CSS มีตัวเลือกอีกมากมายรวมถึงคลาสหลอก ต่อไปนี้เป็นสถานที่เพิ่มเติมสองสามแห่งเพื่อเรียนรู้เพิ่มเติมเกี่ยวกับสิ่งที่มีอยู่:
- เอกสารประกอบตัวเลือก MDN CSS มีรายการหมวดหมู่ที่ครอบคลุม
- ฉันได้เขียนคู่มือสองส่วนสำหรับตัวเลือก CSS ขั้นสูง คุณสามารถเริ่มต้นด้วยส่วนที่หนึ่ง
- สนุกกับการเรียนรู้เกี่ยวกับตัวเลือก CSS กับเกม CSS Diner;
- Kitty Giraudel ได้สร้างเครื่องมือคำอธิบายตัวเลือกที่จะแยกย่อยและอธิบายส่วนต่างๆ ของตัวเลือกที่ให้มา