การฝัง CSS

ฟีเจอร์หนึ่งในเครื่องมือเตรียม CSS ที่เราชื่นชอบตอนนี้ได้รวมอยู่ในภาษาแล้ว ซึ่งก็คือกฎสไตล์ที่ฝังอยู่

Adam Argyle
Adam Argyle

ก่อนที่จะฝัง คุณต้องประกาศตัวเลือกทุกรายการอย่างชัดเจนแยกจากกัน ซึ่งทำให้เกิดการทำซ้ำ กลุ่มสไตล์ชีต และประสบการณ์การเขียนที่กระจัดกระจาย

ก่อน
.nesting {
  color: hotpink;
}

.nesting > .is {
  color: rebeccapurple;
}

.nesting > .is > .awesome {
  color: deeppink;
}

หลังจากการฝังแล้ว คุณจะใช้ตัวเลือกต่อและจัดกลุ่มกฎสไตล์ที่เกี่ยวข้องไว้ด้วยกันได้

หลัง
.nesting {
  color: hotpink;

  > .is {
    color: rebeccapurple;

    > .awesome {
      color: deeppink;
    }
  }
}

ลองทำในเบราว์เซอร์

การฝังช่วยให้นักพัฒนาซอฟต์แวร์ไม่ต้องใช้ตัวเลือกซ้ำๆ และวางกฎสไตล์สำหรับองค์ประกอบที่เกี่ยวข้องไว้ด้วยกัน และยังช่วยให้สไตล์ตรงกับ HTML ที่กําหนดเป้าหมายได้ด้วย หากคอมโพเนนต์ .nesting ในตัวอย่างก่อนหน้านี้ถูกนําออกจากโปรเจ็กต์ คุณสามารถลบทั้งกลุ่มแทนการค้นหาอินสแตนซ์ตัวเลือกที่เกี่ยวข้องในไฟล์

การฝังจะช่วยในเรื่องต่อไปนี้ - การจัดระเบียบ - การลดขนาดไฟล์ - การจัดระเบียบใหม่

การจัดกลุ่มใช้ได้ใน Chrome 112 และลองใช้ใน Safari Technical Preview 162 ได้ด้วย

เริ่มต้นใช้งานการฝัง CSS

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

ตารางกริดสีสันสดใสของวงกลม สามเหลี่ยม และสี่เหลี่ยมจัตุรัสขนาดเล็กและขนาดใหญ่

ภายในแซนด์บ็อกซ์มีวงกลม สามเหลี่ยม และสี่เหลี่ยม บางห้องมีขนาดเล็ก กลาง หรือใหญ่ ส่วนอีกกลุ่มเป็นสีน้ำเงิน สีชมพู หรือสีม่วง ทั้งหมดอยู่ภายในองค์ประกอบ .demo ที่มี ต่อไปนี้คือตัวอย่างองค์ประกอบ HTML ที่คุณกําลังกําหนดเป้าหมาย

<div class="demo">
  <div class="sm triangle pink"></div>
  <div class="sm triangle blue"></div>
  <div class="square blue"></div>
  <div class="sm square pink"></div>
  <div class="sm square blue"></div>
  <div class="circle pink"></div>
  …
</div>

ตัวอย่างการฝัง

การฝัง CSS ช่วยให้คุณกำหนดสไตล์สำหรับองค์ประกอบภายในบริบทของตัวเลือกอื่นได้

.parent {
  color: blue;

  .child {
    color: red;
  }
}

ในตัวอย่างนี้ ตัวเลือกคลาส .child จะฝังอยู่ภายในตัวเลือกคลาส .parent ซึ่งหมายความว่าตัวเลือก .child ที่ฝังอยู่จะมีผลกับองค์ประกอบที่เป็นองค์ประกอบย่อยขององค์ประกอบที่มีคลาส .parent เท่านั้น

ตัวอย่างนี้เขียนโดยใช้สัญลักษณ์ & แทนก็ได้ เพื่อระบุตำแหน่งที่ควรวางคลาสหลักอย่างชัดเจน

.parent {
  color: blue;

  & .child {
    color: red;
  }
}

