ירושה

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, והוא מתחיל להתפשט במסמך.

html {
  color: lightslategray;
}

המאפיין color עובר בירושה כברירת מחדל לאלמנטים אחרים. לאלמנט html יש color: lightslategray,0x0A>לכן לכל האלמנטים שיכולים לקבל בירושה צבע יהיה עכשיו צבע של lightslategray.

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

רק הטקסט בתוך התג <p> יהיה מוטה כי הוא הרכיב המקונן ברמה הכי עמוקה. ההורשה מתבצעת רק כלפי מטה, ולא כלפי מעלה לרכיבי הורה.

אילו נכסים עוברים בירושה כברירת מחדל?

לא כל מאפייני ה-CSS עוברים בירושה כברירת מחדל, אבל יש הרבה מאפיינים שכן עוברים בירושה. לעיון, הנה רשימה מלאה של מאפיינים שמועברים בירושה כברירת מחדל, מתוך חומר העזר של W3 בנושא כל מאפייני ה-CSS:

איך פועל תהליך ההורשה

לכל רכיב HTML מוגדרים כברירת מחדל כל מאפייני ה-CSS עם ערך התחלתי. ערך ראשוני הוא מאפיין שלא עובר בירושה ומופיע כברירת מחדל אם לא ניתן לחשב ערך לאלמנט הזה בשיטת הקסקייד.

מאפיינים שאפשר להעביר בירושה מועברים בהיררכיה כלפי מטה, ורכיבי צאצא מקבלים ערך מחושב שמייצג את הערך של רכיב האב. המשמעות היא שאם לרכיב הורה מוגדר font-weight עם הערך bold, כל רכיבי הצאצא יהיו מודגשים, אלא אם לרכיב הצאצא מוגדר font-weight עם ערך אחר, או אם לגיליון הסגנונות של סוכן המשתמש יש ערך של 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 יתווספו עוד כמה מאפיינים? רק הכלל שהוגדר עבור 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

כמו שלמדתם בשיעור על הסגנון המדורג, סגנונות מגיעים ממקורות שונים – סגנונות בסיסיים של סוכן המשתמש, סגנונות של העדפות המשתמש וסגנונות שאתם יוצרים. מילת המפתח revert מבטלת סגנונות שמוגדרים באותו מקור שבו נעשה שימוש במילת המפתח revert.

האפשרות הזו שימושית אם הגדרתם סגנון מסוים, אבל אתם לא רוצים שהוא יחול במקרים מסוימים. המאפיינים inherit,‏ initial ו-unset מציינים איך לחשב את הערך של סגנון, אבל המאפיין revert רק מציין שסגנונות אחרים שכתבתם לא יחולו.

p {
  padding: 2em;
}

aside p {
  padding: revert;
}

בקטע הקוד הזה, לרכיבי <p> מוגדר ריווח פנימי, אבל כשרכיב <p> נמצא בתוך <aside>, לא מוגדר לו padding בכלל. במקום זאת, הוא חוזר לסגנון של העדפת משתמש (אם הוגדר כזה) או לסגנונות הבסיסיים של סוכן המשתמש.

מילת המפתח revert-layer

שכבות מדורגות הן דרך שימושית לארגן את הסגנונות ולקבוע את סדר העדיפות שלהם במקור של המחבר ב-CSS. מילת המפתח revert-layer דומה ל-revert, אבל במקום לציין שאף אחד מהסגנונות של המחבר לא צריך לחול על מאפיין, היא מבטלת רק את הסגנונות בשכבה הנוכחית.

אם אתם משתמשים בספריית ממשק משתמש של צד שלישי, כדאי לייבא אותה לשכבה ולהוסיף שינויים לשכבה עם עדיפות גבוהה יותר. אחר כך תוכלו להסיר את ההגדרה באמצעות revert-layer, ובמקומה ישמשו ברירות המחדל של ספריית ממשק המשתמש.

אם בשום שכבה אחרת לא מצוין ערך למאפיין, הוא יתנהג כמו revert וישתמש בערך ממקור קודם.

בדיקת ההבנה

בדיקת הידע בנושא ירושה

אילו מהמאפיינים הבאים עוברים בירושה כברירת מחדל?

animation
האנימציות לא מועברות לילדים.
font-size
🎉
color
🎉
text-align
🎉
line-height
🎉

איזה ערך מתנהג כמו inherit אלא אם אין ממה לרשת, ואז הוא מתנהג כמו initial?

reset
הערך לא תקין, צריך לנסות שוב.
unset
🎉
superset
הערך לא תקין, צריך לנסות שוב.

משאבים