100vh
. فیلیپ براونن علت این اتفاق را بررسی می کند و راه حلی برای رفع آن ارائه می دهد.
اخیراً توسط یک دانش آموز از من خواسته شد تا در مورد یک کمک کنم به ظاهر مشکل ساده او روی یک وبسایت برای یک کافیشاپ کار میکرد که دارای هدر چسبنده بود، و میخواست قسمت قهرمان درست زیر آن سرصفحه، بقیه فضای عمودی موجود در نمای دید را پوشش دهد.
در اینجا یک نسخه نمایشی بصری از جلوه مورد نظر برای وضوح وجود دارد.
به نظر می رسد باید به اندازه کافی آسان باشد، درست است؟ مطمئن بودم (بخوانید: با اعتماد به نفس بیش از حد) که حل مشکل فقط چند دقیقه طول می کشد، اما متوجه شدم که چاهی بسیار عمیق تر از آن چیزی است که من تصور می کردم.
قبل از غواصی، اجازه دهید نگاهی گذرا به نشانه گذاری اولیه و CSS بیندازیم تا ببینیم با چه چیزی کار می کنیم:
Hero Content
Main Content
.header {
position: sticky;
top: 0; /* Offset, otherwise it won't stick! */
}
/* etc. */
با آن اعلامیه ها، .header
به بالای صفحه می چسبد. و با این حال .hero
عنصر زیر آن به طور ذاتی اندازه باقی می ماند. این چیزی است که ما می خواهیم تغییر دهیم.
میوه کم آویزان
اولین انگیزه ای که ممکن است داشته باشید، همانطور که من انجام دادم، این است که هدر و قهرمان را در نوعی کانتینر والد محصور کنید و آن ظرف را بدهید. 100vh
تا آن را به نمای درت باز کند. پس از آن، میتوانیم از Flexbox برای توزیع بچهها استفاده کنیم و قهرمان را بزرگ کنیم تا فضای باقیمانده را پر کند.
Hero Content
Main Content
.container {
height: 100vh;
display: flex;
flex-direction: column;
}
.hero {
flex-grow: 1;
}
/* etc. */
این در نگاه اول درست به نظر می رسد، اما مراقب باشید که هنگام عبور از قهرمان چه اتفاقی می افتد.
هدر چسبنده در ظرف اصلی خود به دام می افتد! اما.. چرا?
اگر شما هم مثل من هستید، این رفتار حداقل در ابتدا غیر شهودی است. شاید این را شنیده باشید sticky
ترکیبی است از relative
و fixed
موقعیتیابی، به این معنی که در جریان عادی سند شرکت میکند، اما فقط تا زمانی که به لبههای محفظه پیمایشی خود برخورد کند، در این مرحله تبدیل میشود. fixed
. در حین مشاهده sticky
از آنجایی که ترکیبی از مقادیر دیگر میتواند یک یادگاری مفید باشد، نمیتواند یک تفاوت مهم بین آن را درک کند sticky
و fixed
عناصر:
الف position: fixed
عنصر به والدی که در آن قرار دارد یا هیچ یک از اجدادش اهمیتی نمی دهد. از جریان عادی سند خارج میشود و خود را مستقیماً از درگاه دید خارج میکند، گویی در فاصله معینی از لبه صفحه در جای خود چسبانده شده است.
برعکس، الف position: sticky
عنصر همراه با لبههای درگاه دید (یا نزدیکترین محفظه پیمایشی بعدی) هل داده میشود، اما هرگز از مرزهای والد مستقیم خود فرار نخواهد کرد. خوب، حداقل اگر از نظر بصری حساب نکنید transform
-داشتن آن بنابراین راه بهتری برای فکر کردن در مورد آن ممکن است، دزدی از کریس کویر باشد “position: sticky
به یک معنا، یک محدوده محلی است position: fixed
” این یک تصمیم طراحی عمدی است، تصمیمی که اجازه میدهد هدرهای چسبناک مخصوص بخش مانند آنهایی که توسط فهرستهای الفبایی در رابطهای تلفن همراه معروف شدهاند، وجود داشته باشد.
خوب، پس این رویکرد برای مصیبت ما ممنوع است. ما باید راه حلی پیدا کنیم که شامل یک ظرف در اطراف هدر نباشد.
ثابت شد، اما حل نشد
شاید بتوانیم زندگی خود را کمی ساده تر کنیم. به جای یک ظرف، چه می شود اگر ما به .header
عنصر الف ثابت شده است ارتفاع مثلاً 150px
? سپس، تنها کاری که باید انجام دهیم این است که آن را تعریف کنیم .hero
ارتفاع عنصر به عنوان height: calc(100vh - 150px)
.
این رویکرد یه جورایی کار می کند، اما جنبه های منفی موذیانه تر از تلاش قبلی ما هستند، زیرا ممکن است بلافاصله آشکار نشوند. احتمالاً متوجه شدید که هدر خیلی بلند است و ما میخواهیم کمی ریاضی انجام دهیم تا در مورد ارتفاع بهتر تصمیم بگیریم.
کمی به آینده فکر کنیم،
- چه می شود اگر
.header
کودکان باید در اندازههای مختلف صفحه نمایش خود را بپیچند یا دوباره مرتب کنند یا برای حفظ خوانایی در تلفن همراه رشد کنند؟ - اگر جاوا اسکریپت محتوا را دستکاری کند چه؟
همه این چیزها می تواند به طور ماهرانه ای را تغییر دهد .header
اندازه ایدهآل، و تعقیب مقادیر مناسب ارتفاع برای هر سناریو، این پتانسیل را دارد که به یک کابوس نگهداری از نقاط شکست غیرقابل مدیریت و اعداد جادویی تبدیل شود – به خصوص اگر در نظر بگیریم که این کار نه تنها برای .header
بلکه همچنین .hero
عنصری که به آن بستگی دارد.
من استدلال می کنم که این راه حل نیز فقط احساس می کند اشتباه ارتفاع ثابت یکی از قابلیتهای اصلی چیدمان CSS را میشکند – روشی که عناصر به طور خودکار رشد میکنند و برای انطباق با محتویاتشان کوچک میشوند – و عدم اتکا به آن معمولاً زندگی ما را میسازد. سخت تر، ساده تر نیست.
بنابراین، ما با…
رویکردی بدیع
اکنون که محدودیتهایی را که در حال کار با آنها هستیم فهمیدیم، راه دیگری برای بیان مشکل این است که میخواهیم .header
و .hero
به به صورت جمعی دهانه 100vh
بدون اینکه عناصر را به وضوح اندازه بگیرید یا آنها را در یک ظرف بپیچانید. در حالت ایده آل، ما پیدا می کنیم چیزی که در حال حاضر است 100vh
و آنها را با آن تراز کنید. اینجاست که به ذهنم رسید که display: grid
ممکن است فقط آنچه را که ما نیاز داریم فراهم کند!
بیایید این را امتحان کنیم: ما اعلام می کنیم display: grid
بر روی body
عنصر و قبل از .header
که ما تماس خواهیم گرفت .above-the-fold-spacer
. این عنصر جدید ارتفاع می گیرد 100vh
و تمام عرض شبکه را در بر می گیرد. در مرحله بعد، به فاصلهگذار خود میگوییم که باید دو ردیف شبکه را اشغال کند و آن را به بالای صفحه متصل میکنیم.
این عنصر باید کاملاً خالی باشد زیرا ما هرگز نمیخواهیم قابل مشاهده باشد یا در صفحهخوانها ثبت شود. ما فقط از آن به عنوان عصا استفاده می کنیم تا به شبکه بگوییم چگونه رفتار کند.
Hero Content
Main Content
body {
display: grid;
}
.above-the-fold-spacer {
height: 100vh;
/* Span from the first to the last grid column line */
/* (Negative numbers count from the end of the grid) */
grid-column: 1 / -1;
/* Start at the first grid row line, and take up 2 rows */
grid-row: 1 / span 2;
}
/* etc. */
این ماده جادویی است
با اضافه کردن فاصلهگذار، دو ردیف شبکه ایجاد کردهایم که با هم دقیقا بردار 100vh
. اکنون، در اصل تنها کاری که باقی مانده است، گفتن آن است .header
و .hero
عناصر برای تراز کردن خود با آن ردیف های موجود. ما باید به آنها بگوییم که از همان خط ستون شبکه شروع کنند .above-the-fold-spacer
عنصر به طوری که آنها سعی نمی کنند در کنار آن بنشینند. اما با انجام آن… تا-دا!
دلیل این کار این است که یک محفظه شبکهای میتواند دارای چندین کودک باشد که یک سلول مشابه را روی هم قرار میدهند. در موقعیتی مانند آن، بلندترین عنصر فرزند، ارتفاع کلی ردیف شبکه را تعیین می کند – یا در این مورد، ارتفاع ترکیبی دو ردیف (100vh
).
برای کنترل اینکه دو عنصر قابل مشاهده دقیقا چگونه فضای موجود را بین خود تقسیم می کنند، می توانیم از آن استفاده کنیم grid-template-rows
دارایی من آن را طوری ساختم که ردیف اول استفاده می کند min-content
به جای 1fr
. این امر ضروری است تا .header
به اندازه فضا را اشغال نمی کند .hero
اما در عوض فقط آنچه را که نیاز دارد می گیرد و به قهرمان اجازه می دهد بقیه را داشته باشد.
راه حل کامل ما در اینجا آمده است:
body {
display: grid;
grid-template-rows: min-content 1fr;
}
.above-the-fold-spacer {
height: 100vh;
grid-column: 1 / -1;
grid-row: 1 / span 2;
}
.header {
position: sticky;
top: 0;
grid-column-start: 1;
grid-row-start: 1;
}
.hero {
grid-column-start: 1;
grid-row-start: 2;
}
و voila: یک هدر چسبنده با اندازه دلخواه بالای یک قهرمان که رشد می کند تا فضای قابل مشاهده باقیمانده را پر کند!
هشدارها و افکار نهایی
شایان ذکر است که ترتیب HTML عناصر در اینجا مهم است. اگر تعریف کنیم .above-the-fold-spacer
بعد از ما .hero
بخش، آن را پوشش داده و دسترسی به عناصر زیر را مسدود می کند. ما می توانیم با اعلام هر یک از این موارد کار کنیم order: -1
، z-index: -1
، یا visibility: hidden
.
به خاطر داشته باشید که این یک مثال ساده است. برای مثال، اگر می خواهید یک نوار کناری در سمت چپ صفحه خود اضافه کنید، باید تنظیم کنید که عناصر از کدام ستون شروع می شوند. با این حال، در اکثر موارد، استفاده از یک رویکرد CSS Grid احتمالاً نسبت به وظیفه سیزیفی مدیریت دستی و هماهنگ کردن مقادیر ارتفاع چندین عنصر، دردسر کمتری دارد.
یکی دیگر از مزایای این رویکرد این است که آن است سازگار. اگر تصمیم دارید که یک گروه از سه عنصر به جای دو عنصر، ارتفاع صفحه را بگیرد، باید فاصلهدهنده نامرئی را در سه ردیف قرار دهید و عناصر قابل مشاهده را به ردیف مناسب اختصاص دهید. حتی اگر محتوای عنصر قهرمان باعث افزایش ارتفاع آن شود 100vh
، شبکه بدون شکستن چیزی سازگار می شود. حتی در تمام مرورگرهای مدرن به خوبی پشتیبانی می شود.
هر چه بیشتر در مورد این تکنیک فکر می کنم، بیشتر متقاعد می شوم که در واقع کاملاً تمیز است. سپس دوباره، شما می دانید که چگونه وکلا می توانند خود را در مورد استدلال های خود صحبت کنند؟ اگر میتوانید راهحل سادهتری را که من نادیده گرفتهام بیاندیشید، با خیال راحت تماس بگیرید و به من اطلاع دهید!
(gg, yk)
منبع: https://smashingmagazine.com/2024/09/sticky-headers-full-height-elements-tricky-combination/