ตัวอย่างทั้ง 2 รูปแบบนี้ทํางานได้เหมือนกัน และเหตุผลที่คุณมีตัวเลือกจะชัดเจนขึ้นเมื่อดูตัวอย่างขั้นสูงเพิ่มเติมในบทความนี้

การเลือกวงกลม

ในตัวอย่างนี้ เราต้องเพิ่มสไตล์เพื่อทำให้วงกลมในหน้าเดโมเบลอและจางลง

หากไม่มีการวางซ้อน CSS ในปัจจุบันจะมีลักษณะดังนี้

.demo .circle {
  opacity: .25;
  filter: blur(25px);
}

เมื่อใช้การฝัง คุณจะดำเนินการได้ 2 วิธีดังนี้

/* & is explicitly placed in front of .circle */
.demo {
  & .circle {
    opacity: .25;
    filter: blur(25px);
  }
}

หรือ

/* & + " " space is added for you */
.demo {
  .circle {
    opacity: .25;
    filter: blur(25px);
  }
}

ผลลัพธ์คือองค์ประกอบทั้งหมดภายใน .demo ที่มีคลาส .circle จะเบลอและแทบมองไม่เห็น

ตารางกริดรูปทรงสีสันสดใสไม่มีวงกลมอีกต่อไป แต่จะปรากฏเป็นจุดเล็กๆ บนพื้นหลัง
ลองใช้เดโม

การเลือกสามเหลี่ยมและสี่เหลี่ยมจัตุรัส

งานนี้ต้องเลือกองค์ประกอบที่ฝังหลายรายการ หรือเรียกอีกอย่างว่าตัวเลือกกลุ่ม

หากไม่มีการวางซ้อน CSS ในปัจจุบันมี 2 วิธีดังนี้

.demo .triangle,
.demo .square {
  opacity: .25;
  filter: blur(25px);
}

หรือใช้ :is()

/* grouped with :is() */
.demo :is(.triangle, .square) {
  opacity: .25;
  filter: blur(25px);
}

เมื่อมีการฝัง คุณจะเขียนได้ 2 วิธีดังนี้

.demo {
  & .triangle,
  & .square {
    opacity: .25;
    filter: blur(25px);
  }
}

หรือ

.demo {
  .triangle, .square {
    opacity: .25;
    filter: blur(25px);
  }
}

ผลลัพธ์ มีเพียงองค์ประกอบ .circle ที่เหลืออยู่ภายใน .demo

ตารางกริดรูปทรงหลากสีเหลือไว้เพียงวงกลมเท่านั้น ส่วนรูปทรงอื่นๆ แทบมองไม่เห็น
ลองใช้เดโม

การเลือกสามเหลี่ยมและวงกลมขนาดใหญ่

งานนี้ต้องมีตัวเลือกแบบผสม ซึ่งองค์ประกอบต้องมีทั้ง 2 คลาสเพื่อให้เลือกได้

หากไม่มีการวางซ้อน CSS ในปัจจุบันจะมีลักษณะดังนี้

.demo .lg.triangle,
.demo .lg.square {
  opacity: .25;
  filter: blur(25px);
}

หรือ

.demo .lg:is(.triangle, .circle) {
  opacity: .25;
  filter: blur(25px);
}

เมื่อมีการฝัง คุณจะเขียนได้ 2 วิธีดังนี้

.demo {
  .lg.triangle,
  .lg.circle {
    opacity: .25;
    filter: blur(25px);
  }
}

หรือ

.demo {
  .lg {
    &.triangle,
    &.circle {
      opacity: .25;
      filter: blur(25px);
    }
  }
}

ผลลัพธ์คือ สามเหลี่ยมและวงกลมขนาดใหญ่ทั้งหมดซ่อนอยู่ใน .demo

ตารางกริดสีสันสดใสจะแสดงเฉพาะรูปร่างขนาดเล็กและกลางเท่านั้น
ลองใช้เดโม
เคล็ดลับสำหรับมือโปรเกี่ยวกับตัวเลือกแบบผสมและการฝัง

สัญลักษณ์ & มีประโยชน์ในสถานการณ์นี้ เนื่องจากแสดงวิธีต่อเชื่อมตัวเลือกที่ฝังไว้อย่างชัดเจน ลองพิจารณาตัวอย่างต่อไปนี้

