การสืบทอด

The CSS Podcast - 005: Inheritance

สมมติว่าคุณเพิ่งเขียน CSS เพื่อให้องค์ประกอบมีลักษณะเหมือนปุ่ม

<a href="http://example.com" class="my-button">I am a button link</a>
.my-button {
  display: inline-block;
  padding: 1rem 2rem;
  text-decoration: none;
  background: pink;
  font: inherit;
  text-align: center;
}

จากนั้นเพิ่มองค์ประกอบลิงก์ไปยังบทความเนื้อหา โดยมีclassค่าเป็น .my-button แต่มีปัญหาคือ ข้อความไม่ได้เป็นสีที่คุณคาดหวัง เหตุการณ์นี้เกิดขึ้นได้อย่างไร

พร็อพเพอร์ตี้ CSS บางรายการจะรับค่ามาจากองค์ประกอบระดับบนหากคุณไม่ได้ระบุค่าสำหรับพร็อพเพอร์ตี้นั้น ในกรณีของปุ่มนี้ ปุ่มจะรับค่า color จาก CSS นี้

article a {
  color: maroon;
}

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

ขั้นตอนการรับค่า

ดูวิธีการทำงานของการรับค่า โดยใช้ข้อมูลโค้ด HTML นี้

<html>
  <body>
    <article>
      <p>Lorem ipsum dolor sit amet.</p>
    </article>
  </body>
</html>

องค์ประกอบราก (<html>) จะไม่รับค่าใดๆ เนื่องจากเป็นองค์ประกอบแรกในเอกสาร เพิ่ม CSS บางส่วนในองค์ประกอบ HTML และ CSS จะเริ่มเรียงซ้อนลงในเอกสาร

html {
  color: lightslategray;
}

องค์ประกอบอื่นๆ จะสืบทอดพร็อพเพอร์ตี้ color โดยค่าเริ่มต้น องค์ประกอบ html มี color: lightslategray ดังนั้นตอนนี้องค์ประกอบทั้งหมดที่รับค่าสีได้จะมีสีเป็น lightslategray

body {
  font-size: 1.2em;
}
p {
  font-style: italic;
}

เฉพาะ <p> เท่านั้นที่จะมีข้อความเป็นตัวเอียงเนื่องจากเป็นองค์ประกอบที่ฝังลึกที่สุด การรับค่าจะไหลลงเท่านั้น ไม่ไหลกลับไปยังองค์ประกอบระดับบนสุด

พร็อพเพอร์ตี้ใดที่รับค่าตามค่าเริ่มต้น

คุณสมบัติ CSS บางอย่างไม่ได้สืบทอดโดยค่าเริ่มต้น แต่ก็มีคุณสมบัติอีกหลายอย่างที่สืบทอด เพื่อเป็นข้อมูลอ้างอิง นี่คือรายการพร็อพเพอร์ตี้ทั้งหมดที่รับค่าโดยค่าเริ่มต้น ซึ่งนำมาจากข้อมูลอ้างอิง W3 ของพร็อพเพอร์ตี้ CSS ทั้งหมด

วิธีการทำงานของการรับช่วง

องค์ประกอบ HTML ทุกรายการมีพร็อพเพอร์ตี้ CSS ทุกรายการที่กำหนดโดยค่าเริ่มต้นพร้อมค่าเริ่มต้น ค่าเริ่มต้นคือพร็อพเพอร์ตี้ที่ไม่ได้สืบทอดและจะแสดงเป็นค่าเริ่มต้น หากแคสเคดคำนวณค่าสำหรับองค์ประกอบนั้นไม่สำเร็จ

พร็อพเพอร์ตี้ที่รับค่าได้จะเรียงซ้อนลงมา และองค์ประกอบย่อยจะได้รับค่าที่คำนวณแล้วซึ่งแสดงค่าขององค์ประกอบระดับบนสุด ซึ่งหมายความว่าหากองค์ประกอบระดับบนสุดมีfont-weightเป็น bold องค์ประกอบย่อยทั้งหมดจะเป็นตัวหนา เว้นแต่font-weightขององค์ประกอบย่อยจะตั้งค่าเป็นค่าอื่น หรือสไตล์ชีตของ User Agent มีค่าสำหรับfont-weightขององค์ประกอบนั้น

