تصویر شاخص نوشته: چرا z-index کار نمی‌کند؟ stacking context به زبان ساده

چرا z-index کار نمی‌کند؟ stacking context به زبان ساده

۲ شهریور ۱۴۰۵ مبانی وب

این صحنه را هر کسی که CSS نوشته دیده: منویی زیر یک بنر گیر کرده. می‌نویسی z-index: 9999. تکان نمی‌خورد. می‌نویسی z-index: 999999. باز هم زیر است. عدد که گناهی ندارد — پس مشکل کجاست؟

مشکل این است که z-index آن چیزی نیست که اسمش قول می‌دهد. یک مسابقهٔ سراسری بین همهٔ عناصر صفحه نیست؛ مسابقه‌ای است که فقط بین هم‌گروهی‌ها برگزار می‌شود. و اسم آن گروه‌ها stacking context است. تا گروه‌ها را نبینی، هیچ عددی نجاتت نمی‌دهد.

صفحه تخت نیست

مرورگر عناصر را روی هم می‌چیند، مثل ورق‌های کاغذ روی میز. وقتی دو عنصر هم‌پوشان می‌شوند، یکی باید بالا باشد. پیش‌فرضش ساده است: چیزی که در HTML دیرتر آمده، بالاتر می‌نشیند.

z-index این ترتیب را دستکاری می‌کند — ولی نه برای هر عنصری. z-index فقط روی عنصری اثر دارد که از جریان عادی بیرون آمده باشد: مثلاً position: relative یا absolute یا fixed داشته باشد. روی یک عنصر معمولیِ استایل‌نخورده، z-index بی‌صدا نادیده گرفته می‌شود — و این اولین دلیلِ رایجِ «کار نمی‌کند» است.

گروه‌ها: stacking context

حالا قسمت اصلی. بعضی عناصر با گرفتنِ بعضی استایل‌ها، دور بچه‌هایشان یک حباب می‌کشند. داخل حباب، بچه‌ها با z-index هر جوری خواستند جابه‌جا می‌شوند؛ اما از بیرون، کلِ حباب یک ورقِ واحد است که یک جایگاه دارد. به این حباب می‌گویند stacking context.

z-index یک عنصر فقط در داخل حبابِ والدش معنا دارد. دو عنصر از دو حباب متفاوت هرگز مستقیم با هم مقایسه نمی‌شوند — اول حباب‌هایشان مقایسه می‌شوند.

مثل مسابقات مدرسه‌ای: شاگرد اولِ یک کلاس، هر چقدر هم درخشان، اگر کلاسش در رده‌بندیِ مدرسه سوم شده باشد، بالاتر از بچه‌های کلاسِ اول نمی‌ایستد. آن z-index: 999999 شاگرد اولِ کلاسِ سوم است.

چه چیزهایی حباب می‌سازند؟

فهرست کامل بلند است، ولی اینها عاملان همیشگیِ ماجرا هستند:

این استایل روی والد حباب می‌سازد؟
position: relative/absolute + یک z-index عددی بله
position: fixed یا sticky بله
opacity کمتر از ۱ بله — و این یکی همه را غافلگیر می‌کند
transform، filter، perspective (هر مقداری) بله
position: static (پیش‌فرض) نه

آن ردیفِ opacity را دوباره بخوان. والدی که فقط یک opacity: 0.99 دارد — مثلاً برای یک انیمیشنِ محوشدن — بی‌سروصدا دور همهٔ فرزندانش حباب کشیده و z-index آن‌ها را از مسابقهٔ بزرگ حذف کرده است. transform هم همین‌طور؛ و چون انیمیشن‌ها پر از transform و opacity هستند، این باگ معمولاً درست بعد از «قشنگ‌کاری» پیدایش می‌شود.

دستور کارِ رفعِ اشکال، قدم به قدم

دفعهٔ بعد که z-index جواب نداد، به‌جای بزرگ‌ترکردنِ عدد این مسیر را برو:

۱. دو عنصرِ درگیر را پیدا کن — کدام رو است، کدام باید رو باشد. ۲. از هر کدام به سمت بالا برو و اولین والدی را پیدا کن که حباب می‌سازد (جدول بالا). ابزار Inspect مرورگر همین‌جا بهترین دوستت است. ۳. اگر هر دو در یک حباب‌اند → مشکل واقعاً z-index است؛ حالا عدد را عوض کن (و مطمئن شو عنصر position دارد). ۴. اگر در دو حبابِ متفاوت‌اند → z-index بچه‌ها را رها کن. یا به خودِ حباب‌ها z-index بده تا ترتیبِ گروه‌ها عوض شود، یا آن استایلی که حبابِ ناخواسته ساخته (opacity ،transform…) را از والد بردار.

قدم ۴ همان لحظه‌ای است که ساعت‌ها کلنجار به یک تغییرِ یک‌خطی تبدیل می‌شود.

و یک عادت خوب: عددهای کوچک

وقتی stacking context را بفهمی، دیگر به ۹۹۹۹۹۹ نیازی نیست. در پروژه‌های تمیز، z-index ها از مجموعهٔ کوچکی می‌آیند — مثلاً ۱ برای کارت‌های برجسته، ۱۰ برای هدر چسبان، ۱۰۰ برای منوی باز، ۱۰۰۰ برای مودال. فاصلهٔ بین‌شان جا برای آینده می‌گذارد و عددها خودشان سند می‌شوند.

اگر پایه‌های این ماجرا — اینکه اصلاً position چه می‌کند و چرا بعضی عناصر «از جریان بیرون می‌آیند» — هنوز برایت مه‌آلود است، مسیر درست از سلکتورها و ماجرای cascade و specificity می‌گذرد؛ و دورهٔ تاروپود همهٔ این‌ها را با تمرین زنده در مرورگر، از صفر می‌سازد.

جمع‌بندی: z-index قد آدم‌ها نیست، رتبهٔ آن‌ها در گروهِ خودشان است. تا وقتی گروه‌بندی — stacking context — را نبینی، بزرگ‌کردنِ عدد فقط شاگرد اولِ همان کلاسِ سوم ساختن است.