SASS extend

כיצד מעבירים תכונות מסלקטור אחד לסלקטור אחר ב-SASS.

במאמר הקודם דיברנו על SASS import. במאמר הזה אנו נדבר על הרחבה או הורשה ב-SASS. מדובר באחד הפיצ'רים החשובים והשימושיים ביותר ב-SASS שכל אחד, מתישהו, ישתמש בו.

באופן עקרוני, הורשה מאד מאד חשובה – במיוחד אם יש לנו אלמנטים בדף שיש להם תכונות משותפות. למשל דף הודעות למשתמשים שיכול להיות סתם הודעה (הפעולה שעשית נעשתה בהצלחה!), שגיאה (לא מילאת את שדה השם) או אזהרה חמורה (ביצעת פעולה לא חוקית והמשטרה בדרך אליך, יא סוטה!). כל ההודעות – בין אם מדובר בהודעה רגילה, שגיאה או אזהרה חמורה, נמצאות בראשית הדף, לכולן יש פינות עגולות, פונט בגודל 1em – מה ששונה ביניהם הוא הצבע. במקום לכתוב שוב ושוב border-radius: 3px בכל class ו-class כמו איזה אוויל, אפשר פשוט ליצור class להודעה ולרשת ממנו.

איך עושים את זה? עם extend! קודם כל, אנו יוצרים את ה-class. במקרה שלנו, class של הודעה שממנו כולם ירשו:


.message {
	border: 1px solid #000;
	border-radius: 10px;
}


עד כאן, לא משהו ששווה לכתוב עליו הביתה, עכשיו, ניצור class של הודעה ונירש ממנו את כל התכונות של ה-message. איך? ככה:


.notice {
	border-color: blue;
	@extend .message;
}

אנחנו יכולים ליצור classים נוספים לשגיאה ולשגיאה החמורה ולרשת גם מהן. בסוף זה יראה כך:


.message {
	border: 1px solid #000;
	border-radius: 10px;
}

.notice {
	border-color: blue;
	@extend .message;
}

.error {
	border-color: yellow;
	@extend .message;
}

.alert {
	border-color: red;
	@extend .message;
}

מעכשיו, ב-CSS המקומפל, לכל היורשים יהיו את אותן התכונות של class האם. איך זה יראה? כך:


#main .message, #main .notice, #main .error, #main .alert {
  border: 1px solid #000;
  border-radius: 10px;
}

#main .notice {
  border-color: blue;
}

#main .error {
  border-color: yellow;
}

#main .alert {
  border-color: red;
}


אי אפשר לבצע extend לסלקטור מורכב – כמו למשל extend .whatever a@ או extend #main #whatever@.

אפשר לבצע כמה extend באותו סלקטור, למשל:


#main {
	color: black;
}
#foo {
	font-weight: 700;
}
#bar {
	@extend #main;
	@extend #foo;
}

וזה יקומפל אל:


#main, #bar {
  color: black;
}

#foo, #bar {
  font-weight: 700;
}

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

אפשר גם לבצע extend ל-class שבעצמו מבצע extend. סוג של inception דה לה שמעטא:


#main {
	color: black;
}
#foo {
	font-weight: 700;
	@extend #main;
}
#bar {
	@extend #foo;
}

הקוד יראה כך:


#main, #foo, #bar {
  color: black;
}

#foo, #bar {
  font-weight: 700;
}


איך זה עובד? הנה הסבר קצת מתוסבך: הקומפיילר נתן לmain# את התכונה שלו. המשיך הלאה ל-foo#. כיוון ש-foo# עושה extend לmain#, הוא הוסיף את foo# לשורה של main#. כיוון של- foo# יש גם תכונה משלו, הוא הוסיף אותה באופן עצמאי. ואז הקומפיילר הגיע לbar#. כיוון ש-bar# יורש מfoo#, הקומפיילר החביב הוסיף אותו אל הסלקטור של foo# וכיוון שיש הורשה נוספת מ-main#, הוא הוסיף אותו אל השורה של main#.

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

בואו וניקח את קובץ ה-SCSS הזה לדוגמה:


#baz .whatever .example {
	@extend a;
}

#main #foo #bar a {
	color: black;
}

יש לנו שני סלקטורים בלבד – הסלקטור הראשון עושה extend ל-a. הסלקטור השני מגדיר a עמוק עמוק בתוך סלקטורים אחרים. אם היינו חושבים "באופן הגיוני" היינו מניחים שהתוצאה תהיה משהו באיזור הזה:


#baz .whatever .example {
	color: black;
}

#main #foo #bar a {
	color: black;
}

אבל הקוד שלעיל לא יצא לנו אחרי שנפעיל את הקומפיילר! למה? כי מה שקורה הוא שהקומפיילר עובד לפי התהליך שציינו קודם לכן ולא לפי 'ההגיון שלנו'. כלומר, הקומפיילר ינסה ליצור סלקטורים שיכסו את כל האפשרויות שיש בסלקטור המורכב שלנו. למשל:


#main #foo #bar .example, 
#main #foo #bar .whatever .example, 
#main #foo #bar #baz .whatever .example, 

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


#main #foo #bar a, #main #foo #bar #baz .whatever .example, #baz .whatever #main #foo #bar .example {
  color: black;
}

מה המסקנה? קודם כל לנסות להבין לעומק את extend ושנית, להזהר איתו.
זו גם הסיבה שאם יש לנו media directive – כלומר סלקטור מסוים שרלוונטי רק ל-print או screen או מדיה אחרת – אנחנו לא יכולים לבצע extend לסלקטור מחוץ למדיה, כיוון שאז תהיה התנפחות מטורפת של סלקטורים ב-CSS המקומפל (כי צריך לכסות את כל האפשרויות ויחד עם ה-media directive יצא משהו תפלצתי בגודלו). אם ננסה משהו כזה:


.error {
  border: 1px #f00;
  background-color: #fdd;
}

@media print {
  .seriousError {
    // INVALID EXTEND: .error is used outside of the "@media print" directive
    @extend .error;
    border-width: 3px;
  }

אנו נקבל שגיאה, הסינטקס הנכון יהיה:


@media print {
  .error {
    border: 1px #f00;
    background-color: #fdd;
  }
  .seriousError {
    @extend .error;
    border-width: 3px;
  }
}

לסיכום: SASS extend הוא אחד מהפיצ'רים החזקים ביותר של SASS, אבל אם אנחנו חושבים להשתמש בו באופן רציני יותר משימוש אקראי ובסיסי, צריך לנסות ולהבין אותו לעומק על מנת להמנע מתוצאות לא רצויות וניפוח ה-CSS.

במאמר הבא נדבר על SASS Debug – איך מוצאים ופותרים תקלות בכל הסיפור הזה.

פוסטים נוספים שכדאי לקרוא

צילום מסך של סוואגר
יסודות בתכנות

openAPI

שימוש בתשתית הפופולרית למיפוי ותיעוד של API וגם הסבר בסיסי על מה זה API

גלילה לראש העמוד