اسکرولر اصلی صفحه رو ناخواسته از دست نده
خلاصهٔ کاملتر
وقتی محتوای صفحه از ارتفاع viewport بلندتر باشه، خود viewport تبدیل به اسکرولکنندهٔ صفحه میشه؛ چیزی که مهندسای مرورگر بهش میگن root scroller (یعنی همون باکسی که کل صفحه رو اسکرول میکنه). تو مقالهٔ Polypane اومده که چون این رفتار خودکاره، خیلی از دولوپرها خبر ندارن مرورگر بابتش چه امکاناتی بهشون میده. تو استاندارد مود document.scrollingElement همون رو برمیگردونه و راه راحتِ دسترسی به این اسکرولره.
به گفتهٔ نویسنده، این چیزها فقط به root scroller میرسه و کانتینر تودرتو ازشون خبری نداره: برگردوندن جای اسکرول وقتی کاربر back میزنه، اسکرول با Space و Page Down و کلیدهای جهت بدون اینکه لازم باشه اول روی المنت کلیک کنی، درست جواب دادن window.scrollY و window.scrollTo()، و پرینت و اسکرینشات تمامقد. یعنی صفحهای که قدش به viewport قفل شده، پرینتش فقط یه صفحه درمیاد و بقیهش بریده میشه.
روی موبایل حس خرابی بیشتره. جمع شدن نوار آدرس و تولبار موقع اسکرول، تپ روی status bar تو iOS برای پریدن به بالای صفحه، و pull to refresh و overscroll همهشون فقط با root scroller کار میکنن. با یه اسکرول تودرتو نوار آدرس سرجاش میمونه و کاربر فضای صفحهٔ کمتری داره.
راه معمول از دست دادنش یه app shell ـه: overflow: hidden روی html و body و سپردن اسکرول به یه wrapper با height: 100vh. نسخهٔ موذیترش این دو خط آشناست که تو خیلی از بویلرپلیتها پیدا میشه:
html,
body {
height: 100%;
overflow-x: hidden;
}بهخاطر قاعدهٔ propagation، مقدار overflow روی میره به viewport، پس overflow خود روی خودش میمونه؛ hidden شدن یه محور، محور دیگه رو auto میکنه و که ارتفاع ثابت گرفته خودش میشه اسکرولر صفحه. تشخیصش هم سادست: صفحه رو اسکرول کن و window.scrollY رو تو کنسول بگیر، اگه صفر بود یه چیز دیگه داره اسکرول میخوره. برای پیدا کردن اون المنت یه لیسنر scroll با capture: true روی document بذار و ببین target چیه.
برای هر چیدمان رایج هم جایگزین سالم داره. اگه فقط میخوای صفحه حداقل به قدِ صفحهٔ نمایش باشه، جای height از min-height استفاده کن و اصلاً wrapper نخواه:
body {
min-height: 100svh;
display: flex;
flex-direction: column;
}
main {
flex: 1;
}برای app shell، هدر و سایدبار رو با position: sticky نگه دار جای گریدی که به ارتفاع viewport قفل شده. برای اسکرول افقی اضافه، overflow-x: clip بذار نه hidden (چون clip محتوا رو میبره ولی اسکرولکننده نمیسازه) و بهتر از اون خودِ المنت سرریز رو پیدا کن. برای مودال هم html:has(dialog[open]) با overflow: hidden کنار scrollbar-gutter: stable کافیه. اسکرول تودرتو جای خودش بد نیست، چیزی که باید ازش فرار کنی اسکرول تودرتویی ـه که کل viewport رو گرفته.
نکات کلیدی:
- document.scrollingElement تو استاندارد مود ـه و تو کوئرکس مود ؛ برای یه داکیومنت هیچوقت عوض نمیشه.
- اگه بعد از اسکرول window.scrollY صفر باشه، اسکرول دست یه کانتینر تودرتوئه.
- ترکیب height: 100% و overflow-x: hidden روی html و body، خود body رو اسکرولر صفحه میکنه.
- overflow-x: clip سرریز افقی رو میبره ولی اسکرولکننده نمیسازه.
- min-height: 100svh جای height: 100vh هم root scroller رو حفظ میکنه هم باگ بلندتر بودن ۱۰۰vh از صفحهٔ موبایل رو نداره.
- از Polypane 31، بجِ scroll روی المنتی که همون لحظه اسکرول میخوره هایلایت میشه.




