جاوااسکریپت اسناد استاتیک را به نرمافزار تبدیل کرد. اپلیکیشنهای تکصفحهای (SPA) حس آنی بودن دارند. بدون بارگذاری مجدد کل صفحه، بدون پرشهای سفید صفحه. اما این سرعت هزینهای دارد که بسیاری از تیمها نادیده میگیرند: زیرساختهای اصلی وب شروع به پوسیدن میکنند. ناوبری (Navigation) شکننده میشود. موتورهای جستجو در دنبال کردن مسیرها دچار مشکل میشوند. صفحهخوانها (Screen readers) گم میشوند. و کاربران خود را در رابطهایی میبینند که شبیه وبسایت هستند اما مانند اپلیکیشنهای دسکتاپِ خراب رفتار میکنند.
مقصر معمولاً یک div با یک هندلر onClick است.
از استفاده از Div به عنوان لینک خودداری کنید
یک div هیچ معنای معنایی (semantic) ندارد. آن فقط یک جعبه است. وقتی یک هندلر کلیک به آن میبندید و از آن برای هدایت کاربران به یک نمای جدید استفاده میکنید، در واقع از مرورگر میخواهید که با یک جعبه مقوایی مانند یک در رفتار کند. مرورگر این کار را نمیپذیرد؛ و هر ابزار دیگری که بر پایه مرورگر ساخته شده نیز همینطور.
صفحهخوانها یک div را به عنوان لینک یا دکمه اعلام نمیکنند. آنها از روی آن عبور میکنند یا آن را به عنوان متن ساده میخوانند. کاربری که با صدا ناوبری میکند، نمیتواند آن را هدف قرار دهد. خزنده (crawler) موتورهای جستجو که صفحه شما را برای یافتن URLهای قابل کشف اسکن میکند، چیزی برای دنبال کردن نمیبیند. مسیر شما عملاً وجود ندارد.
بدتر از آن، رفتارهایی را که کاربران از قبل میشناسند از دست میدهید. یک لینک واقعی به کاربر اجازه میدهد با راستکلیک آن را در تب جدید باز کند، مقصد را بوکمارک کند یا آدرس را برای اشتراکگذاری کپی کند. کاربران کیبورد انتظار دارند با کلید Tab به آن برسند و با Enter آن را باز کنند. یک div هیچکدام از اینها را ارائه نمیدهد. حتی اگر tabIndex و role="link" و شنوندههای کیبورد (keyboard listeners) را به آن اضافه کنید، در واقع دارید چیزی را که مرورگر رایگان در اختیار شما قرار میدهد، به شکلی ضعیف بازسازی میکنید. و همیشه یک مورد خاص (edge case) را فراموش خواهید کرد. همیشه.
برای مقصد از Anchor و برای عملیات از Button استفاده کنید
HTML از قبل این مشکل را حل کرده است. سردرگمی از اینجا شروع میشود که هر دو عنصر قابل کلیک به نظر میرسند، بنابراین توسعهدهندگان با آنها به گونهای برخورد میکنند که گویی قابل جایگزینی هستند. اما اینطور نیست.
زمانی که میخواهید کاربر را به یک URL جدید منتقل کنید، از تگ <a استفاده کنید. نه یک تعویض نمای شبیهسازی شده، نه یک تغییر وضعیت (state change)، بلکه یک مکان واقعی. ویژگی href باید شامل یک آدرس واقعی باشد:
<a href="/docs">Documentation</a>
همین و بس. اگر کاربر قرار است به جایی برود، از لینک استفاده کنید.
زمانی که اتفاقی در صفحه فعلی میافتد، از <button> استفاده کنید. دکمهها برای عملیاتی مانند موارد زیر هستند:
- باز کردن یک مودال (modal)
- ارسال یک فرم
- ذخیره تنظیمات
- باز و بسته کردن یک منو
لینکها برای مقصد هستند. دکمهها برای عملیات هستند. ترکیب این دو باعث سردرگمی در رابط کاربری شده و انتظارات کاربر را از بین میبرد.
اجازه دهید مرورگر وظیفهاش را انجام دهد
مرورگرهای مدرن نتیجه دههها تکامل و استانداردسازی هستند. آنها امنیت، تاریخچه (history)، پیشدریافت (prefetching) و قابلیت دسترسی (accessibility) را بهتر از هر کد جاوااسکریپتی که خودتان بنویسید، مدیریت میکنند.
یک تگ anchor واقعی به طور خودکار پشته تاریخچه (history stack) مرورگر را تغذیه میکند. با منوی راستکلیک (context menu) بومی کار میکند. وقتی کاربر روی آن نگه میدارد (hover) یا روی آن تمرکز میکند (focus)، در الگوریتمهای پیشدریافت داخلی مرورگر مشارکت میکند و باعث میشود اپلیکیشن شما بدون نوشتن حتی یک خط کد، سریعتر به نظر برسد. این تگ به ترجیحات کاربر برای باز کردن لینکها احترام میگذارد و با مدیریتکنندههای رمز عبور، ابزارهای ترجمه و حالتهای مطالعه (reader modes) همکاری میکند.
وقتی آن را با یک تابع ناوبری جاوااسکریپتی جایگزین میکنید، در واقع از تمام این قابلیتها چشمپوشی میکنید. شما فقط ویژگیها را از دست نمیدهید؛ بلکه کاربران را مجبور میکنید عادتهایی را که در تمام سایتهای دیگر اینترنت ساختهاند، رها کنند. این یک تصمیم فنی نیست؛ این یک تجربه کاربری خصمانه است.
بررسی کنید که فریمورک شما واقعاً چه چیزی رندر میکند
React Router، Vue Router، کامپوننتهای Link در Next.js و SvelteKit. این ابزارها باعث میشوند ناوبری سمت کلاینت (client-side routing) بیدردسر به نظر برسد. اما انتزاع (abstraction) باعث بروز خطا میشود.
DOM خود را بررسی کنید. ابزارهای توسعهدهنده (developer tools) مرورگر را باز کنید و به عناصری که فریمورک شما تولید میکند نگاه کنید. یک کامپوننت <Link> باید در HTML نهایی به عنوان یک تگ واقعی <a با ویژگی href معتبر رندر شود. اگر به صورت span یا div یا هر چیز دیگری بدون یک href مناسب رندر شد، یعنی انتزاع شما شکست خورده است. کامپوننت را اصلاح کنید. مقدار پیشفرض را بازنویسی (override) کنید. از پراپ passHref یا معادل آن در فریمورک استفاده کنید. بدون تایید، به فریمورک اعتماد نکنید که حتماً کار را درست انجام میدهد.
این موضوع برای عدم تطابق در فرآیند hydration نیز اهمیت دارد. اگر سرور یک لینک را رندر کند و کلاینت آن را به یک غیرلینک تبدیل (hydrate) کند، باگهای دسترسیپذیری ایجاد میکنید که ردیابی آنها دشوار است؛ زیرا HTML در کد منبع شما درست به نظر میرسد اما در DOM زنده اشتباه است.
مقاصد جعلی را ممنوع کنید
الگویی وجود دارد که انگار قرار نیست از بین برود: href="javascript:void(0)". توسعهدهندگان زمانی از آن استفاده میکنند که چیزی میخواهند که شبیه لینک باشد اما مانند دکمه عمل کند؛ معمولاً به این دلیل که نمیخواهند یک دکمه را استایلدهی کنند یا به دلیل محدودیتهای یک کد قدیمی (legacy codebase) مجبور به این کار هستند.
متوقف شوید. این یک URL نیست. این هیچ مقصدی به مرورگر نمیدهد. تاریخچه مرورگر (history stack) را با حالتهای غیرقابل استفاده پر میکند. تاریخچه مرورگر و قابلیت دسترسی (accessibility) را مختل میکند. این یک تله است. اگر به رفتار کلیک بدون ناوبری (navigation) نیاز دارید، به یک <button> نیاز دارید. آن را هر طور که میخواهید استایلدهی کنید. CSS اهمیتی نمیدهد که عنصر یک دکمه است یا یک لینک. اما کاربران شما اهمیت میدهند.
متنی بنویسید که توضیح دهد کاربر به کجا میرود
کلمات داخل لینک شما اهمیت دارند. کاربران صفحهخوان (screen reader) اغلب لیستی از تمام لینکهای صفحه را برای بررسی سریع باز میکنند. اگر تمام لینکهای شما «بیشتر بخوانید» یا «اینجا کلیک کنید» باشد، آن لیست به یک نویز بیفایده تبدیل میشود.
دقیق باشید. اینها را مقایسه کنید:
- بد:
<a href="/security/api-guide">Read more</a> - خوب:
<a href="/security/api-guide">Read the API security guide</a>
دومی دقیقاً به کاربر میگوید که چه چیزی پیدا خواهد کرد. این کار به موتورهای جستجو درباره صفحه مقصد زمینه (context) میدهد. همچنین باعث میشود لیست لینکهای شما قابل پیمایش باشد. متن لینک توصیفی، یکی از ارزانترین دستاوردهای شما در زمینه قابلیت دسترسی است.
جدی تست کنید
اگر معماری را تأیید نکنید، هیچ معنایی ندارد.
اول، جریان کار با کیبورد را تست کنید. ماوس خود را جدا کنید. با کلید Tab از میان تمام عناصر تعاملی سایت خود عبور کنید. هر لینک واقعی باید یک کادر تمرکز (focus outline) قابل مشاهده نشان دهد؛ نه یک درخشش ملایم که در پسزمینه شما ناپدید میشود، بلکه یک حلقه واضح که یک چشم خسته بتواند آن را تشخیص دهد. کلید Enter را فشار دهید. باید لینک را فعال کند. اگر Tab از روی عنصری میپرد، یا اگر Enter کاری انجام نمیدهد، شما یک باگ دارید.
دوم، مسیرها (routes) را در سطح سرور تست کنید. مسیریابی سمت کلاینت (Client-side routing) تنها یک لایه ظاهری است. اگر کاربری /dashboard/reports را بوکمارک کند و فردا برگردد، یا صفحه را رفرش کند، سرور شما باید بداند چگونه آن صفحه را ارائه دهد. پروکسی معکوس (reverse proxy) یا فریمورک سرور خود را طوری تنظیم کنید که برای مسیرهای ناشناخته به پوسته اپلیکیشن (application shell) بازگردد، یا مستقیماً HTML صحیح را ارائه دهد. نمایش خطای 404 هنگام رفرش کردن، یک باگ کوچک نیست؛ بلکه یک وعده شکسته است.
جاوااسکریپت یک لایه قدرتمند است
