۲۳. کیفیت رابط؛ فوکوس، فارسیسازی و اندازهگیری کارایی
کدی که فقط با ماوس روی رایانهٔ نویسنده کار میکند، تحویل کامل نیست. در این درس کیفیت را به رفتار قابل مشاهده تبدیل میکنیم: پس از حذف ردیف فوکوس کجاست، کاربر خطا را چگونه میفهمد، مبلغ چگونه نمایش داده میشود و کندی از کدام بخش میآید؟ هدف اضافهکردن تزیین نیست؛ میخواهیم سه پروژه در استفادهٔ واقعی قابل فهمتر و قابل بررسیتر باشند.
بازسازی DOM نباید محل کاربر را گم کند
در سبد همراه، renderCart ردیفها را دوباره میسازد. دکمهای که فعال شده ممکن است دیگر همان گره قبلی نباشد؛ بنابراین فوکوس را پس از رسم به دکمهٔ متناظر برمیگردانیم. اگر کاهش تعداد، ردیف آخر را حذف کرده باشد، عنوان سبد مقصد جایگزین است. عنوان در HTML با tabindex=-1 قابل فوکوس برنامهای شده و وارد ترتیب عادی Tab نمیشود.
نمونهٔ زیر الگوی مستقلی است. تابع renderRows و ساختار داده در این مثال به پروژه اضافه نشدهاند؛ مقصود، ترتیب ثبت مقصد و بازگرداندن فوکوس است. در فایل واقعی app.mjs همین تصمیم را در handler تعداد سبد پیدا کنید و با آن مقایسه کنید.
// فرض: این عناصر از قبل در صفحهٔ تمرین وجود دارند.
const host = document.querySelector('#cart');
const fallback = document.querySelector('#cart-heading');
function restoreCartFocus(id, delta) {
const buttons = [...host.querySelectorAll('button[data-id][data-delta]')];
const destination = buttons.find(button =>
button.dataset.id === id && button.dataset.delta === String(delta)
);
(destination ?? fallback).focus();
}قبل از فراخوانی، DOM تازه باید ساخته شده باشد. انتخاب با جستوجوی dataset در این نمونه از چسباندن شناسهٔ دلخواه به selector جلوگیری میکند. صرف focusکردن body مقصد مفیدی نیست. فوکوس را هم بعد از هر رندر بیدلیل جابهجا نکنید؛ کاربر هنگام تایپ جستوجو نباید ناگهان از input خارج شود. تصمیم به نوع تعامل وابسته است، نه فقط تابع render.
نمایش فارسی با دادهٔ محاسباتی فرق دارد
در مدل، مبلغ تومان عدد صحیح است. هنگام نمایش میتوانیم رقمها و جداکننده را محلی کنیم، اما مقدار فرمتشده را دوباره جمع نمیزنیم. این نمونه در Node.js یا مرورگر اجرا میشود؛ فاصلهها و جهت نمایش ممکن است با محیط متفاوت باشد، بنابراین آزمون مالی را روی عدد خام بنویسید.
const amount = 240000;
const formatter = new Intl.NumberFormat('fa-IR');
console.log(formatter.format(amount) + ' تومان');
console.log(amount + 10000); // 250000
const user = '<کاربر آزمایشی>';
const output = document.createElement('p'); // بخش مرورگری نمونه
output.textContent = user + '؛ مجموع: ' + formatter.format(amount);در Node سه خط نخست را اجرا کنید؛ document فقط در مرورگر صفحه وجود دارد. تاریخ ورودی هزینه در این پروژه قالب استاندارد YYYY-MM-DD دارد و تقویم انتخابگر مرورگر ممکن است به زبان محیط وابسته باشد. نمایش فارسی تاریخ، به معنی تبدیل ذخیرهٔ استاندارد به رشتهٔ ظاهری نیست. قبل از افزودن تقویم جلالی، قرارداد تبدیل و روز نامعتبر را جدا آزمون کنید.
کندی را با یک پرسش مشخص بسنجید
برای کاتالوگ بزرگ میخواهیم بدانیم فیلتر و مرتبسازی چقدر هزینه دارد. نمونهٔ زیر را از یک فایل موقت در ریشهٔ بسته اجرا کنید. فقط مدل را اندازه میگیرد و شامل ساخت DOM، چیدمان و نقاشی صفحه نیست؛ این محدوده را در گزارش حفظ کنید.
import { searchProducts } from './projects/shop/model.mjs';
const rows = Array.from({ length: 1000 }, (_, i) => ({
id: 'p-' + i, name: 'کتاب ' + i, category: 'کتاب', price: i, stock: 1
}));
performance.mark('filter-start');
for (let i = 0; i < 100; i++) {
searchProducts(rows, { query: 'کتاب', sort: 'price' });
}
performance.mark('filter-end');
performance.measure('filter-100', 'filter-start', 'filter-end');
console.log(performance.getEntriesByName('filter-100')[0].duration);یک عدد روی رایانهٔ شما معیار عمومی سرعت نیست. چند بار با داده و نسخهٔ یکسان اجرا و توزیع نتیجه را مقایسه کنید. اگر هدف بررسی تایپ جستوجوست، در DevTools ضبط Performance از همان تعامل بگیرید و ببینید زمان در JavaScript مصرف میشود یا رندر. افزودن debounce ممکن است تعداد اجرا را کم کند، اما تأخیر احساسشده هم میافزاید؛ بدون اندازهگیری، «بهینهتر» نامیدن آن دقیق نیست.
تمرین، پاسخ راهنما و معیار تحویل
بدون ماوس وارد فروشگاه شوید، محصول اضافه کنید، تعداد را کاهش دهید تا ردیف حذف شود و سپس با Tab ادامه دهید. مقصد باید قابل پیشبینی و فوکوس دیدنی باشد. در مرکز تیکت نیز خطای نسخه را ایجاد و بررسی کنید آیا متن پیام، قدم بعدی را روشن میکند. پیام فقط نباید با تغییر رنگ منتقل شود؛ متن خوانا لازم است.
برای بخش کارایی، نتیجهٔ اندازهگیری مدل را با ضبط تعامل صفحه مقایسه کنید. پاسخ راهنما: تفاوت ممکن است از ساخت صدها گره و محاسبهٔ چیدمان بیاید، پس تغییر الگوریتم فیلتر لزوماً کندی رابط را حل نمیکند. تحویل شامل شرح دستگاه و مرورگر، دادهٔ آزمون، مسیر صفحهکلید و یک تصمیم مبتنی بر اندازهگیری است. این تمرین دستی است؛ سبزبودن npm test به معنی عبور از آن نیست.
منابع رسمی برای مطالعهٔ بیشتر
https://web.dev/articles/using-tabindex
https://developer.mozilla.org/en-US/docs/Web/API/Performance/measure
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat
تألیف و مثالهای اختصاصی: تحریریه درسآوند.