การฝัง 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 ในตัวอย่างก่อนหน้าออกจากโปรเจ็กต์ คุณก็ลบทั้งกลุ่มแทนที่จะค้นหาไฟล์สำหรับอินสแตนซ์ตัวเลือกที่เกี่ยวข้องได้

การซ้อนกันสามารถช่วยในเรื่องต่อไปนี้ - องค์กร - การลดขนาดไฟล์ - การเปลี่ยนโครงสร้างภายในโค้ด

การทำ Nest มีให้บริการใน Chrome 112 และยังลองใช้ใน Safari Technical Preview 162 ได้อีกด้วย

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

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

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

ภายในแซนด์บ็อกซ์จะมีวงกลม สามเหลี่ยม และสี่เหลี่ยมจัตุรัส มีทั้งขนาดเล็ก กลาง หรือใหญ่ ส่วนบางประเภทก็เป็นสีน้ำเงิน ชมพู หรือม่วง โดยทั้งหมดอยู่ภายในเอลิเมนต์ที่มี .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 สำหรับการซ้อนมีตัวอย่างเพิ่มเติมมากมาย หากต้องการดูข้อมูลเพิ่มเติมเกี่ยวกับไวยากรณ์ ผ่านตัวอย่าง เราจะพูดถึงตัวอย่างที่ถูกต้องและไม่ถูกต้องจำนวนมาก

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

การฝัง @media

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

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

.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 */
  }
}

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

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

มีสถานการณ์ไวยากรณ์ที่ซ้อนกันอยู่ 2-3 แบบที่ไม่ถูกต้องและอาจทำให้คุณประหลาดใจหากคุณได้ซ้อนอยู่ในตัวประมวลผลล่วงหน้า

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

แบบแผนการตั้งชื่อคลาส 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 แบบใน & ซึ่งคงลำดับแบบ Cascade ตามที่ผู้เขียนตั้งใจไว้ สีขององค์ประกอบ .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 Nesting เป็นเวอร์ชัน 1 เท่านั้น เวอร์ชัน 2 จะแนะนำน้ำตาลแบบไวยากรณ์ที่มีกฎให้อ่านได้มากกว่าและน่าจะมีกฎที่ต้องจดจำน้อยลง มีความต้องการจำนวนมากในการแยกวิเคราะห์การซ้อนเพื่อให้ไม่จํากัดหรือมีช่วงเวลาที่ยุ่งยาก

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

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

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