جاوااسکریپت | JavaScript
527 subscribers
704 photos
158 videos
4 files
566 links
کانال @IR_javascript حاوی اطلاعات مفید در حوزه برنامه نویس فرانت که بصورت روزانه بروز می‌شود.
در این کانال شما به:
[1] مطالب تازه
[2] تحلیل‌های عمیق
[3] نکات آموزشی
[4] چالش
[5] ابزار و راهنمایی‌های کاربردی
دسترسی خواهید داشت.

🆔@IR_javascript
Download Telegram
👏 <template> و <slot>

قهرمانان فراموش‌شدهٔ فرانت‌اند که شایستهٔ توجه هستند

همهٔ ما فریم‌ورک‌ها را می‌شناسیم و دوست داریم، اما باید قبول کنیم که مرورگرها می‌توانند کارهای بسیار بیشتری انجام دهند. دو عنصر که اغلب نادیده گرفته می‌شوند اما می‌توانند زندگی توسعه‌دهندگان را بسیار ساده‌تر کنند، <template> و <slot> هستند. این عناصر امکان ساخت «مینی‌کامپوننت‌ها» را مستقیماً در مرورگر فراهم می‌کنند، بدون نیاز به React، Vue یا Svelte.

❗️ <template>

عنصر <template> برای ذخیرهٔ بخش‌هایی از کد HTML استفاده می‌شود که تا زمانی که با جاوااسکریپت به صورت دستی درج نشوند، در صفحه نمایش داده نمی‌شوند. این ابزار ایده‌آل برای ایجاد بلوک‌های تکرارشوندهٔ HTML است.

<template id="card">
<div class="card">
<h3 class="title"></h3>
<p class="text"></p>
</div>
</template>

<script>
const tpl = document.querySelector('#card');
const clone = tpl.content.cloneNode(true);
clone.querySelector('.title').textContent = 'Hello';
document.body.appendChild(clone);
</script>


❗️ <slot>

عنصر <slot> درون کامپوننت‌های سفارشی برای درج HTML خارجی استفاده می‌شود، که اجازه می‌دهد کامپوننت‌های رابط کاربری کامل بدون فریم‌ورک‌های خارجی ایجاد شوند. این روش هم معنایی و دسترس‌پذیر است و هم سفارشی‌سازی کامپوننت‌ها را ساده می‌کند.

<my-button>
<span>Click me</span>
</my-button>