วิธีรับค่าและควบคุมการรับค่าอย่างชัดเจน

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

inherit คีย์เวิร์ด

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

strong {
  font-weight: 900;
}

ข้อมูลโค้ด CSS นี้จะตั้งค่าองค์ประกอบ <strong> ทั้งหมดให้มี font-weight เป็น 900 แทนค่าเริ่มต้น bold ซึ่งจะเทียบเท่ากับ font-weight: 700

.my-component {
  font-weight: 500;
}

คลาส .my-component จะตั้งค่า font-weight เป็น 500 แทน หากต้องการให้องค์ประกอบ <strong> ภายใน .my-component เป็น font-weight: 500 ด้วย ให้เพิ่ม

.my-component strong {
  font-weight: inherit;
}

ตอนนี้องค์ประกอบ <strong> ภายใน .my-component จะมี font-weight เป็น 500

คุณสามารถตั้งค่านี้อย่างชัดเจนได้ แต่หากใช้ inherit และ CSS ของ .my-component มีการเปลี่ยนแปลงในอนาคต คุณจะมั่นใจได้ว่า <strong> จะอัปเดตตามโดยอัตโนมัติ

initial คีย์เวิร์ด

การรับค่าอาจทำให้เกิดปัญหากับองค์ประกอบของคุณ และ initial จะมีตัวเลือกการรีเซ็ตที่มีประสิทธิภาพให้คุณ

คุณได้เรียนรู้ไปแล้วก่อนหน้านี้ว่าพร็อพเพอร์ตี้ทุกรายการมีค่าเริ่มต้นใน CSS คีย์เวิร์ด initial จะตั้งค่าพร็อพเพอร์ตี้กลับเป็นค่าเริ่มต้นเดิม

aside strong {
  font-weight: initial;
}

ข้อมูลโค้ดนี้จะนำความหนาของตัวอักษรออกจากองค์ประกอบ <strong> ทั้งหมดภายในองค์ประกอบ <aside> และเปลี่ยนเป็นความหนาปกติแทน ซึ่งเป็นค่าเริ่มต้น

unset คีย์เวิร์ด

พร็อพเพอร์ตี้ unset จะทํางานแตกต่างกันหากมีการรับค่าพร็อพเพอร์ตี้โดยค่าเริ่มต้นหรือไม่ หากพร็อพเพอร์ตี้ได้รับการรับค่าโดยค่าเริ่มต้น คีย์เวิร์ด unset จะเหมือนกับ inherit หากพร็อพเพอร์ตี้ไม่ได้รับค่ามาโดยค่าเริ่มต้น คีย์เวิร์ด unset จะเท่ากับ initial

การจดจำว่าพร็อพเพอร์ตี้ CSS ใดที่รับค่าโดยค่าเริ่มต้นอาจเป็นเรื่องยาก แต่unset จะช่วยคุณได้ในบริบทนี้ เช่น color จะรับค่าโดยค่าเริ่มต้น แต่ margin จะไม่รับค่า คุณจึงเขียนได้ดังนี้

/* Global color styles for paragraph in authored CSS */
p {
  margin-top: 2em;
  color: goldenrod;
}

/* The p needs to be reset in asides, so you can use unset */
aside p {
  margin: unset;
  color: unset;
}

ตอนนี้ระบบได้นำ margin ออกแล้ว และ color จะกลับไปเป็นค่าที่คำนวณแล้วซึ่งรับช่วงมา

คุณใช้ค่า unset กับพร็อพเพอร์ตี้ all ได้ด้วย จากตัวอย่างก่อนหน้า จะเกิดอะไรขึ้นหากสไตล์ส่วนกลาง p มีพร็อพเพอร์ตี้เพิ่มเติมอีก 2-3 รายการ จะมีเพียงกฎที่ตั้งค่าไว้สำหรับ margin และ color เท่านั้นที่มีผล

