آیا میدانستید دکمهٔ ارسال فرم میتواند خارج از خود فرم قرار بگیرد؟
در بیشتر مواقع، دکمهٔ ارسال را داخل تگ <form> قرار میدهیم و این کار کاملاً درست است.
اما گاهی بهدلیل محدودیتهای چیدمان (layout) یا دلایل دیگر، منطقیتر است که دکمهٔ ارسال بیرون از تگ <form> قرار داده شود.
در این حالت میتوانیم بهسادگی با استفاده از ویژگیهای form و id دکمه را به فرم موردنظر متصل کنیم.
به همین روش، در صورت نیاز میتوان سایر عناصر کنترلی مثل textarea، checkbox و موارد مشابه را هم به یک فرم خاص مرتبط کرد، حتی اگر خارج از آن فرم قرار داشته باشند.
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
در بیشتر مواقع، دکمهٔ ارسال را داخل تگ <form> قرار میدهیم و این کار کاملاً درست است.
اما گاهی بهدلیل محدودیتهای چیدمان (layout) یا دلایل دیگر، منطقیتر است که دکمهٔ ارسال بیرون از تگ <form> قرار داده شود.
در این حالت میتوانیم بهسادگی با استفاده از ویژگیهای form و id دکمه را به فرم موردنظر متصل کنیم.
به همین روش، در صورت نیاز میتوان سایر عناصر کنترلی مثل textarea، checkbox و موارد مشابه را هم به یک فرم خاص مرتبط کرد، حتی اگر خارج از آن فرم قرار داشته باشند.
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
👍3❤1👌1
این ۴ خط را به فایل `.vscode/settings.json` اضافه کنید تا تجربهٔ توسعهتان بلافاصله راحتتر و خواناتر شود.
با این تنظیمات، نام تبها در VS Code واضحتر نمایش داده میشود (مثلاً مشخص میشود هر
نتیجه:
وقتی چندین فایل
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
با این تنظیمات، نام تبها در VS Code واضحتر نمایش داده میشود (مثلاً مشخص میشود هر
page یا layout مربوط به کدام پوشه است):{
"workbench.editor.customLabels.patterns": {
"**/app/**/{page,layout,index}.{ts,tsx}": "(${dirname})/${filename}.${extname}",
"**/index.{ts,tsx}": "${dirname}/index.${extname}"
}
}نتیجه:
وقتی چندین فایل
page.tsx یا layout.tsx باز دارید، دیگر همه شبیه هم دیده نمیشوند و سریعتر متوجه میشوید هر فایل به کدام مسیر یا بخش پروژه تعلق دارد.#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
غولِ دوستداشتنی ما: TypeScript به محبوبترین زبان برنامهنویسی در GitHub تبدیل شد 🥳
و اگر JavaScript و TypeScript را با هم حساب کنیم، اختلاف با بقیه زبانها واقعاً چشمگیر میشود.
گزارش کامل GitHub برای سال ۲۰۲۵:
https://github.blog/news-insights/octoverse/octoverse-a-new-developer-joins-github-every-second-as-ai-leads-typescript-to-1/
#️⃣#discussion
👥@IR_javascript_group
🆔@IR_javascript
و اگر JavaScript و TypeScript را با هم حساب کنیم، اختلاف با بقیه زبانها واقعاً چشمگیر میشود.
گزارش کامل GitHub برای سال ۲۰۲۵:
https://github.blog/news-insights/octoverse/octoverse-a-new-developer-joins-github-every-second-as-ai-leads-typescript-to-1/
#️⃣#discussion
👥@IR_javascript_group
🆔@IR_javascript
❤2
در اینترنت به یک ابزار تحلیل CSS جالب برخوردم که میتواند هر وبسایتی را بررسی کند. این سرویس قادر است میزان و مقایسهٔ ویژگی اختصاصیبودن سلکتورها را محاسبه کند، پالت رنگی، سایهها، حاشیهها، متغیرهای CSS و چیدمانهای گرید را نمایش دهد. حتی تلاش میکند پیشنهاد دهد که آیا مهاجرت به کلاسهای اتمیک منطقی است یا خیر.
صادقانه بگویم، برای پیدا کردن ناهنجاریها در یک سایت، ابزار بسیار کارآمدی است؛ از اندازههای اعشاری و متغیرهای غیرمنتظره گرفته تا فونتهای پیشبینینشده. حتی میتوان نگاهی به کدنویسی رقبا انداخت و ایرادهای نهچندان ایدئال آنها را دید و کمی هم حس رضایت شخصی را تقویت کرد. امکان مقایسهٔ دو نسخهٔ متفاوت از یک سایت نیز فراهم است.
🔗https://cssstats.com/
#️⃣#tool
👥@IR_javascript_group
🆔@IR_javascript
صادقانه بگویم، برای پیدا کردن ناهنجاریها در یک سایت، ابزار بسیار کارآمدی است؛ از اندازههای اعشاری و متغیرهای غیرمنتظره گرفته تا فونتهای پیشبینینشده. حتی میتوان نگاهی به کدنویسی رقبا انداخت و ایرادهای نهچندان ایدئال آنها را دید و کمی هم حس رضایت شخصی را تقویت کرد. امکان مقایسهٔ دو نسخهٔ متفاوت از یک سایت نیز فراهم است.
🔗https://cssstats.com/
#️⃣#tool
👥@IR_javascript_group
🆔@IR_javascript
❤1
فشردهسازی میتواند حجم فایل یک SVG را بدون کاهش کیفیت بصری آن کاهش دهد. برای این کار میتوانید از ابزارهای آنلاین مختلفی مانند SVGOMG یا iLoveIMG استفاده کنید که فرایند فشردهسازی را ساده میکنند. کافی است فایل SVG خود را در این ابزارها بارگذاری کنید و تنظیمات فشردهسازی دلخواه را اعمال نمایید. سپس میتوانید آن را بهعنوان یک فایل جدید ذخیره و دانلود کنید. حتماً فایل را با نامی متفاوت ذخیره کنید تا نسخهٔ فشردهشده را از نسخهٔ اصلی تشخیص دهید (هرچند نسخهٔ فشردهشده باید حجم کمتری داشته باشد).
#️⃣#tool
👥@IR_javascript_group
🆔@IR_javascript
#️⃣#tool
👥@IR_javascript_group
🆔@IR_javascript
بدون robots.txt — بدون حضور در نتایج Google
موضوعی غیرمنتظره. Alan Smith یک مورد جالب را به اشتراک میگذارد: در مقطعی، ترافیک ارگانیک سایت از گوگل به صفر رسید. دلیل احتمالی این اتفاق آن بوده که سایت فاقد فایل robots.txt بوده است — فایلی در ریشهٔ سایت که باید به رباتها اعلام کند کدام بخشها قابل خزش هستند و کدام بخشها نیستند.
بررسیهای بعدی نشان داده که حتی در مستندات پشتیبانی نیز یک ویدیوی کامل دربارهٔ این نکته وجود دارد.
صادقانه بگویم، من هرگز با چنین موردی مواجه نشدهام، چون در همهٔ پروژههای شخصیام فایل robots.txt بهصورت خودکار از پروژهٔ قبلی کپی میشود. بااینحال، جالب است که اگر گوگلبات مجوز صریحی برای گشتوگذار در سایت دریافت نکند، خودسرانه عمل نمیکند و واقعاً وارد سایت نمیشود. منطقی است — اما در عین حال شگفتآور.
🔗https://www.alanwsmith.com/en/37/wa/jz/s1/
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
موضوعی غیرمنتظره. Alan Smith یک مورد جالب را به اشتراک میگذارد: در مقطعی، ترافیک ارگانیک سایت از گوگل به صفر رسید. دلیل احتمالی این اتفاق آن بوده که سایت فاقد فایل robots.txt بوده است — فایلی در ریشهٔ سایت که باید به رباتها اعلام کند کدام بخشها قابل خزش هستند و کدام بخشها نیستند.
بررسیهای بعدی نشان داده که حتی در مستندات پشتیبانی نیز یک ویدیوی کامل دربارهٔ این نکته وجود دارد.
صادقانه بگویم، من هرگز با چنین موردی مواجه نشدهام، چون در همهٔ پروژههای شخصیام فایل robots.txt بهصورت خودکار از پروژهٔ قبلی کپی میشود. بااینحال، جالب است که اگر گوگلبات مجوز صریحی برای گشتوگذار در سایت دریافت نکند، خودسرانه عمل نمیکند و واقعاً وارد سایت نمیشود. منطقی است — اما در عین حال شگفتآور.
🔗https://www.alanwsmith.com/en/37/wa/jz/s1/
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
👍1
Data Loaders در Vue Router
دیتا لودرها کار با دادههای ناهمگام (مثل واکشی API) را در Vue Router ساده و یکپارچه میکنند. با آنها میتوانید به شکل مؤثر دادهها را قبل از رندر کامپوننت آماده کنید و همزمان از کتابخانههایی مثل Pinia Colada یا Apollo بهره ببرید.
لودر بهطور خودکار هنگام تغییر مسیر اجرا میشود و دادهها قبل از رندر کامپوننت آماده هستند. همچنین میتوانید همان لودر را در چند کامپوننت دوباره استفاده کنید و دادهها بهصورت اشتراکی مدیریت شوند.
🔗https://uvr.esm.is/data-loaders/
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
دیتا لودرها کار با دادههای ناهمگام (مثل واکشی API) را در Vue Router ساده و یکپارچه میکنند. با آنها میتوانید به شکل مؤثر دادهها را قبل از رندر کامپوننت آماده کنید و همزمان از کتابخانههایی مثل Pinia Colada یا Apollo بهره ببرید.
// src/pages/users/[id].vue
<script lang="ts">
import { defineBasicLoader } from 'unplugin-vue-router/data-loaders/basic'
import { getUserById } from '../api'
export const useUserData = defineBasicLoader('/users/[id]', async (route) => {
return getUserById(route.params.id)
})
</script>
لودر بهطور خودکار هنگام تغییر مسیر اجرا میشود و دادهها قبل از رندر کامپوننت آماده هستند. همچنین میتوانید همان لودر را در چند کامپوننت دوباره استفاده کنید و دادهها بهصورت اشتراکی مدیریت شوند.
🔗https://uvr.esm.is/data-loaders/
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
🧐 چرا نباید منطق کسبوکار را در کامپوننتهای UI نوشت
اغلب یک الگوی تکراری را میبینم: یک کامپوننت کوچک با چند شرط ساده که پس از چند قابلیت کوچک به موجودی عظیم تبدیل میشود 🤯 — هم رابط را نمایش میدهد، هم تصمیم میگیرد، هم با API ارتباط برقرار میکند و کمی «جادو» هم انجام میدهد. در نتیجه، بهجای یک UI تمیز، مجموعهای از منطق پنهان خواهیم داشت که نگهداری آن بعداً بسیار دشوار است.
---
🧩 چگونه این اتفاق میافتد؟
در ابتدا همه چیز منطقی به نظر میرسد: زمانبندی محدود، فیتچر کوچک — سادهتر است که پردازش را مستقیم در کامپوننت قرار دهیم. سپس «بعداً بیرون میکشیم» هیچگاه اتفاق نمیافتد و کامپوننت رشد میکند:
➖ فراخوانی API و تبدیل پاسخها
➖ تصمیمگیری درباره گامهای فرایند
➖ شامل شاخههای قوانین کسبوکار
➖ منبعی برای کپیپیست هنگام استفاده مجدد
---
🧩 مشکل چیست؟
☑️ ادغام نقشها — کامپوننت باید رابط را نمایش دهد، نه مدیریت فرایند را. وقتی UI شروع به «تفکر» میکند، سیستم بسیار شکننده میشود.
☑️ از بین رفتن قابلیت استفاده مجدد — نیاز به همان منطق در جای دیگر باعث کپیپیست، ناهماهنگی و باگها میشود.
☑️ ریسک تغییرات — تغییر نمایش، فرایند را خراب میکند؛ تغییر قانون، UI را بهطور غیرمنتظره میشکند.
☑️ پیچیدگی تست
---
🧩 چرا با این حال منطق را در کامپوننتها مینویسند؟
⏺️ سریعتر در شروع پروژه
⏺️ «برای یک قابلیت کوچک» بهنظر میرسد نیازی به جداکردن نیست
⏺️ تیم خسته است یا استاندارد معماری وجود ندارد
اما معمولاً همان «قابلیت کوچک» رشد میکند و بدهی فنی انباشته میشود 😥
---
🧩 چگونه بهتر کد را سازماندهی کنیم؟
✔️ کامپوننتهای UI — مسئول نمایش و واکنش به تعاملات کاربر
✔️ منطق کسبوکار — در سرویسها، استورها یا ماژولها
✔️ یکپارچهسازی — کامپوننتها سرویسها یا اکشنها را فراخوانی کرده و دادهها/وضعیتهای آماده را دریافت میکنند
مثال جریان داده:
---
🧩 چگونه خود را بررسی کنیم؟
اگر قالب (template) را از کامپوننت حذف کنیم و کد باقیمانده هنوز معنای مستقلی داشته باشد — یعنی منطق کسبوکار به کامپوننت نفوذ کرده است. این نشانهای برای تفکر درباره ریفکتورینگ است.
💡 نتیجهگیری:
UI = لایه نمایش
منطق کسبوکار = لایه تصمیمگیری
ادغام آنها ممکن است در ابتدا راحت باشد، اما در بلندمدت تقریباً همیشه منجر به افزایش پیچیدگی، شکنندگی سیستم و بدهی فنی میشود 🙈
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
اغلب یک الگوی تکراری را میبینم: یک کامپوننت کوچک با چند شرط ساده که پس از چند قابلیت کوچک به موجودی عظیم تبدیل میشود 🤯 — هم رابط را نمایش میدهد، هم تصمیم میگیرد، هم با API ارتباط برقرار میکند و کمی «جادو» هم انجام میدهد. در نتیجه، بهجای یک UI تمیز، مجموعهای از منطق پنهان خواهیم داشت که نگهداری آن بعداً بسیار دشوار است.
---
🧩 چگونه این اتفاق میافتد؟
در ابتدا همه چیز منطقی به نظر میرسد: زمانبندی محدود، فیتچر کوچک — سادهتر است که پردازش را مستقیم در کامپوننت قرار دهیم. سپس «بعداً بیرون میکشیم» هیچگاه اتفاق نمیافتد و کامپوننت رشد میکند:
➖ فراخوانی API و تبدیل پاسخها
➖ تصمیمگیری درباره گامهای فرایند
➖ شامل شاخههای قوانین کسبوکار
➖ منبعی برای کپیپیست هنگام استفاده مجدد
---
🧩 مشکل چیست؟
☑️ ادغام نقشها — کامپوننت باید رابط را نمایش دهد، نه مدیریت فرایند را. وقتی UI شروع به «تفکر» میکند، سیستم بسیار شکننده میشود.
☑️ از بین رفتن قابلیت استفاده مجدد — نیاز به همان منطق در جای دیگر باعث کپیپیست، ناهماهنگی و باگها میشود.
☑️ ریسک تغییرات — تغییر نمایش، فرایند را خراب میکند؛ تغییر قانون، UI را بهطور غیرمنتظره میشکند.
☑️ پیچیدگی تست
---
🧩 چرا با این حال منطق را در کامپوننتها مینویسند؟
⏺️ سریعتر در شروع پروژه
⏺️ «برای یک قابلیت کوچک» بهنظر میرسد نیازی به جداکردن نیست
⏺️ تیم خسته است یا استاندارد معماری وجود ندارد
اما معمولاً همان «قابلیت کوچک» رشد میکند و بدهی فنی انباشته میشود 😥
---
🧩 چگونه بهتر کد را سازماندهی کنیم؟
✔️ کامپوننتهای UI — مسئول نمایش و واکنش به تعاملات کاربر
✔️ منطق کسبوکار — در سرویسها، استورها یا ماژولها
✔️ یکپارچهسازی — کامپوننتها سرویسها یا اکشنها را فراخوانی کرده و دادهها/وضعیتهای آماده را دریافت میکنند
مثال جریان داده:
کامپوننت → اکشن / use-case → سرویس → مخزن/API → سرویس → use-case → کامپوننت (وضعیت بهروز شده)---
🧩 چگونه خود را بررسی کنیم؟
اگر قالب (template) را از کامپوننت حذف کنیم و کد باقیمانده هنوز معنای مستقلی داشته باشد — یعنی منطق کسبوکار به کامپوننت نفوذ کرده است. این نشانهای برای تفکر درباره ریفکتورینگ است.
💡 نتیجهگیری:
UI = لایه نمایش
منطق کسبوکار = لایه تصمیمگیری
ادغام آنها ممکن است در ابتدا راحت باشد، اما در بلندمدت تقریباً همیشه منجر به افزایش پیچیدگی، شکنندگی سیستم و بدهی فنی میشود 🙈
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
❤3👍1
خاصبودگی در CSS (معادل Specificity) به معنای «وزن» یا «قدرت» یک سلکتور است.
هرچه خاصبودگی یک سلکتور بیشتر باشد، در صورت تعارض با سایر قوانین، احتمال برنده شدن آن بیشتر است.
بهصورت خلاصه، ترتیب قدرت به این شکل است:
استایل درونخطی > شناسه (ID) > کلاس و شبهکلاس > عناصر و شبهعناصر
خاصبودگی معمولاً به شکل یک چهارتایی نمایش داده میشود، مانند:
(صفر، صفر، صفر، یک)
این اعداد بهترتیب نشاندهندهٔ تعداد استایلهای درونخطی، شناسهها، کلاسها/شبهکلاسها و عناصر هستند.
بنابراین «خاصبودگی» یعنی میزان اولویت یک قانون CSS هنگام بروز تداخل.
#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
هرچه خاصبودگی یک سلکتور بیشتر باشد، در صورت تعارض با سایر قوانین، احتمال برنده شدن آن بیشتر است.
بهصورت خلاصه، ترتیب قدرت به این شکل است:
استایل درونخطی > شناسه (ID) > کلاس و شبهکلاس > عناصر و شبهعناصر
خاصبودگی معمولاً به شکل یک چهارتایی نمایش داده میشود، مانند:
(صفر، صفر، صفر، یک)
این اعداد بهترتیب نشاندهندهٔ تعداد استایلهای درونخطی، شناسهها، کلاسها/شبهکلاسها و عناصر هستند.
بنابراین «خاصبودگی» یعنی میزان اولویت یک قانون CSS هنگام بروز تداخل.
#myId#myId#myId span {
/* 0-3-0-1 */
}
.myClass.myClass.myClass span {
/* 0-0-3-1 */
}#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
«میخواهم برای تگ p داخل header استایل تعریف کنم، اما نمیخواهم خاصبودگی را افزایش دهم» 😎
گاهی سادهترین کار — تعیین استایل برای p داخل header — به رقابتی بیپایان میان سلکتورها تبدیل میشود. شما مینویسید header p { … }، فرد دیگری .text { … } اضافه میکند، شما سلکتورها را پیچیدهتر میکنید و پروژه بهآرامی در وزنهای «جهنمی» CSS فرو میرود. 😞 خوشبختانه یک راهکار ظریف و بسیار کاربردی وجود دارد: استفاده از :where()
🧩 :where() چه میکند:
:where() یک شبهکلاس در CSS است که فهرستی از سلکتورها را میپذیرد و بدون توجه به محتوای درون آن، همواره خاصبودگی صفر دارد.
💡 یعنی حتی اگر درون آن #id .class element قرار دهید، «وزن» نهایی همچنان صفر خواهد بود — برای کل سازهی :where().
🔍 یادآوری کوتاهی درباره خاصبودگی:
خاصبودگی — وزن سلکتور است. بهصورت ساده: استایل درونخطی بیشتر از شناسه، شناسه بیشتر از کلاس یا شبهکلاس، و آنها بیشتر از عناصر یا شبهعناصر وزن دارند.
الگوریتمهای CSS هنگام بروز تعارض از آن برای تعیین برنده استفاده میکنند:
⏺️ p → خاصبودگی (صفر، صفر، صفر، یک)
⏺️ :where(header) p → :where(header) صفر میدهد، p یک میدهد → در مجموع (صفر، صفر، صفر، یک)
⏺️ header p → header بهعلاوه p → (صفر، صفر، صفر، دو) — این سلکتور از قبل سنگینتر است
➡️ p و :where(header) p خاصبودگی یکسانی دارند — در صورت تعارض، قانونی که دیرتر تعریف شده باشد برنده خواهد شد.
در نتیجه، از :where() برای تعریف قواعد پیشفرض وابسته به کانتکست، بدون افزایش خاصبودگی استفاده کنید: ریست یا نرمالسازی، تایپوگرافی پایه، پیشفرضها درون کانتینرهای لایهبندی. این کار به شما اجازه میدهد کانتکست موردنظر را تعریف کنید، بدون آنکه آبشار را بر هم بزنید یا سلکتورها را سنگینتر کنید. 👍
#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
گاهی سادهترین کار — تعیین استایل برای p داخل header — به رقابتی بیپایان میان سلکتورها تبدیل میشود. شما مینویسید header p { … }، فرد دیگری .text { … } اضافه میکند، شما سلکتورها را پیچیدهتر میکنید و پروژه بهآرامی در وزنهای «جهنمی» CSS فرو میرود. 😞 خوشبختانه یک راهکار ظریف و بسیار کاربردی وجود دارد: استفاده از :where()
🧩 :where() چه میکند:
:where() یک شبهکلاس در CSS است که فهرستی از سلکتورها را میپذیرد و بدون توجه به محتوای درون آن، همواره خاصبودگی صفر دارد.
💡 یعنی حتی اگر درون آن #id .class element قرار دهید، «وزن» نهایی همچنان صفر خواهد بود — برای کل سازهی :where().
🔍 یادآوری کوتاهی درباره خاصبودگی:
خاصبودگی — وزن سلکتور است. بهصورت ساده: استایل درونخطی بیشتر از شناسه، شناسه بیشتر از کلاس یا شبهکلاس، و آنها بیشتر از عناصر یا شبهعناصر وزن دارند.
الگوریتمهای CSS هنگام بروز تعارض از آن برای تعیین برنده استفاده میکنند:
⏺️ p → خاصبودگی (صفر، صفر، صفر، یک)
⏺️ :where(header) p → :where(header) صفر میدهد، p یک میدهد → در مجموع (صفر، صفر، صفر، یک)
⏺️ header p → header بهعلاوه p → (صفر، صفر، صفر، دو) — این سلکتور از قبل سنگینتر است
➡️ p و :where(header) p خاصبودگی یکسانی دارند — در صورت تعارض، قانونی که دیرتر تعریف شده باشد برنده خواهد شد.
در نتیجه، از :where() برای تعریف قواعد پیشفرض وابسته به کانتکست، بدون افزایش خاصبودگی استفاده کنید: ریست یا نرمالسازی، تایپوگرافی پایه، پیشفرضها درون کانتینرهای لایهبندی. این کار به شما اجازه میدهد کانتکست موردنظر را تعریف کنید، بدون آنکه آبشار را بر هم بزنید یا سلکتورها را سنگینتر کنید. 👍
#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
آشنایی با معماری مبتنی بر Feature 🤩
معماری Feature-based کد را بر اساس قابلیتهای کسبوکاری سازماندهی میکند، نه لایههای فنی. به همین دلیل تغییرات موضعی میمانند، تیمها مستقلتر کار میکنند و پایگاه کد همزمان با رشد محصول، قابلفهم باقی میماند.
این معماری بر ایدهٔ بومیسازی کانتکست بنا شده است: هر فیچر بهصورت یک ماژول مستقل طراحی میشود که تمام اجزای موردنیاز برای عملکرد خود را درونش دارد — رابط کاربری، وضعیت، منطق کسبوکار، ارتباط با API و تستها. در نتیجه، توسعهدهنده برای اعمال یک تغییر لازم نیست میان دهها پوشه جابهجا شود، زیرا تمام کد مرتبط در یک محل قرار دارد و بهصورت یک واحد منسجم خوانده میشود. 👍
این رویکرد بهویژه همراه با رشد تیم بهخوبی مقیاسپذیر است. زمانی که فیچرها ایزوله باشند، چند توسعهدهنده یا چند تیم میتوانند بهطور موازی روی بخشهای مختلف محصول کار کنند، بیآنکه مزاحم یکدیگر شوند. همچنین با گذر زمان میتوان هر فیچر را بدون ریفکتورینگهای دردناک به یک پکیج یا سرویس مستقل تبدیل کرد. پیامد مثبت دیگر، سادهتر شدن تستنویسی و ریفکتورینگ است؛ زیرا مرز مسئولیتها بهروشنی مشخص است و رفتار هر فیچر را میتوان بهصورت ایزوله بررسی کرد.
❗️❗️❗️❗️
با این حال، معماری Feature-based بهخودیخود کار نمیکند — به قواعد مشخص نیاز دارد. مهمترین اصل، تعریف مرزهای شفاف و قراردادهای عمومی است. فیچرها نباید مستقیماً به جزئیات داخلی یکدیگر ایمپورت داشته باشند و تعامل میان آنها باید صرفاً از طریق API عمومیِ بهصراحت تعریفشده انجام شود. کد مشترک به بخش shared منتقل میشود، اما این بخش باید محدود و پایدار بماند؛ اگر shared بدون کنترل رشد کند، بهسرعت به یک مونولیت پنهان تبدیل میشود و ایزولاسیون فیچرها را از بین میبرد.
👩💻 نمونهای حداقلی از ساختار یک فیچر در فرانتاند:
فایل index.ts نقشی کلیدی دارد: بهصورت شفاف مشخص میکند که چه بخشهایی از فیچر مجاز به استفاده از بیرون هستند و سایر قسمتها بهعنوان جزئیات داخلی پیادهسازی باقی میمانند. همچنین فایل README با توضیح کوتاه درباره هدف فیچر و API عمومی آن، ورود توسعهدهندگان جدید را آسانتر میکند و به حفظ مرزهای معماری کمک مینماید. 😁
‼️ درک محدودیتهای این رویکرد نیز ضروری است. برای پروژههای کوچک، معماری Feature-based ممکن است بیشازحد پیچیده باشد، زیرا به انضباط، مستندسازی و اتوماسیون نیاز دارد. اما برای محصولاتی که در حال رشد هستند، به ابزاری برای مدیریت پیچیدگی تبدیل میشود: تغییرات موضعی و قابلپیشبینی میشوند، پایگاه کد خواناتر میشود و تیمها استقلال بیشتری پیدا میکنند.
در نهایت، معماری Feature-based صرفاً درباره پوشهها نیست، بلکه درباره تفکر مبتنی بر فیچر، مرزهای شفاف و کنترل وابستگیهاست. با تعریف قواعد درست و اتوماسیون مناسب، این رویکرد به سیستم اجازه میدهد رشد کند، بدون آنکه شفافیت و سرعت توسعه را از دست بدهد. 👍
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
معماری Feature-based کد را بر اساس قابلیتهای کسبوکاری سازماندهی میکند، نه لایههای فنی. به همین دلیل تغییرات موضعی میمانند، تیمها مستقلتر کار میکنند و پایگاه کد همزمان با رشد محصول، قابلفهم باقی میماند.
این معماری بر ایدهٔ بومیسازی کانتکست بنا شده است: هر فیچر بهصورت یک ماژول مستقل طراحی میشود که تمام اجزای موردنیاز برای عملکرد خود را درونش دارد — رابط کاربری، وضعیت، منطق کسبوکار، ارتباط با API و تستها. در نتیجه، توسعهدهنده برای اعمال یک تغییر لازم نیست میان دهها پوشه جابهجا شود، زیرا تمام کد مرتبط در یک محل قرار دارد و بهصورت یک واحد منسجم خوانده میشود. 👍
این رویکرد بهویژه همراه با رشد تیم بهخوبی مقیاسپذیر است. زمانی که فیچرها ایزوله باشند، چند توسعهدهنده یا چند تیم میتوانند بهطور موازی روی بخشهای مختلف محصول کار کنند، بیآنکه مزاحم یکدیگر شوند. همچنین با گذر زمان میتوان هر فیچر را بدون ریفکتورینگهای دردناک به یک پکیج یا سرویس مستقل تبدیل کرد. پیامد مثبت دیگر، سادهتر شدن تستنویسی و ریفکتورینگ است؛ زیرا مرز مسئولیتها بهروشنی مشخص است و رفتار هر فیچر را میتوان بهصورت ایزوله بررسی کرد.
❗️❗️❗️❗️
با این حال، معماری Feature-based بهخودیخود کار نمیکند — به قواعد مشخص نیاز دارد. مهمترین اصل، تعریف مرزهای شفاف و قراردادهای عمومی است. فیچرها نباید مستقیماً به جزئیات داخلی یکدیگر ایمپورت داشته باشند و تعامل میان آنها باید صرفاً از طریق API عمومیِ بهصراحت تعریفشده انجام شود. کد مشترک به بخش shared منتقل میشود، اما این بخش باید محدود و پایدار بماند؛ اگر shared بدون کنترل رشد کند، بهسرعت به یک مونولیت پنهان تبدیل میشود و ایزولاسیون فیچرها را از بین میبرد.
👩💻 نمونهای حداقلی از ساختار یک فیچر در فرانتاند:
src/
features/
Cart/
components/
hooks/
api.ts
index.ts // API
README.md
shared/
ui/
utils/
فایل index.ts نقشی کلیدی دارد: بهصورت شفاف مشخص میکند که چه بخشهایی از فیچر مجاز به استفاده از بیرون هستند و سایر قسمتها بهعنوان جزئیات داخلی پیادهسازی باقی میمانند. همچنین فایل README با توضیح کوتاه درباره هدف فیچر و API عمومی آن، ورود توسعهدهندگان جدید را آسانتر میکند و به حفظ مرزهای معماری کمک مینماید. 😁
‼️ درک محدودیتهای این رویکرد نیز ضروری است. برای پروژههای کوچک، معماری Feature-based ممکن است بیشازحد پیچیده باشد، زیرا به انضباط، مستندسازی و اتوماسیون نیاز دارد. اما برای محصولاتی که در حال رشد هستند، به ابزاری برای مدیریت پیچیدگی تبدیل میشود: تغییرات موضعی و قابلپیشبینی میشوند، پایگاه کد خواناتر میشود و تیمها استقلال بیشتری پیدا میکنند.
در نهایت، معماری Feature-based صرفاً درباره پوشهها نیست، بلکه درباره تفکر مبتنی بر فیچر، مرزهای شفاف و کنترل وابستگیهاست. با تعریف قواعد درست و اتوماسیون مناسب، این رویکرد به سیستم اجازه میدهد رشد کند، بدون آنکه شفافیت و سرعت توسعه را از دست بدهد. 👍
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
👍3
آشنایی با CSR (Client-Side Rendering) 👨🏫
Client-Side Rendering یا CSR رویکردی در رندرینگ است که در آن سرور تنها یک اسکلت حداقلی HTML ارسال میکند و کل رابط کاربری و منطق اصلی در مرورگر کاربر و با استفاده از JavaScript ساخته میشود. مرورگر اسکریپتها را دانلود میکند، کد را اجرا میکند و ساختار DOM را در سمت کاربر شکل میدهد.
مهمترین مزیت CSR، تعاملپذیری پس از مقداردهی اولیه است. رابط کاربری روان و پاسخگو عمل میکند: پیادهسازی ویجتهای پیچیده، شخصیسازی پویا و ناوبری بدون بارگذاری مجدد صفحه بهسادگی امکانپذیر است.
با این حال، این انعطاف هزینه دارد — بار اصلی ساخت رابط کاربری بر دوش دستگاه کاربر قرار میگیرد. 😐
تا زمانی که مرورگر در حال دانلود، تجزیه و اجرای JavaScript است، صفحه ممکن است تنها بخشی از محتوا را نمایش دهد یا حتی کاملاً «سفید» باقی بماند. حتی اگر رابط کاربری قابل مشاهده باشد، اغلب هنوز تعاملپذیری کامل ندارد: کلیکها و ورودیها با تأخیر پردازش میشوند. این مسئله بهویژه در دستگاههای ضعیفتر و شبکههای کندتر محسوس است. ☹️
همچنین باید تأثیر CSR بر ایندکسگذاری و پیشنمایش را در نظر گرفت. اگر بخش قابلتوجهی از محتوا تنها در سمت کاربر تولید شود، موتورهای جستوجو و شبکههای اجتماعی برای تحلیل صحیح صفحه و تولید اسنیپتها با دشواری بیشتری مواجه میشوند.
در نتیجه، CSR بیش از همه برای اپلیکیشنهای با تعاملپذیری بالا مناسب است — مانند داشبوردها، پنلهای مدیریتی و سرویسهای داخلی که کاربر زمان زیادی در آنها سپری میکند و پس از بارگذاری اولیه، روانی عملکرد برایش اهمیت دارد. 👍
برای صفحات محتوایی و بازاریابی معمولاً رویکردهای دیگری انتخاب میشوند که در ادامه به آنها خواهیم پرداخت. 😉
برای کاهش معایب CSR معمولاً از این راهکارها استفاده میشود:
⏺️ تقسیم باندلها،
⏺️ بارگذاری تنبل کد،
⏺️ و البته مدیریت دقیق حجم و زمان اجرای JavaScript.
چنین بهینهسازیهایی کمک میکند انعطافپذیری رندرینگ سمت کاربر حفظ شود، بدون آنکه تجربه کاربری قربانی شود.
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
Client-Side Rendering یا CSR رویکردی در رندرینگ است که در آن سرور تنها یک اسکلت حداقلی HTML ارسال میکند و کل رابط کاربری و منطق اصلی در مرورگر کاربر و با استفاده از JavaScript ساخته میشود. مرورگر اسکریپتها را دانلود میکند، کد را اجرا میکند و ساختار DOM را در سمت کاربر شکل میدهد.
مهمترین مزیت CSR، تعاملپذیری پس از مقداردهی اولیه است. رابط کاربری روان و پاسخگو عمل میکند: پیادهسازی ویجتهای پیچیده، شخصیسازی پویا و ناوبری بدون بارگذاری مجدد صفحه بهسادگی امکانپذیر است.
با این حال، این انعطاف هزینه دارد — بار اصلی ساخت رابط کاربری بر دوش دستگاه کاربر قرار میگیرد. 😐
تا زمانی که مرورگر در حال دانلود، تجزیه و اجرای JavaScript است، صفحه ممکن است تنها بخشی از محتوا را نمایش دهد یا حتی کاملاً «سفید» باقی بماند. حتی اگر رابط کاربری قابل مشاهده باشد، اغلب هنوز تعاملپذیری کامل ندارد: کلیکها و ورودیها با تأخیر پردازش میشوند. این مسئله بهویژه در دستگاههای ضعیفتر و شبکههای کندتر محسوس است. ☹️
همچنین باید تأثیر CSR بر ایندکسگذاری و پیشنمایش را در نظر گرفت. اگر بخش قابلتوجهی از محتوا تنها در سمت کاربر تولید شود، موتورهای جستوجو و شبکههای اجتماعی برای تحلیل صحیح صفحه و تولید اسنیپتها با دشواری بیشتری مواجه میشوند.
در نتیجه، CSR بیش از همه برای اپلیکیشنهای با تعاملپذیری بالا مناسب است — مانند داشبوردها، پنلهای مدیریتی و سرویسهای داخلی که کاربر زمان زیادی در آنها سپری میکند و پس از بارگذاری اولیه، روانی عملکرد برایش اهمیت دارد. 👍
برای صفحات محتوایی و بازاریابی معمولاً رویکردهای دیگری انتخاب میشوند که در ادامه به آنها خواهیم پرداخت. 😉
برای کاهش معایب CSR معمولاً از این راهکارها استفاده میشود:
⏺️ تقسیم باندلها،
⏺️ بارگذاری تنبل کد،
⏺️ و البته مدیریت دقیق حجم و زمان اجرای JavaScript.
چنین بهینهسازیهایی کمک میکند انعطافپذیری رندرینگ سمت کاربر حفظ شود، بدون آنکه تجربه کاربری قربانی شود.
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
👍1
INP: پاسخگویی صفحه چگونه اندازهگیری میشود 🤨
گاهی یک سایت سریع بارگذاری میشود و محتوا تقریباً بلافاصله ظاهر میشود، اما استفاده از آن چندان لذتبخش نیست. کلیک با تأخیر اجرا میشود، فیلد ورودی دیر واکنش نشان میدهد و رابط کاربری انگار «در حال فکر کردن» است. از نظر فنی همهچیز بارگذاری شده، اما حس پاسخگویی چندان مطلوب نیست. 😥
چنین وضعیتهایی را INP (Interaction to Next Paint) توصیف میکند.
INP معیاری است که نشان میدهد رابط کاربری با چه سرعتی به اعمال کاربر واکنش نشان میدهد. این معیار فاصله زمانی بین یک اقدام کاربر (کلیک، لمس یا ورود داده) و لحظهای را اندازهگیری میکند که مرورگر رندر فریم بعدی — که نتیجه آن اقدام را نمایش میدهد — به پایان میرساند. به بیان ساده، مدتزمان بین عمل کاربر و بازخورد بصری.
نکته مهم این است که INP یک تعامل خاص را اندازهگیری نمیکند، بلکه صدک هفتاد و پنجم تمام تعاملات در طول چرخه حیات صفحه را در نظر میگیرد. یعنی این معیار نشان میدهد کاربر در اغلب مواقع چه تجربهای دارد، نه بهترین یا بدترین نمونه منفرد.
از سال دو هزار و بیست و چهار، INP به مجموعه Core Web Vitals اضافه شده و جایگزین FID شده است. دلیل آن روشن است: FID تنها اولین تعامل را در نظر میگرفت، در حالیکه INP همه تعاملات کاربر را پوشش میدهد و تصویر کاملتری از پاسخگویی واقعی صفحه ارائه میدهد. ‼️
در عمل، INP بالا تقریباً همیشه ناشی از اشغال بودن رشته اصلی مرورگر است. اگر کاربر با رابط تعامل کند، اما مرورگر مشغول اجرای وظایف طولانی باشد، پردازش رویدادها به تعویق میافتد و بازخورد بصری با تأخیر نمایش داده میشود.
عوامل رایج در تضعیف INP عبارتاند از:
⏺️ وظایف طولانی در رشته اصلی؛
⏺️ هندلرهای رویداد پیچیده؛
⏺️ محاسبات همزمان هنگام کلیک یا ورود داده؛
⏺️ حجم زیاد JavaScript شخص ثالث؛
⏺️ بهروزرسانیهای سنگین DOM.
بهبود INP معمولاً به راهکارهای پیچیده نیاز ندارد — در بیشتر موارد کافی است وظایف طولانی را کوتاه و خرد کنید، منطق هندلرهای رویداد را سادهتر سازید، محاسبات سنگین را به Web Worker منتقل کنید، بارگذاری کدهای غیرضروری را به تعویق بیندازید و تأثیر اسکریپتهای شخص ثالث را کاهش دهید. 👍
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
گاهی یک سایت سریع بارگذاری میشود و محتوا تقریباً بلافاصله ظاهر میشود، اما استفاده از آن چندان لذتبخش نیست. کلیک با تأخیر اجرا میشود، فیلد ورودی دیر واکنش نشان میدهد و رابط کاربری انگار «در حال فکر کردن» است. از نظر فنی همهچیز بارگذاری شده، اما حس پاسخگویی چندان مطلوب نیست. 😥
چنین وضعیتهایی را INP (Interaction to Next Paint) توصیف میکند.
INP معیاری است که نشان میدهد رابط کاربری با چه سرعتی به اعمال کاربر واکنش نشان میدهد. این معیار فاصله زمانی بین یک اقدام کاربر (کلیک، لمس یا ورود داده) و لحظهای را اندازهگیری میکند که مرورگر رندر فریم بعدی — که نتیجه آن اقدام را نمایش میدهد — به پایان میرساند. به بیان ساده، مدتزمان بین عمل کاربر و بازخورد بصری.
نکته مهم این است که INP یک تعامل خاص را اندازهگیری نمیکند، بلکه صدک هفتاد و پنجم تمام تعاملات در طول چرخه حیات صفحه را در نظر میگیرد. یعنی این معیار نشان میدهد کاربر در اغلب مواقع چه تجربهای دارد، نه بهترین یا بدترین نمونه منفرد.
از سال دو هزار و بیست و چهار، INP به مجموعه Core Web Vitals اضافه شده و جایگزین FID شده است. دلیل آن روشن است: FID تنها اولین تعامل را در نظر میگرفت، در حالیکه INP همه تعاملات کاربر را پوشش میدهد و تصویر کاملتری از پاسخگویی واقعی صفحه ارائه میدهد. ‼️
در عمل، INP بالا تقریباً همیشه ناشی از اشغال بودن رشته اصلی مرورگر است. اگر کاربر با رابط تعامل کند، اما مرورگر مشغول اجرای وظایف طولانی باشد، پردازش رویدادها به تعویق میافتد و بازخورد بصری با تأخیر نمایش داده میشود.
عوامل رایج در تضعیف INP عبارتاند از:
⏺️ وظایف طولانی در رشته اصلی؛
⏺️ هندلرهای رویداد پیچیده؛
⏺️ محاسبات همزمان هنگام کلیک یا ورود داده؛
⏺️ حجم زیاد JavaScript شخص ثالث؛
⏺️ بهروزرسانیهای سنگین DOM.
بهبود INP معمولاً به راهکارهای پیچیده نیاز ندارد — در بیشتر موارد کافی است وظایف طولانی را کوتاه و خرد کنید، منطق هندلرهای رویداد را سادهتر سازید، محاسبات سنگین را به Web Worker منتقل کنید، بارگذاری کدهای غیرضروری را به تعویق بیندازید و تأثیر اسکریپتهای شخص ثالث را کاهش دهید. 👍
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
رابط برنامهنویسی «Compression Streams» — فشردهسازی بومی gzip/deflate مستقیم در مرورگر 🤔
اخیراً دوباره یاد Compression Streams API افتادم — و این دقیقاً از همان مواردی است که پلتفرم خودش از قبل قابلیتی را دارد که قبلاً برایش مجبور بودیم یک کتابخانهٔ جداگانه اضافه کنیم. 🤭
این یک API داخلی و جریانی برای فشردهسازی و بازفشردهسازی دادهها در قالبهای gzip و deflate است. این قابلیت بر پایهٔ Streams کار میکند؛ یعنی دادهها بهصورت بخشبخش پردازش میشوند، بدون اینکه لازم باشد کل محتوا یکجا در حافظه بارگذاری شود.
اگر بخواهیم خلاصه بگوییم: حالا میتوان دادهها را مستقیم در سمت کلاینت فشرده و بازفشرده کرد — بدون هیچ وابستگی خارجی.
🧩 چه نکاتی را باید به خاطر سپرد؟
✔️ قالبها: gzip و deflate
✔️ کلاسها: CompressionStream و DecompressionStream
✔️ بر پایهٔ Streams کار میکند — دادهها بهصورت قطعهای پردازش میشوند، بدون بارگذاری کامل در حافظه
✔️ از طریق pipeThrough() استفاده میشود — و بهراحتی در هر زنجیرهٔ استریم جا میگیرد
ReadableStream → CompressionStream → WritableStream — و همهٔ اینها بهصورت «در لحظه» انجام میشود.
🧩 مثال: فشردهسازی با gzip
🧩 کجا کاربرد دارد؟
⏺️ فشردهسازی دادهها پیش از ارسال به سرور؛
⏺️ ذخیرهسازی یا پردازش فایلهای بزرگ JSON / CSV / لاگها؛
⏺️ کاهش وابستگی به کتابخانهها — بهویژه هنگام کار با فایلهای حجیم
از معایب آن: فقط gzip و deflate پشتیبانی میشوند (نه آرشیوهای ZIP)، امکان تنظیم سطح فشردهسازی وجود ندارد، و در مرورگرهای قدیمی ممکن است این API در دسترس نباشد. 😕
در نهایت، این یک قابلیت ساده و بیسروصدای پلتفرم است — و برای بیشتر کارهای معمول فشردهسازی کاملاً کافی است. 👍
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
اخیراً دوباره یاد Compression Streams API افتادم — و این دقیقاً از همان مواردی است که پلتفرم خودش از قبل قابلیتی را دارد که قبلاً برایش مجبور بودیم یک کتابخانهٔ جداگانه اضافه کنیم. 🤭
این یک API داخلی و جریانی برای فشردهسازی و بازفشردهسازی دادهها در قالبهای gzip و deflate است. این قابلیت بر پایهٔ Streams کار میکند؛ یعنی دادهها بهصورت بخشبخش پردازش میشوند، بدون اینکه لازم باشد کل محتوا یکجا در حافظه بارگذاری شود.
اگر بخواهیم خلاصه بگوییم: حالا میتوان دادهها را مستقیم در سمت کلاینت فشرده و بازفشرده کرد — بدون هیچ وابستگی خارجی.
🧩 چه نکاتی را باید به خاطر سپرد؟
✔️ قالبها: gzip و deflate
✔️ کلاسها: CompressionStream و DecompressionStream
✔️ بر پایهٔ Streams کار میکند — دادهها بهصورت قطعهای پردازش میشوند، بدون بارگذاری کامل در حافظه
✔️ از طریق pipeThrough() استفاده میشود — و بهراحتی در هر زنجیرهٔ استریم جا میگیرد
ReadableStream → CompressionStream → WritableStream — و همهٔ اینها بهصورت «در لحظه» انجام میشود.
🧩 مثال: فشردهسازی با gzip
async function compress(text) {
const encoder = new TextEncoder();
const stream = new Blob([encoder.encode(text)]).stream();
const compressed = stream
.pipeThrough(new CompressionStream("gzip"));
return await new Response(compressed).blob();
}🧩 کجا کاربرد دارد؟
⏺️ فشردهسازی دادهها پیش از ارسال به سرور؛
⏺️ ذخیرهسازی یا پردازش فایلهای بزرگ JSON / CSV / لاگها؛
⏺️ کاهش وابستگی به کتابخانهها — بهویژه هنگام کار با فایلهای حجیم
از معایب آن: فقط gzip و deflate پشتیبانی میشوند (نه آرشیوهای ZIP)، امکان تنظیم سطح فشردهسازی وجود ندارد، و در مرورگرهای قدیمی ممکن است این API در دسترس نباشد. 😕
در نهایت، این یک قابلیت ساده و بیسروصدای پلتفرم است — و برای بیشتر کارهای معمول فشردهسازی کاملاً کافی است. 👍
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
چطور محتوای «پنهان، اما قابلجستوجو» بسازیم؟ 🔍
تصور کنید یک صفحهٔ طولانی با بخش پرسشهای متداول، آکاردئونها و قسمتهای جمعشونده دارید. کاربر Ctrl/Cmd+F را میزند، یک کلمه وارد میکند و... مرورگر هیچ چیزی نشان نمیدهد، چون متن موردنظر داخل یک آکاردئون بسته پنهان شده است. آزاردهنده است، نه؟ 😔
برای چنین موقعیتهایی، ویژگی hidden="until-found" وجود دارد — یک صفت که اجازه میدهد محتوا از نظر بصری مخفی باشد، اما همچنان برای جستوجو در صفحه قابلدسترسی بماند. اگر مرورگر تطابقی پیدا کند، بهصورت خودکار بخش مربوطه را باز میکند و امکان هماهنگسازی رابط کاربری از طریق رویداد beforematch را فراهم میسازد.
در اصل، این یک بهبود تدریجی است:
✔️ اگر مرورگر از این قابلیت پشتیبانی کند → جستوجو بهصورت «جادویی» عمل میکند؛
✔️ اگر پشتیبانی نکند → رابط کاربری همچنان کاملاً قابل استفاده باقی میماند.
مزیت اصلی این رویکرد، داشتن یک رابط جمعوجور بدون از دست دادن قابلیت پیدا شدن محتواست. کاربر همچنان میتواند با جستوجوی معمولی، سریع بخش موردنظر را پیدا کند، حتی اگر داخل آکاردئون پنهان شده باشد.
اگر مرورگر نتواند محتوا را خودکار باز کند، اتفاق بدی نمیافتد. کافی است پشتیبانی از رویداد beforematch را بررسی کنید و یک سناریوی جایگزین فعال کنید — مثلاً همهٔ بخشها را باز کنید یا از جستوجوی سفارشی استفاده کنید.
‼️ نکاتی که باید به خاطر داشت
⏺️ استفاده از beforematch برای هماهنگ کردن آکاردئون و اسکرول به محل پیدا شده بسیار کاربردی است؛
⏺️ hidden="until-found" همان display: none نیست: رفتار APIهای مربوط به چیدمان (مثل getBoundingClientRect، content-visibility، contain) ممکن است متفاوت باشد، پس بهتر است حتماً تست شود؛
⏺️ همهٔ مرورگرها از این قابلیت پشتیبانی نمیکنند، بنابراین برای مرورگرهای بدون پشتیبانی باید از قبل یک fallback در نظر گرفت: باز کردن صریح بخشهای مهم یا ارائهٔ جستوجوی جایگزین. 😁
🔗https://codepen.io/web-dot-dev/pen/Rwxveab
🔗https://developer.chrome.com/docs/css-ui/hidden-until-found
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
تصور کنید یک صفحهٔ طولانی با بخش پرسشهای متداول، آکاردئونها و قسمتهای جمعشونده دارید. کاربر Ctrl/Cmd+F را میزند، یک کلمه وارد میکند و... مرورگر هیچ چیزی نشان نمیدهد، چون متن موردنظر داخل یک آکاردئون بسته پنهان شده است. آزاردهنده است، نه؟ 😔
برای چنین موقعیتهایی، ویژگی hidden="until-found" وجود دارد — یک صفت که اجازه میدهد محتوا از نظر بصری مخفی باشد، اما همچنان برای جستوجو در صفحه قابلدسترسی بماند. اگر مرورگر تطابقی پیدا کند، بهصورت خودکار بخش مربوطه را باز میکند و امکان هماهنگسازی رابط کاربری از طریق رویداد beforematch را فراهم میسازد.
در اصل، این یک بهبود تدریجی است:
✔️ اگر مرورگر از این قابلیت پشتیبانی کند → جستوجو بهصورت «جادویی» عمل میکند؛
✔️ اگر پشتیبانی نکند → رابط کاربری همچنان کاملاً قابل استفاده باقی میماند.
مزیت اصلی این رویکرد، داشتن یک رابط جمعوجور بدون از دست دادن قابلیت پیدا شدن محتواست. کاربر همچنان میتواند با جستوجوی معمولی، سریع بخش موردنظر را پیدا کند، حتی اگر داخل آکاردئون پنهان شده باشد.
اگر مرورگر نتواند محتوا را خودکار باز کند، اتفاق بدی نمیافتد. کافی است پشتیبانی از رویداد beforematch را بررسی کنید و یک سناریوی جایگزین فعال کنید — مثلاً همهٔ بخشها را باز کنید یا از جستوجوی سفارشی استفاده کنید.
if (!('onbeforematch' in document)) {
// منطق جایگزین (fallback)
}‼️ نکاتی که باید به خاطر داشت
⏺️ استفاده از beforematch برای هماهنگ کردن آکاردئون و اسکرول به محل پیدا شده بسیار کاربردی است؛
⏺️ hidden="until-found" همان display: none نیست: رفتار APIهای مربوط به چیدمان (مثل getBoundingClientRect، content-visibility، contain) ممکن است متفاوت باشد، پس بهتر است حتماً تست شود؛
⏺️ همهٔ مرورگرها از این قابلیت پشتیبانی نمیکنند، بنابراین برای مرورگرهای بدون پشتیبانی باید از قبل یک fallback در نظر گرفت: باز کردن صریح بخشهای مهم یا ارائهٔ جستوجوی جایگزین. 😁
🔗https://codepen.io/web-dot-dev/pen/Rwxveab
🔗https://developer.chrome.com/docs/css-ui/hidden-until-found
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
codepen.io
hidden="until-found" demo showing styling of element nested inside container.
...
انسجام و پیوستگی: چگونه کدی بنویسیم که از تغییر دادنش نترسیم 🧩
پروژه را مثل یک خانه تصور کنید. اتاقها همان ماژولها و کامپوننتها هستند. در یک خانهٔ خوب، هر اتاق کاربرد مشخصی دارد: آشپزخانه برای آشپزی، اتاق خواب برای استراحت، حمام برای بهداشت. اما اگر در یک اتاق هم اجاق باشد، هم تخت، هم ماشین لباسشویی، زندگی سخت و آشفته میشود — این یعنی انسجام پایین: اجزای داخل یک ماژول مشغول کارهای متفاوتی هستند. 😏
درها و راهروها همان رابطها و APIهایی هستند که ماژولها را به هم وصل میکنند. درهای خوب ساده و قابل پیشبینیاند: میتوانید محتوای یک اتاق را تغییر دهید بدون اینکه بقیهٔ خانه تحت تأثیر قرار بگیرد. اما درهای بد یعنی نبودِ مرزهای درست، جایی که مجبور میشوید «از دیوار رد شوید»: هر تغییر، زنجیرهای از اصلاحات را به دنبال میآورد. این همان پیوستگی بالا است.
خانهٔ ایدهآل یعنی اتاقهای مشخص و درهای مرتب. در کدنویسی هم یعنی: هر ماژول یک وظیفه انجام دهد و تا حد ممکن دربارهٔ بقیهٔ سیستم کم بداند. چنین کدی راحتتر تست میشود، آسانتر تغییر میکند و بهتر رشد میکند.
☑️ به خاطر بسپاریم:
انسجام (Cohesion) — یعنی اینکه اجزای داخل یک ماژول/کلاس/سرویس چقدر روی یک هدف مشترک متمرکز هستند.
انسجام بالا = ماژول یک کار مشخص انجام میدهد و آن را خوب انجام میدهد.
پیوستگی (Coupling) — یعنی اینکه یک ماژول چقدر به دیگر ماژولها وابسته است.
پیوستگی پایین = میتوان یک ماژول را تغییر داد بدون اینکه کل سیستم دچار موج اصلاحات شود.
ایدهآل: انسجام بالا + پیوستگی پایین.
🧩 مزیت اصلی چیست؟
☑️ نگهداری کد ارزانتر و سریعتر میشود؛
☑️ تغییرات محدود و موضعی میمانند — با اثرات جانبی کمتر؛
☑️ معماری انعطافپذیر باقی میماند: میتوان پیادهسازیها را عوض کرد بدون اینکه سیستم بشکند؛
☑️ و البته تست کردن سادهتر است — ماژولی که فقط یک مسئولیت دارد، راحتتر موک میشود و پوشش تست بهتری میگیرد.
یک مثال کوچک در تصویر بالا 👆
و البته، معماری خوب یک ایدئولوژی نیست، بلکه یک ابزار است: زمان را ذخیره میکند، نگهداری را سادهتر میکند و تغییرات را امنتر. اما همیشه ممکن است استثناهایی وجود داشته باشد: گاهی لازم است برای یک راهحل فوری یا یک نمونهٔ اولیه، کمی از پاکیزگی کد صرفنظر کنیم. مهم این است که این مصالحهها آگاهانه و موقتی باشند، نه اینکه تبدیل به عادت شوند. 😊
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
پروژه را مثل یک خانه تصور کنید. اتاقها همان ماژولها و کامپوننتها هستند. در یک خانهٔ خوب، هر اتاق کاربرد مشخصی دارد: آشپزخانه برای آشپزی، اتاق خواب برای استراحت، حمام برای بهداشت. اما اگر در یک اتاق هم اجاق باشد، هم تخت، هم ماشین لباسشویی، زندگی سخت و آشفته میشود — این یعنی انسجام پایین: اجزای داخل یک ماژول مشغول کارهای متفاوتی هستند. 😏
درها و راهروها همان رابطها و APIهایی هستند که ماژولها را به هم وصل میکنند. درهای خوب ساده و قابل پیشبینیاند: میتوانید محتوای یک اتاق را تغییر دهید بدون اینکه بقیهٔ خانه تحت تأثیر قرار بگیرد. اما درهای بد یعنی نبودِ مرزهای درست، جایی که مجبور میشوید «از دیوار رد شوید»: هر تغییر، زنجیرهای از اصلاحات را به دنبال میآورد. این همان پیوستگی بالا است.
خانهٔ ایدهآل یعنی اتاقهای مشخص و درهای مرتب. در کدنویسی هم یعنی: هر ماژول یک وظیفه انجام دهد و تا حد ممکن دربارهٔ بقیهٔ سیستم کم بداند. چنین کدی راحتتر تست میشود، آسانتر تغییر میکند و بهتر رشد میکند.
☑️ به خاطر بسپاریم:
انسجام (Cohesion) — یعنی اینکه اجزای داخل یک ماژول/کلاس/سرویس چقدر روی یک هدف مشترک متمرکز هستند.
انسجام بالا = ماژول یک کار مشخص انجام میدهد و آن را خوب انجام میدهد.
پیوستگی (Coupling) — یعنی اینکه یک ماژول چقدر به دیگر ماژولها وابسته است.
پیوستگی پایین = میتوان یک ماژول را تغییر داد بدون اینکه کل سیستم دچار موج اصلاحات شود.
ایدهآل: انسجام بالا + پیوستگی پایین.
🧩 مزیت اصلی چیست؟
☑️ نگهداری کد ارزانتر و سریعتر میشود؛
☑️ تغییرات محدود و موضعی میمانند — با اثرات جانبی کمتر؛
☑️ معماری انعطافپذیر باقی میماند: میتوان پیادهسازیها را عوض کرد بدون اینکه سیستم بشکند؛
☑️ و البته تست کردن سادهتر است — ماژولی که فقط یک مسئولیت دارد، راحتتر موک میشود و پوشش تست بهتری میگیرد.
یک مثال کوچک در تصویر بالا 👆
و البته، معماری خوب یک ایدئولوژی نیست، بلکه یک ابزار است: زمان را ذخیره میکند، نگهداری را سادهتر میکند و تغییرات را امنتر. اما همیشه ممکن است استثناهایی وجود داشته باشد: گاهی لازم است برای یک راهحل فوری یا یک نمونهٔ اولیه، کمی از پاکیزگی کد صرفنظر کنیم. مهم این است که این مصالحهها آگاهانه و موقتی باشند، نه اینکه تبدیل به عادت شوند. 😊
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
جاوااسکریپت | JavaScript
Video
امروزه استفاده از
اما تصور کنید اگر میتوانستید به عناصر چسبنده به والد دسترسی پیدا کنید و با آنها افکتها و رفتارهای جالب ایجاد کنید، چه؟
خبر خوب اینکه این امکان اکنون وجود دارد! اخیراً قابلیت اسکرول-کوئریها به عنوان بخشی از کانتینر-کوئریها معرفی شده است. همانطور که از نامشان پیداست: [Container scroll-state queries](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_conditional_rules/Container_scroll-state_queries).
مثلاً میخواهید یک تصویر در تصویر (PIP) بسازید، بهطوری که خواندن مقاله متوقف نشود؟
یا برعکس، تبلیغات در صفحه قطع نشود؟ :)
کاملاً امکانپذیر است: [نمونه عملی در CodePen](https://codepen.io/alinaki/pen/WbvMOPB)
نکته جالب اینکه میتوان چند
برای مرورگرهایی که هنوز از اسکرول-کوئریها پشتیبانی نمیکنند، میتوانید همیشه از یک IntersectionObserver استفاده کنید (که در نمونه نیز وجود دارد).
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
position: sticky کاملاً رایج و معمول شده است.اما تصور کنید اگر میتوانستید به عناصر چسبنده به والد دسترسی پیدا کنید و با آنها افکتها و رفتارهای جالب ایجاد کنید، چه؟
خبر خوب اینکه این امکان اکنون وجود دارد! اخیراً قابلیت اسکرول-کوئریها به عنوان بخشی از کانتینر-کوئریها معرفی شده است. همانطور که از نامشان پیداست: [Container scroll-state queries](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_conditional_rules/Container_scroll-state_queries).
مثلاً میخواهید یک تصویر در تصویر (PIP) بسازید، بهطوری که خواندن مقاله متوقف نشود؟
یا برعکس، تبلیغات در صفحه قطع نشود؟ :)
کاملاً امکانپذیر است: [نمونه عملی در CodePen](https://codepen.io/alinaki/pen/WbvMOPB)
@container scroll-state(stuck: top) {
.pip {
width: 200px;
transform: translate(-50%, 0%)
translate(calc(50vw - (50% + 1rem)), calc(100vh - (100% + 1rem)));
}
}نکته جالب اینکه میتوان چند
translate را با هم ترکیب کرد تا نتایج ریاضیاتی و حرکتی جالبی ایجاد شود.برای مرورگرهایی که هنوز از اسکرول-کوئریها پشتیبانی نمیکنند، میتوانید همیشه از یک IntersectionObserver استفاده کنید (که در نمونه نیز وجود دارد).
#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
MDN Web Docs
Using container scroll-state queries - CSS | MDN
Container scroll-state queries are a type of container query. Rather than selectively applying styles to descendant elements based on the container's size, scroll-state queries allow you to selectively apply styles to descendant elements based on the container's…
👍1
This media is not supported in your browser
VIEW IN TELEGRAM
🖤 سالروز وفات حضرت خدیجه کبری (س)
⚫️ رسول الله صلی الله علیه و آله:
◎ خدا زنی بهتر از خدیجه به من نداد. هنگامی که مردم تکذیبم میکردند، او مرا تصدیق کرد و هنگامی که مردم مرا تحریم کردند، با ثروتش کمکم کرد. و خدا از او فرزندانی به من داد در حالی که از دیگر زنانم به من فرزندی نداد.
#️⃣#event
👥@IR_javascript_group
🆔@IR_javascript
⚫️ رسول الله صلی الله علیه و آله:
◎ خدا زنی بهتر از خدیجه به من نداد. هنگامی که مردم تکذیبم میکردند، او مرا تصدیق کرد و هنگامی که مردم مرا تحریم کردند، با ثروتش کمکم کرد. و خدا از او فرزندانی به من داد در حالی که از دیگر زنانم به من فرزندی نداد.
#️⃣#event
👥@IR_javascript_group
🆔@IR_javascript
❤10👎5