<script>
class MyButton extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<button class="btn">
<slot></slot>
</button>
`;
}
}
customElements.define('my-button', MyButton);
</script>


📌 مرورگرها اکنون آن‌قدر هوشمند شده‌اند که حتی فریم‌ورک‌های پیچیده هم لازم نیستند! همهٔ این‌ها به لطف Web Components، Islands، Partial Hydration و SSR امکان‌پذیر است. و استفاده از این دو عنصر <template> و <slot> باعث می‌شود بسیاری از مسائل که قبلاً تنها با فریم‌ورک‌های کوچک حل می‌شدند، حالا بدون بار اضافی و پیچیدگی قابل مدیریت باشند.

#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
2
ویژگی‌های منطقی در CSS — چگونه استایل‌ها را منعطف و جهانی کنیم

همیشه به حاشیه‌ها از بالا، راست، پایین و چپ فکر می‌کنیم. اما در دنیایی که جهت متن می‌تواند تغییر کند، این روش محدودکننده می‌شود. اینجا logical properties به کمک می‌آیند. آن‌ها اجازه می‌دهند حاشیه‌ها و اندازه‌ها را بر اساس جریان متن تعیین کنید، نه بر اساس سمت‌ها.

ℹ️ logical properties چیست و چه کاربردی دارند؟

inline جهت متن را مشخص می‌کند (مثلاً از چپ به راست یا راست به چپ)

block جهت بلوک‌ها را تعیین می‌کند، معمولاً از بالا به پایین، بسته به جریان سند

برای حاشیه‌های افقی از margin-inline-start و margin-inline-end استفاده می‌شود

برای حاشیه‌های عمودی از padding-block-start و padding-block-end

برای موقعیت و مرزها از border-inline و inset-block استفاده می‌شود

مزایا و کاربردها:

بین‌المللی‌سازی
در محصولاتی که چند زبان پشتیبانی می‌کنند، logical properties باعث صرفه‌جویی در زمان تنظیم استایل‌ها می‌شود.

طرح‌های قابل تغییر جهت
برای کامپوننت‌هایی که ممکن است «برگردانده» شوند (مثل آواتار سمت چپ/راست یا سایدبار)، logical properties نیاز به شرط‌نویسی در CSS را حذف می‌کند.

/* روش قدیمی */
.card {
padding-top: 12px;
padding-left: 16px;
}

/* با ویژگی‌های منطقی */
.card {
padding-block-start: 12px;
padding-inline-start: 16px;
}

/* خلاصهٔ کوتاه:
margin-inline — حاشیه‌های افقی
margin-block — حاشیه‌های عمودی
padding-inline — فاصله داخلی افقی
padding-block — فاصله داخلی عمودی
inset-inline — موقعیت افقی (چپ/راست)
inset-block — موقعیت عمودی (بالا/پایین) */




#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
📸 ویژگی‌های loading`، `decoding و fetchpriority — چگونه با ویژگی‌های بومی، بارگذاری تصاویر را سریع‌تر کنیم

سه ویژگی ساده و بومی HTML وجود دارند که می‌توانند بارگذاری تصاویر را بسیار سریع‌تر کرده و رابط کاربری شما را پاسخگوتر کنند. اگرچه تمام مرورگرهای مدرن از آن‌ها پشتیبانی می‌کنند، اما در عمل به ندرت استفاده می‌شوند. در ادامه هر یک را بررسی می‌کنیم:

⬅️ `loading="lazy"` — بارگذاری تنبل

ویژگی loading="lazy" باعث می‌شود مرورگر بارگذاری تصاویر خارج از دید کاربر را به تعویق بیندازد. این کار ترافیک کاربر را کاهش می‌دهد، زمان تا تعامل کامل (TTI) را کم می‌کند و فشار روی دستگاه‌های ضعیف را کاهش می‌دهد. از این ویژگی برای تمام تصاویر زیر اولین صفحه، کارت‌ها، فهرست‌ها و گالری‌ها استفاده کنید.
⚠️ اما برای تصاویر مهم در بالای صفحه، مانند لوگو یا تصاویر Hero، از این ویژگی استفاده نکنید تا از تأخیر در بارگذاری جلوگیری شود.

⚡️ `decoding="async"` — رمزگشایی غیرهمزمان

ویژگی async به مرورگر اجازه می‌دهد تصاویر را به صورت غیرهمزمان رمزگشایی کند**، بدون اینکه رندر سایر عناصر را متوقف کند. این کار باعث کاهش تأخیرهای بصری و افزایش سرعت بارگذاری صفحات می‌شود. به‌ویژه برای تصاویر بزرگ و عناصر در فیدها یا لیست‌ها مفید است و تجربهٔ کاربری روان‌تر را فراهم می‌کند.

🔫
fetchpriority="high | low" — مدیریت اولویت بارگذاری**

این ویژگی تعیین می‌کند که تصویر با چه اولویتی بارگذاری شود: high برای بارگذاری فوری و low برای بارگذاری با تأخیر. استفاده از آن باعث کاهش رقابت منابع، افزایش سرعت LCP و کاهش تأخیرهای بصری در رندر صفحه می‌شود.

👀 نمونه‌ها و نحوهٔ استفادهٔ ترکیبی

<!-- بارگذاری تنبل -->
<img src="hero.png" loading="lazy" />

<!-- رمزگشایی غیرهمزمان -->
<img src="photo.jpg" decoding="async" />

<!-- مدیریت اولویت بارگذاری -->
<img src="hero.jpg" fetchpriority="high" />
<img src="avatar.jpg" loading="lazy" fetchpriority="low" />

<!-- ترکیب همه ویژگی‌ها -->
<img src="hero.jpg" decoding="async" fetchpriority="high" />
<img src="thumb.jpg" loading="lazy" decoding="async" fetchpriority="low" />


استفاده از این سه ویژگی به‌صورت هوشمندانه می‌تواند سرعت بارگذاری تصاویر و تجربهٔ کاربری سایت شما را به طور قابل توجهی بهبود دهد.


#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
👍2
📣 همگام‌سازی وضعیت بین تب‌ها: BroadcastChannel، SharedWorker و localStorage

وقتی کاربر چند تب از یک اپلیکیشن را باز می‌کند، اغلب مشکل همگام‌سازی وضعیت پیش می‌آید. مثلاً کاربر در یک تب از حساب کاربری خارج می‌شود، اما در تب‌های دیگر اپلیکیشن همچنان او را وارد شده فرض می‌کند. در سال ۲۰۲۵ چند ابزار برای حل این مسئله داریم: BroadcastChannel**، **SharedWorker و برای مرورگرهای قدیمی، localStorage.

---

BroadcastChannel — راه‌حل ساده برای همگام‌سازی پیام‌ها

این API امکان ارسال و دریافت پیام‌ها بین تب‌ها را به صورت همزمان فراهم می‌کند. مناسب برای:

* اطلاع‌رسانی ورود/خروج
* انتخاب تم
* به‌روزرسانی کش
* اطلاع از نسخهٔ جدید اپلیکیشن

مزایا: ساده، سریع، کار در پس‌زمینه
محدودیت‌ها: وضعیت مشترک پیچیده را پشتیبانی نمی‌کند

// BroadcastChannel
const channel = new BroadcastChannel("session");

channel.onmessage = (e) => {
console.log("message:", e.data);
};

channel.postMessage({ loggedOut: true });


---

SharedWorker — وقتی همه تب‌ها مثل یک اپلیکیشن عمل می‌کنند

ابزاری برای کار متمرکز بین تب‌ها که امکان همگام‌سازی وضعیت، کشینگ و مدیریت درخواست‌ها را فراهم می‌کند. مناسب برای اپلیکیشن‌هایی که نیاز به هماهنگی بین تب‌ها دارند.

مزایا: ذخیرهٔ مرکزی وضعیت، اشتراک منابع، کاهش درخواست‌های تکراری API
محدودیت‌ها: در Safari پشتیبانی نمی‌شود، دیباگ پیچیده و نیاز به معماری حساب‌شده دارد

// main.js
const worker = new SharedWorker("worker.js");
worker.port.postMessage({ type: "ping" });
worker.port.onmessage = (e) => console.log(e.data);

// worker.js
onconnect = (e) => {
const port = e.ports[0];
port.onmessage = () => port.postMessage("pong");
};


---

LocalStorage + storage event — روش قدیمی ولی کارآمد

یکی از ساده‌ترین روش‌های همگام‌سازی داده‌ها، مناسب برای:

* مرورگرهای قدیمی
* پروژه‌هایی بدون ServiceWorker یا SharedWorker
* سناریوهای سادهٔ همگام‌سازی تب‌ها

مزایا: پیاده‌سازی ساده، سازگاری بالا
محدودیت‌ها: فقط رشته‌ها ذخیره می‌شوند، رویداد storage در تب تغییر دهنده رخ نمی‌دهد و ممکن است کندتر از BroadcastChannel باشد

// LocalStorage + storage event
window.addEventListener("storage", (e) => {
if (e.key === "logout") {
// همگام‌سازی وضعیت
}
});


---

📌 جمع‌بندی:

* برای اعلان‌ها و رویدادهای ورود/خروج → BroadcastChannel
* برای اشتراک وضعیت در PWA و داشبوردها → SharedWorker
* برای مرورگرهای قدیمی → localStorage + storage event

هر ابزار ویژگی‌های خاص خود را دارد و انتخاب آن بستگی به پیچیدگی پروژه، مرورگرهای هدف و نوع داده‌ها دارد.


#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
👍1
Media is too big
VIEW IN TELEGRAM
کش کردن ETag در HTTP یک مکانیزم برای اعتبارسنجی کش وب است.
در این ویدئو، خواهیم آموخت که ETag‌ها چه هستند و چگونه با هدرهای If-None-Match و If-Match و کدهای وضعیت HTTP ۳۰۴ و ۴۱۲ مرتبط می‌شوند.


🔗https://www.youtube.com/watch?v=tWu9lBlghOc
#️⃣#tip #dub
👥@IR_javascript_group
🆔@IR_javascript
🎭 چگونه یک API فرانت‌اند بدون دردسر بنویسیم

هنگام توسعهٔ API فرانت‌اند، مهم است که به یاد داشته باشیم این فقط یک لایهٔ نازک روی بک‌اند نیست. این یک قرارداد کامل است که مشخص می‌کند محصول چقدر کاربرپسند خواهد بود، تیم چگونه آن را درک می‌کند و چه مشکلاتی در آینده ممکن است رخ دهد. در ادامه یک چک‌لیست برای ساخت API ارائه می‌شود که نه‌تنها کار می‌کند، بلکه به‌راحتی قابل نگهداری است.

⬆️ پیش‌بینی‌پذیری API

یکی از بزرگ‌ترین مشکلات، عدم انسجام است. نام‌های متفاوت فیلدها، ساختارهای مختلف پاسخ یا کدهای خطا می‌توانند هر تلاش برای کار پایدار با API را خراب کنند. اگر توسعه‌دهنده با یکی از اندپوینت‌های شما کار کرده باشد، باید اطمینان داشته باشد که اندپوینت بعدی تقریباً مشابه خواهد بود. این موضوع به جلوگیری از مشکلات نگهداری و پیاده‌سازی سریع ویژگی‌های جدید کمک می‌کند.

⬆️ مستندسازی خودکار

مستندسازی دستی به‌سرعت منسوخ می‌شود. از ابزارهایی استفاده کنید که مستندسازی را به‌صورت خودکار تولید کنند. از OpenAPI یا Swagger، JSON Schema و تولید خودکار تایپ‌های TypeScript و کلاینت استفاده کنید. با این روش، API و مستندات همیشه به‌روز خواهند بود و فرانت‌اند پایدارتر خواهد شد.

⬆️ ثبات نوع داده‌ها

مشکلات زمانی آغاز می‌شوند که فیلدهای داده‌ای هم می‌توانند null باشند و هم ممکن است اصلاً وجود نداشته باشند. این موضوع رابط کاربری را خراب کرده و اپلیکیشن را ناپایدار می‌کند. برای مثال، بهتر است همیشه ساختار یکسانی با مقادیر صحیح داشته باشیم تا فیلدهای تصادفی با انواع دادهٔ متفاوت. اطمینان حاصل کنید که داده‌ها همیشه پایدار و پیش‌بینی‌پذیر باشند.

⬆️ از «مقدارهای جادویی» پرهیز کنید

نمونهٔ بد یک API: { "status": 3 }. این مقدار چه معنایی دارد؟ این قابل خواندن نیست. بهتر است از مقادیر قابل فهم استفاده کنید، مثل { "status": "blocked" }. این کار تعداد خطاها را کاهش می‌دهد، نگهداری و خواندن API را آسان‌تر می‌کند و فهم توسعه‌دهندگان را افزایش می‌دهد.

⬆️ خطاها فقط کد وضعیت نیستند

خطاها باید با دقت مدیریت شوند. کدهای خطای پایه مانند ۲۰۰ و ۵۰۰ برای عملکرد خوب کافی نیستند. به جای آن از کدهای خطای واضح همراه با توضیح استفاده کنید. برای مثال، اگر جلسه منقضی شده باشد، پاسخ دهید:

{
"error": {
"code": "INVALID_TOKEN",
"message": "Token expired"
}
}


این کار به بازیابی مؤثر جلسات، retry و بهبود تجربهٔ کاربری کمک می‌کند.

⬆️ هنگام نوشتن API به نیازهای فرانت‌اند توجه کنید:

• کاهش تعداد درخواست‌های کوچک
• قابلیت کش کردن
• داده‌های نرمال‌شده
• پاسخ‌های فشرده

فرانت‌اند دوست ندارد API پاسخ‌های حجیم با داده‌های اضافی بازگرداند. به صفحه‌بندی، batch کردن و ETag‌های پایدار برای منابع توجه کنید.

⬆️ همه چیز مورد نیاز برای کش کردن را فراهم کنید

‏API باید از کش پشتیبانی کند. از مکانیزم‌هایی مانند ETag**، **Cache-Control**، **Last-Modified و max-age استفاده کنید. این امکان را به فرانت‌اند می‌دهد که کش را به‌صورت مؤثر استفاده کرده، بار را کاهش دهد و سرعت را افزایش دهد.

⬆️ از over-fetching و under-fetching محافظت کنید

اطمینان حاصل کنید که API داده‌های اضافی (over-fetching) ارسال نمی‌کند، مثل زمانی که نیازی به نقش‌ها یا تنظیمات کاربران نیست. همچنین فرانت‌اند را مجبور نکنید چندین درخواست برای دریافت داده‌های مورد نیاز ارسال کند (under-fetching). از view model‌هایی استفاده کنید که برای صفحات خاص UI طراحی شده‌اند.

⬆️ نسخه‌بندی API را با دقت انجام دهید

نسخه‌بندی API باید هدفمند باشد. مسیر ساده با شماره نسخه‌ها مانند /v2 یا /v3 کافی نیست. اطمینان حاصل کنید که هر نسخه داخلی سازگار است و امکان بازگشت فراهم باشد. نسخه‌بندی باید پیش‌بینی‌پذیر و واضح باشد.

⬆️ یک لایۀ کلاینت ایجاد کنید

فرانت‌اند باید بتواند کارهایی مانند retry، لغو درخواست‌ها، debounce، timeout و مدیریت توکن را به‌صورت مستقل انجام دهد. این‌ها نباید مسئولیت بک‌اند باشند. یک لایۀ واحد API برای کلاینت با استفاده از custom fetch wrapper، axios instance یا TanStack Query ایجاد کنید. یک کلاس واحد برای همهٔ درخواست‌ها، رفتار پایدارتر و کنترل بهتر روی درخواست‌ها را تضمین می‌کند.

📌 یک API فرانت‌اند عالی فقط مجموعه‌ای از نقاط ورود نیست، بلکه یک سیستم کامل است که باید پیش‌بینی‌پذیر و پایدار باشد. بیایید APIهایی بسازیم که فرانت‌اند را خراب نکند و زندگی کل تیم را ساده‌تر کند.

#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
2
چطور رفتار برنامه را وقتی یک درخواست مشخص شکست می‌خورد بررسی کنیم؟ 🥲

وقتی تست می‌کنیم، معمولاً در مرورگر حالت «بدون اینترنت» را فعال می‌کنیم. این کار مفید است، اما همیشه واقع‌بینانه نیست؛ در دنیای واقعی معمولاً کل اتصال قطع نمی‌شود، بلکه فقط یک درخواست مشخص به API با خطا مواجه می‌شود. به همین دلیل، گاهی لازم است ببینیم برنامه دقیقاً در چنین شرایطی چگونه رفتار می‌کند. 😁

اینجاست که DevTools به کمک ما می‌آید. در Chrome می‌توان نه‌تنها کل شبکه را قطع کرد، بلکه یک درخواست مشخص را هم مسدود نمود:

✔️ DevTools را باز کنید → تب Network
✔️ درخواست موردنظر را پیدا کنید، روی آن راست‌کلیک کنید → Block request URL / مسدود کردن URL درخواست
✔️ صفحه را رفرش کنید یا عمل مربوطه را تکرار کنید و ببینید برنامه دقیقاً به شکست همین درخواست چگونه واکنش نشان می‌دهد

با این روش می‌توانید موارد زیر را تست کنید:

⏺️ سناریوهای fallback، وقتی بخشی از داده‌ها در دسترس نیست
⏺️ مدیریت خطاها و پیام‌ها یا نوتیفیکیشن‌هایی که به کاربر نمایش داده می‌شود
⏺️ وضعیت لودینگ و اسپینرها، تا مطمئن شوید رابط کاربری در مواجهه با مشکل شبکه رفتار درستی دارد

این کار به‌ویژه زمانی مفید است که سناریوی پیچیده‌ای دارید که به چند API وابسته است؛ می‌توانید چند درخواست را به‌صورت انتخابی مسدود کنید و خرابی‌های جزئی را شبیه‌سازی نمایید. چیزی شبیه یک استرس‌تست هدفمند برای سناریوهای خاص، بدون اینکه لازم باشد اتصال همهٔ سرویس‌ها را به‌طور کامل قطع کنید.

📌 یک عادت کوچک که می‌تواند کیفیت محصول را بالا ببرد:
به‌جای اینکه فقط حالت «آفلاین» را تست کنید، شکست خوردن درخواست‌های تکی را هم بررسی کنید — این حالت به مشکلات واقعی‌ای که کاربر با آن‌ها مواجه می‌شود بسیار نزدیک‌تر است.


#️⃣#tool
👥@IR_javascript_group
🆔@IR_javascript
👍1
Cumulative Layout Shift:
متریکی است که نشان می‌دهد عناصر صفحه پس از اولین رندر، با چه فرکانسی و تا چه حد به‌صورت غیرمنتظره جابه‌جا می‌شوند. مرورگر برای هر جابه‌جایی محاسبه می‌کند چه بخشی از صفحه تحت تأثیر قرار گرفته و عناصر چه مسافتی حرکت کرده‌اند، سپس این مقادیر را با هم جمع می‌زند. در نتیجه، CLS نه سرعت بارگذاری، بلکه پایداری رابط کاربری در طول زمان را اندازه‌گیری می‌کند.

📚 از کجا می‌آید؟
اگر در لحظهٔ اولین محاسبهٔ layout، مرورگر نداند یک عنصر چه فضایی را اشغال خواهد کرد، برای آن فضایی رزرو نمی‌کند. وقتی آن عنصر ظاهر می‌شود یا اندازه‌اش تغییر می‌کند، مرورگر مجبور است چیدمان صفحه را دوباره محاسبه کند و کاربر نتیجه را به‌صورت یک «پرش» می‌بیند.
تصاویر: اصلی‌ترین و ساده‌ترین مورد
وقتی یک <img> بدون ابعاد از پیش تعیین‌شده بارگذاری می‌شود، مرورگر نمی‌تواند ارتفاع آن را محاسبه کند. وقتی تصویر ظاهر می‌شود، تمام محتوای زیر آن تغییر مکان می‌دهد.
راه حل: از قبل به مرورگر اطلاع دهید - عرض و ارتفاع واقعی را مشخص کنید.
آنها اندازه ذاتی (n نسبت ابعاد) را تنظیم می‌کنند
<img
src="/img/hero.jpg"
width="1200"
height="675"
alt=""
/>

همه تصاویر در تگ‌های <img> قرار نمی‌گیرند. تصاویر پس‌زمینه، کارت‌ها و اجزای پویا اغلب از طریق CSS یا جاوا اسکریپت می‌آیند. در این موارد، عرض، ارتفاع و سایر ویژگی‌ها نقش دارند.
نسبت ابعاد را در نظر می‌گیرد.
.media {
}
aspect-ratio: 16/9;
width: 100%;


فونت‌ها: منبع نادیده گرفته شده‌ی تغییرات
حتی با تصاویر بی‌نقص، CLS می‌تواند به دلیل فونت‌ها باقی بماند. وقتی صفحه لود می‌شود:
۱. مرورگر هنوز فونت اصلی سایت را دانلود نکرده
۲. باید تصمیم بگیرد:
متن را نشان بدهد یا
صبر کند تا فونت دانلود شود؟
اگر بعداً فونت اصلی برسد و جای فونت موقت (fallback) را بگیرد، اندازه‌ی حروف و فاصله‌ی خطوط تغییر می‌کند چیدمان صفحه می‌پرد (CLS)
چه چیزهایی کمک می‌کند:
1-استفاده از swap

font-display: swap;

// متن را فوراً نشان بده
// اول با یک فونت موقت (fallback)
// وقتی فونت اصلی آمد، آن را جایگزین کن

2-استفاده از فونت‌های fallback با متریک‌های نزدیک به فونت اصلی
3-پری لود کردن فونت‌ها فقط در موارد واقعاً بحرانی و ضروری
@font-face {
font-family: MyFont;
src: url(myfont.woff2) format("woff2");
font-display: swap;
}


تبلیغات، iframe و ویجت‌ها

بلوک‌های تبلیغاتی و iframeها معمولاً به‌صورت ناهمگام بارگذاری می‌شوند و بعد از اولین رندر ظاهر می‌گردند. اگر از قبل فضایی برای آن‌ها در نظر گرفته نشده باشد، با ورودشان محتوا را به پایین «هل می‌دهند» و باعث جابه‌جایی چیدمان می‌شوند.

قاعدهٔ کاربردی بسیار ساده است:
هر بلوک خارجی یا ناهمگام باید از پیش یک اسلات مشخص داشته باشد؛
چه با ارتفاع ثابت، چه با aspect-ratio، یا با استفاده از placeholder / skeleton.


#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
‏tree-shaking در فونت‌ها

همان‌طور که فایل‌های بزرگ JavaScript به چند فایل کوچک‌تر تقسیم می‌شوند و فقط در صورت نیاز بارگذاری می‌گردند، فایل‌های فونت هم می‌توانند به زیرمجموعه‌های کوچک‌تر شکسته شوند. به این کار font subsetting یا «تقسیم فونت به زیرمجموعه‌ها» گفته می‌شود.

تعداد گلیف‌ها در یک فونت به زبان‌هایی بستگی دارد که آن فونت پشتیبانی می‌کند. بعضی فونت‌ها تقریباً تمام کاراکترهای یونیکد را در خود دارند، در حالی که فونت‌هایی که فقط زبان انگلیسی را پوشش می‌دهند، مجموعهٔ بسیار محدودتری از نمادها را شامل می‌شوند. هرچه تعداد گلیف‌ها بیشتر باشد، حجم فایل فونت هم بزرگ‌تر خواهد بود.

در سایت‌های فارسی‌زبان (و روسی‌زبان)، معمولاً فقط از حروف سیریلیک یا فارسی، اعداد و نشانه‌های اصلی نگارشی استفاده می‌شود. در زبان‌هایی مثل اوکراینی، بلاروسی و سایر زبان‌های اسلاوی، علاوه بر حروف اصلی سیریلیک، تعدادی نماد از «سیریلیک توسعه‌یافته» هم به کار می‌رود.

کاراکترهای لاتین معمولاً استفادهٔ بسیار محدودی دارند؛ چه برسد به نمادهای گروه CJK (چینی، ژاپنی، کره‌ای) یا سایر زبان‌ها. این دقیقاً همان مشکلی است که در مورد کد JavaScript استفاده‌نشده داریم و با code splitting و tree-shaking حل می‌شود. برای فونت‌ها هم می‌توان همین رویکرد را به کار گرفت.

به‌جای یک فونت بزرگ با مجموعه‌ای عظیم از نمادها، می‌توان آن را به چند فونت کوچک‌تر تقسیم کرد:

* فونت سیریلیک پایه: حروف اصلی سیریلیک، اعداد، نشانه‌های رایج نگارشی؛ می‌توان نقل‌قول‌های تایپوگرافیک، خط تیرهٔ بلند، سه‌نقطه و موارد مشابه را هم اضافه کرد؛
* فونت سیریلیک توسعه‌یافته: حروف و نمادهای خاص (علامت‌های دیاکریتیک) برای زبان‌های اوکراینی، بلاروسی، بلغاری، صربی و دیگر زبان‌ها؛
* فونت لاتین پایه: حروف اصلی الفبای لاتین؛
* فونت لاتین توسعه‌یافته: حروف و نمادهای خاص (دیاکریتیک‌ها) برای زبان‌هایی مثل آلمانی، لهستانی، لیتوانیایی، ترکی و غیره؛
* سایر زیرمجموعه‌ها در صورت نیاز.

در یک سایت فارسی یا روسی‌زبان، حروف لاتین معمولاً برای اصطلاحات، نام‌ها و ابزارهای ترجمه لازم می‌شوند. خود من سایت‌های انگلیسی‌زبان را می‌بینم و گاهی از قابلیت ترجمهٔ صفحه استفاده می‌کنم؛ خارجی‌ها هم همین کار را می‌کنند. خوشایند نیست که به‌جای فونت طراحی‌شدهٔ سایت، ناگهان Times New Roman نمایش داده شود.

---

## روش‌های تقسیم فونت

چند راه برای این کار وجود دارد. ساده‌ترین روش استفاده از Google Fonts است؛
نه به این معنا که فونت را مستقیم از آن سرویس لود کنید (که توصیه نمی‌شود)، بلکه برای گرفتن زیرمجموعه‌های آماده:

۱. فونت موردنظر را در Google Fonts پیدا می‌کنید؛
۲. لینک تولیدشده داخل <link rel="stylesheet"> را کپی می‌کنید؛
۳. لینک را در یک تب جدید باز می‌کنید؛ مجموعه‌ای از دستورهای @font-face می‌بینید؛
۴. زیرمجموعه‌های موردنیاز (که با کامنت مشخص شده‌اند) را کپی می‌کنید؛
۵. فایل‌های فونت مربوطه را از آدرس‌های src دانلود می‌کنید؛
۶. دستورهای @font-face را به پروژه اضافه کرده و لینک‌ها را به فایل‌های دانلودشده تغییر می‌دهید.

روش دیگر استفاده از ابزار آنلاین Font Subsetting است. در این روش، فونت را آپلود می‌کنید، نمادها یا بازه‌های یونیکد موردنظر را مشخص می‌کنید و خروجیِ فونتی را می‌گیرید که فقط شامل همان کاراکترهاست.

روش سوم استفاده از ابزار خط فرمان glyphhanger است که جزئیات آن در مقالهٔ زک لِدِرمن توضیح داده شده است. (https://www.zachleat.com/web/glyphhanger/)

---

## نقش unicode-range

برای اینکه مرورگر بداند چه زمانی و کدام زیرمجموعهٔ فونت را بارگذاری کند، باید بداند هر فونت شامل چه کاراکترهایی است. این اطلاعات با ویژگی unicode-range در دستور @font-face مشخص می‌شود:

/* cyrillic */
@font-face {
/* ... */
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}


ویژگی unicode-range فهرستی از کاراکترها را در قالب یونیکد مشخص می‌کند. می‌توان:

* یک نماد مشخص را تعیین کرد (مثلاً U+2116 که همان علامت شماره «№» است)،
* یا یک بازه را مشخص نمود (مثلاً U+0410-044F از حرف «А» تا «я»).

فهرست کامل مقادیر یونیکد را می‌توان در جدول‌های یونیکد مشاهده کرد.

هنگام بارگذاری صفحه، مرورگر تمام کاراکترهای موجود را بررسی می‌کند. اگر حتی یک کاراکتر از عناصر استفاده‌کنندهٔ فونت، داخل بازهٔ unicode-range باشد، آن فونت دانلود می‌شود. اگر هیچ‌کدام از کاراکترهای صفحه در آن بازه نباشند، فونت اصلاً بارگذاری نخواهد شد.


#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
👏 رویکردی نوین برای کار با تم‌ها در CSS با استفاده از ‎@property

پیش‌تر توسعه‌دهندگان ناچار بودند با جاوااسکریپت و استایل‌ها مستقیماً در کد سروکله بزنند. اما اکنون مرورگرها از ‎@property‎ پشتیبانی می‌کنند؛ قابلیتی که امکان ساخت متغیرهای CSS با نوع مشخص، مقدار پیش‌فرض و حتی انیمیشن را فراهم می‌کند.

📝 نمونه‌های پیاده‌سازی:

/* @property چیست؟ */
@property --theme-hue {
syntax: "<number>";
inherits: true;
initial-value: 120;
}

/* تم‌دهی بدون JavaScript */
@property --bg {
syntax: "<color>";
inherits: true;
initial-value: #fff;
}

:root {
--bg: #fff;
--text: #000;
}

[data-theme="dark"] {
--bg: #000;
--text: #fff;
}

body {
background: var(--bg);
color: var(--text);
transition: background .3s, color .3s;
}

/* متغیرهای قابل انیمیشن */
@property --hue {
syntax: "<number>";
inherits: true;
initial-value: 200;
}

body {
background: hsl(var(--hue) 80% 50%);
transition: --hue 0.4s ease;
}

body.dark {
--hue: 320;
}

/* تم‌دهی پویا بدون بازنویسی CSS
با @property می‌توان به‌راحتی مجموعه‌ای از متغیرها را برای تغییر پویای تم‌ها مدیریت کرد */
@property --radius {
syntax: "<length>";
initial-value: 4px;
}

.card {
border-radius: var(--radius);
}

/* اکنون می‌توان میزان گردی گوشه‌ها را در لحظه و با کلاس‌های ساده تغییر داد */
:root.compact {
--radius: 2px;
}

:root.rounded {
--radius: 12px;
}


❗️@property چیست؟

‎CSS @property‎ روشی است برای تعریف متغیرهای CSS به‌عنوان یک ویژگی کامل. این قابلیت امکان تعیین نوع مشخص، مقدار پیش‌فرض و حتی اعمال انیمیشن را فراهم می‌کند. با این رویکرد، متغیر نه‌تنها تایپ‌دار می‌شود، بلکه به‌درستی به ارث می‌رسد، بخشی از منطق انیمیشن‌پذیر می‌شود و در صورت دریافت مقادیر نادرست دچار اختلال نخواهد شد.

— تم‌دهی بدون JavaScript: با استفاده از ‎@property‎ می‌توان تم‌ها را بدون استفاده از جاوااسکریپت تغییر داد. کافی است از ویژگی ‎data-theme="dark"‎ در تگ ‎html‎ استفاده شود تا همه‌چیز به‌صورت نیتیو انیمیت شود.

— متغیرهای قابل انیمیشن: پیش از این، متغیرهای CSS از انیمیشن پشتیبانی نمی‌کردند، اما با ‎@property‎ حتی تم‌های پیچیده نیز قابل انیمیشن هستند. اکنون می‌توان ‎transition‎ را مستقیماً روی متغیر اعمال کرد تا تغییرات به‌صورت روان انجام شود و ساخت تم‌های پویا ساده‌تر گردد.

— تایپ‌گذاری و باگ‌های کمتر: یکی از مشکلات اصلی پیش از ‎@property‎ این بود که مرورگر مقادیر نادرست را بی‌سروصدا می‌پذیرفت. برای مثال ‎--hue: red;‎. بدون ‎@property‎ این مقدار صرفاً نادیده گرفته می‌شد. اما با ‎@property‎، در صورت نامعتبر بودن مقدار، ویژگی به مقدار پیش‌فرض خود بازمی‌گردد.

— دیزاین توکن‌های نیتیو و کامل: با ‎@property‎ می‌توان توکن‌هایی برای جنبه‌های مختلف طراحی مانند رنگ‌ها، فاصله‌ها، مقیاس‌بندی، انیمیشن‌ها و تایپوگرافی ایجاد کرد؛ آن هم بدون نیاز به ابزارهای بیلد.

📌 در سال دوهزار و بیست و پنج، پشتیبانی مرورگرها از ‎@property‎ بسیار مطلوب است و مرورگرهایی مانند Chrome، Edge، Safari و Firefox آن را پشتیبانی می‌کنند؛ بنابراین می‌توان با خیال راحت از این قابلیت در محیط پروداکشن استفاده کرد. اگر هنوز از روش‌های قدیمی همراه با جاوااسکریپت زیاد و راه‌حل‌های موقتی استفاده می‌کنید، وقت آن رسیده که رویکرد خود را به‌روز کنید.


#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
1👍1
فرض کنید مشغول پیاده‌سازی صفحه‌ای هستید که پر است از بلوک‌های جاسازی‌شده، آکاردئون‌ها، تب‌ها و دیگر عناصر تعاملی که منتظر اقدام کاربرند.

اما آکاردئون‌ها یک ایراد آشکار دارند: جست‌وجوی درون‌صفحه، محتوای پنهان را هایلایت نمی‌کند؛ چرا که المان‌ها مخفی‌اند.

از همین‌رو، کروم از نسخه‌ی ۱۰۲ به بعد برای ویژگی hidden مقدار منحصربه‌فردی تحت عنوان until-found در نظر گرفته که معنایش روشن است: «پنهان بمان تا زمانی که کاربر به‌دنبالش بگردد».

پست وبلاگ توسعه‌دهندگان:
https://developer.chrome.com/docs/css-ui/hidden-until-found

مثالِ آماده، برای آنان که حوصله‌ی خواندن مقاله را ندارند:
https://codepen.io/web-dot-dev/pen/JjMxmom

کافی‌ست کلمه‌ای را که در یکی از آکاردئون‌ها پنهان شده جست‌وجو کنید.

برای مرورگرهایی که این ویژگی را نمی‌شناسند یا برای ویجت‌های رابط کاربری دیگر، پیشنهاد می‌کنند از پشتیبانی رویداد onbeforematch مطمئن شوید: https://developer.mozilla.org/en-US/docs/Web/API/Element/beforematch_event

اگر ('onbeforematch' در document.body وجود نداشت) {
// همه‌ی محتوای پنهان را گسترش بده
}


راه‌حلی نسبتاً عجیب است، اما واضح است که مشکل وجود دارد و باید چاره‌ای اندیشید.

#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
1
⚡️ OffscreenCanvas: چگونه انتقال رندر به Worker باعث بهبود عملکرد می‌شود

وقتی صحبت از عملکرد فرانت‌اند به میان می‌آید، مشکل اغلب در JavaScript کند نیست، بلکه در نخ اصلی است؛ نخی که هم‌زمان رویدادها را پردازش می‌کند، layout را محاسبه می‌کند، رابط کاربری را می‌کشد و canvas را رندر می‌کند. OffscreenCanvas راهکاری است که امکان انتقال رندر به یک نخ جداگانه را فراهم می‌کند؛ در نتیجه نخ اصلی آزاد می‌شود و عملکرد کلی برنامه بهبود می‌یابد.

❗️ OffscreenCanvas چیست؟

‏OffscreenCanvas یک canvas است که به DOM متصل نیست. می‌توان آن را در نخ اصلی ایجاد کرد، به Web Worker منتقل نمود و بدون مسدود کردن رابط کاربری در آن رسم انجام داد. این کار باعث می‌شود نخ اصلی برای پردازش رویدادها، انیمیشن‌ها و رندر layout و paint آزاد بماند و منابع خود را صرف پردازش گرافیک نکند.

❗️ چه زمانی باید از OffscreenCanvas استفاده کرد؟

رندر در یک نخ جداگانه به‌ویژه برای وظایف پیچیده‌ای مانند گرافیک و بصری‌سازی بسیار مفید است. برای مثال، بازی‌ها، شبیه‌سازی‌ها، نمودارهای پیچیده یا سیستم‌های ذره‌ای می‌توانند به‌طور قابل‌توجهی از OffscreenCanvas سود ببرند، زیرا این کار از افت FPS که معمولاً هنگام رندر در نخ اصلی رخ می‌دهد جلوگیری می‌کند. همچنین این راهکار برای کار با حجم بالای داده‌ها، مانند heatmapها، timelineها یا نمودارها و بصری‌سازی‌های بلادرنگ با به‌روزرسانی‌های مداوم، گزینه‌ای مناسب است.

❗️ چرا این روش سریع‌تر است؟

مهم‌ترین مزیت OffscreenCanvas عدم مسدود شدن نخ اصلی است. canvas دیگر باعث مسدود شدن reflow و repaint نمی‌شود؛ مسائلی که اغلب منجر به تأخیر و افت FPS می‌شوند. انتقال رندر به یک نخ جداگانه امکان رندر موازی و FPS پایدار را فراهم می‌کند؛ موضوعی که به‌خصوص روی دستگاه‌های ضعیف‌تر به‌وضوح قابل مشاهده است.

❗️ محدودیت‌های OffscreenCanvas

چند محدودیت وجود دارد که باید در نظر گرفته شوند. نخست این‌که پشتیبانی مرورگرها محدود است: OffscreenCanvas در مرورگرهای مبتنی بر Chromium و Firefox در دسترس است و در Safari پشتیبانی آن ناقص و همراه با ظرافت‌ها و محدودیت‌هایی است. بنابراین همیشه باید برای مرورگرهای ناسازگار یک fallback به canvas معمولی در نظر گرفت.

دوم این‌که هنگام کار با Worker امکان تعامل مستقیم با DOM وجود ندارد و تمام داده‌ها باید از طریق پیام‌ها منتقل شوند. این موضوع مستلزم تغییر در معماری برنامه است. همچنین باید توجه داشت که اشکال‌زدایی چنین راهکارهایی دشوارتر است، زیرا منطق رندر بین نخ‌ها تقسیم می‌شود و در تب استاندارد Canvas در DevTools نیز به‌طور کامل پشتیبانی نمی‌شود.

❗️ چه زمانی نباید از OffscreenCanvas استفاده کرد؟

اگر پروژهٔ شما مشکل جدی از نظر عملکرد ندارد و رندر به‌ندرت انجام می‌شود، استفاده از OffscreenCanvas می‌تواند غیرضروری باشد. این راهکار بیشتر برای مسائل پیچیده با نیازهای بالای عملکرد مناسب است؛ برای مثال، محاسبات سنگین گرافیکی و بصری‌سازی‌هایی که به FPS پایدار نیاز دارند.

📝 چگونه از OffscreenCanvas استفاده کنیم؟

// تمام رندر در یک نخ جداگانه انجام می‌شود و نخ اصلی برای پردازش UI و تعامل با کاربر آزاد می‌ماند.
// در نخ اصلی یک canvas معمولی می‌سازیم و آن را به worker منتقل می‌کنیم:
const canvas = document.querySelector('canvas')
const offscreen = canvas.transferControlToOffscreen()
worker.postMessage({ canvas: offscreen }, [offscreen])

// در worker، canvas را دریافت می‌کنیم و شروع به رسم می‌کنیم:
self.onmessage = ({ data }) => {
const ctx = data.canvas.getContext('2d')
ctx.fillRect(0, 0, 100, 100)
}


📌 OffscreenCanvas ابزار فوق‌العاده‌ای برای کار با گرافیک در مرورگر است. این قابلیت باعث می‌شود همه‌چیز سریع‌تر انجام شود، به‌ویژه زمانی که با تصاویر پیچیده یا بازی‌ها سر و کار دارید. اگر در پروژهٔ شما مشکلات FPS به وجود آمده یا رسم گرافیک دشوار شده است، کافی است رندر را به Worker منتقل کنید. این یکی از بهترین روش‌ها برای بهبود عملکرد است، بدون آن‌که مجبور باشید کل برنامه را از نو بازنویسی کنید.
واحد های اندازه گیری CSS (قسمت پنجم)

واحدهای نسبی جدید CSS: واحدهای کانتینری (Container Units) برای رابط‌های کاربری تطبیقی

در CSS به‌صورت سنتی برای پیاده‌سازی رابط‌های واکنش‌گرا از واحدهایی مانند vw و vh استفاده می‌شود. بااین‌حال، این واحدها در لِی‌اوت‌های پیچیده کارایی لازم را ندارند. مدیاکوئری‌ها، راه‌حل‌های موقتی و استفاده از درصدها تا حدی این مشکل را برطرف می‌کنند، اما با معرفی Container Queries و Container Units، بسیاری از این پیچیدگی‌ها عملاً به گذشته تعلق دارند.

واحدهای Container Units چیستند؟

‏Container Units واحدهای اندازه‌گیری‌ای هستند که بر اساس ابعاد یک کانتینر محاسبه می‌شوند؛ کانتینری که با استفاده از ویژگی container-type تعریف شده است. این رویکرد به ما اجازه می‌دهد تطبیق‌پذیری را با دقت و راحتی بسیار بیشتری پیاده‌سازی کنیم و به‌جای تکیه بر اندازه کل صفحه، ابعاد واقعی هر کامپوننت درون کانتینر خودش را در نظر بگیریم.

مهم‌ترین این واحدها عبارت‌اند از:

• cqw — معادل یک درصد از عرض کانتینر
• cqh — معادل یک درصد از ارتفاع کانتینر
• cqi — معادل یک درصد از اندازه inline کانتینر (وابسته به writing-mode)
• cqb — معادل یک درصد از اندازه block کانتینر
• cqmin و cqmax — به‌ترتیب حداقل و حداکثر ابعاد کانتینر

چرا این موضوع اهمیت دارد؟

• استقلال کامپوننت‌ها
کامپوننت‌ها می‌توانند بدون وابستگی به context کلی صفحه، به‌صورت مستقل تطبیق پیدا کنند. مهم نیست در کجا استفاده شوند؛ آن‌ها خودشان را با اندازه کانتینر والد وفق می‌دهند.

• کاهش نیاز به مدیاکوئری‌ها
به‌جای نوشتن تعداد زیادی مدیاکوئری، می‌توان به‌سادگی از @container به‌همراه cqw استفاده کرد. این کار هم حجم کد را کمتر می‌کند و هم خوانایی آن را بهبود می‌بخشد.

• حذف بسیاری از هک‌های جاوااسکریپتی
دیگر نیازی نیست برای محاسبه ابعاد و تنظیم تایپوگرافی یا فاصله‌ها از ResizeObserver استفاده شود. اکنون می‌توان همه این کارها را مستقیماً با CSS انجام داد.

نکات مهم و ظریف

— کانتینر حتماً باید تعریف شود؛ برای مثال با container-type: inline-size.
— این واحدها فقط درون کانتینر عمل می‌کنند، بنابراین انتخاب نوع مناسب کانتینر اهمیت زیادی دارد.
— واحدهای cqi و cqb به‌ویژه زمانی بسیار مفید هستند که نیاز به پشتیبانی از writing-mode عمودی یا رابط‌های چندزبانه و بین‌المللی دارید.

چه زمانی باید از Container Units استفاده کرد؟

— در طراحی سیستم‌های طراحی (Design System) و کامپوننت‌های قابل استفاده مجدد.
— در لِی‌اوت‌های پیچیده مانند داشبوردها یا ویرایشگرها.
— در معماری‌های میکروفِرانت‌اند، جایی که کامپوننت‌ها باید کاملاً مستقل از یکدیگر باشند.

📝 مثال‌ها:

/* در این مثال، اندازه فونت .card__title به عرض کانتینر .card وابسته است، نه به عرض کل صفحه. */
.card {
container-type: inline-size;
}

.card__title {
font-size: 4cqw;
}

/* تایپوگرافی */
.title {
font-size: clamp(16px, 3cqw, 24px);
}

/* فاصله‌گذاری */
.card {
padding: 4cqi;
}


📌 تطبیق‌پذیری مبتنی بر viewport دیگر متعلق به دیروز است. Container Units گامی جدید به‌سوی تفکر کامپوننت‌محور در CSS محسوب می‌شوند. حالا کامپوننت‌های شما به صفحه وابسته نیستند و رفتاری قابل پیش‌بینی دارند؛ انگار CSS هوشمندتر شده، نه پیچیده‌تر.


#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
👍21
چه زمانی باید اولویت بارگذاری را مشخص کنیم؟ 🤔

لود تنبل (‎loading="lazy"‎) بسیار کاربردی است: با به‌تعویق‌انداختن بارگذاری تصاویری که هنوز در دید کاربر نیستند، هم در مصرف ترافیک صرفه‌جویی می‌کند و هم سرعت بارگذاری صفحه را افزایش می‌دهد.

اما اگر برای تصاویر حیاتی اولویت مشخص نکنید، مرورگر خودش درباره ترتیب بارگذاری تصمیم می‌گیرد — و این تصمیم همیشه با اهداف شما هم‌راستا نیست. ☹️ در نتیجه ممکن است تصویر اصلی یا هدر دیرتر از حد لازم لود شود؛ این موضوع LCP را تضعیف می‌کند و حس «کند بودن» صفحه را به کاربر منتقل می‌کند. اگر ابعاد تصویر هم از قبل مشخص نشده باشند، حتی می‌تواند به CLS منجر شود.

🧩 چه کار باید کرد؟

✔️ برای تصاویر غیرحیاتی — با خیال راحت از ‎loading="lazy"‎ استفاده کنید. هم ترافیک را کاهش می‌دهد و هم به UX آسیبی نمی‌زند.

✔️ برای تصاویر حیاتی در اسکرین اول — فقط به ‎loading="lazy"‎ تکیه نکنید:
   یا ‎loading="lazy"‎ را حذف کنید،
   یا اولویت را صراحتاً مشخص کنید (‎fetchpriority="high"‎)،
   یا منبع را پیش‌بارگذاری کنید (‎<link rel="preload" as="image" href="…">‎).

✔️ همیشه ‎width‎ و ‎height‎ را مشخص کنید یا از ‎aspect-ratio‎ استفاده کنید — این کار جلوی CLS را می‌گیرد.

✔️ از ‎srcset‎ و ‎sizes‎ (و فرمت‌های مدرن مثل WebP و AVIF) استفاده کنید — تا مرورگر بتواند بهترین فایل را متناسب با دستگاه انتخاب کند.

✔️ LCP را در شرایط واقعی بررسی کنید — تست‌های لوکال همیشه رفتار واقعی در ذکر را بازتاب نمی‌دهند.

🧩 مثال:

<!-- تصویر مهم -->
<link rel="preload" as="image" href="/image.jpg">
<img src="/image.jpg" width="1200" height="600" fetchpriority="high" alt="...">

<!-- تصویر غیرحیاتی -->
<img src="/thumb.jpg" loading="lazy" width="400" height="300" alt="...">


🧩 چند اشتباه رایج:

⏺️ اعمال ‎loading="lazy"‎ روی همه تصاویر بدون استثنا — و از دست دادن کنترل روی LCP؛
⏺️ تکیه صرف بر ‎fetchpriority‎ و فراموش‌کردن ‎preload‎ و فرمت‌های تطبیقی؛
⏺️ پیش‌بارگذاری تعداد زیادی منبع — که می‌تواند کل فرایند بارگذاری را بدتر کند.

در نهایت، لود تنبل را باید به‌صورت گزینشی استفاده کرد. برای تصاویر اسکرین اول، اولویت را صریحاً مشخص کنید (با ‎preload‎ یا ‎fetchpriority‎) یا اصلاً آن‌ها را تنبل لود نکنید؛ برای بقیه تصاویر، با خیال راحت از لود تنبل استفاده کنید. به این شکل هم در مصرف ترافیک صرفه‌جویی می‌کنید و هم تجربه‌ای سریع و قابل‌پیش‌بینی به کاربر می‌دهید. 👍

#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
2👍1
پردازش ترکیب‌های کلیدی 🧑‍💻

از ترکیب‌های کلیدی برای کارهای رایج استفاده می‌شود: ذخیره داده‌ها، جابه‌جایی بین صفحات، یا افزایش سرعت کار. در JavaScript پیاده‌سازی این موضوع نسبتاً ساده است، به شرطی که بدانیم رویدادهای صفحه‌کلید چگونه کار می‌کنند.

کمی تئوری

✔️ ترکیب کلید در واقع یک رویداد واحد ‎keydown‎ است که داخل آن بررسی می‌کنیم:
  ⏺️ کدام کلید فشرده شده است؛
  ⏺️ آیا کلیدهای کمکی (Ctrl، Shift، Alt، Meta) نگه داشته شده‌اند یا نه؛

✔️ در مرورگر رویداد جداگانه‌ای با نام «Ctrl+S» وجود ندارد — همه‌چیز با شرط‌گذاری انجام می‌شود.

ویژگی‌های مهم رویداد

✔️ ‎event.key‎ — کاراکتر یا نام کلید (مثل "a" یا "Enter"
✔️ ‎event.code‎ — کلید فیزیکی روی کیبورد (مثل "KeyA" یا "Enter"
✔️ ‎event.ctrlKey‎، ‎event.shiftKey‎، ‎event.altKey‎، ‎event.iss.onetaKey‎ — کلیدهای کمکی.

مثال ساده: ‎Ctrl + S‎

document.addEventListener('keydown', (event) => {
if (event.ctrlKey && event.code === 'KeyS') {
event.preventDefault(); // در صورت نیاز، رفتار پیش‌فرض را لغو می‌کنیم
// منطق موردنظر ما
}
});


به چه نکاتی باید توجه کرد؟

هنگام تایپ در ‎input‎ و ‎textarea‎، هات‌کی‌ها را رهگیری نکنید؛
در macOS معمولاً به‌جای ‎Ctrl‎ از ‎Meta‎ (کلید Cmd) استفاده می‌شود؛
نگه‌داشتن یک کلید باعث تکرار چندباره رویداد ‎keydown‎ می‌شود؛
برای هات‌کی‌ها بهتر است از ‎event.code‎ استفاده کنید تا به چیدمان کیبورد وابسته نباشید.

و در نهایت، در استفاده از هات‌کی‌های سراسری زیاده‌روی نکنید — آن‌ها باید مکمل رابط کاربری باشند، نه جایگزین آن. 😁


#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
This media is not supported in your browser
VIEW IN TELEGRAM
ساده‌ترین روش برای غیرفعال کردن چندین عنصر فرم

می‌توانیم ویژگی «غیرفعال» را روی عنصر <fieldset> قرار دهیم تا به‌طور خودکار تمامی عناصر فرم تو در تو غیرفعال شوند.

#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
👍1
This media is not supported in your browser
VIEW IN TELEGRAM
تشخیص زمانی که کاربر بین تب‌های مرورگر جابه‌جا می‌شود و تغییر favicon

این ترفند را می‌توان با استفاده از Page Visibility API پیاده‌سازی کرد.

Page Visibility API یک رابط برنامه‌نویسی مرورگر است که قابل مشاهده بودن صفحه را بررسی می‌کند. این API کمک می‌کند تا تشخیص دهیم آیا صفحه‌ی جاری پنهان یا کوچک شده است و از این طریق امکان کنترل رفتار صفحه و مدیریت استفاده از منابع فراهم می‌شود.

#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
This media is not supported in your browser
VIEW IN TELEGRAM
نود و نه درصد توسعه‌دهندگان از این ترفند در Chrome DevTools خبر ندارند 🤯

می‌توانید تم تاریک را روی هر سایتی فعال کنید، حتی اگر آن سایت خود تم تاریک نداشته باشد. برای این کار، DevTools را باز کرده، روی آیکون قلم‌مو کلیک کنید و گزینه‌ی Automatic Dark Mode را انتخاب نمایید.

این روش برای زمانی که در حال طراحی تم تاریک برای وب‌سایت خود هستید ایده‌آل است — شما فوراً پیش‌نمایشی از رابط کاربری تقریباً نود درصدی دریافت می‌کنید.

#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
2