כברירת מחדל, לפקדי טפסים כמו <input>, <textarea> ו-<select> יש מימדים קבועים. הגודל שלהם נשאר קבוע, לא משנה כמה תוכן המשתמש מזין או בוחר.
כדי לאפשר לפקדים האלה להתכווץ או להתרחב באופן אוטומטי בהתאם לתוכן שלהם (כולל פלייסהולדרים), משתמשים במאפיין ה-CSS field-sizing: content.
שינוי גודל אוטומטי של פקדי טפסים
הגדרת field-sizing: content בשדות קלט, בתיבות בחירה או באזורי טקסט מאפשרת לשנות את הגודל שלהם באופן דינמי כשהמשתמש מקליד או בוחר אפשרויות. עם זאת, כדי להבטיח חוויית משתמש טובה, צריך להביא בחשבון את העיצוב שעובר בירושה, את ברירות המחדל של הפריסה ואת מגבלות המינימום/מקסימום.
כדי למנוע בעיות בפריסה, מומלץ להגדיר גם את min-inline-size (או min-width) וגם את max-inline-size (או max-width) לצד field-sizing: content בשדות להזנת טקסט. הגודל המינימלי מונע מהקלט להתכווץ לרוחב של אפס כשהוא ריק (מה שהופך אותו לבלתי ניתן ללחיצה), והגודל המקסימלי מבטיח שהוא לא יתרחב ללא הגבלה וישבש את פריסת הדף.
באזורי טקסט, אם מאפשרים שינוי גודל אוטומטי אופקי, זה יכול לגרום לחוויית משתמש לא נעימה (לדוגמה, אזור טקסט עם פלייסהולדר ארוך יתכווץ בפתאומיות בצורה אופקית כשהמשתמש יקליד תו בודד). כדי למנוע את זה, צריך להגדיר רוחב מפורש (כמו width: 100%) לאזור הטקסט. כך אזור הטקסט שומר על רוחב אופקי יציב, ועדיין משנה את הגודל שלו באופן אוטומטי בצורה אנכית כשהתוכן גולש לשורה הבאה.
מומלץ להגדיר max-inline-size (או max-width) ברכיבי <select> באמצעות field-sizing: content כדי לוודא שאפשרויות ארוכות מאוד שנבחרו לא ישבשו את פריסת הדף.
/* Applies horizontal auto-sizing to inputs */
input {
/* Instructs the element to size itself to fit its content */
field-sizing: content;
/* Reset explicit width if inherited from global styles */
width: auto;
/* Prevents the input from collapsing and disappearing when empty */
min-inline-size: 15ch;
/* Prevents infinite horizontal growth */
max-inline-size: 50ch;
}
textarea {
/* Instructs the element to size itself to fit its content */
field-sizing: content;
/* Reset explicit height if inherited from global styles */
height: auto;
/* Use a fixed width to prevent jarring horizontal shifts when replacing a long placeholder */
width: 50ch; /* or 100% depending on your layout */
/* Sets a reasonable minimum height (e.g., 3 lines) for empty textareas */
min-block-size: 3lh;
/* Prevents infinite vertical growth. Once a textarea hits this */
/* height, it will stop growing and show a vertical scrollbar. */
max-block-size: 10lh;
}
select {
/* Sizes the select element to fit the active option only */
field-sizing: content;
/* Prevents the dropdown from expanding infinitely and breaking the layout */
max-inline-size: 50ch;
}
חשוב: מאפייני width ו-height מפורשים מבטלים את field-sizing: content בכל פקדי הטופס. אם ב-CSS הגלובלי של הפרויקט שלכם מוגדרים קלט, בחירה או אזורי טקסט ל-width: 100%, אתם צריכים לאפס אותם באופן מפורש ל-width: auto (או width: fit-content) באלמנטים שמשתמשים ב-field-sizing: content כדי שהשינוי האוטומטי של הגודל יעבוד. לעומת זאת, צריך להגדיר במפורש את height: auto באזורי טקסט אם הוגדר בעבר גובה קבוע באופן גלובלי.
חשוב: פריסות של רשת ו-Flexbox בדרך כלל מותחות באופן משתמע את הצאצאים שלהן כדי למלא את המרחב הזמין. אם פקד טופס כלשהו שמשתמש ב-field-sizing: content לא מתכווץ, צריך לבדוק את מאפייני היישור של הקונטיינר שלו ולהחיל align-self: start או justify-self: start על פקד הטופס כדי לשנות את המתיחה.
אסטרטגיות חלופיות
צריך להתייחס ל-field-sizing כשיפור הדרגתי (progressive enhancement). בדפדפנים שלא תומכים במאפיין, פקדי הטופס חוזרים באופן הדרגתי להתנהגות ברירת המחדל שלהם, שהיא גודל קבוע. המשתמשים פשוט יגללו כרגיל בתוכן שחורג מהגודל הקבוע של שדות הקלט ואזורי הטקסט.
אם שדות שגדלים באופן דינמי נדרשים בהחלט לדפדפנים ישנים יותר, צריך להשתמש בזיהוי תכונות ובפתרון עקיף מורכב. הדרך הכי טובה להגדיר חלופה לאזורי טקסט היא להשתמש בטריק של CSS Grid, שבו רכיב פסאודו מוסתר משקף את הקלט של המשתמש בזמן אמת, ומאלץ את מאגר הרשת (ואת אזור הטקסט שבתוכו) להתרחב.
<!-- The textarea is wrapped in a container that will mirror its value -->
<div class="growable-textarea" data-replicated-value="">
<textarea></textarea>
</div>
// Only attach the fallback event listeners if field-sizing is unsupported
if (!CSS.supports('field-sizing', 'content')) {
document.querySelectorAll('.growable-textarea > textarea').forEach(textarea => {
textarea.addEventListener('input', () => {
textarea.parentNode.dataset.replicatedValue = textarea.value;
});
});
}
/* Only apply the fallback if field-sizing is NOT supported */
@supports not (field-sizing: content) {
.growable-textarea {
display: grid;
}
/* The pseudo-element and textarea must share the exact same cell, font, and padding */
.growable-textarea::after,
.growable-textarea > textarea {
grid-area: 1 / 1 / 2 / 2;
font: inherit;
padding: 0.5rem;
border: 1px solid #999;
}
/* The pseudo-element renders the copied text invisibly to stretch the grid */
.growable-textarea::after {
/* The space is necessary for trailing empty lines to be rendered */
content: attr(data-replicated-value) " ";
white-space: pre-wrap;
visibility: hidden;
}
.growable-textarea > textarea {
resize: none;
overflow: hidden;
}
}
בגלל המורכבות של שכפול סגנונות וסנכרון המצב בין צומתי DOM לכל פקד טופס, מומלץ להסתמך על התנהגות ברירת המחדל של רכיבי קלט קבועים ברוב האפליקציות, אלא אם שינוי גודל דינמי הוא קריטי לחוויית המשתמש.