.demo {
  .lg {
    .triangle,
    .circle {
      opacity: .25;
      filter: blur(25px);
    }
  }
}

แม้ว่าจะเป็นวิธีฝังที่ถูกต้อง แต่ผลลัพธ์จะไม่ตรงกับองค์ประกอบที่คุณอาจคาดหวัง สาเหตุคือ หากไม่มี & เพื่อระบุผลลัพธ์ที่ต้องการของ .lg.triangle, .lg.circle ที่รวมกัน ผลลัพธ์จริงจะเป็น .lg .triangle, .lg .circle ซึ่งเป็นตัวเลือกที่สืบทอด

เลือกรูปทรงทั้งหมดยกเว้นรูปสีชมพู

งานนี้ต้องใช้คลาสจำลองแบบปฏิเสธฟังก์ชัน ซึ่งองค์ประกอบต้องไม่มีตัวเลือกที่ระบุ

หากไม่มีการวางซ้อน CSS ในปัจจุบันจะมีลักษณะดังนี้

.demo :not(.pink) {
  opacity: .25;
  filter: blur(25px);
}

เมื่อมีการฝัง คุณจะดำเนินการได้ 2 วิธีดังนี้

.demo {
  :not(.pink) {
    opacity: .25;
    filter: blur(25px);
  }
}

หรือ

.demo {
  & :not(.pink) {
    opacity: .25;
    filter: blur(25px);
  }
}

ผลลัพธ์ รูปร่างทั้งหมดที่ไม่ใช่สีชมพูจะซ่อนอยู่ใน .demo:

ตารางกริดสีสันสดใสจะเปลี่ยนเป็นโมโนโครม โดยจะแสดงเฉพาะรูปทรงสีชมพู
ลองใช้เดโม
ความแม่นยำและความยืดหยุ่นด้วย &

สมมติว่าคุณต้องการกําหนดเป้าหมาย .demo ด้วยตัวเลือก :not() & จำเป็นสำหรับ การดำเนินการต่อไปนี้

.demo {
  &:not() {
    ...
  }
}

สูตรนี้รวม .demo และ :not() เป็น .demo:not() ซึ่งต่างจากตัวอย่างก่อนหน้าที่ต้องใช้ .demo :not() การช่วยเตือนนี้มีความสําคัญอย่างยิ่งเมื่อต้องการฝังการโต้ตอบ :hover

.demo {
  &:hover {
    /* .demo:hover */
  }

  :hover {
    /* .demo :hover */
  }
}

ตัวอย่างการฝังเพิ่มเติม

ข้อกำหนด CSS สำหรับการฝังมีตัวอย่างเพิ่มเติมมากมาย หากต้องการดูข้อมูลเพิ่มเติมเกี่ยวกับไวยากรณ์ ผ่านตัวอย่าง เราจะพูดถึงตัวอย่างที่ถูกต้องและไม่ถูกต้องจำนวนมาก

ตัวอย่างต่อไปอีก 2-3 ตัวอย่างจะแนะนำฟีเจอร์การซ้อน CSS สั้นๆ เพื่อช่วยให้คุณเข้าใจความสามารถต่างๆ ที่มี

การฝัง @media

การย้ายไปยังส่วนอื่นของสไตลชีตเพื่อค้นหาเงื่อนไขของ Media Query ที่แก้ไขตัวเลือกและสไตล์ของตัวเลือกนั้นอาจทำให้เสียสมาธิ ปัญหานี้หมดไปเมื่อคุณฝังเงื่อนไขไว้ในบริบทได้โดยตรง

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

.card {
  font-size: 1rem;

  @media (width >= 1024px) {
    font-size: 1.25rem;
  }
}

นอกจากนี้ คุณยังใช้ & อย่างชัดแจ้งได้เช่นกัน

.card {
  font-size: 1rem;

  @media (width >= 1024px) {
    &.large {
      font-size: 1.25rem;
    }
  }
}

ตัวอย่างนี้แสดงไวยากรณ์แบบขยายที่มี & พร้อมกับกําหนดเป้าหมาย.large การ์ดเพื่อแสดงให้เห็นว่าฟีเจอร์การฝังเพิ่มเติมยังคงทํางานต่อไป

