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

🆔@IR_javascript
Download Telegram
‏**Mediabunny** یک کتابخانهٔ جاوااسکریپت برای خواندن، نوشتن و تبدیل فایل‌های رسانه‌ای مانند MP4، WebM و MP3 است، مستقیماً در مرورگر اجرا می‌شود. هدف آن ارائهٔ یک مجموعهٔ کامل برای عملیات رسانه‌ای با کارایی بالا روی وب است.

این کتابخانه از صفر و با TypeScript خالص نوشته شده، هیچ وابستگی‌ای ندارد، بسیار سریع است و از قابلیت tree-shaking پشتیبانی کامل می‌کند؛ به این معنا که فقط کدهایی که استفاده می‌کنید وارد پروژه می‌شوند. می‌توان آن را شبیه FFmpeg تصور کرد، اما به‌طور کامل برای وب از پایه ساخته شده است.


🔗https://mediabunny.dev/
#️⃣#npm_module
👥@IR_javascript_group
🆔@IR_javascript
2🔥1
‍‏**Peaks.js نسخهٔ چهار: کامپوننت رابط کاربری برای تعامل با نمودار موج صدا**

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


🔗https://codeberg.org/chrisn/peaks.js
#️⃣#npm_module
👥@IR_javascript_group
🆔@IR_javascript
👍2
یک سرور HTTP نمی‌تواند به صورت خودکار یک اتصال به مرورگر برقرار کند؛ بنابراین همیشه مرورگر آغازگر ارتباط است. حالا برای دریافت به‌روزرسانی‌های لحظه‌ای از سرور HTTP چه باید کرد؟

دو رویکرد اصلی وجود دارد:

🔹 مرورگر بار اصلی را بر دوش می‌کشد:

* Polling کوتاه‌مدت (Short Polling): مرورگر به‌طور مکرر درخواست می‌فرستد تا زمانی که داده‌ی جدید را دریافت کند.
* Polling بلندمدت (Long Polling): سرور درخواست را باز نگه می‌دارد و تنها زمانی پاسخ می‌دهد که داده‌ی تازه آماده شود.

🔹 همکاری مرورگر و سرور:

* WebSocket: یک اتصال دائمی و دوسویه برقرار می‌شود. پس از آن، سرور می‌تواند هر زمان داده‌ی جدید را ارسال کند و مرورگر نیز می‌تواند درخواست‌های تازه بفرستد.
* SSE (Server-Sent Events): یک اتصال یک‌طرفه ایجاد می‌شود که در آن فقط سرور می‌تواند داده‌های جدید را برای مرورگر ارسال کند. مرورگر نمی‌تواند درخواست تازه‌ای از همان کانال به سرور بفرستد.

👉 در نتیجه، انتخاب میان این روش‌ها بستگی به نیاز برنامه دارد: اگر ارتباط دوطرفه نیاز باشد، WebSocket بهترین گزینه است؛ اگر کافی است فقط سرور داده‌ها را به‌طور خودکار به مرورگر بفرستد، SSE ساده‌تر و سبک‌تر است.




#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
👍1
🔹 مقایسه toString() و String() 🤨

یکی از نیازهای رایج در برنامه‌نویسی، تبدیل داده‌ها به رشته است. برای این کار دو رویکرد اصلی وجود دارد: toString() و String(). هرچند عملکرد آن‌ها شبیه به هم است، اما یک تفاوت کلیدی میانشان وجود دارد. بیایید بررسی کنیم! 😁

---

### ۱️⃣ toString() — متد شیء

این متد یک شیء را به رشته تبدیل می‌کند، اما:

✔️ برای انواع اولیه (مانند عدد یا رشته) نمایش متنی آن‌ها را برمی‌گرداند؛
✔️ برای اشیاء (مثل آرایه یا تابع) خروجی به نوع آن وابسته است؛
✔️ در صورت فراخوانی روی null یا undefined خطا تولید می‌کند.

const num = 42;
console.log(num.toString()); // "42"

const arr = [1, 2, 3];
console.log(arr.toString()); // "1,2,3"

// خطا در null یا undefined
null.toString(); // TypeError


