### Visual Viewport API: ردیابی بزرگنمایی و تغییرات ناحیهی دید 🤩
اگر تا به حال به این فکر کردهاید که چطور میتوان تغییرات در بزرگنمایی (zoom) صفحه یا واکنش به تغییر ابعاد پنجره در دستگاههای موبایل را شناسایی کرد، خبر خوبی برایتان دارم — من بهتازگی با API جالبی به نام Visual Viewport API آشنا شدم! 🎉
**Visual Viewport API** ابزاری است که به شما امکان میدهد بفهمید کاربر در حال حاضر سایت شما را *چگونه میبیند* و بتوانید رابط کاربری را متناسب با اقدامات او تنظیم کنید.
این API به شما دسترسی میدهد به ناحیهی دید بصری — یعنی آن بخش از صفحه که در لحظه روی نمایشگر دیده میشود، نه کل محتوای صفحه.
---
### با Visual Viewport API چه اطلاعاتی میتوان بهدست آورد؟
#### 🔍 ردیابی بزرگنمایی (Zoom)
از طریق ویژگی
---
#### 📏 تغییر اندازهی ناحیهی دید
زمانیکه اندازهی بخش قابلمشاهدهی صفحه تغییر میکند (برای مثال، هنگام بزرگنمایی یا چرخش صفحهنمایش)، میتوان از
---
#### ➡️ تشخیص جابهجایی ناحیهی دید
با استفاده از
---
### پشتیبانی از رویدادهای
این API از رویدادهای
---
### ✅ جمعبندی
با استفاده از Visual Viewport API میتوانید رابطهایی طراحی کنید که نسبت به رفتارهای کاربر مانند بزرگنمایی یا پیمایش (scroll) واکنش نشان دهند. این ویژگی بهویژه برای طراحی رابطهای موبایل بسیار ارزشمند است. 🙂
—
دموی این قابلیت را میتوانید در این لینک مشاهده کنید:
🔗 مشاهده کد در CodePen
#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
اگر تا به حال به این فکر کردهاید که چطور میتوان تغییرات در بزرگنمایی (zoom) صفحه یا واکنش به تغییر ابعاد پنجره در دستگاههای موبایل را شناسایی کرد، خبر خوبی برایتان دارم — من بهتازگی با API جالبی به نام Visual Viewport API آشنا شدم! 🎉
**Visual Viewport API** ابزاری است که به شما امکان میدهد بفهمید کاربر در حال حاضر سایت شما را *چگونه میبیند* و بتوانید رابط کاربری را متناسب با اقدامات او تنظیم کنید.
این API به شما دسترسی میدهد به ناحیهی دید بصری — یعنی آن بخش از صفحه که در لحظه روی نمایشگر دیده میشود، نه کل محتوای صفحه.
---
### با Visual Viewport API چه اطلاعاتی میتوان بهدست آورد؟
#### 🔍 ردیابی بزرگنمایی (Zoom)
از طریق ویژگی
window.visualViewport.scale
میتوان تغییرات در سطح بزرگنمایی صفحه را شناسایی کرد. هر زمان کاربر صفحه را بزرگتر یا کوچکتر کند، این مقدار بهروز میشود.---
#### 📏 تغییر اندازهی ناحیهی دید
زمانیکه اندازهی بخش قابلمشاهدهی صفحه تغییر میکند (برای مثال، هنگام بزرگنمایی یا چرخش صفحهنمایش)، میتوان از
window.visualViewport.width
و window.visualViewport.height
برای دریافت ابعاد جدید استفاده کرد. این کار به تطبیق بهتر رابط کاربری با اندازههای مختلف کمک میکند.---
#### ➡️ تشخیص جابهجایی ناحیهی دید
با استفاده از
window.visualViewport.offsetLeft
و window.visualViewport.offsetTop
میتوان جابهجایی ناحیهی دید را نسبت به ابتدای صفحه پیگیری کرد. این قابلیت برای جایگذاری دقیق عناصر روی صفحه بسیار کاربردی است.---
### پشتیبانی از رویدادهای
resize
و scroll
این API از رویدادهای
resize
و scroll
نیز پشتیبانی میکند، که هنگام تغییر وضعیت ناحیهی دید فعال میشوند. این موضوع به شما امکان میدهد محتوای رابط کاربری را *بهصورت لحظهای* بهروزرسانی کنید — قابلیتی ایدهآل برای رابطهای پویا و تعاملی.---
### ✅ جمعبندی
با استفاده از Visual Viewport API میتوانید رابطهایی طراحی کنید که نسبت به رفتارهای کاربر مانند بزرگنمایی یا پیمایش (scroll) واکنش نشان دهند. این ویژگی بهویژه برای طراحی رابطهای موبایل بسیار ارزشمند است. 🙂
—
دموی این قابلیت را میتوانید در این لینک مشاهده کنید:
🔗 مشاهده کد در CodePen
#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
codepen.io
Untitled
...
یکی از ویژگیهای CSS برای جدولها که اغلب نادیده گرفته میشود، اما میتواند بهطور چشمگیری کار شما را سادهتر کند:
##
این ویژگی تعیین میکند مرورگر چگونه ابعاد ستونها و سطرهای یک جدول را محاسبه کند.
###
با استفاده از این ویژگی، میتوان سرعت رندر جدول را افزایش داد و ظاهر آن را تحت کنترل قرار داد.
در حالت پیشفرض، مرورگر اندازهی ستونها را بر اساس محتوای درون آنها محاسبه میکند. این کار ممکن است مخصوصاً در جدولهای بزرگ، فرآیند رندر را کند کند.
اما با کمک
---
### مقادیر قابل استفاده برای
✔️
ابعاد ستونها بهصورت خودکار و بر اساس محتوای آنها تعیین میشود.
مناسب برای جدولهایی با محتوای پویا، اما ممکن است باعث کاهش سرعت رندر شود.
✔️
همهی ستونها دارای عرض ثابت خواهند بود، بدون توجه به محتوای داخلی آنها.
این گزینه رندر جدول را سریعتر کرده و کارایی را بهبود میبخشد، زیرا مرورگر از ابتدا از اندازههای تعیینشده استفاده میکند.
---
اگر جدول شما شامل دادههای زیادی است یا به ستونهایی با عرض یکسان نیاز دارید، استفاده از
🔗 [نمونه در CodePen](https://codepen.io/katrin_profrontend/pen/zxvKRPo)
#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
##
table-layout
— کلیدی برای بهینهسازی جدولهااین ویژگی تعیین میکند مرورگر چگونه ابعاد ستونها و سطرهای یک جدول را محاسبه کند.
###
table-layout
دقیقاً چه میکند؟با استفاده از این ویژگی، میتوان سرعت رندر جدول را افزایش داد و ظاهر آن را تحت کنترل قرار داد.
در حالت پیشفرض، مرورگر اندازهی ستونها را بر اساس محتوای درون آنها محاسبه میکند. این کار ممکن است مخصوصاً در جدولهای بزرگ، فرآیند رندر را کند کند.
اما با کمک
table-layout
میتوان ابعاد ستونها را بهصورت ثابت تعیین کرد تا مرورگر دیگر نیازی به محاسبهی آنها نداشته باشد و از همان ابتدا، از اندازههای مشخصشده استفاده کند.---
### مقادیر قابل استفاده برای
table-layout
:✔️
auto
(مقدار پیشفرض):ابعاد ستونها بهصورت خودکار و بر اساس محتوای آنها تعیین میشود.
مناسب برای جدولهایی با محتوای پویا، اما ممکن است باعث کاهش سرعت رندر شود.
✔️
fixed
:همهی ستونها دارای عرض ثابت خواهند بود، بدون توجه به محتوای داخلی آنها.
این گزینه رندر جدول را سریعتر کرده و کارایی را بهبود میبخشد، زیرا مرورگر از ابتدا از اندازههای تعیینشده استفاده میکند.
---
اگر جدول شما شامل دادههای زیادی است یا به ستونهایی با عرض یکسان نیاز دارید، استفاده از
table-layout: fixed
بهترین انتخاب خواهد بود. 👍🔗 [نمونه در CodePen](https://codepen.io/katrin_profrontend/pen/zxvKRPo)
#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
codepen.io
table-layout
...
❤3
### بهبود عملکرد رویدادهای پیمایش (Scroll) 👨🏫
بهتازگی با این هشدار مواجه شدم:
> "\[Violation] Added non-passive event listener to a scroll-blocking 'wheel' event. Consider marking event handler as 'passive' to make the page more responsive."
این پیام باعث شد رویکردم به مدیریت رویدادهای پیمایش را بازبینی کنم.
زمانی که با رویدادهایی مانند پیمایش با ماوس (
بهطور پیشفرض، مرورگرها میتوانند رفتار استاندارد (مانند پیمایش) را مسدود کنند، بهویژه اگر در تابع پردازشگر از
این مسأله ممکن است باعث تأخیر در واکنش رابط کاربری**، خصوصاً در دستگاههای همراه شود. 😔
---
### راهحل: استفاده از گزینهی `{ passive: true }` 🤨
با افزودن گزینهی `{ passive: true }` هنگام افزودن شنوندهی رویداد (Event Listener)، به مرورگر اطلاع میدهیم که قرار نیست رفتار پیشفرض را لغو کنیم.
در نتیجه، مرورگر میتواند بلافاصله اقدام به انجام رفتار استاندارد (مثل پیمایش) کند و منتظر اجرای کامل کد شما نماند.
```javascript
element.addEventListener('wheel', (event) => {
// کد مربوطه
}, { passive: true });
```
---
### چرا این موضوع اهمیت دارد؟
✔️ **افزایش سرعت واکنش رویدادها
مرورگر بلافاصله پیمایش را آغاز میکند و این باعث بهبود پاسخگویی رابط کاربری میشود — خصوصاً در تلفنهای هوشمند و تبلتها.
✔️ عدم انسداد رندر شدن صفحه
با استفاده از
---
### چند نکتهی مهم:
۱️⃣ مقدار پیشفرض `passive` برابر با false است
یعنی در حالت عادی میتوان از
اما مرورگرها در مورد برخی رویدادها مانند
۲️⃣ اگر شما `passive: true` را صراحتاً تعیین کنید و در تابع مربوطه از
---
### جمعبندی:
استفاده از گزینهی
#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
بهتازگی با این هشدار مواجه شدم:
> "\[Violation] Added non-passive event listener to a scroll-blocking 'wheel' event. Consider marking event handler as 'passive' to make the page more responsive."
این پیام باعث شد رویکردم به مدیریت رویدادهای پیمایش را بازبینی کنم.
زمانی که با رویدادهایی مانند پیمایش با ماوس (
wheel
) یا حرکت لمسی (touchmove
) سروکار داریم، معمولاً چالشهایی در زمینهی عملکرد (Performance) به وجود میآید.بهطور پیشفرض، مرورگرها میتوانند رفتار استاندارد (مانند پیمایش) را مسدود کنند، بهویژه اگر در تابع پردازشگر از
event.preventDefault()
استفاده شود.این مسأله ممکن است باعث تأخیر در واکنش رابط کاربری**، خصوصاً در دستگاههای همراه شود. 😔
---
### راهحل: استفاده از گزینهی `{ passive: true }` 🤨
با افزودن گزینهی `{ passive: true }` هنگام افزودن شنوندهی رویداد (Event Listener)، به مرورگر اطلاع میدهیم که قرار نیست رفتار پیشفرض را لغو کنیم.
در نتیجه، مرورگر میتواند بلافاصله اقدام به انجام رفتار استاندارد (مثل پیمایش) کند و منتظر اجرای کامل کد شما نماند.
```javascript
element.addEventListener('wheel', (event) => {
// کد مربوطه
}, { passive: true });
```
---
### چرا این موضوع اهمیت دارد؟
✔️ **افزایش سرعت واکنش رویدادها
مرورگر بلافاصله پیمایش را آغاز میکند و این باعث بهبود پاسخگویی رابط کاربری میشود — خصوصاً در تلفنهای هوشمند و تبلتها.
✔️ عدم انسداد رندر شدن صفحه
با استفاده از
{ passive: true }
مرورگر دیگر منتظر اجرای کامل تابع پردازشگر نمیماند، که این موضوع باعث کاهش تأخیر و افزایش روانی حرکت صفحه میشود.---
### چند نکتهی مهم:
۱️⃣ مقدار پیشفرض `passive` برابر با false است
یعنی در حالت عادی میتوان از
event.preventDefault()
برای لغو رفتار پیشفرض استفاده کرد.اما مرورگرها در مورد برخی رویدادها مانند
wheel`، `touchmove
و touchstart
در سطوحی مثل window`، `document
و `document.body`، بهصورت خودکار passive را برابر با true قرار میدهند تا از ایجاد تاخیر جلوگیری شود.۲️⃣ اگر شما `passive: true` را صراحتاً تعیین کنید و در تابع مربوطه از
event.preventDefault()
استفاده نمایید، مرورگر این دستور را نادیده خواهد گرفت.---
### جمعبندی:
استفاده از گزینهی
{ passive: true }
یک تغییر ساده اما مؤثر برای بهبود روانی و واکنشپذیری اپلیکیشن است — بهویژه در سناریوهایی که با پیمایش و تعامل لمسی سروکار داریم. ✅#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
🔥1
استفاده از await در سطح بالای ماژولهای ES
نوشتن کدهای ناهمگام (asynchronous) در جاوااسکریپت تا چندی پیش یک محدودیت مهم داشت: کلیدواژهی await تنها درون یک تابع async قابل استفاده بود. این موضوع با معرفی ویژگی Top-Level Await در استاندارد ES2022 تغییر کرد؛ قابلیتی مدرن در ماژولهای ES که امکان اجرای الگوهای جدیدی از کد ناهمگام را در سطح ماژول فراهم میکند.
Top-Level Await چیست؟
بهطور سنتی، دستور await تنها درون توابع async معتبر بود:
اکنون با استفاده از Top-Level Await، میتوانید آن را در سطح بالای ماژول ES اجرا کنید:
در گذشته برای استفاده از await در سطح بالا، ناچار بودید آن را درون یک تابع ناهمگام خوداجرا (IIFE) بپیچید:
با Top-Level Await میتوانید با چنین کدهای زائد خداحافظی کنید و منطق ناهمگام خود را بهصورت مستقیم در بالاترین سطح ماژول بنویسید؛ کدی سادهتر، خواناتر و تمیزتر.
⚠️ نکته مهم
Top-Level Await فقط در ماژولهای جاوااسکریپت پشتیبانی میشود، نه در CommonJS (یعنی require) و نه در تگهای <script> سنتی که فاقد ویژگی type="module" هستند (زیرا این حالت در «مد کلاسیک» اجرا میشود). همچنین، فایلهای با پسوند .cjs در Node.js از آن پشتیبانی نمیکنند.
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
نوشتن کدهای ناهمگام (asynchronous) در جاوااسکریپت تا چندی پیش یک محدودیت مهم داشت: کلیدواژهی await تنها درون یک تابع async قابل استفاده بود. این موضوع با معرفی ویژگی Top-Level Await در استاندارد ES2022 تغییر کرد؛ قابلیتی مدرن در ماژولهای ES که امکان اجرای الگوهای جدیدی از کد ناهمگام را در سطح ماژول فراهم میکند.
Top-Level Await چیست؟
بهطور سنتی، دستور await تنها درون توابع async معتبر بود:
// ❌ خطای نحوی (SyntaxError) در خارج از تابع async
const result = await fetchData();
اکنون با استفاده از Top-Level Await، میتوانید آن را در سطح بالای ماژول ES اجرا کنید:
// ✅ این کد در یک ماژول ES قابل اجرا است
const result = await fetchData();
در گذشته برای استفاده از await در سطح بالا، ناچار بودید آن را درون یک تابع ناهمگام خوداجرا (IIFE) بپیچید:
(async () => {
const result = await fetchData();
})();
با Top-Level Await میتوانید با چنین کدهای زائد خداحافظی کنید و منطق ناهمگام خود را بهصورت مستقیم در بالاترین سطح ماژول بنویسید؛ کدی سادهتر، خواناتر و تمیزتر.
⚠️ نکته مهم
Top-Level Await فقط در ماژولهای جاوااسکریپت پشتیبانی میشود، نه در CommonJS (یعنی require) و نه در تگهای <script> سنتی که فاقد ویژگی type="module" هستند (زیرا این حالت در «مد کلاسیک» اجرا میشود). همچنین، فایلهای با پسوند .cjs در Node.js از آن پشتیبانی نمیکنند.
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
شکستن کلمات همراه با خط تیره 😉
موضوع ساده به نظر میرسد — وقتی یک کلمه طولانی در یک خط جا نمیشود، باید آن را با خط تیره به خط بعد منتقل کرد. اما چطور این کار را بهدرستی انجام دهیم تا هم ظاهر متن زیبا باشد و هم ساختار آن بههم نریزد؟
بیایید بررسی کنیم برای این کار در CSS چه نیاز داریم. ☺️
برای شکستن صحیح کلمات همراه با خط تیره، سه ویژگی اصلی مورد نیاز است:
✔️ overflow-wrap: break-word; — این ویژگی باعث میشود کلمات طولانی (مثل نشانیهای اینترنتی یا اصطلاحات پیچیده) در صورت کمبود فضا به خط بعد منتقل شوند.
✔️ word-break: break-word; — این ویژگی اجازه میدهد کلمات در نقاط مناسب نیز شکسته شوند، اگر در یک خط جا نشوند.
✔️ hyphens: auto; — مسئول اضافه کردن خودکار خط تیره هنگام شکستن کلمات است، بر اساس قواعد زبان مورد استفاده.
نمونه کد:
حالا همهچیز طبق قواعد زبان کار خواهد کرد: کلمات همراه با خط تیره شکسته میشوند و در عین حال متن، منظم و زیبا باقی میماند. 👍
#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
موضوع ساده به نظر میرسد — وقتی یک کلمه طولانی در یک خط جا نمیشود، باید آن را با خط تیره به خط بعد منتقل کرد. اما چطور این کار را بهدرستی انجام دهیم تا هم ظاهر متن زیبا باشد و هم ساختار آن بههم نریزد؟
بیایید بررسی کنیم برای این کار در CSS چه نیاز داریم. ☺️
برای شکستن صحیح کلمات همراه با خط تیره، سه ویژگی اصلی مورد نیاز است:
✔️ overflow-wrap: break-word; — این ویژگی باعث میشود کلمات طولانی (مثل نشانیهای اینترنتی یا اصطلاحات پیچیده) در صورت کمبود فضا به خط بعد منتقل شوند.
✔️ word-break: break-word; — این ویژگی اجازه میدهد کلمات در نقاط مناسب نیز شکسته شوند، اگر در یک خط جا نشوند.
✔️ hyphens: auto; — مسئول اضافه کردن خودکار خط تیره هنگام شکستن کلمات است، بر اساس قواعد زبان مورد استفاده.
نمونه کد:
.text {
overflow-wrap: break-word; /* اجازه شکستن کلمات طولانی */
word-break: break-word; /* شکستن کلمات در نقاط مناسب */
hyphens: auto; /* افزودن خودکار خط تیره هنگام شکستن */
}
حالا همهچیز طبق قواعد زبان کار خواهد کرد: کلمات همراه با خط تیره شکسته میشوند و در عین حال متن، منظم و زیبا باقی میماند. 👍
#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
یادآوری دربارهی `.setProperty()` و متغیرهای CSS 😉
همهی ما متد `.setProperty()` میشناسیم و معمولاً زمانی به یادش میافتیم که لازم باشد یک ویژگی CSS را مستقیماً از طریق JavaScript تغییر دهیم.
اما بهراحتی میتوان فراموش کرد که با همین متد میتوان متغیرهای CSS را نیز تغییر داد — و این در بسیاری موارد بسیار کارآمدتر است.
برای نمونه، یک متغیر سراسری در `:root`:
و در JavaScript:
اکنون تمام عناصری که مقدار
کجا استفاده از متغیرها از طریق `.setProperty()` بیشتر به چشم میآید؟
✔️ انیمیشنها — تنها با تغییر یک متغیر، تمام استایلهای مرتبط بهصورت روان و از طریق CSS transition بهروز میشوند.
✔️ استفاده در content — میتوان متن یا نمادی را در یک متغیر ذخیره کرد و آن را بهصورت پویا تغییر داد.
در نتیجه، `.setProperty()` تنها برای تغییر ویژگیها نیست، بلکه ابزاری قدرتمند برای مدیریت متغیرهای CSS نیز بهشمار میرود ⚡️
#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
همهی ما متد `.setProperty()` میشناسیم و معمولاً زمانی به یادش میافتیم که لازم باشد یک ویژگی CSS را مستقیماً از طریق JavaScript تغییر دهیم.
اما بهراحتی میتوان فراموش کرد که با همین متد میتوان متغیرهای CSS را نیز تغییر داد — و این در بسیاری موارد بسیار کارآمدتر است.
برای نمونه، یک متغیر سراسری در `:root`:
:root {
--main-color: #333;
}
و در JavaScript:
// تغییر خود متغیر، نه ویژگی مستقیم
document.documentElement.style.setProperty('--main-color', '#ff6600');
اکنون تمام عناصری که مقدار
color: var(--main-color)
دارند، بهطور خودکار مقدار جدید را دریافت میکنند. 👍کجا استفاده از متغیرها از طریق `.setProperty()` بیشتر به چشم میآید؟
✔️ انیمیشنها — تنها با تغییر یک متغیر، تمام استایلهای مرتبط بهصورت روان و از طریق CSS transition بهروز میشوند.
✔️ استفاده در content — میتوان متن یا نمادی را در یک متغیر ذخیره کرد و آن را بهصورت پویا تغییر داد.
.btn::after {
content: var(--btn-label);
}
btn.style.setProperty('--btn-label', '"✓"');
در نتیجه، `.setProperty()` تنها برای تغییر ویژگیها نیست، بلکه ابزاری قدرتمند برای مدیریت متغیرهای CSS نیز بهشمار میرود ⚡️
#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
👍1
من همیشه فکر میکردم توسعهی فرانتاند بهمراتب دشوارتر از بکاند است، اما توضیح این موضوع برای سایر توسعهدهندگان همیشه سخت بود. امروز خواستم نظر یک مدل زبانی (که بهنوعی نمایندهی «نظر کلی کارشناسان» است) را بررسی کنم.
نتیجه جالب بود:
توسعهی فرانتاند برای انسانها سختتر است، زیرا بکاند بیشتر ماهیتی ریاضی و مبتنی بر قواعد دارد و درک آن چندان دشوار نیست؛ همهچیز با فرمولها و الگوهای قابل پیشبینی پیش میرود.
اما فرانتاند؟ اینجا قیاس بینظیری مطرح شد: زیستشناسی!
🔹 مقیاسپذیری بکاند: شبیه ریاضیات؛ با افزودن نودهای بیشتر، شارد کردن پایگاه داده یا بهینهسازی کوئریها، سیستم طبق فرمولها و قوانین قابلپیشبینی رشد میکند.
🔹 تغییرات فرانتاند: شبیه زیستشناسی؛ جهشهای کوچک (مثل دستگاه جدید، ویژگی تازهی مرورگر یا نیاز متفاوت در تجربهی کاربری) رفتاری نوظهور، اغلب آشفته و موردی ایجاد میکنند که باید تکبهتک با آنها سازگار شد.
زیستشناسی بسیار پیچیدهتر از ریاضیات است — زیرا ریاضیات قانونمند و قطعی است، اما زیستشناسی پدیدهای نوظهور، پر از استثنا، وابسته به زمینه و همواره در حال تغییر.
به همین دلیل است که فرانتاند (شبیه زیستشناسی) برای انسانها بهمراتب شلوغتر و دشوارتر به نظر میرسد، حتی اگر بکاند (شبیه ریاضیات) از نظر انتزاعی ژرفتر باشد.
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
نتیجه جالب بود:
توسعهی فرانتاند برای انسانها سختتر است، زیرا بکاند بیشتر ماهیتی ریاضی و مبتنی بر قواعد دارد و درک آن چندان دشوار نیست؛ همهچیز با فرمولها و الگوهای قابل پیشبینی پیش میرود.
اما فرانتاند؟ اینجا قیاس بینظیری مطرح شد: زیستشناسی!
🔹 مقیاسپذیری بکاند: شبیه ریاضیات؛ با افزودن نودهای بیشتر، شارد کردن پایگاه داده یا بهینهسازی کوئریها، سیستم طبق فرمولها و قوانین قابلپیشبینی رشد میکند.
🔹 تغییرات فرانتاند: شبیه زیستشناسی؛ جهشهای کوچک (مثل دستگاه جدید، ویژگی تازهی مرورگر یا نیاز متفاوت در تجربهی کاربری) رفتاری نوظهور، اغلب آشفته و موردی ایجاد میکنند که باید تکبهتک با آنها سازگار شد.
زیستشناسی بسیار پیچیدهتر از ریاضیات است — زیرا ریاضیات قانونمند و قطعی است، اما زیستشناسی پدیدهای نوظهور، پر از استثنا، وابسته به زمینه و همواره در حال تغییر.
به همین دلیل است که فرانتاند (شبیه زیستشناسی) برای انسانها بهمراتب شلوغتر و دشوارتر به نظر میرسد، حتی اگر بکاند (شبیه ریاضیات) از نظر انتزاعی ژرفتر باشد.
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
🤔1
استفاده از `distinctUntilChanged` و `debounceTime`: بهینهسازی جستجو 👩💻
وقتی فرم ایجاد میکنیم، مهم است که از ارسال درخواستهای غیرضروری به سرور جلوگیری کنیم. دو اپراتور RxJS به نامهای `debounceTime` و `distinctUntilChanged` در این زمینه به ما کمک میکنند:
* ✔️
* ✔️
### مثال:
### عملکرد هر اپراتور:
⭐️
⭐️
---
✅ نتیجه: ترکیب این دو اپراتور به شما کمک میکند کنترل بهتری روی درخواستها در فرمهای جستجو داشته باشید، عملکرد را بهبود بخشید و بار روی سرور را کاهش دهید. 😉
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
وقتی فرم ایجاد میکنیم، مهم است که از ارسال درخواستهای غیرضروری به سرور جلوگیری کنیم. دو اپراتور RxJS به نامهای `debounceTime` و `distinctUntilChanged` در این زمینه به ما کمک میکنند:
* ✔️
debounceTime
اجرای عملیات را با تأخیر انجام میدهد.* ✔️
distinctUntilChanged
مقادیر تکراری را نادیده میگیرد.### مثال:
this.searchForm.controls.searchField.valueChanges
.pipe(
debounceTime(300), // صبر میکنیم ۳۰۰ میلیثانیه بعد از آخرین ورودی
distinctUntilChanged() // مقادیر تکراری را نادیده میگیریم
)
.subscribe(searchTerm => {
console.log('درخواست به سرور:', searchTerm);
this.searchData(searchTerm); // ارسال درخواست
});
### عملکرد هر اپراتور:
⭐️
debounceTime(300)
— ارسال درخواست را ۳۰۰ میلیثانیه بعد از آخرین ورودی به تأخیر میاندازد. این کار باعث میشود برای هر کاراکتر یک درخواست ارسال نشود، که مخصوصاً هنگام تایپ سریع بسیار مفید است.⭐️
distinctUntilChanged()
— تضمین میکند که درخواست تنها وقتی مقدار تغییر کند ارسال شود. اگر کاربر همان عبارت جستجو را دوباره وارد کند، درخواست تکراری ارسال نخواهد شد.---
✅ نتیجه: ترکیب این دو اپراتور به شما کمک میکند کنترل بهتری روی درخواستها در فرمهای جستجو داشته باشید، عملکرد را بهبود بخشید و بار روی سرور را کاهش دهید. 😉
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
نحوه تنظیم Autocomplete با گزینههای محدود، ولی با امکان وارد کردن مقادیر دلخواه در TypeScript ❓
اخیراً با یک چالش جالب مواجه شدم: میخواستم یک نوع (Type) بسازم که هم بتواند هر رشتهای را بپذیرد، و هم برای چند مقدار از پیش تعریفشده رفتاری خاص داشته باشد.
معمولاً این کار با
### راهکار: استفاده از
این تکنیک اجازه میدهد نوعی تعریف کنیم که انعطافپذیر باشد و همزمان در Autocomplete گزینههای مشخص را پیشنهاد دهد.
#### مثال:
### توضیح:
*
* با این حال، Autocomplete تنها
* دلیلش این است که ترکیب
---
✅ نتیجه: این روش برای مواقعی مناسب است که میخواهید انعطاف نوع را حفظ کنید و همزمان به راحتی بتوانید گزینههای از پیش تعریفشده را در Autocomplete نمایش دهید. 👍
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
اخیراً با یک چالش جالب مواجه شدم: میخواستم یک نوع (Type) بسازم که هم بتواند هر رشتهای را بپذیرد، و هم برای چند مقدار از پیش تعریفشده رفتاری خاص داشته باشد.
معمولاً این کار با
enum
یا اشیاء حل میشد، اما یک روش شیکتر هم وجود دارد! ❔### راهکار: استفاده از
string & {}
در انتهای Unionاین تکنیک اجازه میدهد نوعی تعریف کنیم که انعطافپذیر باشد و همزمان در Autocomplete گزینههای مشخص را پیشنهاد دهد.
#### مثال:
type MyType = 'Option1' | 'Option2' | string & {};
### توضیح:
*
MyType
میتواند هر رشتهای را بپذیرد.* با این حال، Autocomplete تنها
'Option1'
و 'Option2'
را پیشنهاد میدهد.* دلیلش این است که ترکیب
string & {}
امکان استفاده از رشتهها را حفظ میکند ولی پیشنهادها محدود به مقادیر مشخص شده در Union میماند.---
✅ نتیجه: این روش برای مواقعی مناسب است که میخواهید انعطاف نوع را حفظ کنید و همزمان به راحتی بتوانید گزینههای از پیش تعریفشده را در Autocomplete نمایش دهید. 👍
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
اختصارات منطقی در انتسابها در JavaScript 🤔
ECMAScript ۲۰۲۱ سه اپراتور مفید را اضافه کرده است:
✔️ انتساب
✔️ انتساب
✔️ انتساب
‼️ نکته مهم: چرا
به طور خلاصه: اپراتورهای اختصاری مقدار سمت چپ انتساب (LHS) را فقط یک بار محاسبه میکنند، در حالی که حالت
👩💻 مثال کمینه و قابل بازتولید:
➡️ در عبارت
نتیجه:
اپراتورهای
پیشنهاد استفاده:
⏺️
⏺️
⏺️
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
ECMAScript ۲۰۲۱ سه اپراتور مفید را اضافه کرده است:
✔️ انتساب
x ||= y
— اگر x
مقدار «فالس» داشته باشد → `x = y`؛✔️ انتساب
x ??= y
— اگر x
مقدار null
یا undefined
داشته باشد → x = y
(صفر، رشته خالی و false
حفظ میشوند)؛✔️ انتساب
x &&= y
— اگر x
مقدار «ترو» داشته باشد → x = y
.‼️ نکته مهم: چرا
x ||= y
و x = x || y
همیشه معادل هم نیستندبه طور خلاصه: اپراتورهای اختصاری مقدار سمت چپ انتساب (LHS) را فقط یک بار محاسبه میکنند، در حالی که حالت
x = x || y
ممکن است LHS را دو بار محاسبه کند. این مسئله زمانی مشکلساز میشود که LHS فقط یک متغیر ساده نباشد، بلکه یک عبارت شامل فراخوانیها، getterها یا کلیدهای محاسبهشده باشد.👩💻 مثال کمینه و قابل بازتولید:
let i = 0;
function getKey() { console.log('getKey'); return 'k' + (++i); }
const a = {};
a[getKey()] ||= 1; // getKey یک بار فراخوانی میشود — ایمن
i = 0;
const b = {};
b[getKey()] = b[getKey()] || 1; // getKey دو بار فراخوانی میشود — اثر جانبی!
➡️ در عبارت
b[...] = b[...] || 1`، `getKey()
ممکن است در فراخوانیهای مختلف کلیدهای متفاوت تولید کند یا اثرات جانبی دیگری داشته باشد — در نتیجه منطق برنامه خراب میشود. اما ||=
چنین خطری ندارد، چون ارجاع به b[getKey()]
فقط یک بار گرفته میشود.نتیجه:
اپراتورهای
||=
, ??=
و &&=
تنها «شیرینی سینتکسی» نیستند؛ بلکه روشی برای جلوگیری از باگهای پنهان هستند، به خصوص زمانی که LHS شامل عبارات با اثر جانبی است.پیشنهاد استفاده:
⏺️
??=
برای مقداردهی پیشفرض واقعی،⏺️
||=
برای زمانی که هر مقدار فالس مدنظر است،⏺️
&&=
برای بهروزرسانی شرطی. 👍#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
آشنایی با ویژگی rel در تگ <link> و تأثیر مقادیر آن بر رندر صفحه 🤨
تگ <link> برای اتصال منابع خارجی به سند HTML استفاده میشود.
ویژگی کلیدی این تگ، rel است که نوع ارتباط با منبع را مشخص میکند: این منابع میتوانند شامل استایلها، فونتها، آیکونها، مانیفستها و موارد دیگر باشند. نحوه پردازش این منابع توسط مرورگر کاملاً وابسته به مقدار این ویژگی است و همین موضوع به طور مستقیم بر رندر صفحه، سرعت بارگذاری و دسترسیپذیری آن تأثیر میگذارد. 👨🏫
در تصاویر بالا، من به تفصیل مقادیر اصلی ویژگی rel و تأثیر آنها بر عملکرد صفحه را بررسی کردهام. ⬆️
توصیههای کاربردی:
✔️ فونت و CSS های حیاتی را فوراً بارگذاری کنید (با stylesheet یا preload)؛
✔️ برای منابع از دامنههای دیگر از preconnect یا dns-prefetch استفاده کنید؛
✔️ منابع کماهمیتتر را میتوان به صورت غیرهمزمان بارگذاری کرد (prefetch).
نتیجهگیری:
استفاده از این تکنیکها به کاهش تاخیرها، کاهش اثر «پرش صفحه» (FOUC) و بهبود تجربه کاربری کمک میکند، بهویژه در دستگاههای موبایل.
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
تگ <link> برای اتصال منابع خارجی به سند HTML استفاده میشود.
ویژگی کلیدی این تگ، rel است که نوع ارتباط با منبع را مشخص میکند: این منابع میتوانند شامل استایلها، فونتها، آیکونها، مانیفستها و موارد دیگر باشند. نحوه پردازش این منابع توسط مرورگر کاملاً وابسته به مقدار این ویژگی است و همین موضوع به طور مستقیم بر رندر صفحه، سرعت بارگذاری و دسترسیپذیری آن تأثیر میگذارد. 👨🏫
در تصاویر بالا، من به تفصیل مقادیر اصلی ویژگی rel و تأثیر آنها بر عملکرد صفحه را بررسی کردهام. ⬆️
توصیههای کاربردی:
✔️ فونت و CSS های حیاتی را فوراً بارگذاری کنید (با stylesheet یا preload)؛
✔️ برای منابع از دامنههای دیگر از preconnect یا dns-prefetch استفاده کنید؛
✔️ منابع کماهمیتتر را میتوان به صورت غیرهمزمان بارگذاری کرد (prefetch).
نتیجهگیری:
استفاده از این تکنیکها به کاهش تاخیرها، کاهش اثر «پرش صفحه» (FOUC) و بهبود تجربه کاربری کمک میکند، بهویژه در دستگاههای موبایل.
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
چهکار کنیم وقتی خطاها به هم مرتبط هستند؟ 🤨
ما اغلب با خطاهایی در کد مواجه میشویم. اما اگر خطا تنها خود خطا نباشد و نتیجه خطای دیگری باشد، چه باید کرد؟
در JavaScript از سپتامبر سال دو هزار و بیست و یک، ابزاری برای مدیریت چنین مواردی معرفی شده است: `cause`. بیایید ببینیم چگونه میتوان با آن کار کرد. 😁
📚 \`cause\`\` چیست؟
مثال استفاده:
در اینجا ما یک خطای جدید ایجاد میکنیم و خطای اولیه را در `cause` قرار میدهیم. این کمک میکند بفهمیم چه چیزی منجر به مشکل جدید شده است.
چه زمانی مفید است؟ 🤔
✔️ برای پاکیزگی کد: به جای دستکاریهای پیچیده در stack، یک راه ساده و قابل فهم.
✔️ برای اشکالزدایی: مشخص میشود که یک خطا چگونه به خطای دیگر منجر شده است و این کار باعث سرعت یافتن علت مشکل میشود.
نمونه خروجی:
در نهایت، پارامتر `cause` به مرتبط کردن خطاها کمک میکند**، که باعث کاهش «سر و صدای اضافی» در کد و **سرعت بخشیدن به فرآیند شناسایی و رفع مشکلات میشود. 👍
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
ما اغلب با خطاهایی در کد مواجه میشویم. اما اگر خطا تنها خود خطا نباشد و نتیجه خطای دیگری باشد، چه باید کرد؟
در JavaScript از سپتامبر سال دو هزار و بیست و یک، ابزاری برای مدیریت چنین مواردی معرفی شده است: `cause`. بیایید ببینیم چگونه میتوان با آن کار کرد. 😁
📚 \`cause\`\` چیست؟
cause
یک پارامتر است که به شما اجازه میدهد مشخص کنید کدام خطا منجر به خطای فعلی شده است. این کار اشکالزدایی و تشخیص مشکل را سادهتر میکند و یک زنجیره واضح از خطاها ایجاد میکند.مثال استفاده:
try {
throw new Error("بارگذاری فایل موفق نبود");
} catch (err) {
throw new Error("خطا در پردازش درخواست", { cause: err });
}
در اینجا ما یک خطای جدید ایجاد میکنیم و خطای اولیه را در `cause` قرار میدهیم. این کمک میکند بفهمیم چه چیزی منجر به مشکل جدید شده است.
چه زمانی مفید است؟ 🤔
✔️ برای پاکیزگی کد: به جای دستکاریهای پیچیده در stack، یک راه ساده و قابل فهم.
✔️ برای اشکالزدایی: مشخص میشود که یک خطا چگونه به خطای دیگر منجر شده است و این کار باعث سرعت یافتن علت مشکل میشود.
نمونه خروجی:
Error: خطا در پردازش درخواست
at <anonymous>:4:9
Caused by: Error: بارگذاری فایل موفق نبود
at <anonymous>:2:9
در نهایت، پارامتر `cause` به مرتبط کردن خطاها کمک میکند**، که باعث کاهش «سر و صدای اضافی» در کد و **سرعت بخشیدن به فرآیند شناسایی و رفع مشکلات میشود. 👍
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
👍1👎1
Screencast of the different caret-shape possible values.webm
960.6 KB
نحو تعریف ویژگی caret-shape بسیار ساده است:
caret-shape: auto | bar | block | underscore;
مقدار اولیه auto است، که به مرورگر اجازه میدهد شکل نشانگر را بر اساس استانداردهای پلتفرم در موقعیتهای مختلف تعیین کند. اما تا کنون همیشه از نشانگر خط عمودی (|) استفاده شده است.
شما میتوانید نشانگر بلوک (█) یا زیرخط (_) را انتخاب کنید، که برای برخی اپلیکیشنها مانند ویرایشگر کد کاربردی و جلوهای جذاب دارد.
#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
caret-shape: auto | bar | block | underscore;
مقدار اولیه auto است، که به مرورگر اجازه میدهد شکل نشانگر را بر اساس استانداردهای پلتفرم در موقعیتهای مختلف تعیین کند. اما تا کنون همیشه از نشانگر خط عمودی (|) استفاده شده است.
شما میتوانید نشانگر بلوک (█) یا زیرخط (_) را انتخاب کنید، که برای برخی اپلیکیشنها مانند ویرایشگر کد کاربردی و جلوهای جذاب دارد.
#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
آلودگی پروتوتایپ (Prototype Pollution)
تعریف:
آلودگی پروتوتایپ یک حمله تزریقی است که محیطهای اجرای JavaScript را هدف میگیرد. در این نوع حمله، مهاجم میتواند مقادیر پیشفرض ویژگیهای یک شیء را کنترل کند. این امکان به مهاجم اجازه میدهد منطق برنامه را دستکاری کند و در موارد شدید منجر به انکار سرویس (DoS) یا حتی اجرای کد از راه دور شود.
فرض کنید یک شرکت به نام startup.io API خود را برای مدیریت دادههای کاربران ارائه کرده است. به دلیل محدودیت زمان و فشار سهامداران، تیم توسعه امنیت API را جدی نگرفته است و بسیاری از مشکلات گزارش شده توسط اسکنرهای امنیتی نادیده گرفته شدهاند. یکی از آسیبپذیریهای موجود، آلودگی پروتوتایپ است.
دو نقطه انتهایی API که اهمیت دارند:
POST https://api.startup.io/users/:userId برای بهروزرسانی دادههای کاربر
GET https://api.startup.io/users/:userId/role برای دریافت نقش امنیتی کاربر (admin یا user)
نمونه حمله: ارتقای دسترسی با آلودگی پروتوتایپ
ابتدا سعی میکنیم نقش خود را به admin تغییر دهیم:
نتیجه:
{ "role": "user" }
روش ساده کار نکرد، نقش همچنان user باقی ماند.
با استفاده از آلودگی پروتوتایپ و پیشوند جادویی proto میتوانیم به موفقیت برسیم:
نتیجه:
{ "role": "admin" }
BOOM! موفق شدیم با ارسال payload جادویی، نقش خود را به admin تغییر دهیم.
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
تعریف:
آلودگی پروتوتایپ یک حمله تزریقی است که محیطهای اجرای JavaScript را هدف میگیرد. در این نوع حمله، مهاجم میتواند مقادیر پیشفرض ویژگیهای یک شیء را کنترل کند. این امکان به مهاجم اجازه میدهد منطق برنامه را دستکاری کند و در موارد شدید منجر به انکار سرویس (DoS) یا حتی اجرای کد از راه دور شود.
فرض کنید یک شرکت به نام startup.io API خود را برای مدیریت دادههای کاربران ارائه کرده است. به دلیل محدودیت زمان و فشار سهامداران، تیم توسعه امنیت API را جدی نگرفته است و بسیاری از مشکلات گزارش شده توسط اسکنرهای امنیتی نادیده گرفته شدهاند. یکی از آسیبپذیریهای موجود، آلودگی پروتوتایپ است.
دو نقطه انتهایی API که اهمیت دارند:
POST https://api.startup.io/users/:userId برای بهروزرسانی دادههای کاربر
GET https://api.startup.io/users/:userId/role برای دریافت نقش امنیتی کاربر (admin یا user)
نمونه حمله: ارتقای دسترسی با آلودگی پروتوتایپ
ابتدا سعی میکنیم نقش خود را به admin تغییر دهیم:
curl -H "Content-Type: application/json" -X POST -d '{"role": "admin"}' https://api.startup.io/users/1337 && curl -X GET https://api.startup.io/users/1337/role
نتیجه:
{ "role": "user" }
روش ساده کار نکرد، نقش همچنان user باقی ماند.
با استفاده از آلودگی پروتوتایپ و پیشوند جادویی proto میتوانیم به موفقیت برسیم:
curl -H "Content-Type: application/json" -X POST -d '{"about": {"__proto__": {"role": "admin"}}}' https://api.startup.io/users/1337 && curl -X GET https://api.startup.io/users/1337/role
نتیجه:
{ "role": "admin" }
BOOM! موفق شدیم با ارسال payload جادویی، نقش خود را به admin تغییر دهیم.
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
# بارگذاری تنبل تصاویر پسزمینه با API
وقتی تصاویر خارج از صفحه (offscreen) را با ویژگی HTML
---
## API IntersectionObserver چیست؟
به همین دلیل، IntersectionObserver برای تشخیص ورود عناصر خارج از صفحه به محدوده دید کاربر و بارگذاری تنبل پسزمینهها بسیار مناسب است.
---
## مزایای استفاده از IntersectionObserver برای عملکرد وب
* این API به صورت غیرهمزمان خارج از thread اصلی اجرا میشود و بنابراین بار پردازشی کمتری نسبت به event listenerهای سنتی scroll دارد.
* قبل از آن، توسعهدهندگان برای lazy load مجبور بودند event listenerهای scroll، resize و غیره را به صورت دستی اضافه کنند که باعث افزایش بار پردازشی و کندی مرورگر میشد.
* IntersectionObserver از thread ترکیب (compositor thread) مرورگر استفاده میکند و تغییرات تقاطع نزدیک به هم را در یک callback گروهبندی میکند، بنابراین از محاسبات تکراری DOM جلوگیری میشود.
نتیجه: مصرف CPU کمتر، اسکرول روانتر و تجربه کاربری بهتر.
---
## مراحل پیادهسازی بارگذاری تنبل پسزمینه
### ۱. علامتگذاری عناصر خارج از صفحه با کلاس
* عناصر با کلاس
* تصاویر اولیه (مثلاً دو تصویر اول) با
---
### ۲. جایگزینی تصویر پسزمینه با رنگ پیشفرض در CSS
* رنگ خاکستری به عنوان placeholder استفاده میشود.
* با حذف کلاس
* ویژگی
---
### ۳. مشاهده تقاطع عناصر با viewport در JavaScript
*
*
*
---
### ۴. پشتیبانی از کاربران بدون JavaScript
* برای کاربرانی که JavaScript غیرفعال دارند، تصاویر پسزمینه بارگذاری و نمایش داده میشوند.
* توجه: در این حالت، بارگذاری تنبل فعال نمیشود و تصاویر در ابتدا دانلود میشوند.
---
## جمعبندی
* IntersectionObserver یک روش مدرن و بهینه برای lazy load تصاویر پسزمینه است.
* کاهش حجم اولیه صفحه، افزایش سرعت بارگذاری و بهبود Core Web Vitals از مزایای آن است.
* این روش با استفاده از thread ترکیب مرورگر و اجرای غیرهمزمان، از بلاک شدن thread اصلی جلوگیری میکند و تجربه کاربری روانتری ایجاد میکند.
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
IntersectionObserver
وقتی تصاویر خارج از صفحه (offscreen) را با ویژگی HTML
loading
بارگذاری تنبل (lazy) میکنیم، کار سادهای است. اما برای تصاویر پسزمینه که با CSS اضافه شدهاند، کمی پیچیدهتر است. در این حالت باید با JavaScript تشخیص دهیم که چه زمانی این تصاویر به دید کاربر نزدیک میشوند تا بارگذاری آنها آغاز شود.---
## API IntersectionObserver چیست؟
IntersectionObserver
یک API بومی وب است که در مرورگرهای مدرن پشتیبانی میشود. این API به شما امکان میدهد تا به صورت غیرهمزمان (asynchronously) بررسی کنید که آیا یک عنصر HTML با یکی از والدینش، مانند viewport، در حال تقاطع (intersect) است یا خیر.به همین دلیل، IntersectionObserver برای تشخیص ورود عناصر خارج از صفحه به محدوده دید کاربر و بارگذاری تنبل پسزمینهها بسیار مناسب است.
---
## مزایای استفاده از IntersectionObserver برای عملکرد وب
* این API به صورت غیرهمزمان خارج از thread اصلی اجرا میشود و بنابراین بار پردازشی کمتری نسبت به event listenerهای سنتی scroll دارد.
* قبل از آن، توسعهدهندگان برای lazy load مجبور بودند event listenerهای scroll، resize و غیره را به صورت دستی اضافه کنند که باعث افزایش بار پردازشی و کندی مرورگر میشد.
* IntersectionObserver از thread ترکیب (compositor thread) مرورگر استفاده میکند و تغییرات تقاطع نزدیک به هم را در یک callback گروهبندی میکند، بنابراین از محاسبات تکراری DOM جلوگیری میشود.
نتیجه: مصرف CPU کمتر، اسکرول روانتر و تجربه کاربری بهتر.
---
## مراحل پیادهسازی بارگذاری تنبل پسزمینه
### ۱. علامتگذاری عناصر خارج از صفحه با کلاس
deferred
<div class="bg-1"><p>CTA Area 01</p></div>
<div class="bg-2"><p>CTA Area 02</p></div>
<div class="bg-3"><p>CTA Area 03</p></div>
<div class="bg-4 deferred"><p>CTA Area 04</p></div>
<div class="bg-5 deferred"><p>CTA Area 05</p></div>
<!-- و به همین ترتیب -->
* عناصر با کلاس
deferred
از تصویر پسزمینه خالی استفاده میکنند و هنگام نزدیک شدن به viewport، تصاویر بارگذاری میشوند.* تصاویر اولیه (مثلاً دو تصویر اول) با
<link rel="preload">
از قبل بارگذاری میشوند تا LCP سریعتر شود.---
### ۲. جایگزینی تصویر پسزمینه با رنگ پیشفرض در CSS
.bg-1 {
background: #d6d6d6 url("images/flowers-01.jpg") no-repeat;
}
.deferred {
background-image: none;
}
* رنگ خاکستری به عنوان placeholder استفاده میشود.
* با حذف کلاس
deferred
توسط JavaScript، تصاویر پسزمینه واقعی بارگذاری و نمایش داده میشوند.* ویژگی
aspect-ratio
برای حفظ اندازه مشابه تصاویر استفاده میشود.---
### ۳. مشاهده تقاطع عناصر با viewport در JavaScript
document.addEventListener("DOMContentLoaded", () => {
const deferredElements = document.querySelectorAll(".deferred");
const elementObserver = new IntersectionObserver(
(entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.remove("deferred");
observer.unobserve(entry.target);
}
});
},
{
root: null,
rootMargin: "200px 0px",
threshold: 0,
}
);
deferredElements.forEach(el => elementObserver.observe(el));
});
*
root: null
→ viewport به عنوان ریشه*
rootMargin: "200px 0px"
→ شروع بارگذاری ۲۰۰px قبل از رسیدن عنصر به viewport*
threshold: 0
→ شروع بارگذاری به محض ورود جزئی عنصر---
### ۴. پشتیبانی از کاربران بدون JavaScript
<noscript>
<style>
.bg-4 { background-image: url(images/flowers-04.jpg); }
.bg-5 { background-image: url(images/flowers-05.jpg); }
.bg-6 { background-image: url(images/flowers-06.jpg); }
</style>
</noscript>
* برای کاربرانی که JavaScript غیرفعال دارند، تصاویر پسزمینه بارگذاری و نمایش داده میشوند.
* توجه: در این حالت، بارگذاری تنبل فعال نمیشود و تصاویر در ابتدا دانلود میشوند.
---
## جمعبندی
* IntersectionObserver یک روش مدرن و بهینه برای lazy load تصاویر پسزمینه است.
* کاهش حجم اولیه صفحه، افزایش سرعت بارگذاری و بهبود Core Web Vitals از مزایای آن است.
* این روش با استفاده از thread ترکیب مرورگر و اجرای غیرهمزمان، از بلاک شدن thread اصلی جلوگیری میکند و تجربه کاربری روانتری ایجاد میکند.
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
راههای متعددی برای ریفرش یا بارگذاری مجدد صفحه وجود دارد و هر کدام میتوانند در موقعیتهای مختلف مفید باشند. برای مثال، اخیراً با یک منبع اینترنتی برخورد کردم که لیستی کامل از روشهای مختلف ریفرش صفحه با JavaScript ارائه کرده است: phpied.com
👀
به جای اینکه در صدها روش غرق شویم، بهتر است روی سه روش اصلی و پرکاربرد تمرکز کنیم که بیشترین کنترل را روی ناوبری و بهروزرسانی صفحه ارائه میدهند:
# ریفرش صفحه در JavaScript
### ۱.
متد
---
### ۲.
متد
---
### ۳.
متد
* بدون پارامتر: صفحه با توجه به کش مرورگر بارگذاری میشود
* با پارامتر
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
👀
به جای اینکه در صدها روش غرق شویم، بهتر است روی سه روش اصلی و پرکاربرد تمرکز کنیم که بیشترین کنترل را روی ناوبری و بهروزرسانی صفحه ارائه میدهند:
# ریفرش صفحه در JavaScript
### ۱.
location.assign(url)
— رفتن به URL جدیدمتد
assign()
برای ناوبری به یک URL جدید استفاده میشود و صفحه جاری را در تاریخچه مرورگر نگه میدارد. یعنی بعد از رفتن به URL جدید، کاربر میتواند با دکمه «بازگشت» به صفحه قبلی برگردد.location.assign('https://example.com');
---
### ۲.
location.replace(url)
— رفتن بدون ذخیره در تاریخچهمتد
replace()
شبیه assign()
است، اما صفحه فعلی را در تاریخچه مرورگر ذخیره نمیکند. بنابراین بعد از رفتن به URL جدید، با دکمه «بازگشت» نمیتوان به صفحه قبلی بازگشت.location.replace('https://example.com');
---
### ۳.
location.reload()
— بارگذاری مجدد صفحه جاریمتد
reload()
برای ریفرش یا بارگذاری مجدد صفحه جاری استفاده میشود.* بدون پارامتر: صفحه با توجه به کش مرورگر بارگذاری میشود
* با پارامتر
true
: کش نادیده گرفته شده و صفحه دوباره از سرور بارگذاری میشودlocation.reload(); // بارگذاری مجدد با کش
location.reload(true); // بارگذاری مجدد بدون کش
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
❤1👍1
# استفاده از Custom Highlight API در مرورگرها
اخیراً متوجه شدم که Firefox 140 (ژوئن ۲۰۲۵) از Custom Highlight API پشتیبانی میکند، و حالا این ویژگی روی تمام مرورگرهای اصلی در دسترس است. با این API میتوان به متنهایی که در JavaScript با کلاس
برای درک ساده، مراحل اصلی به این صورت است:
1. یک textNode انتخاب کنید.
2. یک Range بسازید و محدودهی شروع و پایان آن را مشخص کنید:
3. با `CSS.highlights.set()`، این Range را به یک نام هایلایت متصل کنید:
4. در CSS با
---
## مثال عملی: هایلایت یک کلمه
در DevTools میتوان دید که کلمه
---
## چرا مفید است؟
* استایل بدون دستکاری DOM: نیازی به اضافه کردن
* کاهش هزینههای محاسباتی: اضافه کردن و حذف
* قابلیت هایلایت چند محدودهای: یک Highlight میتواند چند Range را پوشش دهد، که برای مثال میتوان برای ایجاد یک سیستم جستجو روی متن کاربرد دارد.
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
اخیراً متوجه شدم که Firefox 140 (ژوئن ۲۰۲۵) از Custom Highlight API پشتیبانی میکند، و حالا این ویژگی روی تمام مرورگرهای اصلی در دسترس است. با این API میتوان به متنهایی که در JavaScript با کلاس
Range()
به دست میآوریم، استایل دلخواه اعمال کرد.برای درک ساده، مراحل اصلی به این صورت است:
1. یک textNode انتخاب کنید.
const textNode = document.querySelector("p").firstChild;
2. یک Range بسازید و محدودهی شروع و پایان آن را مشخص کنید:
const range = new Range();
range.setStart(textNode, startIndex);
range.setEnd(textNode, endIndex);
3. با `CSS.highlights.set()`، این Range را به یک نام هایلایت متصل کنید:
const highlight = new Highlight(range);
CSS.highlights.set("our-highlight", highlight);
4. در CSS با
::highlight(name)
آن را استایل دهید:::highlight(our-highlight) {
background-color: yellow;
}
---
## مثال عملی: هایلایت یک کلمه
const WORD_TO_HIGHLIGHT = "wisdom";
const NAME_OF_HIGHLIGHT = "our-highlight";
const textNode = document.querySelector("p").firstChild;
const textContent = textNode.textContent;
const startIndex = textContent.indexOf(WORD_TO_HIGHLIGHT);
const endIndex = startIndex + WORD_TO_HIGHLIGHT.length;
const range = new Range();
range.setStart(textNode, startIndex);
range.setEnd(textNode, endIndex);
const highlight = new Highlight(range);
CSS.highlights.set(NAME_OF_HIGHLIGHT, highlight);
در DevTools میتوان دید که کلمه
"wisdom"
با CSS استایل داده شده، بدون اینکه هیچ المنتی در اطراف آن اضافه شود.---
## چرا مفید است؟
* استایل بدون دستکاری DOM: نیازی به اضافه کردن
<span>
یا عناصر دیگر نیست، که باعث کاهش وزن DOM و بهبود عملکرد میشود.* کاهش هزینههای محاسباتی: اضافه کردن و حذف
<span>
میتواند باعث Reflow و Repaint شود و عملکرد UX را کند کند.* قابلیت هایلایت چند محدودهای: یک Highlight میتواند چند Range را پوشش دهد، که برای مثال میتوان برای ایجاد یک سیستم جستجو روی متن کاربرد دارد.
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
آیا شما هم هنوز برای استفاده از بکاسلش در رشتههای قالببندیشده، آن را دو بار تکرار میکنید؟ 🔍
چند روز پیش دیدم که شخصی در رشتههای قالببندیشده (template strings) برای کاراکترهای اِسکیپشده مثل
-----
### `String.raw` چیست؟
این متد به شما اجازه میدهد تا با رشتههای قالببندیشده به گونهای کار کنید که دنبالههای اِسکیپ (مانند
مثال:
به جای اینکه هر بکاسلش یا کاراکتر خط جدید را دو بار اِسکیپ کنید، همانطور که در یک رشته معمولی لازم بود:
-----
### مزایای `String.raw`
➡️ با استفاده از `String.raw`**، کد شما تمیزتر و خواناتر میشود، زیرا نیازی به اِسکیپکردن دوباره کاراکترهایی مانند `\`، `n` یا `t` ندارید.
**این قابلیت در کجا مفید است؟
* مسیرهای فایل: اگر با مسیرهای ویندوز کار میکنید که شامل بکاسلشهای زیادی است، `String.raw` شما را از نوشتن
* عبارات باقاعده (Regular Expressions): اگر در الگوهای خود بکاسلش یا کاراکترهای خاص زیادی دارید، `String.raw` به جلوگیری از تفسیر این کاراکترها کمک کرده و آنها را به شکل اصلیشان حفظ میکند.
* رشتههای چندخطی: زمانی که لازم است کاراکترهای خط جدید یا تب را بهصورت متن ذخیره کنید، نه اینکه واقعاً خط را بشکنید یا تب ایجاد کنید.
در نهایت، این یک روش سریع و ساده برای اجتناب از مراحل اضافی در کار با رشتههای قالببندیشده و حفظ دقت در خروجی است. 👍
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
چند روز پیش دیدم که شخصی در رشتههای قالببندیشده (template strings) برای کاراکترهای اِسکیپشده مثل
\n
(خط جدید) یا \t
(تب)، بکاسلشها (\\
) را تکرار میکرد. اما راهحل بسیار سادهتری وجود دارد و آن استفاده از `String.raw` است. 😁-----
### `String.raw` چیست؟
این متد به شما اجازه میدهد تا با رشتههای قالببندیشده به گونهای کار کنید که دنبالههای اِسکیپ (مانند
\n`، `\t`، `\\
و دیگر موارد) بهصورت «خام» باقی بمانند. یعنی اگر نیاز به نوشتن یک رشته با مسیر فایل یا کاراکترهای خط جدید و تب دارید، `String.raw` بدون نیاز به اِسکیپکردنهای اضافی، تمام کار را برای شما انجام میدهد. 👍مثال:
const path = `C:\Users\Name\Documents\file.txt`;
console.log(path); // C:UsersNameDocuments ile.txt
const path = String.raw`C:\Users\Name\Documents\file.txt`;
console.log(path); // C:\Users\Name\Documents\file.txt
به جای اینکه هر بکاسلش یا کاراکتر خط جدید را دو بار اِسکیپ کنید، همانطور که در یک رشته معمولی لازم بود:
const path = `C:\\Users\\Name\\Documents\\file.txt`;
console.log(path); // C:\Users\Name\Documents\file.txt
-----
### مزایای `String.raw`
➡️ با استفاده از `String.raw`**، کد شما تمیزتر و خواناتر میشود، زیرا نیازی به اِسکیپکردن دوباره کاراکترهایی مانند `\`، `n` یا `t` ندارید.
**این قابلیت در کجا مفید است؟
* مسیرهای فایل: اگر با مسیرهای ویندوز کار میکنید که شامل بکاسلشهای زیادی است، `String.raw` شما را از نوشتن
\\
بینیاز میکند.* عبارات باقاعده (Regular Expressions): اگر در الگوهای خود بکاسلش یا کاراکترهای خاص زیادی دارید، `String.raw` به جلوگیری از تفسیر این کاراکترها کمک کرده و آنها را به شکل اصلیشان حفظ میکند.
* رشتههای چندخطی: زمانی که لازم است کاراکترهای خط جدید یا تب را بهصورت متن ذخیره کنید، نه اینکه واقعاً خط را بشکنید یا تب ایجاد کنید.
در نهایت، این یک روش سریع و ساده برای اجتناب از مراحل اضافی در کار با رشتههای قالببندیشده و حفظ دقت در خروجی است. 👍
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
👍1
🤔 چطور بین iframe و صفحه اصلی ارتباط برقرار کنیم؟
ارتباط بین iframe و صفحه والد میتواند از طریق روشهای زیر انجام شود:
* متد `postMessage` (بهترین روش)
* دسترسی به
* انتقال دادهها از طریق localStorage یا cookies
---
🟠 `postMessage` – روشی امن برای دامنههای مختلف
متد
---
🚩 ارسال داده از `iframe` به صفحه والد
*کد در iframe (child.html):*
*کد در صفحه والد (index.html):*
> علامت
---
🚩 ارسال داده از والد به `iframe`
*کد در صفحه والد (index.html):*
*کد در iframe (child.html):*
---
🚩 دسترسی به `window.frames` و `window.parent` *(فقط اگر در همان دامنه باشند)*
اگر iframe و صفحه اصلی در یک دامنه قرار داشته باشند، میتوان مستقیماً به آبجکتهای window دسترسی داشت.
صفحه والد → iframe:
iframe → صفحه والد:
---
🚩 استفاده از `localStorage` یا `cookies` *(اگر هر دو پنجره در یک دامنه باشند)*
میتوان دادهها را در localStorage یا cookies ذخیره کرد و طرف دیگر آنها را خواند.
نوشتن در localStorage از iframe:
خواندن localStorage در صفحه والد:
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
ارتباط بین iframe و صفحه والد میتواند از طریق روشهای زیر انجام شود:
* متد `postMessage` (بهترین روش)
* دسترسی به
window.frames
یا window.parent
(اگر در همان دامنه باشند)* انتقال دادهها از طریق localStorage یا cookies
---
🟠 `postMessage` – روشی امن برای دامنههای مختلف
متد
window.postMessage()
امکان ارسال پیام بین پنجرههای مختلف (iframe، popup، تبهای دیگر) را حتی در صورت قرار داشتن در دامنههای مختلف فراهم میکند.---
🚩 ارسال داده از `iframe` به صفحه والد
*کد در iframe (child.html):*
// ارسال پیام به پنجره والد
window.parent.postMessage({ type: "hello", data: "سلام والد!" }, "*");
*کد در صفحه والد (index.html):*
window.addEventListener("message", (event) => {
console.log("پیام دریافت شده از iframe:", event.data);
});
> علامت
*
در postMessage
به این معناست که پیام به هر دامنهای ارسال میشود. بهتر است دامنه مشخصی را تعیین کنید، مثلاً:window.parent.postMessage({ type: "hello" }, "https://example.com");
---
🚩 ارسال داده از والد به `iframe`
*کد در صفحه والد (index.html):*
const iframe = document.getElementById("myIframe");
// منتظر میمانیم iframe بارگذاری شود
iframe.onload = () => {
iframe.contentWindow.postMessage({ type: "greeting", data: "سلام iframe!" }, "*");
};
*کد در iframe (child.html):*
window.addEventListener("message", (event) => {
console.log("پیام دریافت شده از والد:", event.data);
});
---
🚩 دسترسی به `window.frames` و `window.parent` *(فقط اگر در همان دامنه باشند)*
اگر iframe و صفحه اصلی در یک دامنه قرار داشته باشند، میتوان مستقیماً به آبجکتهای window دسترسی داشت.
صفحه والد → iframe:
const iframe = document.getElementById("myIframe");
// دسترسی به window داخل iframe
iframe.contentWindow.document.body.style.backgroundColor = "lightblue";
iframe → صفحه والد:
console.log(window.parent.document.title); // دسترسی به عنوان صفحه
---
🚩 استفاده از `localStorage` یا `cookies` *(اگر هر دو پنجره در یک دامنه باشند)*
میتوان دادهها را در localStorage یا cookies ذخیره کرد و طرف دیگر آنها را خواند.
نوشتن در localStorage از iframe:
localStorage.setItem("message", "سلام از iframe!");
خواندن localStorage در صفحه والد:
console.log(localStorage.getItem("message")); // "سلام از iframe!"
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
آشنایی با
تگ
بدون این تگ، مرورگرها در تلفنهای همراه معمولاً صفحه را «مثل نسخه دسکتاپ» (با عرض حدود نهصد و هشتاد پیکسل) رندر میکنند و در نتیجه همه چیز بسیار کوچک و فشرده به نظر میرسد.
📚 سینتکس پایهای:
این حالت در اکثر موارد استفاده میشود:
* عرض viewport برابر با عرض واقعی صفحه نمایش دستگاه است.
* مقیاس اولیه برابر با صد درصد است.
مقدارهای
👀 تگ
**چند نکته کاربردی:
1️⃣ تگ را در `<head>` و تا حد امکان زود قرار دهید.
مرورگر باید قبل از رندر کردن صفحه آن را ببیند.
2️⃣ از `width=device-width, initial-scale=1.0` بهعنوان پیشفرض استفاده کنید.
3️⃣ چند تگ viewport وارد نکنید.
مرورگرها معمولاً تنها یکی را اعمال میکنند یا رفتار صفحه غیرقابل پیشبینی میشود.
و فراموش نکنید که طراحی و مقیاسبندی را روی دستگاههای واقعی و شبیهسازها بررسی کنید 🥰
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
<meta name="viewport">
🤓تگ
<meta name="viewport">
یک مِتا تگ است که به مرورگرهای موبایل میگوید چگونه ابعاد پنجرهٔ دید مجازی (viewport) را تنظیم کنند و صفحه را چگونه مقیاسبندی کنند.بدون این تگ، مرورگرها در تلفنهای همراه معمولاً صفحه را «مثل نسخه دسکتاپ» (با عرض حدود نهصد و هشتاد پیکسل) رندر میکنند و در نتیجه همه چیز بسیار کوچک و فشرده به نظر میرسد.
📚 سینتکس پایهای:
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
این حالت در اکثر موارد استفاده میشود:
* عرض viewport برابر با عرض واقعی صفحه نمایش دستگاه است.
* مقیاس اولیه برابر با صد درصد است.
مقدارهای
content
را میتوانید در تصویر بالا مشاهده کنید 😁👀 تگ
<meta name="viewport">
به تنهایی سایت را واکنشگرا نمیکند**؛ فقط یک «بوم» برای رندر فراهم میکند.**چند نکته کاربردی:
1️⃣ تگ را در `<head>` و تا حد امکان زود قرار دهید.
مرورگر باید قبل از رندر کردن صفحه آن را ببیند.
2️⃣ از `width=device-width, initial-scale=1.0` بهعنوان پیشفرض استفاده کنید.
3️⃣ چند تگ viewport وارد نکنید.
مرورگرها معمولاً تنها یکی را اعمال میکنند یا رفتار صفحه غیرقابل پیشبینی میشود.
و فراموش نکنید که طراحی و مقیاسبندی را روی دستگاههای واقعی و شبیهسازها بررسی کنید 🥰
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
🔥1