ดูข้อมูลเพิ่มเติมเกี่ยวกับการฝัง @rules

การวางซ้อนได้ทุกที่

ตัวอย่างทั้งหมดจนถึงตอนนี้ยังคงปรากฏอยู่หรือต่อท้ายบริบทก่อนหน้า คุณสามารถเปลี่ยนหรือจัดเรียงบริบทใหม่ทั้งหมดได้หากต้องการ

.card {
  .featured & {
    /* .featured .card */
  }
}

สัญลักษณ์ & แสดงการอ้างอิงออบเจ็กต์ตัวเลือก (ไม่ใช่สตริง) และสามารถวางไว้ที่ใดก็ได้ในตัวเลือกที่ฝัง โดยสามารถวางได้หลายครั้ง

.card {
  .featured & & & {
    /* .featured .card .card .card */
  }
}

แม้ว่าตัวอย่างนี้อาจดูไร้ประโยชน์ แต่ก็มีบางสถานการณ์ที่การทําซ้ำบริบทตัวเลือกจะมีประโยชน์

ตัวอย่างการซ้อนที่ไม่ถูกต้อง

รูปแบบนิพจน์ที่ฝังซ้อนกันบางรูปแบบไม่ถูกต้องและอาจทำให้คุณประหลาดใจหากคุณฝังซ้อนในโปรแกรมประมวลผลข้อมูลล่วงหน้า

การซ้อนและการเชื่อมต่อ

แบบแผนการตั้งชื่อคลาส CSS จำนวนมากใช้การซ้อนที่สามารถต่อหรือต่อท้ายตัวเลือกราวกับว่าเป็นสตริง ซึ่งใช้ไม่ได้กับการซ้อน CSS เนื่องจากตัวเลือกไม่ใช่สตริง แต่เป็นการอ้างอิงออบเจ็กต์

.card {
  &--header {
    /* is not equal to ".card--header" */
  }
}

ดูคำอธิบายที่ละเอียดยิ่งขึ้นได้ในข้อกำหนด

ตัวอย่างการฝังที่ยุ่งยาก

การฝังภายในรายการตัวเลือกและ :is()

พิจารณาการบล็อก CSS ที่ซ้อนอยู่ต่อไปนี้

.one, #two {
  .three {
    /* some styles */
  }
}

นี่เป็นตัวอย่างแรกซึ่งเริ่มต้นด้วยรายการตัวเลือก จากนั้นฝังรายการย่อยต่อ ตัวอย่างก่อนหน้านี้จบด้วยรายการตัวเลือกเท่านั้น ตัวอย่างการวางซ้อนนี้ไม่มีสิ่งใดที่ไม่ถูกต้อง แต่ก็มีรายละเอียดการใช้งานที่อาจยุ่งยากเกี่ยวกับการซ้อนรายการตัวเลือก โดยเฉพาะรายการที่มีตัวเลือกรหัส