---

### ۲️⃣ String() — تابع سراسری

این یک تابع عمومی برای تبدیل هر نوع داده به رشته است:

✔️ با تمام مقادیر (حتی null و undefined) کار می‌کند؛
✔️ به جای خطا، عبارت "null" یا "undefined" برمی‌گرداند.

console.log(String(42));        // "42"
console.log(String([1, 2])); // "1,2"
console.log(String(null)); // "null"
console.log(String(undefined)); // "undefined"


---

### چند نکته تکمیلی

✔️ برای اشیاء و آرایه‌هایی که متد toString را بازنویسی نکرده‌اند، خروجی پیش‌فرض خواهد بود (مانند "[object Object]" یا "1,2,3"). برای نمایش متفاوت باید متد toString بازنویسی شود؛
✔️ اگر نیاز به سریال‌سازی شیء به JSON دارید، از JSON.stringify() استفاده کنید؛
✔️ در بیشتر مواقع، متد toString() اندکی سریع‌تر از String() است، چون بررسی‌های اضافه ندارد. با این حال، این تفاوت معمولاً ناچیز است و در عملکرد روزمره کد تأثیر محسوسی ندارد.

---

به‌طور معمول، من از toString() استفاده می‌کنم چون ساده و مستقیم است. اما اگر نیاز باشد مقدار null یا undefined را هم پردازش کنم، سراغ String() می‌روم، زیرا بدون ایجاد خطا، به‌خوبی از عهده این کار برمی‌آید. 👍


#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
👍2
تفاوت Imperative و Declarative در جاوااسکریپت

---

### کد دستوری (Imperative)

در کدنویسی دستوری، شما به کامپیوتر دقیقاً می‌گویید که در هر مرحله چه کاری انجام دهد و جزئیات را مرحله‌به‌مرحله مشخص می‌کنید.

برای نمونه:

فرض کنید تابعی به نام filteredArray داریم که وظیفه‌اش فیلتر کردن یک آرایه است. در حالت دستوری ما با یک حلقه روی آرایه پیمایش می‌کنیم، سپس بررسی می‌کنیم که آیا عدد کمتر از ده است یا نه. اگر کمتر از ده بود، آن را به آرایه‌ای خالی (به نام regularArray) اضافه می‌کنیم.

نمونه کد دستوری:

function filteredArray(arr) {
let regularArray = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i] < 10) {
regularArray.push(arr[i]);
}
}
return regularArray;
}

console.log(filteredArray([1, 2, 7, 15, 20])); // [1, 2, 7]


در اینجا ما دقیقاً توضیح داده‌ایم که در هر مرحله چه اتفاقی رخ می‌دهد. همین موضوع باعث می‌شود این کدنویسی Imperative (دستوری) باشد.

---

### کد اعلامی (Declarative)

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

همان مثال بالا را در نظر بگیرید. این بار ما همچنان می‌خواهیم اعداد کمتر از ده را فیلتر کنیم، اما به جای نوشتن مراحل، از متد داخلی filter استفاده می‌کنیم:

نمونه کد اعلامی:

function filteredArray(arr) {
return arr.filter(item => item < 10);
}

console.log(filteredArray([1, 2, 7, 15, 20])); // [1, 2, 7]


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

---

جمع‌بندی:

* در Imperative شما «چگونگی انجام کار» را با جزئیات بیان می‌کنید.
* در Declarative شما فقط «آنچه می‌خواهید» را بیان می‌کنید و جزئیات را به زبان یا ابزار می‌سپارید.



#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
👍1
چگونه مرورگر <script> را هنگام ساخت درخت DOM پردازش می‌کند؟ 🧐

هنگامی که مرورگر HTML را تجزیه می‌کند، تگ‌های <script> می‌توانند ساخت DOM را متوقف کنند و این موضوع روی کارایی صفحه تأثیر می‌گذارد. نحوه‌ی کار به شرح زیر است:

1️⃣ اسکریپت‌های هم‌زمان (synchronous) (`<script>` بدون async یا defer)
این اسکریپت‌ها ساخت DOM را تا زمان بارگذاری و اجرای خود متوقف می‌کنند. نتیجه، کند شدن رندر صفحه است.