/* Global color styles for paragraph in authored CSS */
p {
    margin-top: 2em;
    color: goldenrod;
    padding: 2em;
    border: 1px solid;
}

/* Not all properties are accounted for anymore */
aside p {
    margin: unset;
    color: unset;
}

หากคุณเปลี่ยนaside pเป็นall: unsetแทน ไม่ว่าในอนาคตจะใช้รูปแบบส่วนกลางใดกับ p ระบบก็จะยกเลิกการตั้งค่าเสมอ

aside p {
    margin: unset;
    color: unset;
    all: unset;
}

revert คีย์เวิร์ด

ดังที่ได้เรียนรู้ในบทเรียนเกี่ยวกับการเรียงซ้อน สไตล์มาจากแหล่งที่มาต่างๆ ได้แก่ สไตล์พื้นฐานของ User Agent, สไตล์ที่ผู้ใช้กำหนด และสไตล์ที่คุณสร้างขึ้น คีย์เวิร์ด revert จะยกเลิกรูปแบบที่ตั้งค่าไว้ในต้นทางเดียวกันกับที่ใช้คีย์เวิร์ด revert

ซึ่งจะมีประโยชน์เมื่อคุณตั้งค่าสไตล์ไว้แล้ว แต่ไม่ต้องการให้ใช้ในบางอินสแตนซ์ แม้ว่า inherit, initial และ unset จะระบุวิธีคำนวณค่าของสไตล์ แต่ revert จะระบุเพียงว่าสไตล์อื่นๆ ที่คุณเขียนจะไม่มีผล

p {
  padding: 2em;
}

aside p {
  padding: revert;
}

ข้อมูลโค้ดนี้จะให้ระยะห่างภายในแก่องค์ประกอบ <p> แต่เมื่อองค์ประกอบ <p> อยู่ภายใน <aside> จะไม่มีการระบุ padding เลย แต่จะเปลี่ยนกลับไปเป็นสไตล์ค่ากำหนดของผู้ใช้ (หากมีการตั้งค่าไว้) หรือสไตล์พื้นฐานของ User Agent

revert-layer คีย์เวิร์ด

เลเยอร์ Cascade เป็นวิธีที่มีประโยชน์ในการจัดระเบียบสไตล์ และจัดลำดับความสำคัญของสไตล์ภายในต้นทางของผู้เขียนของ Cascade revert-layer คีย์เวิร์ดคล้ายกับ revert แต่แทนที่จะระบุว่าไม่ควรใช้สไตล์ผู้เขียนใดๆ กับพร็อพเพอร์ตี้ คีย์เวิร์ดนี้จะยกเลิกสไตล์ในเลเยอร์ปัจจุบันเท่านั้น

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

หากเลเยอร์อื่นๆ ไม่ได้ระบุค่าสำหรับพร็อพเพอร์ตี้ดังกล่าว พร็อพเพอร์ตี้จะทำงานเหมือน revert และใช้ค่าจากต้นทางก่อนหน้า

ทดสอบความเข้าใจ

ทดสอบความรู้เกี่ยวกับการรับค่า

พร็อพเพอร์ตี้ใดต่อไปนี้จะรับค่าโดยค่าเริ่มต้น

animation
การเคลื่อนไหวจะไม่ส่งต่อให้ลูก
font-size
🎉
color
🎉
text-align
🎉
line-height
🎉

ค่าใดที่ทำงานเหมือน inherit เว้นแต่จะไม่มีอะไรให้รับช่วงและ จากนั้นจะทำงานเหมือน initial

reset
ไม่ใช่ค่าที่ถูกต้อง โปรดลองอีกครั้ง
unset
🎉
superset
ไม่ใช่ค่าที่ถูกต้อง โปรดลองอีกครั้ง

แหล่งข้อมูล