เพื่อให้ความตั้งใจของการฝังทำงานได้ เบราว์เซอร์จะรวมรายการตัวเลือกที่ไม่ใช่การฝังด้านในสุดไว้ด้วย :is() การแยกบรรทัดนี้จะช่วยรักษาการจัดกลุ่มของรายการตัวเลือกภายในบริบทที่เขียน ผลข้างเคียงของการจัดกลุ่ม :is(.one, #two) นี้คือการใช้ความเฉพาะเจาะจงของคะแนนสูงสุดภายในตัวเลือกในวงเล็บ :is() ทํางานแบบนี้เสมอ แต่อาจทำให้ประหลาดใจเมื่อใช้ไวยากรณ์การฝัง เนื่องจากไม่ใช่สิ่งที่เขียนไว้ สรุปเคล็ดลับคือ การวางซ้อนด้วยรหัสและรายการตัวเลือกอาจทําให้ตัวเลือกมีความเฉพาะเจาะจงสูงมาก

หากต้องการสรุปตัวอย่างที่ซับซ้อนให้ชัดเจน ระบบจะนำบล็อกที่ซ้อนกันก่อนหน้าไปใช้กับเอกสาร ดังนี้

:is(.one, #two) .three {
  /* some styles */
}

โปรดคอยสังเกตหรือสอนโปรแกรมตรวจไวยากรณ์ให้เตือนเมื่อมีการฝังภายในรายการตัวเลือกที่ใช้ตัวเลือกรหัส ความเฉพาะเจาะจงของการฝังทั้งหมดภายในรายการตัวเลือกนั้นจะสูง

การผสมการฝังและการประกาศ

ลองดูบล็อก CSS ที่ฝังอยู่ต่อไปนี้

.card {
  color: green;
  & { color: blue; }
  color: red;
}

สีขององค์ประกอบ .card จะเป็น blue

ระบบจะยกการประกาศสไตล์ที่ผสมกันไว้ที่ด้านบน ราวกับว่าเขียนขึ้นก่อนที่จะมีการฝัง ดูรายละเอียดเพิ่มเติมได้ในข้อกำหนด

แต่ก็มีวิธีแก้ปัญหา ตัวอย่างต่อไปนี้จะตัดสไตล์สี 3 สไตล์ใน & ซึ่งจะรักษาลําดับการแสดงผลตามลําดับตามผู้เขียนต้องการ สีขององค์ประกอบ .card จะเป็นสีแดง

.card {
  color: green;
  & { color: blue; }
  & { color: red; }
}

อันที่จริงแล้ว แนวทางปฏิบัติแนะนำคือให้ตัดสไตล์ใดก็ตามที่ตามหลังการฝังด้วย &

.card {
  color: green;

  @media (prefers-color-scheme: dark) {
    color: lightgreen;
  }

  & {
    aspect-ratio: 4/3;
  }
}

การตรวจหาฟีเจอร์

การตรวจหาการฝัง CSS ทำได้ 2 วิธี ได้แก่ ใช้การฝังหรือใช้ @supports เพื่อตรวจสอบความสามารถในการแยกวิเคราะห์ตัวเลือกการฝัง

ภาพหน้าจอสาธิต Codepen ของ Bramus ซึ่งถามว่าเบราว์เซอร์รองรับการซ้อน CSS หรือไม่ ใต้คำถามดังกล่าวจะมีช่องสีเขียวซึ่งแสดงถึงการสนับสนุน

การใช้การซ้อน

html {
  .has-nesting {
    display: block;
  }

  .no-nesting {
    display: none;
  }
}

วิธีใช้ @supports

@supports (selector(&)) {
  /* nesting parsing available */
}

เพื่อนร่วมงานของฉัน Bramus มี Codepen ที่ยอดเยี่ยมซึ่งแสดงกลยุทธ์นี้

การแก้ไขข้อบกพร่องด้วยเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome

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

ภาพหน้าจอของไวยากรณ์การฝังของเครื่องมือสําหรับนักพัฒนาเว็บใน Chrome

Chrome 113 วางแผนที่จะรองรับการฝัง CSS เพิ่มเติม โปรดอดใจรอ

อนาคต

การฝัง CSS มีเพียงเวอร์ชัน 1 เท่านั้น เวอร์ชัน 2 จะเพิ่มรูปแบบคำสั่งที่อ่านง่ายขึ้นและอาจลดกฎที่ต้องจำ ผู้ใช้จำนวนมากต้องการการแยกวิเคราะห์การฝังที่ไม่มีการจํากัดหรือมีช่วงเวลาที่ยุ่งยาก

การฝังเป็นการเพิ่มประสิทธิภาพที่สำคัญมากสำหรับภาษา CSS ซึ่งส่งผลต่อการเขียน งานด้านสถาปัตยกรรมของ CSS แทบทุกด้าน ผลกระทบที่ยิ่งใหญ่นี้ต้องได้รับการสำรวจและทำความเข้าใจอย่างละเอียดก่อนที่จะระบุเวอร์ชัน 2 ได้อย่างมีประสิทธิภาพ

สุดท้ายนี้ นี่คือตัวอย่างที่ใช้ @scope, การฝัง และ @layer ร่วมกัน ทั้งหมดน่าตื่นเต้นมากเลย

การ์ดสีอ่อนบนพื้นหลังสีเทา การ์ดมีชื่อและข้อความ ปุ่มดำเนินการ 2-3 ปุ่ม และรูปภาพสไตล์ไซเบอร์พังก์