2️⃣ اسکریپت‌های غیرهم‌زمان (async)
این اسکریپت‌ها هم‌زمان با HTML بارگذاری می‌شوند**، اما **به محض بارگذاری فایل اجرا می‌شوند. ترتیب اجرای آن‌ها پیش‌بینی‌ناپذیر است و ممکن است رندر صفحه را قطع کند.

3️⃣ اسکریپت‌های defer
این اسکریپت‌ها به صورت هم‌زمان بارگذاری می‌شوند اما پس از تجزیه کامل HTML اجرا می‌شوند و ترتیب اجرای آن‌ها همان ترتیب قرارگیری در سند است.

4️⃣ اسکریپت‌های ماژولی (`type="module"`)
این اسکریپت‌ها مانند defer بارگذاری می‌شوند**، اما **در حالت strict اجرا می‌شوند و از import/export پشتیبانی می‌کنند. اجرای آن‌ها پس از بارگذاری HTML انجام می‌شود و از top-level await پشتیبانی می‌کند.

5️⃣ اسکریپت‌های داینامیک (با `document.createElement('script')`)
به طور پیش‌فرض مانند async رفتار می‌کنند.

👩‍🎓 چند توصیه:

✔️ برای اکثر اسکریپت‌ها از defer استفاده کنید تا رندر صفحه مسدود نشود**؛
✔️ برای اسکریپت‌های مستقل (مثلاً اسکریپت‌های تحلیلی) از **async
استفاده کنید؛
✔️ تعداد اسکریپت‌های هم‌زمان در <head> را کمینه کنید — این موضوع بارگذاری صفحه را کند می‌کند.

به یاد داشته باشید، اسکریپت‌ها نه تنها می‌توانند DOM را مسدود کنند**، بلکه روی **رندر صفحه نیز تأثیر می‌گذارند و سرعت بارگذاری و تجربه کاربری را کاهش می‌دهند. استفاده درست از async و defer باعث سرعت بالاتر بارگذاری و رندر بهتر می‌شود. 👍




#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
👍1
می‌دانستید که nth-child در CSS از چند سال پیش به صورت استاندارد قابل استفاده شده است؟

منظورم این است: قبلاً انتخاب شما محدود بود؛ می‌توانستی از nth-child برای شماره‌گذاری گره‌ها (تگ‌ها) به‌طور عمومی استفاده کنی یا nth-of-type برای شماره‌گذاری نوع خاصی از گره‌ها (div، p، section).

چطور می‌توانستم دومین عنصر با کلاس .star را انتخاب کنم؟

و چیزی که من نمی‌دانستم این است که nth-child اکنون می‌تواند یک سلکتور به شکل آرگومان of بگیرد!

#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
👍1
خسته شدید هر بار از کنسول راحت خود بیرون بیایید و به caniuse.com بروید تا ببینید کدام مرورگرها از subgrid پشتیبانی می‌کنند؟

معرفی می‌کنم caniuse-cli

مثال استفاده:

$ caniuse "viewport units"
$ caniuse @property


برای zsh هم قابلیت autocomplete دارد.


🔗https://www.npmjs.com/package/@bramus/caniuse-cli
#️⃣#npm_module
👥@IR_javascript_group
🆔@IR_javascript
1
Media is too big
VIEW IN TELEGRAM
توسعه‌دهندگان گوگل کروم MCP برای Chrome DevTools را معرفی کردند!
MCP مخفف *Model Context Protocol* است؛ پروتکلی که روشی یکپارچه برای اتصال ابزارهای خارجی و منابع داده به مدل‌های هوش مصنوعی تعریف می‌کند.

اما چرا این مهم است؟ چون در واقع همان حلقه‌ی مفقوده در فرایند توسعه است: این امکان را می‌دهد که یک عامل (Agent) واقعاً به داده‌های یک برنامه‌ی واقعی دسترسی پیدا کند،

معیارهای زنده‌ی سرعت و پایداری، کد واقعیِ در حال اجرا ــ همه این‌ها پیش‌تر در اختیار DevTools بود و حالا در دسترس عامل نیز قرار می‌گیرد.

تصور کنید بتوانید بپرسید: «وقتی ایمیل اشتباه وارد می‌کنم، اعتبارسنجی عمل نمی‌کند؛ بررسی کن چرا.» و واقعاً هم بررسی خواهد کرد!

تمام رویدادهای موردنیاز پشتیبانی می‌شوند؛ حتی بارگذاری فایل‌ و دریافت وضعیت شبکه.

بی‌اغراق، این واقعاً شگفت‌انگیز است. حالا می‌توانید آن را به ابزارهایی مثل Jira و Figma متصل کنید، همه چیز را به مرورگر بسپارید و خودتان با خیال راحت فقط تماشا کنید.
#️⃣#tool
👥@IR_javascript_group
🆔@IR_javascript
This media is not supported in your browser
VIEW IN TELEGRAM
فرض کنید یک گالری عکس یا یک لیست آیتم دارید و وقتی اسکرول می‌کنید، هر تصویر یا آیتم به‌صورت دقیق و منظم جلوی چشم شما قرار می‌گیرد، نه اینکه نیمه‌ای از آن نمایش داده شود.

‌‏Scroll Snap دقیقاً همین کار را انجام می‌دهد: وقتی کاربر اسکرول می‌کند، مرورگر به‌صورت خودکار موقعیت صفحه را روی نقاط مشخص (Snap Points) قفل می‌کند. مثل اینکه یک مغناطیس نامرئی روی هر آیتم گذاشته‌ایم که وقتی نزدیک می‌شود، صفحه آن را درست در وسط یا بالای دید شما قرار می‌دهد.
ویژگی scroll-snap-type یک خصوصیت الزامی برای هر کانتینر قابل اسکرول است که می‌خواهید روی آن scroll snapping فعال شود. این ویژگی به سه سؤال برای کانتینر پاسخ می‌دهد:

1-آیا این کانتینر از scroll snapping استفاده می‌کند؟
2-روی کدام محور — افقی (x)، عمودی (y)، بلوک (block) یا خطی (inline) — scroll snapping اعمال شود؟
3-رفتار scroll snapping چگونه باشد؟ آیا همیشه اجباری (mandatory) است، یا فقط زمانی که کاربر به موقعیت اسنپ نزدیک شود فعال می‌شود؟

#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
👍3🔥1
دربارهٔ مشکل مرکزچینی عمودی محتوا در CSS می‌توان افسانه‌ها نوشت

خیلی هم غم‌انگیز است که این داستان‌ها کم‌کم به پایان می‌رسند، زیرا حالا ما نه تنها Flex و Grid را داریم، بلکه یک ویژگی ساده و قابل‌فهم به نام align-content هم در دسترس است

بله، این ویژگی از دیرباز پایه‌ای برای مدل‌های Flex و Grid بوده است، اما از نسخه‌های جدید مرورگرها — Chrome 123**، **Firefox 125 و Safari 17.4 — پشتیبانی می‌شود و اکنون در مدل بلوکی (Block Layout) هم قابل استفاده است! 🎉

مثال ساده:

.container {
align-content: center;
}




#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
👍4
در مرورگر سافاری روی سیستم‌عامل آی‌اواس یک قابلیت جالب وجود دارد: وقتی انگشتتان را روی یک عنصر، مثلاً یک پیوند، نگه دارید، پنجره‌ای شناور باز می‌شود که پیش‌نمایش آن را نمایش می‌دهد. اما گاهی این ویژگی اصلاً کاربردی نیست و لازم است راهی برای غیرفعال کردن آن پیدا شود.

طبق معمول، این روش چندان هم استاندارد نیست
سپاس از اپل، همیشه همین‌طور خاص!

#️⃣#tip #css
👥@IR_javascript_group
🆔@IR_javascript
👍2
می‌دونید اگه توی فایرفاکس داخل یه فیلد <input type="number"> به‌جای عدد، چیزی مثل lol بنویسید چی می‌شه؟

هیچی! قبول می‌کنه 😐

یعنی همون «lol» رو جلوی چشم‌تون نشون می‌ده، انگار که یه عدد درسته. ولی وقتی مقدارش رو توی DOM بخونید، خالیه. یعنی مرورگر می‌گه: «باشه بهت نشونش دادم، ولی اصلاً حسابش نمی‌کنم.» شاهکاره واقعاً!

این یه باگه که از سال ۲۰۱۷ توی Bugzilla بازه و هنوزم وصله نشده:
[Bugzilla #1398528](https://bugzilla.mozilla.org/show_bug.cgi?id=1398528)

طبق استاندارد WHATWG**، ورودی از نوع number فقط باید رشته‌های عددی رو قبول کنه. ولی فایرفاکس هرچی رو قبول می‌کنه — حروف، ایموجی، هرچی بخوای. فقط پشت‌صحنه مقدارش خالی می‌شه. یعنی می‌بینی چی تایپ کردی، ولی مرورگر اون رو معتبر نمی‌دونه. نتیجه؟ تجربه کاربری افتضاح.

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

بریم سراغ کروم:
کروم بعضی وقتا اجازه می‌ده توی فیلد number حرف **e
رو بنویسید، چون ممکنه بخواید عددی مثل 1e10 (فرمت علمی) رو وارد کنید. اما اگه فقط «e» رو بنویسید… فیلد معتبر می‌شه! 😆

جالب‌تر اینکه 1e- رو هم معتبر می‌دونه، ولی 1ee نه.

از اون طرف اگه توی لوکالی باشید که جداکننده اعشار نقطه است، نوشتن 1,5 (با ویرگول) یا قبول می‌شه یا نمی‌شه — بستگی داره به نسخه‌ی مرورگر، وضعیت ماه و احتمالاً میزان قهوه‌ای که برنامه‌نویس کروم خورده!

نتیجه نهایی:

* فایرفاکس: هرچی بنویسی (حتی «سلام») نشون می‌ده، ولی زیرش خالیه.
* کروم: یه فیلتر داره، ولی اونم نصفه‌نیمه و پر از تناقض.

پس چی کار کنیم؟ خودمون کد بزنیم برای اعتبارسنجی، چون امیدی به مرورگرها نیست.

الان این باگ وارد سال هشتم شده، همچنان با برچسب «NEW» خوابیده و با توجه به بحث‌ها، به این زودی هم درست نمی‌شه.

پ.ن. فایرفاکس اخیراً توی نسخه انگلیسیش، خطای اعتبارسنجی رو به زبان فنلاندی نشون داده! 🤦‍♂️

پ.ن.۲. تازه جدای اینا، اصلاً خیلی‌ها معتقدن که <input type="number"> به‌خودش هیچ فایده‌ای نداره و حتی مضر هم هست

#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
👍1🔥1
Capture node screenshot
این قابلیت در DevTools کروم و فایرفاکس به شما اجازه می‌دهد که یک عنصر مشخص (یا همان نود) در صفحه را به‌صورت تصویر ذخیره کنید، بدون اینکه نیاز باشد کل صفحه را اسکرول کنید یا اسکرین‌شات بگیرید.

روش کار به‌طور خلاصه:

1. باز کردن DevTools:

* در کروم یا فایرفکس، کلید F12 یا Ctrl+Shift+I را فشار دهید.

2. انتخاب نود موردنظر:

* در تب Elements یا Inspector**، روی عنصر HTML که می‌خواهید اسکرین‌شاتش را بگیرید کلیک کنید.

3. **گرفتن اسکرین‌شات
:

* در کروم: راست‌کلیک روی نود → گزینه‌ی Capture node screenshot را انتخاب کنید.
* در فایرفکس: راست‌کلیک روی نود → گزینه‌ی Take a screenshot of node یا مشابه آن را انتخاب کنید.

DevTools سپس همان بخش خاص را پردازش کرده و یک تصویر PNG از آن نود ایجاد می‌کند.


#️⃣#tool
👥@IR_javascript_group
🆔@IR_javascript
### fetchpriority در HTML

مرورگر برای نمایش صفحه نیاز دارد منابع لازم مثل CSS، JS، تصاویر و ویدیو را دانلود کند. اول HTML دریافت و تحلیل می‌شود و همزمان اسکنر پیش‌بارگذاری منابع را پیدا و دانلود می‌کند. همه منابع براساس اولویت در شبکه بارگذاری می‌شوند.

ویژگی fetchpriority به شما اجازه می‌دهد اولویت بارگذاری منابع را مشخص کنید:

* high → اولویت بالا
* low → اولویت پایین
* auto → رفتار پیش‌فرض مرورگر

می‌توان از این ویژگی در عناصر زیر استفاده کرد: <link>`، <script>، <img> و <iframe>`.

<img src="lcp-image.jpg" fetchpriority="high">

---

### کاربردهای متداول

* بارگذاری تصاویر مهم صفحه (مثلاً تصاویر LCP) با اولویت بالا
* کاهش اولویت تصاویر غیرضروری (مثل تصاویر کاروسل)
* پیش‌بارگذاری منابع کم‌اهمیت با اولویت پایین
* افزایش یا کاهش اولویت اسکریپت‌ها بسته به نیاز

همچنین می‌توان اولویت را هنگام فراخوانی fetch() مشخص کرد:

const response = await fetch('/resource', { priority: 'low' });


---

### نکته مهم

‏`fetchpriority` ابزار جدیدی است برای کنترل هوشمندانه بارگذاری منابع و مکمل روش‌های قدیمی مثل:

* loading در <img> و <iframe>
* preload در <video> و <link>
* defer و async در <script>

برای جزئیات بیشتر می‌توانید مقاله کامل را در web.dev بخوانید:
[https://web.dev/articles/fetch-priority](https://web.dev/articles/fetch-priority)


#️⃣#tip
👥@IR_javascript_group
🆔@IR_javascript
Media is too big
VIEW IN TELEGRAM
ماشین قابل‌آموزش (Teachable Machine) یک آزمایش تعاملی است که یادگیری ماشین را برای همه ساده‌تر می‌کند؛ به‌طوری‌که مستقیماً در مرورگر اجرا می‌شود و هیچ نیازی به کدنویسی ندارد. روشی سریع و ساده برای ساخت مدل‌های یادگیری ماشین جهت استفاده در وب‌سایت‌ها، اپلیکیشن‌ها و موارد دیگر — بدون نیاز به دانش تخصصی یا کدنویسی.


🔗https://teachablemachine.withgoogle.com/
#️⃣#tool
👥@IR_javascript_group
🆔@IR_javascript
This media is not supported in your browser
VIEW IN TELEGRAM
🖤وفات حضرت معصومه سلام‌الله‌علیها
🏴  رحلت شهادت‌گونه بانوی مهر و وفا، اُخت الرضا، عمه سادات، کریمه اهل بیت علیهم‌السلام، حضرت فاطمه معصومه سلام‌ الله‌ تسلیت باد.

🔗https://www.namasha.com/v/IM2n2FnC
#️⃣#event
👥@IR_javascript_group
🆔@IR_javascript
8👎2😱1
چگونگی تست WebKit یا Safari روی ویندوز و لینوکس
به سادگی از BrowserStack استفاده کنید
اما مشکل اینجاست که همه نمی‌توانند حتی ۱۵۰ دلار در سال برای پلن فریلنسری هزینه کنند… آیا راهی وجود دارد؟

بله، راه وجود دارد!

‏BrowserStack به‌طور فعال از پروژه‌های Open Source پشتیبانی می‌کند و لایسنس یک‌ساله رایگان ارائه می‌دهد!

اگر پروژه‌ی شما چنین شرایطی دارد، کافی است به این صفحه
بروید و لینک مخزن (repository) خود را وارد کنید.

نکته مهم: باید لایسنس مناسب داشته باشید. لیست کامل را پیدا نکردم، اما مطمئنم که GPL، BSD و MIT پوشش داده شده‌اند. من هم خودم Creative Commons Attribution 4.0 International را وارد کردم.
دسترسی هر سال تمدید می‌شود تا زمانی که مخزن در دسترس باشد


#️⃣#tool
👥@IR_javascript_group
🆔@IR_javascript