<?xml version="1.0"?>
<rss version="2.0"><channel><title>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A; JavaScript</title><link>https://academy.hsoub.com/programming/javascript/?d=2</link><description>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A; JavaScript</description><language>ar</language><item><title>&#x625;&#x646;&#x634;&#x627;&#x621; &#x635;&#x648;&#x631; &#x645;&#x62A;&#x641;&#x627;&#x639;&#x644;&#x629; &#x635;&#x648;&#x62A;&#x64A;&#x64B;&#x627; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x645;&#x643;&#x62A;&#x628;&#x629; Three.js</title><link>https://academy.hsoub.com/programming/javascript/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%B5%D9%88%D8%B1-%D9%85%D8%AA%D9%81%D8%A7%D8%B9%D9%84%D8%A9-%D8%B5%D9%88%D8%AA%D9%8A%D9%8B%D8%A7-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%85%D9%83%D8%AA%D8%A8%D8%A9-threejs-r2552/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_04/Three.js.png.9790f5583404453473084f732d71fde5.png" /></p>
<p>
	سنشرح في هذا المقال كيفية بناء محاكي مرئي موسيقى باستخدام Three.js والتعامل مع تقنيات مزامنة الصوت مؤثرات مرئية ثلاثية الأبعاد. ما سنحاول بناءه في هذا المقال هو صور متفاعلة ديناميكيًا مع الصوت بشكل قريب من الفكرة الواردة في  <a href="https://tympanus.net/Tutorials/ParticlesMusicVisualizer" rel="external nofollow">هاذا المشروع</a>.
</p>

<p style="text-align: center;">
	<img alt="001 صورة متفاعلة مع الصوت" class="ipsImage ipsImage_thumbnailed" data-fileid="170542" data-ratio="75.00" data-unique="jlaob2zi5" style="width: 400px; height: auto;" width="800" src="https://academy.hsoub.com/uploads/monthly_2025_04/_.gif.f37e6cb8d41fbe31c8f1a0bcab1b2cfe.gif">
</p>

<h2 id="">
	تهيئة المشروع
</h2>

<p>
	في البداية سننشئ مشروع جديد بأي محرر أكواد نفضله، ونضيف له مكتبة three.js، ثم ننشئ ملف جافا سكريبت ونضع فيه الكود التالي الذي يضمن أن الصوت سيعمل فقط عندما يطلب المستخدم ذلك وينقر في الصفحة، وذلك بهدف تجنب مشكلات تشغيل الصوت التلقائي التي وضعتها المتصفحات الأساسية وضمان تجربة مستخدم أفضل.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1012_10" style=""><span class="pln"> </span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">constructor</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">onClickBinder </span><span class="pun">=</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">init</span><span class="pun">()</span><span class="pln">
    document</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">'click'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">onClickBinder</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  init</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    document</span><span class="pun">.</span><span class="pln">removeEventListener</span><span class="pun">(</span><span class="str">'click'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">onClickBinder</span><span class="pun">)</span><span class="pln">

       </span><span class="pun">مشهد</span><span class="pln"> </span><span class="typ">Three</span><span class="pun">.</span><span class="pln">js </span><span class="pun">بسيط</span><span class="com">//    </span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">renderer </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> THREE</span><span class="pun">.</span><span class="typ">WebGLRenderer</span><span class="pun">()</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">camera </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> THREE</span><span class="pun">.</span><span class="typ">PerspectiveCamera</span><span class="pun">(</span><span class="lit">70</span><span class="pun">,</span><span class="pln"> window</span><span class="pun">.</span><span class="pln">innerWidth </span><span class="pun">/</span><span class="pln"> window</span><span class="pun">.</span><span class="pln">innerHeight</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0.1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10000</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">scene </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> THREE</span><span class="pun">.</span><span class="typ">Scene</span><span class="pun">()</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2 id="-1">
	تحليل البيانات الصوتية
</h2>

<p>
	سنهيئ الآن مدير الصوت (Audio Manager) ومدير الإيقاع (BPM Manager) لتحميل الصوت، وتحليله، ومزامنته مع العناصر المرئية. وسنبدأ تشغيلهما باستخدام <code>async</code> و <code>async</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1012_12" style=""><span class="kwd">async</span><span class="pln"> createManagers</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">App</span><span class="pun">.</span><span class="pln">audioManager </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">AudioManager</span><span class="pun">()</span><span class="pln">
  </span><span class="kwd">await</span><span class="pln"> </span><span class="typ">App</span><span class="pun">.</span><span class="pln">audioManager</span><span class="pun">.</span><span class="pln">loadAudioBuffer</span><span class="pun">()</span><span class="pln">

  </span><span class="typ">App</span><span class="pun">.</span><span class="pln">bpmManager </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">BPMManager</span><span class="pun">()</span><span class="pln">
  </span><span class="typ">App</span><span class="pun">.</span><span class="pln">bpmManager</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">'beat'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">particles</span><span class="pun">.</span><span class="pln">onBPMBeat</span><span class="pun">()</span><span class="pln">
  </span><span class="pun">})</span><span class="pln">
  </span><span class="kwd">await</span><span class="pln"> </span><span class="typ">App</span><span class="pun">.</span><span class="pln">bpmManager</span><span class="pun">.</span><span class="pln">detectBPM</span><span class="pun">(</span><span class="typ">App</span><span class="pun">.</span><span class="pln">audioManager</span><span class="pun">.</span><span class="pln">audio</span><span class="pun">.</span><span class="pln">buffer</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	<strong>ملاحظة</strong>: يشير عدد النبضات في الدقيقة Beats Per Minute -أو BPM اختصارًا- إلى سرعة الإيقاع الموسيقي.
</p>

<p>
	سيحمّل الصنف <code>AudioManager</code> في الكود السابق الصوت من عنوان URL محدد، وفي هذا المقال اخترنا ملف صوتي من موقع Spotify، بعد تحميل الصوت سنحلله ثم نقسّم إشارات الصوت إلى نطاقات ترددية في الوقت الفعلي.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4245_7" style=""><span class="kwd">const</span><span class="pln"> audioLoader </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> THREE</span><span class="pun">.</span><span class="typ">AudioLoader</span><span class="pun">();</span><span class="pln">
audioLoader</span><span class="pun">.</span><span class="pln">load</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">song</span><span class="pun">.</span><span class="pln">url</span><span class="pun">,</span><span class="pln"> buffer </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">audio</span><span class="pun">.</span><span class="pln">setBuffer</span><span class="pun">(</span><span class="pln">buffer</span><span class="pun">);</span><span class="pln">
</span><span class="pun">})</span></pre>

<h2 id="-2">
	بيانات التردد
</h2>

<p>
	علينا فصل نطاق الترددات إلى الترددات المنخفضة والمتوسطة والعالية لحساب السعة amplitude.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="170540" href="https://academy.hsoub.com/uploads/monthly_2025_04/001__.png.1b1743cad70b9837b7f30b8180bc7b48.png" rel=""><img alt="002 نطاق الترددات" class="ipsImage ipsImage_thumbnailed" data-fileid="170540" data-unique="23pukt0y4" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_04/001__.thumb.png.8bd6ea3258e9ed32bf09beba28fdbb3f.png"> </a>
</p>

<p>
	سنقسم نطاقات الترددات إلى frequency bands كما يلي:
</p>

<ul>
	<li>
		ترددات منخفضة Low
	</li>
	<li>
		ترددات متوسطة Mid
	</li>
	<li>
		ترددات عالية High
	</li>
</ul>

<p>
	كل مجموعة لها نطاق ترددات محدد، مثلًا تبدأ الترددات المنخفضة من <code>lowFrequency</code> وتنتهي عند بداية <code>midFrequency</code>. وتبدأ الترددات العالية من <code>midFrequency </code>وتنتهي عند <code>highFrequency</code>.
</p>

<p>
	ولحساب متوسط السعة داخل نطاق معيّن يمكننا ضرب الترددات بالقيمة Buffer length والتيتمثل عدد العينات الصوتية المخزنة في Buffer، ثم نقسم الناتج على معدل أخذ العينات sample rate، وهو عدد العيّنات التي يتم التقاطها في الثانية فهذا يساعدنا على تحديد الموضع الصحيح داخل المصفوفة التي تحتوي على بيانات الترددات الصوتية بناءً على النطاق الترددي. بعد حساب المتوسط، نحول القيمة الناتجة إلى نطاق يتراوح بين 0 و 1، حتى نتمكن من استخدامها بشكل مرن في التطبيقات الرسومية أو الصوتية.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1012_14" style=""><span class="kwd">this</span><span class="pun">.</span><span class="pln">lowFrequency </span><span class="pun">=</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">frequencyArray </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">audioAnalyser</span><span class="pun">.</span><span class="pln">getFrequencyData</span><span class="pun">();</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> lowFreqRangeStart </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">floor</span><span class="pun">((</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">lowFrequency </span><span class="pun">*</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">bufferLength</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">audioContext</span><span class="pun">.</span><span class="pln">sampleRate</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> lowFreqRangeEnd </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">floor</span><span class="pun">((</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">midFrequency </span><span class="pun">*</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">bufferLength</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">audioContext</span><span class="pun">.</span><span class="pln">sampleRate</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> lowAvg </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">normalizeValue</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">calculateAverage</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">frequencyArray</span><span class="pun">,</span><span class="pln"> lowFreqRangeStart</span><span class="pun">,</span><span class="pln"> lowFreqRangeEnd</span><span class="pun">));</span><span class="pln">

</span><span class="pun">نفس</span><span class="pln"> </span><span class="pun">الطريقة</span><span class="pln"> </span><span class="pun">لنطاق</span><span class="pln"> </span><span class="pun">الترددات</span><span class="pln"> </span><span class="pun">المرتفعة</span><span class="pln"> </span><span class="pun">بين</span><span class="pln"> MID </span><span class="pun">و</span><span class="pln">HIGH</span><span class="com">//</span></pre>

<h2 id="-3">
	كشف الإيقاع باستخدام Web Audio Beat Detector
</h2>

<p>
	حسبنا في الفقرة السابقة قيمة سعة الترددات لكنها لا تكفي لوحدها لربط إيقاعات الموسيقى بالتأثيرات المرئية، سنحتاج لمعرفة قيمة الإيقاع BPM أي عدد النبضات في الدقيقة فهذه القيمة أساسية لجعل العناصر تتفاعل بشكل متزامن مع نبضات الموسيقى. لحسابها سنعتمد على استخدام <a href="https://www.google.com/url?q=https://academy.hsoub.com/programming/javascript/%25D9%2585%25D9%2582%25D8%25AF%25D9%2585%25D8%25A9-%25D8%25A5%25D9%2584%25D9%2589-%25D8%25A7%25D9%2584%25D9%2588%25D8%25AD%25D8%25AF%25D8%25A7%25D8%25AA-modules-%25D9%2581%25D9%258A-%25D8%25AC%25D8%25A7%25D9%2581%25D8%25A7%25D8%25B3%25D9%2583%25D8%25B1%25D8%25A8%25D8%25AA-r926/&amp;sa=D&amp;source=docs&amp;ust=1733749715363166&amp;usg=AOvVaw0cbeyS_pvy6mM4Zp4Nbjto" rel="external nofollow">وحدة جافا سكريبت</a>‏ مفتوحة المصدر باسم <code><a href="https://www.npmjs.com/package/web-audio-beat-detector" rel="external nofollow">web audio beat detector</a></code> حيث نحتاج لتمرير القيمة <code>audioBuffer</code> لهذه الوحدة وحساب الإيقاع بشكل غير متزامن كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1012_17" style=""><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> bpm </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">await</span><span class="pln"> guess</span><span class="pun">(</span><span class="pln">audioBuffer</span><span class="pun">);</span></pre>

<h2 id="-4">
	إرسال الإشارات
</h2>

<p>
	بعد أن كشفنا قيمة الإيقاع BPM يمكننا إرسال إشارة بوقوع حدث بشكل متكرر عند كل نبضة إيقاع باستخدام الدالة <code><a href="https://academy.hsoub.com/programming/javascript/%d8%a7%d9%84%d8%ac%d8%af%d9%88%d9%84%d8%a9-%d8%a7%d9%84%d9%85%d9%87%d9%84%d8%a9-settimeout-%d9%88%d8%a7%d9%84%d9%81%d8%aa%d8%b1%d8%a9-setinterval-%d9%81%d9%8a-%d8%ac%d8%a7%d9%81%d8%a7%d8%b3%d9%83%d8%b1%d8%a8%d8%aa-r877/" rel="">setInterval</a></code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1012_19" style=""><span class="kwd">this</span><span class="pun">.</span><span class="pln">interval </span><span class="pun">=</span><span class="pln"> </span><span class="lit">60000</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> bpm</span><span class="pun">;</span><span class="pln"> </span><span class="com">// Convert BPM to interval</span><span class="pln">
</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">intervalId </span><span class="pun">=</span><span class="pln"> setInterval</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">dispatchEvent</span><span class="pun">({</span><span class="pln"> type</span><span class="pun">:</span><span class="pln"> </span><span class="str">'beat'</span><span class="pln"> </span><span class="pun">})</span><span class="pln">
</span><span class="pun">},</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">interval</span><span class="pun">);</span></pre>

<h2 id="-5">
	إنشاء الأشكال المرئية المتفاعلة ديناميكيًا مع الصوت
</h2>

<p>
	سننشئ الآن أشكال لجزيئات ديناميكية لتتفاعل مع الصوت. سنبدأ بتعريف دالتين لإنشاء أشكال هندسية بسيطة، سننشئ مكعب <code>Box</code> وأسطوانة <code>Cylinder</code> بأجزاء وخواص عشوائية لتكون الأشكال الأساسية للجزئيات، سينتج عن هذه الطريقة هيكل مميز في كل مرة.
</p>

<p>
	ثم سنضيف هاتين الدالتين لكائن <code>Three.Points</code> مع <code>ShaderMaterial</code> بسيط.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1012_23" style=""><span class="kwd">const</span><span class="pln"> geometry </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> THREE</span><span class="pun">.</span><span class="typ">BoxGeometry</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> widthSeg</span><span class="pun">,</span><span class="pln"> heightSeg</span><span class="pun">,</span><span class="pln"> depthSeg</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> material </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> THREE</span><span class="pun">.</span><span class="typ">ShaderMaterial</span><span class="pun">({</span><span class="pln">
  side</span><span class="pun">:</span><span class="pln"> THREE</span><span class="pun">.</span><span class="typ">DoubleSide</span><span class="pun">,</span><span class="pln">
  vertexShader</span><span class="pun">:</span><span class="pln"> vertex</span><span class="pun">,</span><span class="pln">
  fragmentShader</span><span class="pun">:</span><span class="pln"> fragment</span><span class="pun">,</span><span class="pln">
  transparent</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
  uniforms</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    size</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> value</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> pointsMesh </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> THREE</span><span class="pun">.</span><span class="typ">Points</span><span class="pun">(</span><span class="pln">geometry</span><span class="pun">,</span><span class="pln"> material</span><span class="pun">)</span></pre>

<p>
	يمكننا الآن إنشاء تمثيلات الهندسية بسمات عشوائية بفترة زمنية محددة، لاحظ الشكل التالي:
</p>

<p>
	<iframe frameborder="0" src="https://codesandbox.io/embed/74vjz2?view=preview&amp;hidenavigation=1" style="width:100%;max-width:100%;height:300px"></iframe>
</p>

<h2 id="vertexshaderfragmentshader">
	توضيح مفهوم Vertex Shader و Fragment Shader
</h2>

<p>
	سنستخدم في فقراتنا التالية مفهومي مظلل الرؤوس Vertex Shader ومظلل الشرائح Fragment Shader في مكتبة Three.js لتخصيص وتعديل مظهر الكائنات الرسومية ثلاثية الأبعاد. فمظلل الرؤوس Vertex Shader هو جزء من الكود يُنفَّذ لكل رأس Vertex في الكائن ثلاثي الأبعاد ونستخدمه لتعديل موقع الرأس بناءً على المعاملات المرسلة من جافا سكريبت، وتغيير شكل الكائن عن طريق تعديل مواقع الرؤوس كتحريكها أو تدويرها ولكن لا يمكن من خلاله تعديل ألوان البكسلات أو التفاصيل الدقيقة الأخرى للكائن.
</p>

<p>
	من ناحية أخرى، يعمل مظلل الشرائح Fragment Shader على تحديد لون كل جزء صغير أو بكسل في الكائن ثلاثي الأبعاد، وهو يحدد اللون النهائي لكل بكسل بناءً على القيم المرسلة من Vertex Shader. ويمكن ضمنه تطبيق مؤثرات لونية على البكسلات كتغيير الألوان أو إضافة تأثيرات مثل الإضاءة أو الظلال. ومن خلال دمج Vertex Shader و Fragment Shader يمكن تحديد مظهر الكائن، حيث يُسمح بتخصيص الشكل والألوان بشكل مرن مما يوفر إمكانيات غير محدودة لإبداع مؤثرات مرئية مميزة.
</p>

<h2 id="-6">
	تحريك جزئيات الشكل عشوائيًا
</h2>

<p>
	سندرج ضوضاء دورانية Curl Noise للمظلل vertex shader لتحريك جزئيات الشكل بحركات طبيعية لها وإضافة حركات دوامية وسلسة لها. حيث سنحرك هذه الجزيئات وننشئ تأثيرات ديناميكية تتحكم بمظهرها وسلوكها. سنبدأ بتحديد موقع هدف نسميه <code>newpos</code> لكل نقطة، ثم نضيف ضوضاء دورانية Cur Noise على طول الشعاع الطبيعي لها ونغير هذه الضوضاء اعتمادا على التردد frequency والسعة amplitude، سيعدل تأثير الضوضاء اللولبية بناءً على المسافة بين النقطة الحالية التي نحركها والهدف <code>d</code> وينتج تحرك سلس يتلاشى تدريجيًا بمعنى أن التغيير يعتمد على المسافة، سيكون التأثير أكبر عندما تكون المسافة صغيرة، ويتناقص تدريجيًا كلما اقتربت النقطة من الهدف بسبب تداخل المسافة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1012_25" style=""><span class="pln">vec3 newpos </span><span class="pun">=</span><span class="pln"> position</span><span class="pun">;</span><span class="pln">
vec3 target </span><span class="pun">=</span><span class="pln"> position </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="pln">normal </span><span class="pun">*</span><span class="pln"> </span><span class="pun">.</span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> curl</span><span class="pun">(</span><span class="pln">newpos</span><span class="pun">.</span><span class="pln">x </span><span class="pun">*</span><span class="pln"> frequency</span><span class="pun">,</span><span class="pln"> newpos</span><span class="pun">.</span><span class="pln">y </span><span class="pun">*</span><span class="pln"> frequency</span><span class="pun">,</span><span class="pln"> newpos</span><span class="pun">.</span><span class="pln">z </span><span class="pun">*</span><span class="pln"> frequency</span><span class="pun">)</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> amplitude</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">float</span><span class="pln"> d </span><span class="pun">=</span><span class="pln"> length</span><span class="pun">(</span><span class="pln">newpos </span><span class="pun">-</span><span class="pln"> target</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> maxDistance</span><span class="pun">;</span><span class="pln">
newpos </span><span class="pun">=</span><span class="pln"> mix</span><span class="pun">(</span><span class="pln">position</span><span class="pun">,</span><span class="pln"> target</span><span class="pun">,</span><span class="pln"> pow</span><span class="pun">(</span><span class="pln">d</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4.</span><span class="pun">));</span></pre>

<p>
	سنضيف أيضا حركة متموجة إلى <code>newpos.z</code> لإضافة مزيد من الحيوية للنقاط المتحركة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1012_27" style=""><span class="pln">newpos</span><span class="pun">.</span><span class="pln">z </span><span class="pun">+=</span><span class="pln"> sin</span><span class="pun">(</span><span class="pln">time</span><span class="pun">)</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">(.</span><span class="lit">1</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> offsetGain</span><span class="pun">);</span></pre>

<p>
	علاوة على ذلك يتغير حجم كل نقطة ديناميكيًا بناءً على مدى قرب النقطة من الهدف وعمقها في المشهد، مما يجعل حركة النقاط تبدو ثلاثية أبعاد أكثر.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1012_29" style=""><span class="pln">gl_PointSize </span><span class="pun">=</span><span class="pln"> size </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="pln">pow</span><span class="pun">(</span><span class="pln">d</span><span class="pun">,</span><span class="lit">3.</span><span class="pun">)</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> offsetSize</span><span class="pun">)</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1.</span><span class="pun">/-</span><span class="pln">mvPosition</span><span class="pun">.</span><span class="pln">z</span><span class="pun">);</span></pre>

<p>
	ستبدو النتيجة النهائية للحركة العشوائية كالتالي:
</p>

<p>
	<iframe frameborder="0" src="https://codesandbox.io/embed/7gzmpd?view=preview&amp;hidenavigation=1" style="width:100%;max-width:100%;height:300px"></iframe>
</p>

<h2 id="-7">
	إضافة الألوان
</h2>

<p>
	سنخفي النقطة في مظلل الشرائح fragment shader باستخدام <a href="https://thebookofshaders.com/07/" rel="external nofollow">دالة تنشئ شكل دائري</a> ثم نحدد تدرج لوني بين اللونين startColor و endColor بناءً على المسافة vDistance بين النقطة والهدف والتي حسبناها في المظلل vertex shader. هذا يعني أن الألوان ستتغير تدريجيًا حسب المسافة أيضًا، مما ينتج تأثيرًا مرئيًا سلسًا عند تحرك النقطة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1012_31" style=""><span class="pln">vec3 circ </span><span class="pun">=</span><span class="pln"> vec3</span><span class="pun">(</span><span class="pln">circle</span><span class="pun">(</span><span class="pln">uv</span><span class="pun">,</span><span class="lit">1.</span><span class="pun">));</span><span class="pln">
vec3 color </span><span class="pun">=</span><span class="pln"> mix</span><span class="pun">(</span><span class="pln">startColor</span><span class="pun">,</span><span class="pln">endColor</span><span class="pun">,</span><span class="pln">vDistance</span><span class="pun">);</span><span class="pln">
gl_FragColor</span><span class="pun">=</span><span class="pln">vec4</span><span class="pun">(</span><span class="pln">color</span><span class="pun">,</span><span class="pln">circ</span><span class="pun">.</span><span class="pln">r </span><span class="pun">*</span><span class="pln"> vDistance</span><span class="pun">);</span></pre>

<p>
	تظهر النتيجة على النحو التالي:
</p>

<p>
	<iframe frameborder="0" src="https://codesandbox.io/embed/t2dprr?view=preview&amp;hidenavigation=1" style="width:100%;max-width:100%;height:300px"></iframe>
</p>

<h2 id="-8">
	دمج الصوت مع المؤثرات المرئية
</h2>

<p>
	سنستخدم الآن إبداعنا لتخصيص بيانات الصوت والإيقاع لكل الخصائص التي نريد تغييرها، سواء في مظلل الرؤوس الذي يتحكم في الشكل والموقع أو في مظلل الشرائح الذي يتحكم في الألوان والتأثيرات. يمكننا أيضًا إضافة بعض الرسوم المتحركة العشوائية لتغيير الحجم والموقع position، والدوران باستخدام مكتبة GSAP، وهي مكتبة تساعد في جعل الأشياء تتحرك وتتغير بسلاسة في الوقت الحقيقي.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1012_33" style=""><span class="pln">update</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// تحديث السعة ديناميكيًا بناءً على بيانات التردد العالي من مدير الصوت</span><span class="pln">
  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">material</span><span class="pun">.</span><span class="pln">uniforms</span><span class="pun">.</span><span class="pln">amplitude</span><span class="pun">.</span><span class="pln">value </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0.8</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> THREE</span><span class="pun">.</span><span class="typ">MathUtils</span><span class="pun">.</span><span class="pln">mapLinear</span><span class="pun">(</span><span class="typ">App</span><span class="pun">.</span><span class="pln">audioManager</span><span class="pun">.</span><span class="pln">frequencyData</span><span class="pun">.</span><span class="pln">high</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0.6</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">0.1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0.2</span><span class="pun">)</span><span class="pln">

  </span><span class="com">// تحديث الإزاحة بناءً على بيانات التردد المنخفض لتغييرات تأثيرات طفيفة</span><span class="pln">
  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">material</span><span class="pun">.</span><span class="pln">uniforms</span><span class="pun">.</span><span class="pln">offsetGain</span><span class="pun">.</span><span class="pln">value </span><span class="pun">=</span><span class="pln"> </span><span class="typ">App</span><span class="pun">.</span><span class="pln">audioManager</span><span class="pun">.</span><span class="pln">frequencyData</span><span class="pun">.</span><span class="pln">mid </span><span class="pun">*</span><span class="pln"> </span><span class="lit">0.6</span><span class="pln">

  </span><span class="com">// تحويل بيانات التردد المنخفض إلى نطاق معين واستخدامه لزيادة قيمة الوقت</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> t </span><span class="pun">=</span><span class="pln"> THREE</span><span class="pun">.</span><span class="typ">MathUtils</span><span class="pun">.</span><span class="pln">mapLinear</span><span class="pun">(</span><span class="typ">App</span><span class="pun">.</span><span class="pln">audioManager</span><span class="pun">.</span><span class="pln">frequencyData</span><span class="pun">.</span><span class="pln">low</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0.6</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0.2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0.5</span><span class="pun">)</span><span class="pln">
  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">time </span><span class="pun">+=</span><span class="pln"> THREE</span><span class="pun">.</span><span class="typ">MathUtils</span><span class="pun">.</span><span class="pln">clamp</span><span class="pun">(</span><span class="pln">t</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0.2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0.5</span><span class="pun">)</span><span class="pln"> </span><span class="com">// تقييد القيمة للتأكد من بقائها ضمن النطاق المطلوب</span><span class="pln">

  </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">material</span><span class="pun">.</span><span class="pln">uniforms</span><span class="pun">.</span><span class="pln">time</span><span class="pun">.</span><span class="pln">value </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">time
</span><span class="pun">}</span></pre>

<h2>
	الخلاصة
</h2>

<p>
	شرحنا في هذا المقال كيفية إنشاء تأثيرات صوتية مزامنة للإيقاع باستخدام مكتبة Three.js. يمكنكم بعد قراءة هذا المقال استخدام Three.js لتحريك الجزيئات بطريقة ديناميكية و مزامنة الصوت وإنشاء مؤثرات مرئية مزامنة للإيقاع والتحكم بها.
</p>

<p>
	ترجمة -وبتصرف- لمقال <a href="https://tympanus.net/codrops/2023/12/19/creating-audio-reactive-visuals-with-dynamic-particles-in-three-js/" rel="external nofollow">Creating Audio-Reactive Visuals with Dynamic Particles in Three.js</a> لصاحبه Tiago Canzian.
</p>

<h2 id="-9">
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-webgl-%D8%AA%D8%B9%D8%B1%D9%8A%D9%81-r481/" rel="">مقدمة إلى WebGL - تعريف - جافاسكربت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-webgl-%D8%A7%D9%84%D8%AA%D9%86%D9%82%D9%84-%D9%81%D9%8A-%D8%A7%D9%84%D9%81%D8%B6%D8%A7%D8%A1-%D9%88%D8%AA%D8%B4%D8%BA%D9%8A%D9%84-%D8%A7%D9%84%D8%A3%D8%B6%D9%88%D8%A7%D8%A1-r485/" rel="">مقدمة إلى WebGL - التنقل في الفضاء وتشغيل الأضواء</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%B3%D9%84%D8%B3%D9%84%D8%A9-%D8%A7%D9%84%D9%88%D8%B9%D9%88%D8%AF-promises-chaining-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r916/" rel="">سلسلة الوعود Promises chaining في جافاسكربت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D9%85%D8%B4%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%B4%D8%A7%D8%A6%D8%B9%D8%A9-%D9%84%D9%84%D8%AA%D9%88%D8%A7%D9%81%D9%82-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D9%81%D9%8A-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1972/" rel="">معالجة المشاكل الشائعة للتوافق مع المتصفحات في شيفرة جافاسكربت</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2552</guid><pubDate>Thu, 10 Apr 2025 15:06:00 +0000</pubDate></item><item><title>&#x627;&#x62E;&#x62A;&#x628;&#x627;&#x631; &#x62A;&#x637;&#x628;&#x64A;&#x642;&#x627;&#x62A; &#x62C;&#x627;&#x641;&#x627; &#x633;&#x643;&#x631;&#x64A;&#x628;&#x62A; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x625;&#x637;&#x627;&#x631; &#x639;&#x645;&#x644; Jest</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-jest-r2516/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_02/Jest_.png.a69836eb98822a3af3d4574046b72b8c.png" /></p>
<p>
	نتعرف في مقال اليوم على إطار عمل جيست Jest أحد الأطر الشائعة لاختبار الشيفرات المصدرية المكتوبة باستخدام رياكت React ولغة جافا سكريبت، فهو يقدم مجموعة شاملة من المطابِقات Matchers التي تقارن بين القيم المتوقعة والقيم الفعلية أثناء اختبار الشيفرة مما يسمح بكتابة اختبارات لعدة أنواع من المشاريع. كما يُنَظم الاختبارات في مجموعات اختبار test suites ليُحَسّن الصيانة والمقروئية، وينجز الاختبار بكفاءة وسلاسة.
</p>

<p>
	ستتعرّف في هذا المقال على عدّة اختبارات شائعة الاستخدام في مجال تطوير البرمجيات، وبعض الممارسات المفيدة في عملية الاختبار. إذ سنكتب توابع جافا سكريبت JavaScript ثم سنختبرها باستخدام إطار عمل جيست Jest على خادم Vultr مع تطبيق نود جي إس NodeJS جاهز من صفحة متجر تطبيقات <a href="https://www.vultr.com/marketplace/apps/nodejs" rel="external nofollow">NodeJS marketplace</a>.
</p>

<h2 id="">
	أنواع الاختبارات
</h2>

<p>
	تتضمن عملية تطوير البرمجيات ثلاثة أنواع من الاختبارات لضمان جودة الشيفرة البرمجية وسلامة وظائفها. يخدم كل منها غرضًا محددًا:
</p>

<h3>
	اختبارات الوحدة Unit tests
</h3>

<p>
	تركز هذه الاختبارات على اختبار الوحدات أو المكونات الفردية مثل الدوال functions أو التوابع methods أو الصفوف classes. إذ يضمن هذا الاختبار أن كل مكونات الشيفرة البرمجية تعمل بصورة صحيحة من تلقاء نفسها.
</p>

<h3>
	اختبارات التكامل Integration tests
</h3>

<p>
	تركز هذه الاختبارات على تقييم تفاعل مكونات التطبيق المختلفة مع بعضها البعض. على عكس اختبارات الوحدة، التي تختبر مكونًا واحدًا، تهتم هذه الاختبارات بالتفاعلات بين عدّة وحدات للتأكد من أنها تعمل كما هو متوقع عند دمجها.
</p>

<h3>
	الاختبارات الشاملة End-to-end tests
</h3>

<p>
	تحاكي هذه الاختبارات سيناريوهات الاستخدام الحقيقية عن طريق اختبار التطبيق من البداية إلى النهاية. وتكون مؤتمتة automated عادة، وتتفاعل مع التطبيق كمستخدم حقيقي، مما يضمن أن التطبيق يعمل كما ينبغي.
</p>

<h2 id="-1">
	منهجيات شائعة في عملية الاختبار
</h2>

<p>
	تساعد منهجية الإعداد Arrange واتخاذ الإجراء المناسب Act والتأكيد Assert  أو ما يعرف اختصارًا بمهنجية AAA على تقسيم الاختبار إلى مراحل منفصلة، وبذلك يصبح من السهل على المطورين والمراجعين فهم كل جزء من الاختبار على حدة، وتحديد الأخطاء بسرعة أكبر.
</p>

<p>
	فيما يلي مثال يوضح المنهجية السابقة، حيث سنختبر فيما إذا كان البريد الإلكتروني للمستخدم مكتوبًا بأبجدية رقمية alphanumeric أي مكون من أحرف وأرقام، ويستخدم النطاق example.com:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8273_6" style=""><span class="pln">test</span><span class="pun">(</span><span class="str">"returns false for an invalid domain"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> email </span><span class="pun">=</span><span class="pln"> </span><span class="str">"user123@example.net"</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> result </span><span class="pun">=</span><span class="pln"> isValidEmail</span><span class="pun">(</span><span class="pln">email</span><span class="pun">);</span><span class="pln">
  expect</span><span class="pun">(</span><span class="pln">result</span><span class="pun">).</span><span class="pln">toBe</span><span class="pun">(</span><span class="kwd">false</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	فيما يلي شرح المراحل الثلاثة للاختبار
</p>

<h3>
	مرحلة الإعداد Arrange
</h3>

<p>
	علينا في هذه المرحلة إعداد الشرط الأولي وبيئة الاختبار. بما فيه تهيئة الكائنات objects وإعداد الدخل inputs وضبط البيئة لإنشاء حالة الاستخدام التي نرغب في اختبارها، نريد في مثالنا اختبار عمل الدالة <code>isValidEmail</code> ونتحقق فيما إذا كانت تحدد بدّقة رسائل البريد الإلكتروني غير الصالحة. وهنا أسندنا إلى متغير البريد الإلكتروني سلسلة نصية لا تفي بالمعايير التي حددناها حيث تستخدم النطاق ‎.net بدلاً من ‎.com
</p>

<h3>
	مرحلة اتخاذ الإجراء المناسب Act 
</h3>

<p>
	ننفّذ في هذه المرحلة الإجراء الذي نريد اختباره. قد يتضمن ذلك استدعاء تابع أو التفاعل مع كائن، أو تنفيذ جزء معين من الشيفرة البرمجية. وقد استدعينا في مثالنا الدالة <code>isValidEmail</code> في هذه الخطوة، ثم مررنا متغير البريد الإلكتروني كوسيط.
</p>

<h3>
	مرحلة التأكيد Assert
</h3>

<p>
	في هذه المرحلة، علينا التحقق من أن نتيجة المرحلة السابقة تلبي التوقعات، وقد استخدمنا في مثالنا التابع <code>expect</code> الذي يوفره إطار عمل Jest، للتأكد من أن النتيجة التي أرجعتها الدالة <code>isValidEmail</code> محددة بأنها خاطئة، كما هو متوقع بالنسبة لبريد إلكتروني غير صالح.
</p>

<h2 id="vultr">
	إعداد مجلد المشروع على Vultr
</h2>

<p>
	سننشر أولًا خادم على Vultr ومن الضروري أن نحرص على تحديد الخيار NodeJS في علامة تبويب تطبيقات المتجر Apps Marketplace. بعدها علينا إعداد مشروع Node.js وتثبيت إطار عمل Jest. ثم تعريف دالتين في ملفي جافا سكريبت منفصلين، حيث سنختبر هاتين الدالتين في الخطوات القادمة.
</p>

<p>
	نتأكّد من تثبيت Node.jS باستخدام الأمر التالي:
</p>

<pre class="ipsCode">node --version
</pre>

<p>
	بعدها، ننشئ مجلدًا للمشروع ثم ننتقل إليه:
</p>

<pre class="ipsCode">mkdir sample
cd sample
</pre>

<p>
	نبدأ تشغيل المشروع باستخدام الأمر التالي:
</p>

<pre class="ipsCode">npm init -y
</pre>

<p>
	ثم نثبّت إطار عمل Jest:
</p>

<pre class="ipsCode">npm install --save-dev jest
</pre>

<p>
	نلاحظ أننا استخدمنا الراية ‎--save-dev لتثبيت الحزمة كتبعية تطوير development dependency. ويعني هذا أن الحزمة ستُسخدم فقط خلال مرحلتي التطوير والاختبار، وليس في مرحلة الإنتاج.
</p>

<p>
	بعد ذلك نفتح ملف <code>package.json</code> باستخدام أحد <a href="https://academy.hsoub.com/programming/python/%D9%85%D8%AD%D8%B1%D8%B1-%D8%A3%D9%83%D9%88%D8%A7%D8%AF-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86/" rel="">محررات الأكواد</a>، وسنستخدم في مثالنا محرر النصوص <a href="https://www.nano-editor.org/docs.php" rel="external nofollow">نانو Nano</a>.
</p>

<pre class="ipsCode">nano package.json
</pre>

<p>
	الآن، علينا إضافة سكربت للاختبار
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8273_8" style=""><span class="str">"scripts"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="str">"test"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"jest"</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نغلق الملف باستخدم الاختصار Ctrl+X، ثم ننقر على مفتاح Y لحفظ التغييرات.
</p>

<p>
	بعدها سنشئ ملف جافا سكريبت ونطلق عليه اسم token:
</p>

<pre class="ipsCode">nano token.js
</pre>

<p>
	ننسخ الشيفرة التالية ونلصقها في ملف <code>token.js</code>، إذ توضح هذه الشيفرة كيفية حساب تاريخ انتهاء صلاحية مفتاح تشفير ويب <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D9%8A%D8%AB%D8%A7%D9%82-%D8%B9%D8%A8%D8%B1-%D9%85%D9%81%D8%AA%D8%A7%D8%AD-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D9%85%D8%B4%D9%81%D8%B1-token-authentication-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-nodejs-%D9%88-react-r1135/" rel="">JSON Web Token</a> أو JWT اختصارًا، والتحقق من صلاحيته.
</p>

<p>
	<strong>ملاحظة:</strong> إن شيفرة التحقق التالية هي للتوضيح فقط، حيث يوصى الاعتماد على مكتبات معتمدة عند التعامل مع رموز المصادقة في المشاريع الحقيقية مثل <a href="https://www.npmjs.com/package/jsonwebtoken" rel="external nofollow">مكتبة jsonwebtoken</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8273_10" style=""><span class="com">/**
 *
 * @param {string} token
 * @returns {Date}
 */</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> getTokenExpiryDate</span><span class="pun">(</span><span class="pln">token</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">token</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">;</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> tokenParts </span><span class="pun">=</span><span class="pln"> token</span><span class="pun">.</span><span class="pln">split</span><span class="pun">(</span><span class="str">"."</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> payload </span><span class="pun">=</span><span class="pln"> tokenParts</span><span class="pun">[</span><span class="lit">1</span><span class="pun">];</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> decodedPayload </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Buffer</span><span class="pun">.</span><span class="pln">from</span><span class="pun">(</span><span class="pln">payload</span><span class="pun">,</span><span class="pln"> </span><span class="str">"base64"</span><span class="pun">).</span><span class="pln">toString</span><span class="pun">();</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> payloadObject </span><span class="pun">=</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span class="pln">decodedPayload</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> expiryDate </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">(</span><span class="pln">payloadObject</span><span class="pun">.</span><span class="pln">exp </span><span class="pun">*</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> expiryDate</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">/**
 *
 * @param {string} token
 * @returns {boolean}
 */</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> isTokenExpired</span><span class="pun">(</span><span class="pln">token</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> expiryDate </span><span class="pun">=</span><span class="pln"> getTokenExpiryDate</span><span class="pun">(</span><span class="pln">token</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> currentDate </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">();</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> currentDate </span><span class="pun">&gt;</span><span class="pln"> expiryDate</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> isTokenExpired </span><span class="pun">};</span></pre>

<p>
	تُعَرّف الشيفرة السابقة دالتين هما <code>getTokenExpiryDate</code> و<code>isTokenExpired</code> اللتان تحسبان تاريخ انتهاء صلاحية رمز JWT وتعطيان إما القيمة <code>true</code> أو <code>false</code> بناءً على حالة صلاحية الرمز. 
</p>

<p>
	نحفظ الملف ونغلقه، ثم ننشئ ملف جافا سكريبت آخر باسم email:
</p>

<pre class="ipsCode">nano email.js
</pre>

<p>
	ثم نلصق فيه الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8273_12" style=""><span class="kwd">function</span><span class="pln"> isValidEmail</span><span class="pun">(</span><span class="pln">email</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> emailRegex </span><span class="pun">=</span><span class="pln"> </span><span class="str">/[a-zA-Z0-9]+@example\.com/</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> emailRegex</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="pln">email</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">


module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> isValidEmail </span><span class="pun">};</span></pre>

<p>
	تُعَرّف الشيفرة السابقة الدالة <code>isValidEmail</code> التي تتحقق من صحة بنية البريد الإلكتروني باستخدام <a href="https://academy.hsoub.com/programming/general/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-r1374/" rel="">التعابير النمطية regex</a>، والتي تتيح لنا التحقق من أن جميع عناوين البريد الإلكتروني مكتوبة بأبجدية رقمية وتنتهي بـالنطاق ‎@example.com. والآن، علينا حفظ الملف وإغلاقه.
</p>

<h2 id="unittestsjest">
	كتابة اختبارات الوحدة Unit Tests باستخدام إطار عمل Jest
</h2>

<p>
	سنتعلم الآن كيفية كتابة اختبارات الوحدة باستخدام إطار عمل <a href="https://jestjs.io" rel="external nofollow">جيست Jest</a> لاختبار دوال جافا سكريبت التي عرّفناها سابقًا. سنضع هذه الاختبارات في مجلد منفصل ضمن مجلد المشروع، ونوصي باتباع التسميات الاصطلاحية التي سنعتمدها في مقالنا لأن Jest يتعامل مع الملفات التي تنتهي بالصيغة <code>‎</code>.test.js‎ كملفات اختبار. 
</p>

<p>
	أولًا، ننشئ مجلدّا يدعى <code>tests</code> داخل مجلد <code>sample</code>، ثم ننتقل إليه باستخدام الأمر <code>cd</code> على النحو التالي:
</p>

<pre class="ipsCode">mkdir tests
cd tests
</pre>

<p>
	بعدها ننشئ ملف جافا سكريبت.
</p>

<pre class="ipsCode">nano token.test.js
</pre>

<p>
	ننسخ الشيفرة التالية ونلصقها في الملف.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8273_15" style=""><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> isTokenExpired </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"/root/sample/token.js"</span><span class="pun">);</span><span class="pln">

describe</span><span class="pun">(</span><span class="str">"isTokenExpired"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  test</span><span class="pun">(</span><span class="str">"should return false if the token is not expired"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> token </span><span class="pun">=</span><span class="pln"> </span><span class="str">"eyJ0eXAiOiJKV1QiLCJhbGciOi..."</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> result </span><span class="pun">=</span><span class="pln"> isTokenExpired</span><span class="pun">(</span><span class="pln">token</span><span class="pun">);</span><span class="pln">
    expect</span><span class="pun">(</span><span class="pln">result</span><span class="pun">).</span><span class="pln">toBe</span><span class="pun">(</span><span class="kwd">false</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">

  test</span><span class="pun">(</span><span class="str">"should return true if the token is expired"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> token </span><span class="pun">=</span><span class="pln"> </span><span class="str">"eyJ0eXAiOiJKV1QiLCJhbGciOi..."</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> result </span><span class="pun">=</span><span class="pln"> isTokenExpired</span><span class="pun">(</span><span class="pln">token</span><span class="pun">);</span><span class="pln">
    expect</span><span class="pun">(</span><span class="pln">result</span><span class="pun">).</span><span class="pln">toBe</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	تستخدم الشيفرة السابقة الدالة <code>isTokenExpired</code> التي عرّفناها سابقًا لتحديد ما إذا كانت الدالة تتصرف كما هو متوقع، وتعيد قيمةً منطقيةً تحدد إن كان رمز JWT منتهي الصلاحية أم لا.
</p>

<p>
	<strong>ملاحظة:</strong> إن أردنا أن تتحقق الدالة <code>isTokenExpired</code> من صحة رموز مصادقة حقيقية، يمكننا إنشاء رموز JWT منتهية الصلاحية وأخرى غير منتهية الصلاحية بواسطة أداة مثل <a href="http://jwtbuilder.jamiekurtz.com/" rel="external nofollow">JSON Web Token Builder</a> واستخدامها بدلاً من النصوص المستخدمة في الاختبارات السابقة.
</p>

<p>
	نحفظ الملف ونغلقه، ثم ننشئ ملف جافا سكريبت آخر للاختبار.
</p>

<pre class="ipsCode">nano email.test.js
</pre>

<p>
	ننسخ الشيفرة التالية ونلصقها في ملف الاختبار.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8273_17" style=""><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> isValidEmail </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"/root/sample/email.js"</span><span class="pun">);</span><span class="pln">

describe</span><span class="pun">(</span><span class="str">"isValidEmail"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  test</span><span class="pun">(</span><span class="str">"returns true for a valid email"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> email </span><span class="pun">=</span><span class="pln"> </span><span class="str">"user123@example.com"</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> result </span><span class="pun">=</span><span class="pln"> isValidEmail</span><span class="pun">(</span><span class="pln">email</span><span class="pun">);</span><span class="pln">
    expect</span><span class="pun">(</span><span class="pln">result</span><span class="pun">).</span><span class="pln">toBe</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">

  test</span><span class="pun">(</span><span class="str">"returns false for an invalid domain"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> email </span><span class="pun">=</span><span class="pln"> </span><span class="str">"user123@example.net"</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> result </span><span class="pun">=</span><span class="pln"> isValidEmail</span><span class="pun">(</span><span class="pln">email</span><span class="pun">);</span><span class="pln">
    expect</span><span class="pun">(</span><span class="pln">result</span><span class="pun">).</span><span class="pln">toBe</span><span class="pun">(</span><span class="kwd">false</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	تختبر الشيفرة السابقة دالة <code>isValidEmail</code> من خلال توفير بريد إلكتروني صالح وآخر غير صالح. ثم تتحقق من صحة النتائج عن طريق مطابقة المخرجات المتوقعة مع المخرجات الفعلية.
</p>

<h2 id="jestvultr">
	تشغيل اختبارات Jest على Vultr
</h2>

<p>
	سنتعلم الآن كيف نجري اختبارات باستخدام إطار عمل Jest بطريقتين. إذ سنجري في الطريقة الأولى اختبارات وحدة فردية لكل دالة معرّفة سابقًا. أما في الطريقة الثانية، فسنختبر الدالتين في وقت واحد.
</p>

<h3 id="-2">
	اختبارات وحدة فردية
</h3>

<p>
	أولًا: نختبر صحة رمز المصادقة.
</p>

<pre class="ipsCode">npm test -- tests/token.test.js
</pre>

<p>
	إن نجح الاختبار فيجب أن يبدو الخرج على النحو التالي:
</p>

<pre class="ipsCode">&gt; sample@1.0.0 test
 &gt; jest tests/token.test.js

 PASS  tests/token.test.js
 isTokenExpired
     ✓ should return false if the token is not expired (2 ms)
     ✓ should return true if the token is expired (1 ms)

 Test Suites: 1 passed, 1 total
 Tests:       2 passed, 2 total
 Snapshots:   0 total
 Time:        0.315 s, estimated 1 s
 Ran all test suites matching /tests\/token.test.js/i.
</pre>

<p>
	ثانيًا: نختبر صحة البريد الإلكتروني.
</p>

<pre class="ipsCode">npm test -- tests/email.test.js
</pre>

<p>
	إن نجح الاختبار فسيبدو الخرج على النحو التالي:
</p>

<pre class="ipsCode">&gt; sample@1.0.0 test
&gt; jest tests/email.test.js

PASS  tests/email.test.js
isValidEmail
    ✓ returns true for a valid email (2 ms)
    ✓ returns false for an invalid domain (1 ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        0.451 s
Ran all test suites matching /tests\/email.test.js/i.
</pre>

<h3 id="-3">
	إجراء جميع الاختبارات في وقت واحد
</h3>

<p>
	أولًا: نشغّل جميع الاختبارات في وقت واحد.
</p>

<pre class="ipsCode">npm test
</pre>

<p>
	إن نجح الاختبار فسيبدو الخرج على النحو التالي:
</p>

<pre class="ipsCode">&gt; sample@1.0.0 test
&gt; jest

PASS  tests/email.test.js
PASS  tests/token.test.js

Test Suites: 2 passed, 2 total
Tests:       4 passed, 4 total
Snapshots:   0 total
Time:        0.225 s, estimated 1 s
Ran all test suites.
</pre>

<h2 id="-4">
	ختامًا
</h2>

<p>
	تعرّفنا في هذا المقال على أنواع مختلفة من الاختبارات البرمجية، وعلى بعض المنهجيات والممارسات المفيدة في عملية الاختبار. وتعلّمنا كيفية كتابة وتطبيق اختبارات الوحدة باستخدام إطار عمل Jest على خادم Vultr مثبّت عليه Node.js. حيث يمكن زيادة موثوقية وقوة الشيفرة البرمجية من خلال الاستفادة من أدوات الاختبار مثل Jest لتطوير برامج أكثر استقرارًا وقابلة للصيانة.
</p>

<p>
	ترجمة -وبتصرّف- للمقال <a href="https://developer.mozilla.org/en-US/blog/test-javascript-with-jest-on-vultr/#testing_conventions" rel="external nofollow">Testing JavaScript with Jest on Vultr</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%A7%D8%B3%D8%AA%D8%B1%D8%A7%D8%AA%D9%8A%D8%AC%D9%8A%D8%A7%D8%AA-%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D9%85%D8%B4%D8%A7%D8%B1%D9%8A%D8%B9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%84%D9%84%D8%AA%D9%88%D8%A7%D9%81%D9%82-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-r1954/" rel="">استراتيجيات اختبارات مشاريع الويب للتوافق مع المتصفحات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-mocha-%D9%88-assert-%D9%81%D9%8A-nodejs-r1744/" rel="">اختبار الوحدات البرمجية باستخدام Mocha و Assert في Node.js</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/advanced/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D8%AF%D8%A7%D8%A9-testgrid-%D9%88%D8%A3%D9%87%D9%85%D9%8A%D8%AA%D9%87%D8%A7-%D9%81%D9%8A-%D8%A3%D8%AA%D9%85%D8%AA%D8%A9-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-r2390/" rel="">تعرف على أداة TestGrid وأهميتها في أتمتة الاختبارات البرمجية</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A6%D8%A9-%D9%84%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%A2%D9%84%D9%8A%D8%A9-%D9%81%D9%8A-%D9%85%D8%B4%D8%A7%D8%B1%D9%8A%D8%B9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%84%D9%84%D8%AA%D9%88%D8%A7%D9%81%D9%82-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-r1990/" rel="">إعداد البيئة للاختبارات الآلية في مشاريع الويب للتوافق مع المتصفحات</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2516</guid><pubDate>Fri, 21 Feb 2025 15:00:01 +0000</pubDate></item><item><title>&#x62A;&#x639;&#x631;&#x641; &#x639;&#x644;&#x649; &#x627;&#x644;&#x648;&#x627;&#x62C;&#x647;&#x629; &#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x64A;&#x629; Broadcast Channel API &#x648;&#x62A;&#x637;&#x628;&#x64A;&#x642;&#x627;&#x62A;&#x647;&#x627; &#x627;&#x644;&#x639;&#x645;&#x644;&#x64A;&#x629;</title><link>https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-broadcast-channel-api-%D9%88%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA%D9%87%D8%A7-%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A9-r2499/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2025_01/BroadcastChannelAPI_.png.9e47c47065a4523407cf5c74742a1c7c.png" /></p>
<p>
	تتيح الواجهة البرمجية لقناة البث Broadcast Channel <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> التخاطب بين عدة نوافذ للمتصفح وتبويباته tabs وإطاراته المضمنة iframes وعمّال الويب web workers ضمنه، إذ توفر طريقة سهلة وفعالة لمزامنة البيانات والسلوك في مختلف سياقات المتصفح browser contexts للحصول على تطبيقات ويب أكثر تفاعلًا ومرونة.
</p>

<p>
	سنتعرف على مفاهيم <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel="">الواجهة البرمجية</a> Broadcast Channel <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>، إضافة إلى استخدامها وتطبيقاتها على أرض الواقع، كما سنطبّق مثالًا عمليًا لبناء تطبيق بسيط يستخدم هذه الواجهة البرمجية ويرسل الرسائل بين عدة نوافذ وتبويبات في المتصفح.
</p>

<h2 id="broadcastchannelapi-1">
	مفهوم الواجهة البرمجية للبث Broadcast Channel <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>
</h2>

<p>
	تقدم الواجهة البرمجية لقناة البث Broadcast Channel آلية تخاطب بين عدة سياقات تشترك بنفس المستخدم والمتصفح والأصل origin وما نعنيه بالأصل هنا الموقع أو التطبيق، حيث تعتمد آلية عملها على مبدأ إنشاء قناة بث channel واحدة مشتركة يمكن أن تنضم إليها عدة سياقات أو تغادرها في أية لحظة. فعند انضمام تلك السياقات إلى قناة تصبح قادرة على إرسال الرسائل واستقبالها من خلال هذه القناة، تتيح لنا هذه الآلية تبادل البيانات وانتشار الأحداث ضمن تطبيقنا بسلاسة، وتنفي الحاجة إلى تقنيات التواصل المعقدة التي تجري عادة من جانب <a href="https://academy.hsoub.com/devops/servers/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AE%D8%A7%D8%AF%D9%85-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r574/" rel="">الخادم</a>. فيما يلي لمحة بسيطة عن طريقة استخدام هذه الواجهة البرمجية.
</p>

<p>
	لإنشاء قناة بث أو الانضمام إليها نكتب الكود التالي بلغة جافا سكريبت:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1861_6" style=""><span class="kwd">const</span><span class="pln"> bc </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">BroadcastChannel</span><span class="pun">(</span><span class="str">"test_channel"</span><span class="pun">);</span></pre>

<p>
	لإرسال رسالة عبر قناة البث نكتب التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1861_8" style=""><span class="pln">bc</span><span class="pun">.</span><span class="pln">postMessage</span><span class="pun">(</span><span class="str">"This is a test message"</span><span class="pun">);</span></pre>

<p>
	لاستقبال الرسالة الواردة من قناة البث:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1861_10" style=""><span class="pln">bc</span><span class="pun">.</span><span class="pln">onmessage </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">event</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">event</span><span class="pun">.</span><span class="pln">data</span><span class="pun">);</span><span class="pln">
  </span><span class="com">// { method: "add", note: "This is a test message" }</span><span class="pln">
</span><span class="pun">};</span></pre>

<h2 id="nodejs">
	إنشاء تطبيق Node.js
</h2>

<p>
	لننشئ تطبيق نود جي إس يستخدم الواجهة البرمجية Broadcast Channel <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> نبدأ أولاً بتجهيز الخادم على جهازنا المحلي أو على خدمة سحابية ما وندخل إليه عن طريق بروتوكول <a href="https://academy.hsoub.com/devops/security/ssh/" rel=""><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة"><abbr title="Secure Shell | القشرة (أو الصَدَفة) الآمنة">SSH</abbr></abbr></a>، ثم نعدّ مشروعًا لتطبيق الويب الذي سننشئه. سنستخدم في مقالنا محرر النصوص نانو <a href="https://academy.hsoub.com/programming/workflow/%D9%85%D9%82%D8%A7%D8%B1%D9%86%D8%A9-%D8%A8%D9%8A%D9%86-%D9%85%D8%AD%D8%B1%D8%B1-%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D9%81%D9%8A%D9%85-vim-%D9%88%D9%86%D8%A7%D9%86%D9%88-nano-r1590/" rel="">Nano</a> لإنشاء ملفات المشروع على الخادم وتعديلها، كما سنعتمد جدار الحماية المبسَّط UFW للتحكم بحركة المرور التي سنسمح بمرورها من وإلى الخادم، وسنحدد باستخدامه المنفذ 8080 ليكون المنفذ الوحيد الذي يُسمَح بمرور البيانات الواردة إلى الخادم من خلاله.
</p>

<p>
	سنستخدم لغة جافا سكريبت و<a href="https://academy.hsoub.com/programming/javascript/%D8%A8%D9%8A%D8%A6%D8%A9-nodejs-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-%D8%AE%D8%A7%D8%B1%D8%AC-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD-r1402/" rel="">بيئة Node.js</a> لكتابة ملف index للتطبيق، وسنشغله بالاعتماد على خادم HTTP، علماً أن بإمكاننا استخدام أي نوع آخر من خوادم الويب لتحقيق النتيجة ذاتها.
</p>

<ol>
	<li>
		ننشئ مجلدًا خاصًا بالمشروع، وننتقل إليه
	</li>
</ol>

<pre class="ipsCode">mkdir notes-app
cd notes-app
</pre>

<ol start="2">
	<li>
		ننشئ مشروع Node.js
	</li>
</ol>

<pre class="ipsCode">npm init -y
</pre>

<ol start="3">
	<li>
		نثبّت حزمة http-server لإنشاء خادم الويب
	</li>
</ol>

<pre class="ipsCode">npm install http-server
</pre>

<ol start="4">
	<li>
		ننشئ ملف HTML
	</li>
</ol>

<pre class="ipsCode">nano index.html
</pre>

<ol start="5">
	<li>
		ننسخ الشيفرة البرمجية التالية ونلصقها في ملف <code>index.html</code>
	</li>
</ol>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1861_12" style=""><span class="dec">&lt;!doctype html&gt;</span><span class="pln">
</span><span class="tag">&lt;html</span><span class="pln"> </span><span class="atn">lang</span><span class="pun">=</span><span class="atv">"en"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;head&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"UTF-8"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"viewport"</span><span class="pln"> </span><span class="atn">content</span><span class="pun">=</span><span class="atv">"width=device-width, initial-scale=1.0"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
    </span><span class="tag">&lt;title&gt;</span><span class="pln">Note-taking App</span><span class="tag">&lt;/title&gt;</span><span class="pln">
    </span><span class="tag">&lt;link</span><span class="pln"> </span><span class="atn">rel</span><span class="pun">=</span><span class="atv">"stylesheet"</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"styles.css"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;/head&gt;</span><span class="pln">
  </span><span class="tag">&lt;body&gt;</span><span class="pln">
    </span><span class="tag">&lt;h1&gt;</span><span class="pln">Note-taking App</span><span class="tag">&lt;/h1&gt;</span><span class="pln">
    </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"noteList"</span><span class="tag">&gt;&lt;/div&gt;</span><span class="pln">
    </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"noteForm"</span><span class="tag">&gt;</span><span class="pln">
      </span><span class="tag">&lt;label</span><span class="pln"> </span><span class="atn">for</span><span class="pun">=</span><span class="atv">"noteInput"</span><span class="tag">&gt;</span><span class="pln">New note</span><span class="tag">&lt;/label&gt;</span><span class="pln">
      </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text"</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"noteInput"</span><span class="pln"> </span><span class="atn">placeholder</span><span class="pun">=</span><span class="atv">"A note..."</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
      </span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"addNoteButton"</span><span class="tag">&gt;</span><span class="pln">Add Note</span><span class="tag">&lt;/button&gt;</span><span class="pln">
      </span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"resetNoteButton"</span><span class="tag">&gt;</span><span class="pln">Reset Notes</span><span class="tag">&lt;/button&gt;</span><span class="pln">
    </span><span class="tag">&lt;/div&gt;</span><span class="pln">
    </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"app.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
  </span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<ol start="6">
	<li>
		نحفظ الملف ونغلقه
	</li>
	<li>
		ننشئ ملف التنسيقات CSS
	</li>
</ol>

<pre class="ipsCode">nano styles.css
</pre>

<ol start="8">
	<li>
		ننسخ الشيفرة البرمجية التالية ونلصقها في ملف <code>styles.css</code>
	</li>
</ol>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_1861_14" style=""><span class="pln">body </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">font-family</span><span class="pun">:</span><span class="pln"> Arial</span><span class="pun">,</span><span class="pln"> sans-serif</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">background-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#f4f4f4</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">20px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">


h1 </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#333</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">text-align</span><span class="pun">:</span><span class="pln"> center</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">


</span><span class="pun">#</span><span class="pln">noteList </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">display</span><span class="pun">:</span><span class="pln"> grid</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">row-gap</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10px</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">background-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#fff</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">border</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1px</span><span class="pln"> solid </span><span class="lit">#ddd</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">border-radius</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5px</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10px</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">margin-bottom</span><span class="pun">:</span><span class="pln"> </span><span class="lit">20px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">


</span><span class="pun">#</span><span class="pln">noteList div </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">background-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#f9f9f9</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">border</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1px</span><span class="pln"> solid </span><span class="lit">#ddd</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">border-radius</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3px</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">


</span><span class="pun">#</span><span class="pln">noteForm </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">display</span><span class="pun">:</span><span class="pln"> grid</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">column-gap</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10px</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">align-items</span><span class="pun">:</span><span class="pln"> center</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">grid-template-columns</span><span class="pun">:</span><span class="pln"> max-content </span><span class="lit">1fr</span><span class="pln"> max-content max-content</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">


</span><span class="pun">#</span><span class="pln">noteInput </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10px</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">border</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1px</span><span class="pln"> solid </span><span class="lit">#ddd</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">border-radius</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3px</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">font-size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">16px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">


button </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10px</span><span class="pln"> </span><span class="lit">20px</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">background-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#4caf50</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#fff</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">border</span><span class="pun">:</span><span class="pln"> none</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">border-radius</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3px</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">font-size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">16px</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">cursor</span><span class="pun">:</span><span class="pln"> pointer</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">


</span><span class="kwd">button</span><span class="pun">:</span><span class="pln">hover </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">background-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#45a049</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<ol start="9">
	<li>
		نحفظ الملف ونغلقه
	</li>
</ol>

<h2 id="broadcastchannelapi-2">
	تطبيق الواجهة البرمجية Broadcast Channel <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>
</h2>

<p>
	لننشئ الآن تطبيقًا للتواصل بين نوافذ و تبويبات المتصفح المختلفة ومزامنة البيانات بينها في الوقت الفعلي من خلال اتباع الخطوات التالية:
</p>

<ol>
	<li>
		ننشئ ملف بلغة جافا سكريبت JavaScript في المجلد <code>notes-app</code>
	</li>
</ol>

<pre class="ipsCode">nano app.js
</pre>

<ol start="2">
	<li>
		ننسخ الشيفرة البرمجية التالية ونلصقها في ملف التطبيق <code>app.js</code>
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1861_16" style=""><span class="kwd">const</span><span class="pln"> noteList </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">"noteList"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> noteInput </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">"noteInput"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> addNoteButton </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">"addNoteButton"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> resetNoteButton </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">"resetNoteButton"</span><span class="pun">);</span><span class="pln">


</span><span class="kwd">let</span><span class="pln"> notes </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">


</span><span class="kwd">function</span><span class="pln"> renderNotes</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  noteList</span><span class="pun">.</span><span class="pln">innerHTML </span><span class="pun">=</span><span class="pln"> </span><span class="str">""</span><span class="pun">;</span><span class="pln">


  notes</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">((</span><span class="pln">note</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> noteItem </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">"div"</span><span class="pun">);</span><span class="pln">
    noteItem</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> note</span><span class="pun">;</span><span class="pln">
    noteList</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">noteItem</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span><span class="pln">


addNoteButton</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"click"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> newNote </span><span class="pun">=</span><span class="pln"> noteInput</span><span class="pun">.</span><span class="pln">value</span><span class="pun">.</span><span class="pln">trim</span><span class="pun">();</span><span class="pln">


  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">newNote</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    notes</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">newNote</span><span class="pun">);</span><span class="pln">
    renderNotes</span><span class="pun">();</span><span class="pln">
    noteInput</span><span class="pun">.</span><span class="pln">value </span><span class="pun">=</span><span class="pln"> </span><span class="str">""</span><span class="pun">;</span><span class="pln">


    channel</span><span class="pun">.</span><span class="pln">postMessage</span><span class="pun">({</span><span class="pln"> action</span><span class="pun">:</span><span class="pln"> </span><span class="str">"add"</span><span class="pun">,</span><span class="pln"> note</span><span class="pun">:</span><span class="pln"> newNote </span><span class="pun">});</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span><span class="pln">


resetNoteButton</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"click"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  notes </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">
  renderNotes</span><span class="pun">();</span><span class="pln">


  channel</span><span class="pun">.</span><span class="pln">postMessage</span><span class="pun">({</span><span class="pln"> action</span><span class="pun">:</span><span class="pln"> </span><span class="str">"reset"</span><span class="pln"> </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span><span class="pln">


</span><span class="kwd">const</span><span class="pln"> channel </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">BroadcastChannel</span><span class="pun">(</span><span class="str">"notes-channel"</span><span class="pun">);</span><span class="pln">


channel</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"message"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">event</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> action</span><span class="pun">,</span><span class="pln"> note </span><span class="pun">}</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> event</span><span class="pun">.</span><span class="pln">data</span><span class="pun">;</span><span class="pln">


  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">action </span><span class="pun">===</span><span class="pln"> </span><span class="str">"add"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    notes</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">note</span><span class="pun">);</span><span class="pln">
    renderNotes</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">action </span><span class="pun">===</span><span class="pln"> </span><span class="str">"reset"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    notes </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">
    renderNotes</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span></pre>

<ol start="3">
	<li>
		نحفظ الملف ونغلقه
	</li>
	<li>
		نسمح بمرور طلبات الوصول connections الواردة إلى المنفذ 8080 من خلال التعليمة التالية
	</li>
</ol>

<pre class="ipsCode">sudo ufw allow 8080
</pre>

<ol start="5">
	<li>
		نشغّل الخادم
	</li>
</ol>

<pre class="ipsCode">npx http-server
</pre>

<ol start="6">
	<li>
		نفتح التطبيق من خلال الرابط <code>http://&lt;server-ip&gt;:8080</code> ونفتح الآن التطبيق في نافذتي متصفح أو تبويبي متصفح جنبًا إلى جنب ونضيف ملاحظة لإحداهما، وسنجد أن الملاحظة ظهرت في النافذة الثانية دون الحاجة لتحديث الصفحة
	</li>
</ol>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="166359" href="https://academy.hsoub.com/uploads/monthly_2025_01/addnotes.png.4e94c9b61344752ba73e9ffd67d8f333.png" rel=""><img alt="add notes" class="ipsImage ipsImage_thumbnailed" data-fileid="166359" data-unique="z8ctxqy76" style="width: 500px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_01/addnotes.thumb.png.e0199f99b41f35b699d1bb56e9e69882.png"> </a>
</p>

<p>
	بعدها، ننقر على زر Reset Notes لحذف كافة الملاحظات، سنجد أن الملاحظات حُذفَت من كلا النافذتين دون الحاجة للتحديث أيضًا.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="JPG" data-fileid="166358" href="https://academy.hsoub.com/uploads/monthly_2025_01/resetnotes.JPG.684b7446e8ad586c74e8814afdc5b95d.JPG" rel=""><img alt="reset notes" class="ipsImage ipsImage_thumbnailed" data-fileid="166358" data-unique="snmug9urt" style="width: 500px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2025_01/resetnotes.thumb.JPG.54a6bfdc90fb897f06eb776bd68070c3.JPG"> </a>
</p>

<p>
	سنتعمق الآن في الشيفرة البرمجية المكتوبة في ملف <code>app.js</code>. تتيح لنا الدالة <code>renderNotes</code> إنشاء عنصر لكل ملاحظة نضيفها، وتُمكننا الدالة <code>addNoteButton</code> من إضافة ملاحظات إلى التطبيق، وتبثّ الدالة <code>channel.postMessage</code> إجراء الإضافة إلى النوافذ أو التبويبات الأخرى.
</p>

<p>
	وبآلية مماثلة تتيح لنا الدالة <code>resetNoteButton</code> حذف كافة الملاحظات الموجودة، ثم تبثّ الدالة <code>channel.postMessage</code> إجراء الحذف إلى النوافذ أو التبويبات الأخرى. تُنشَأ في النهاية قناة بث <code>BroadcastChannel</code> جديدة باسم notes-channel تتيح التخاطب بين مختلف النوافذ أو التبويبات التي تشترك بالأصل ذاته، إذ ينصت مستمع الحدث event listener في القناة إلى الأحداث من نوع <code>message</code> ضمنها ويتخذ إجراء وفق المُدخلات المكتوبة فيه.
</p>

<h2 id="broadcastchannelapi-3">
	تطبيقات عملية على الواجهة البرمجية Broadcast Channel <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>
</h2>

<p>
	فيما يلي بعض الأمثلة والتطبيقات العملية لاستخدام تقنية الواجهة البرمجية لقناة البث:
</p>

<ul>
	<li>
		في المواقع الإعلامية والإخبارية لمزامنة الموضع الذي وصلنا إليه في قراءة مقال بين عدة نوافذ
	</li>
	<li>
		في منصات التواصل الاجتماعي لإبلاغ المستخدمين بالتحديثات أو الرسائل أو الإشعارات الجديدة في مختلَف النوافذ أو التبويبات
	</li>
	<li>
		في التطبيقات الإنتاجية لتفعيل المزامنة الفورية للتغييرات التي تطرأ على المستندات أو الملفات بين عدة سياقات
	</li>
</ul>

<h2 id="">
	الخاتمة
</h2>

<p>
	استعرضنا في مقالنا مفهوم الواجهة البرمجية لقناة البث Broadcast Channel <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> واستخدامها وتطبيقاتها العملية، وأنشأنا تطبيقًا بسيطًا متزامنًا لتدوين الملاحظات، وتعلمنا كيف نستفيد من هذه الواجهة البرمجية لإنشاء مواقع مترابطة تتواصل مختلف مكوناتها مع بعضها البعض بسلاسة.
</p>

<p>
	ترجمة -وبتصرّف- للمقال <a href="https://developer.mozilla.org/en-US/blog/exploring-the-broadcast-channel-api-for-cross-tab-communication/#real-world_use_cases_and_examples" rel="external nofollow">Exploring the Broadcast Channel <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> for cross-tab communication</a>.
</p>

<h2 id="-1">
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/php/laravel/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%A5%D8%B4%D8%B9%D8%A7%D8%B1%D8%A7%D8%AA-%D8%B9%D8%A8%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%84%D8%A7%D8%B1%D8%A7%D9%81%D9%8A%D9%84-%D9%88%D9%82%D9%86%D9%88%D8%A7%D8%AA-pusher-r1741/" rel="">إنشاء إشعارات عبر الويب باستخدام لارافيل وقنوات Pusher</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-nodejs-r1463/" rel="">مقدمة إلى Node.js</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-web-apis-r2355/" rel="">مدخل إلى واجهات الويب البرمجية Web APIs</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D9%85%D8%B7%D9%88%D8%B1%D9%8A-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D9%85%D8%AF%D9%85%D8%AC%D8%A9-%D9%81%D9%8A-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-r1439/" rel="">أدوات مطوري الويب المدمجة في المتصفحات</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2499</guid><pubDate>Fri, 24 Jan 2025 15:00:00 +0000</pubDate></item><item><title>&#x62A;&#x648;&#x627;&#x628;&#x639; &#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x627;&#x644;&#x645;&#x62C;&#x645;&#x648;&#x639;&#x627;&#x62A; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627; &#x633;&#x643;&#x631;&#x64A;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%AA%D9%88%D8%A7%D8%A8%D8%B9-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AC%D9%85%D9%88%D8%B9%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2468/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_12/SetJavaScript_.png.b3e5eb3f4eaa1654dd8ffae8827ac1e9.png" /></p>
<p>
	نشرح في هذه المقالة التوابع الجديدة للتعامل مع المجموعة <code>set</code> في لغة جافا سكريبت JavaScript، والتي أصبحت متوفرة ومتاحة على جميع متصفحات الويب منذ النسخة Firefox 127. فمن المفيد الاطلاع على توابع المجموعات لاسيما للمطورين الجدد الذين يتعاملون مع بنية المجموعة <code>set</code> لأول مرة ويتطلعون لاستخدامها في بناء تطبيقاتهم، حيث سنسلط الضوء على المزايا الرئيسية لهذه التوابع ونوضح سبب الحاجة لاستخدامها في بناء التطبيقات من خلال الأمثلة العملية.
</p>

<h2 id="set">
	ما هي التوابع الجديدة للتعامل مع المجموعات
</h2>

<p>
	نستعرض فيما يلي أهم التوابع الجديدة للمجموعة <code>set</code> المدعومة من أغلب المتصفحات الحديثة وهي كالتالي:
</p>

<ul>
	<li>
		التابع <code>()intersection</code> : يعيد مجموعة جديدة تحتوي على العناصر الموجودة في كلا المجموعتين أي تقاطع المجموعتين
	</li>
	<li>
		التابع <code>()union</code>: يعيد مجموعة جديدة تضم جميع العناصر الموجودة في المجموعتين أي اجتماع المجموعتين
	</li>
	<li>
		التابع <code>()difference</code>: يعيد مجموعة جديدة تحتوي على العناصر غير المتشابهة في المجموعتين أي فرق المجموعتين
	</li>
	<li>
		التابع <code>()symetricDifference</code>: يعيد مجموعة جديدة تحتوي على العناصر الموجودة في أحد المجموعتين وغير الموجودة في المجموعة الثانية
	</li>
	<li>
		التابع <code>()isSubsetOf</code>: يعيد قيمة منطقية تشير فيما إذا كانت جميع عناصر المجموعة الأولى مجموعة فرعية محتواة ضمن المجموعة الثانية أي هل المجموعة الأولى مجموعة جزئية من المجموعة الثانية.
	</li>
	<li>
		التابع <code>()isSupersetOf</code>: يعيد قيمة منطقية تشير إلى ما إذا كانت المجموعة الأولى مجموعة شاملة للمجموعة الثانية، أي هل جميع العناصر في المجموعة الثانية موجودة في المجموعة الأولى.
	</li>
	<li>
		التابع <code>()isDisjointFrom</code><span>: </span>يرجع قيمة منطقية تشير فيما إذا كانت المجموعتان مستقلتين أي المجموعة الأولى لا تحتوي على عناصر مشتركة مع المجموعة الثانية.
	</li>
</ul>

<p>
	سنوضح لك في الفقرات القادمة معنى واستخدام هذه التوابع باستخدام الأمثلة العملية.
</p>

<h2 id="javascript">
	ما هي المجموعة في لغة جافا سكريبت JavaScript
</h2>

<p>
	المجموعة Set هي بنية بيانات مشابهة للمصفوفة Array، ولكنها تختلف عنها في أنها لا تسمح بتخزين القيم المكررة. بمعنى آخر، يمكن تخزين كل قيمة في Set لمرة واحدة فقط، حتى إذا كانت القيمة مكررة عند إضافتها.
</p>

<p>
	على سبيل المثال، إذا أخذنا قائمة من العناصر وأضفناها إلى مجموعة Set، ثم فحصنا عناصر المجموعة، سنلاحظ أن العناصر المكررة في القائمة قد حذفت تلقائيًا. إليك مثال توضيحي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="164103" href="https://academy.hsoub.com/uploads/monthly_2024_12/set1.png.ee9ea35d34b6bbc47fdab9ac2e9715fb.png" rel=""><img alt="set1" class="ipsImage ipsImage_thumbnailed" data-fileid="164103" data-unique="21x8l51jv" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2024_12/set1.thumb.png.64b4a8a0085ee72175886575f859cb67.png"> </a>
</p>

<p>
	قد يبدو هذا المثال بسيطًا ولا يعكس بالكامل أهمية استخدام المجموعات، ولكن من المفيد للغاية أن توفر لغة البرمجة طريقة لإنشاء مجموعات فريدة ومميزة من العناصر، خاصة عندما تتعامل مع أنواع أو كائنات أكبر وأكثر تعقيدًا. على سبيل المثال، يمكنك إضافة العناصر إلى مجموعة set بالطريقة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8126_6" style=""><span class="kwd">const</span><span class="pln"> dogs </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Set</span><span class="pun">();</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> yoshi </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Yoshi"</span><span class="pun">,</span><span class="pln"> personality</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Friendly"</span><span class="pln"> </span><span class="pun">};</span><span class="pln">
dogs</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="pln">yoshi</span><span class="pun">);</span></pre>

<p>
	هناك ميزة أخرى لاستخدام المجموعات sets وهي أن التحقق من وجود عنصر في المجموعة أسرع من التحقق من وجوده في مصفوفة، مما يجعلها مثالية في الحالات التي تتطلب مراقبة الأداء عند التعامل مع <a href="https://academy.hsoub.com/programming/general/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D9%85%D9%81%D9%87%D9%88%D9%85-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%B6%D8%AE%D9%85%D8%A9-big-data-r1579/" rel="">البيانات الضخمة</a>. كما يمكنك إنشاء مجموعات جديدة ذات خصائص منطقية محددة بناء على مجموعات موجودة، وهو ما سنناقشه في الفقرات القادمة.
</p>

<h2 id="">
	اجتماع مجموعتين
</h2>

<p>
	يمكننا باستخدام دمج المجموعات التحقق من العناصر الموجودة في إحدى المجموعتين أو كليهما.
</p>

<p>
	لدينا في المثال التالي مجموعتان ونريد الحصول على مجموعة ثالثة تضم جميع عناصر المجموعتين ولكن بدون تكرار، سنكتب كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8126_9" style=""><span class="com">// إنشاء اجتماع المجموعتين</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> unionSet </span><span class="pun">=</span><span class="pln"> set1</span><span class="pun">.</span><span class="kwd">union</span><span class="pun">(</span><span class="pln">set2</span><span class="pun">);</span><span class="pln">

</span><span class="com">// عرض العناصر في الاجتماع</span><span class="pln">
unionSet</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">((</span><span class="pln">item</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> li </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">"li"</span><span class="pun">);</span><span class="pln">
  li</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> item</span><span class="pun">;</span><span class="pln">
  unionList</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">li</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="164104" href="https://academy.hsoub.com/uploads/monthly_2024_12/set2.png.a769b32cb9967a59aec97a18599921bd.png" rel=""><img alt="set2" class="ipsImage ipsImage_thumbnailed" data-fileid="164104" data-unique="xwq9o7m1g" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2024_12/set2.thumb.png.ccca4e0294bec980a030763ca62c2667.png"> </a>
</p>

<p>
	نستخدم عناصر المحتوى النصي <a href="https://wiki.hsoub.com/%D8%AA%D8%B5%D9%86%D9%8A%D9%81:HTML_Text_Content" rel="external">textContent</a> في لغة HTML لكل عنصر من عناصر القائمة، مما يشكل لدينا مجموعات من السلاسل النصية sets of strings. وتكمن أهمية استخدام المجموعات عندما نخزن أنواع متعددة من البيانات داخلها، مثل المصفوفات والكائنات.
</p>

<p>
	إن استخدام المجموعات sets مفيد للغاية لأننا لا نحتاج إلى إجراءات إضافية لإزالة التكرار أو التحقق من المساواة أو أي مقارنات أخرى، فكما شرحنا سابقًا عند استخدام المجموعة، ستكون جميع عناصر المجموعة قيم فريدة وغير مكررة. وبالتالي، فإن التابع <code>union</code> سينتج لنا مجموعة ثالثة تحتوي على العناصر الفريدة الموجودة في إما المجموعة الأولى أو الثانية أو كليهما.
</p>

<h2 id="-1">
	تقاطع مجموعتين
</h2>

<p>
	يمكننا باستخدام تقاطع المجموعات معرفة العناصر الموجودة في كلتا المجموعتين وتحديد العناصر المشتركة بينهما. في المثال التالي، سنستخدم تقاطع المجموعات لاستخراج العناصر المشتركة بين المجموعتين وعرضها بدلاً من دمجها في مجموعة ثالثة كما فعلنا باستخدام التابع <code>union</code> في الفقرة السابقة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8126_11" style=""><span class="com">// إنشاء مجموعة تقاطع المجموعتين</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> intersectionSet </span><span class="pun">=</span><span class="pln"> set1</span><span class="pun">.</span><span class="pln">intersection</span><span class="pun">(</span><span class="pln">set2</span><span class="pun">);</span><span class="pln">

</span><span class="com">// المرور على العناصر في القوائم وتحديد العناصر المشتركة</span><span class="pln">
allListItems</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">((</span><span class="pln">item</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">intersectionSet</span><span class="pun">.</span><span class="pln">has</span><span class="pun">(</span><span class="pln">item</span><span class="pun">.</span><span class="pln">textContent</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    item</span><span class="pun">.</span><span class="pln">className </span><span class="pun">=</span><span class="pln"> </span><span class="str">"match"</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span></pre>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="164105" href="https://academy.hsoub.com/uploads/monthly_2024_12/set3.png.464c4ac02e04a8145a72e7d34b097dd9.png" rel=""><img alt="set3" class="ipsImage ipsImage_thumbnailed" data-fileid="164105" data-unique="h8dwtd13k" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2024_12/set3.thumb.png.a24865fd8cb56921e3f9bf15b0232931.png"> </a>
</p>

<h2 id="-2">
	الفرق التناظري لمجموعة
</h2>

<p>
	سنستعرض في ما يلي مفهوم الفرق التناظري أو المتماثل الذي يسمح لنا بالتحقق من العناصر الموجودة في إحدى المجموعتين فقط وليس كليهما، قد يبدو المفهوم للوهلة الأولى معقدًا بعض الشي .لكنه سيتضح من خلال المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8126_13" style=""><span class="kwd">const</span><span class="pln"> setNotBoth </span><span class="pun">=</span><span class="pln"> set1</span><span class="pun">.</span><span class="pln">symmetricDifference</span><span class="pun">(</span><span class="pln">set2</span><span class="pun">);</span><span class="pln">

allListItems</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">((</span><span class="pln">item</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">setNotBoth</span><span class="pun">.</span><span class="pln">has</span><span class="pun">(</span><span class="pln">item</span><span class="pun">.</span><span class="pln">textContent</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    item</span><span class="pun">.</span><span class="pln">className </span><span class="pun">=</span><span class="pln"> </span><span class="str">"match"</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span></pre>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="164106" href="https://academy.hsoub.com/uploads/monthly_2024_12/set4.png.f0f94fb7dad0114588a0aff9b2145497.png" rel=""><img alt="set4" class="ipsImage ipsImage_thumbnailed" data-fileid="164106" data-unique="4crnmty6k" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2024_12/set4.thumb.png.66fad3a888224fcd859473223f2d27c7.png"> </a>
</p>

<p>
	استخدامنا في هذا الكود التابع <code>symmetricDifference</code> لحساب الاختلاف التناظري بين المجموعتين <code>set1</code> و <code>set2</code>. والنتيجة مجموعة تحتوي على العناصر التي توجد في إحدى المجموعتين ولكن ليس في كليهما. قد يكون ما سبق جديد وغير مفهوم بالنسبة لك، لفهم الموضوع قارن الفرق التناظري للمجوعات مع تقاطع المجموعات، وعندها ستجد فالتابع <code>symmetricDifference</code> يعمل بطريقة منطقية معاكسة للتابع <code>intersection</code>، وهنا سينتهي هذا الالتباس بالنسبة لك.
</p>

<h2 id="-3">
	فرق المجموعات
</h2>

<p>
	يمكننا استخدام تابع الفرق للتحقق من العناصر الموجودة في إحدى المجموعات وغير الموجودة في المجموعة الأخرى. في المثال التالي، سننشئ مجموعتين بدلاً من مجموعة واحدة. بداية ننشئ المجموعة الأولى <code>set1only</code> باستخدام التابع <code>set1.difference(set2)‎</code>، مما يعطينا العناصر الموجودة في المجموعة الأولى فقط.
</p>

<p>
	بعد ذلك، سنقوم بنفس العملية مع المجموعة الثانية <code>set2only</code> لمعرفة العناصر الموجودة في المجموعة الثانية وغير الموجودة في المجموعة الأولى. ويمكننا باستخدام التابع <code>difference</code> لكل مجموعة تحديد العناصر التي لا تظهر في المجموعة الأخرى.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8126_15" style=""><span class="kwd">const</span><span class="pln"> set1only </span><span class="pun">=</span><span class="pln"> set1</span><span class="pun">.</span><span class="pln">difference</span><span class="pun">(</span><span class="pln">set2</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> set2only </span><span class="pun">=</span><span class="pln"> set2</span><span class="pun">.</span><span class="pln">difference</span><span class="pun">(</span><span class="pln">set1</span><span class="pun">);</span><span class="pln">

allListItems</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">((</span><span class="pln">item</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">set1only</span><span class="pun">.</span><span class="pln">has</span><span class="pun">(</span><span class="pln">item</span><span class="pun">.</span><span class="pln">textContent</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    item</span><span class="pun">.</span><span class="pln">className </span><span class="pun">=</span><span class="pln"> </span><span class="str">"setOneMatch"</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">set2only</span><span class="pun">.</span><span class="pln">has</span><span class="pun">(</span><span class="pln">item</span><span class="pun">.</span><span class="pln">textContent</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    item</span><span class="pun">.</span><span class="pln">className </span><span class="pun">=</span><span class="pln"> </span><span class="str">"setTwoMatch"</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span></pre>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="164107" href="https://academy.hsoub.com/uploads/monthly_2024_12/set5.png.72c94966036a1f1df4f59d6c52707a4b.png" rel=""><img alt="set5" class="ipsImage ipsImage_thumbnailed" data-fileid="164107" data-unique="hmnxn2nce" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2024_12/set5.thumb.png.fe450ceb98d22d8e5a0e0bb107078e60.png"> </a>
</p>

<h2 id="-4">
	المجموعة الفرعية والمجموعة الشاملة والمجموعة المنفصلة
</h2>

<p>
	آخر التوابع والمفاهيم الجديدة الخاصة بالمجموعات التي سنتظرق إليها في هذا المقال هي توابع المجموعة الفرعية Subset والمجموعة الشاملة Superset والمجموعة المنفصلة Disjoint from Set.
</p>

<p>
	فالنتيجة في جميع توابع المجموعات التي تعرفنا عليها سابقًا كانت عبارة عن مجموعة جديدة، أما النتيجة في التوابع الحالية فهي عبارة عن قيمة منطقية تشير إلى حالة منطقية معينة.
</p>

<p>
	سنتعرف في البداية على التابعين <code>subset</code> و <code>superset</code>، حيث نحصل على النتيجة من خلال بعض عبارات التحقق المنطقية. على سبيل المثال، يمكننا التحقق مما إذا كانت المجموعة الأولى مجموعة جزئية من المجموعة الثانية، ومن ثم إضافة نص منطقي <code>True</code> أو <code>False</code> للعنصر بناءً على نتيجة التحقق.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8126_17" style=""><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">set1</span><span class="pun">.</span><span class="pln">isSubsetOf</span><span class="pun">(</span><span class="pln">set2</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  oneSubTwo</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">+=</span><span class="pln"> </span><span class="str">" [TRUE]"</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  oneSubTwo</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">+=</span><span class="pln"> </span><span class="str">" [FALSE]"</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="164108" href="https://academy.hsoub.com/uploads/monthly_2024_12/set6.png.8a3313588999479041e41b156a5bfca6.png" rel=""><img alt="set6" class="ipsImage ipsImage_thumbnailed" data-fileid="164108" data-unique="wztntktdj" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2024_12/set6.thumb.png.c1b7c608dfa6f16b7cf997483c583ee0.png"> </a>
</p>

<p>
	يمكننا باستخدام التابع <code>isSubsetOf()‎</code> التحقق مما إذا كانت جميع عناصر المجموعة الأولى تظهر بالكامل في المجموعة الثانية. بينما يمكننا باستخدام التابع <code>isSupersetOf()‎</code> التحقق من العكس أي معرفة إذا كانت المجموعة الأولى تحتوي على جميع عناصر المجموعة الثانية بالإضافة إلى عناصر أخرى.
</p>

<p>
	أما التابع الأخير الذي سنتعرف عليه فهو isDisjointFrom()‎<code>‎</code>، حيث يمكننا من خلاله معرفة ما إذا كانت المجموعتان لا تملكان أي عناصر مشتركة بينهما.
</p>

<p>
	لاحظ في المثال أدناه أن المجموعتين الأولى والثانية لا تحققان شروط التابع isDisjointFrom()‎<code>‎</code>، لأن هناك عنصر مشترك بينهما وهو العنصر <code>C</code>، أما المجموعة الثالثة فهي منفصلة عن المجموعتين الأولى والثانية لأنها لا تحتوي على أي عناصر مشتركة مع أي منهما.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="164109" href="https://academy.hsoub.com/uploads/monthly_2024_12/set7.png.315eb73ee0a77b1b3ee75fde0485c2b7.png" rel=""><img alt="set7" class="ipsImage ipsImage_thumbnailed" data-fileid="164109" data-unique="uuv4aozbu" style="width: 600px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2024_12/set7.thumb.png.d64f5a812e167f67e2ea949c80f7ac9b.png"> </a>
</p>

<h2 id="-5">
	الخلاصة
</h2>

<p>
	إن مفهوم المجموعات sets في لغة جافا سكريبت هو مفهوم مهم ويفترض أنه قد أصبح مفهومًا بالنسبة لك، حيث ستحتاجه في تطبيقات عديدة مثل إزالة التكرار من قائمة تحتوي على بعض القيم المكررة أو التحقق بسرعة مما إذا كان عنصر موجودًا في مجموعة أو إجراء العمليات الرياضية والمنطقية بين المجموعات. نتمنى لكم الاستفادة الجيدة من هذا المقال، وفي حال كان لديكم أي تساؤل يمكن تركه في قسم التعليقات أسفل المقال أو كتابته في <a href="https://academy.hsoub.com/questions/" rel="">قسم الأسئلة والأجوبة</a> في الأكاديمية.
</p>

<p>
	ترجمة وبتصرف للمقال <a href="https://developer.mozilla.org/en-US/blog/javascript-set-methods/" rel="external nofollow">New JavaScript Set methods</a> لكاتبه Brian Smith.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D9%82%D9%88%D8%A7%D8%A6%D9%85-lists-%D9%88%D8%A7%D9%84%D9%85%D8%AC%D9%85%D9%88%D8%B9%D8%A7%D8%AA-sets-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1429/" rel="">القوائم lists والمجموعات sets في جافا</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%88%D8%A3%D9%86%D9%88%D8%A7%D8%B9%D9%87%D8%A7-%D8%A7%D9%84%D8%AA%D8%AC%D9%85%D9%8A%D8%B9%D8%A7%D8%AA-collections-r1288/" rel="">مدخل إلى البيانات وأنواعها: التجميعات Collections</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%87%D9%8A%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D9%85%D8%B5%D9%81%D9%88%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1234/" rel="">هياكل البيانات: الكائنات والمصفوفات في جافاسكريبت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%B5%D9%81%D9%88%D9%81%D8%8C-%D8%A7%D9%84%D9%85%D8%AC%D9%85%D9%88%D8%B9%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D9%82%D9%88%D8%A7%D9%85%D9%8A%D8%B3-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r226/" rel="">التعامل مع الصفوف والمجموعات والقواميس في بايثون</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2468</guid><pubDate>Sun, 22 Dec 2024 15:07:01 +0000</pubDate></item><item><title>&#x62A;&#x639;&#x631;&#x641; &#x639;&#x644;&#x649; &#x623;&#x634;&#x647;&#x631; &#x623;&#x637;&#x631; &#x639;&#x645;&#x644; &#x62C;&#x627;&#x641;&#x627; &#x633;&#x643;&#x631;&#x64A;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D8%B4%D9%87%D8%B1-%D8%A3%D8%B7%D8%B1-%D8%B9%D9%85%D9%84-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2462/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_12/12.png.f9333b745ea9b6c303556e3b9582f5ae.png" /></p>
<p>
	يسعى الكثير من مطوري الويب إلى تعلم إطار عمل جديد في مرحلة ما من مسارهم المهني، فأطر العمل تساعد على إنشاء تطبيقات أفضل وبسرعة أكبر كما أنها توسع خبرات المطورين وتعزز فرصهم في سوق العمل. وتوفر لغة جافا سكريبت الكثير من أطر العمل القوية والمستخدمة في تطوير مواقع وتطبيقات الويب والتي توفر وظائف مفيدة متنوعة، وكما هو معروف فإن لغة جافا سكريبت من أشهر لغات البرمجة المستخدمة في مجال تطوير الويب وهذا يشجع المطورين أكثر على تعلمها واستخدام أطر عملها.
</p>

<p>
	فإذا كنت مهتمًا بتعلم إطار عمل جديد لتطوير تطبيقات الويب الخاصة بك، تابع هذا المقال حيث سنبدأ فيه بشرح سريع لمفهوم إطار العمل في البرمجة، يليه عرض موجز لإثني عشر إطارًا من أفضل أطر العمل المستندة على جافا سكريبت لتختار من بينها ما يناسبك.
</p>

<h2 id="-1">
	ما هو إطار العمل ولماذا نحتاجه؟
</h2>

<p>
	يشير إطار العمل framework عمومًا إلى طريقة تنفيذ العمل، ويُقصد به في مجال البرمجة بيئة العمل working environment التي تمكننا من تنظيم عملنا، فيساعدنا <a href="https://academy.hsoub.com/programming/general/%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-framework/" rel="">إطار العمل البرمجي</a> على تنظيم شيفراتنا وبالتالي يُسَهِّل ويُسَرِّع إنجازنا للمشاريع.
</p>

<p>
	تقسم أُطر العمل البرمجية إلى نوعين رئيسيين:
</p>

<ul>
	<li>
		أطر العمل التي تعمل كمكتبة <strong>library </strong>وهي تتميز بالمرونة، إذ تستطيع أخذ الأجزاء التي تحتاجها منها والتعديل عليها بما يناسب متطلبات عملك
	</li>
	<li>
		أطر العمل المتكاملة التي تفرض عليك طريقة وإجراءات محددة لكيفية إنشاء مشروعك وإنهائه
	</li>
</ul>

<p>
	سنعرض في الفقرات التالية أمثلة عديدة على كلا النوعين ليتضح الفرق بينهما أكثر، لكن دعنا في البداية نجيبك عن السؤال الأهم والذي لا بُدّ أنه تبادر في ذهنك وأذهاننا جميعًا: هل إطار العمل ضروري؟ وهل أحتاجه حقًا <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A8%D8%B1%D9%85%D8%AC-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA/" rel="">كمطور تطبيقات</a>؟
</p>

<p>
	تختلف الإجابة من حالة لأخرى وتتعلق بطبيعة المشروع، لكن الفائدة الكبيرة التي يقدمها إطار العمل على صعيد تنظيم بنية الشيفرة البرمجية وسهولة اختبارها تمنح المطورين سببًا وجيهًا لاستخدام أُطر العمل في مشاريعهم.
</p>

<p>
	فعندما تطور تطبيقاتك باستخدام إطار عمل ستكون شيفراتها المصدرية أكثر تنظيمًا وسيكون بنائها أسهل وأسرع مقارنة ببنائها من دون إطار عمل، وهذا يقودنا بالضرورة إلى الجزء الثاني من فائدة إطار العمل وهو سهولة الاختبار فالشيفرة المنظمة يسهل اختبارها وتصحيح أخطائها وهذه ميزة مهمة لا غنى عنها في تطبيقات الويب فأي <a href="https://academy.hsoub.com/tags/%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%20%D9%88%D9%8A%D8%A8/" rel="">تطبيق ويب</a> أو موقع ويب ينبغي أن يُختَبَر بدقة قبل إطلاقه للعلن.
</p>

<p>
	خلاصة القول يمكنك بالتأكيد بناء تطبيقات ويب ناجحة بلغة <a href="https://academy.hsoub.com/programming/javascript/" rel="">جافا سكريبت JavaScript</a> وحدها فقط من دون أي إطار عمل، لكن استخدام أحد أُطر العمل التي سنعرضها هنا سيسهل عملك ويوفر عليك الكثير من الوقت والجهد حتى في فترة الصيانة التي تلي إطلاق المنتج، فضلًا عن كونه مطلوبًا بكثرة في سوق العمل وتعلمه يعزز فرصتك في الحصول على وظيفة مناسبة.
</p>

<p>
	وإذا كنت <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D9%85%D9%86-%D8%A7%D9%84%D8%B5%D9%81%D8%B1-%D8%AD%D8%AA%D9%89-%D8%A7%D9%84%D8%A7%D8%AD%D8%AA%D8%B1%D8%A7%D9%81-r2046/" rel="">تتقن لغة جافا سكريبت</a> فسيسهل عليك تعلم أي إطار عمل من أُطرها فغالبًا ما يعتمد الإطار على اللغة نفسها، ولن تحتاج لتعلم أكثر من إطار أو إطارين فقط من بين الإثني عشر إطارًا التي سنذكرها تاليًا، وتذكر أن الشركات تفضل تعيين مطوري الويب المتقنين لبعض أطر العمل المعروفة إلى جانب <a href="https://academy.hsoub.com/javascript/" rel="">تعلم لغة البرمجة</a> نفسها.
</p>

<p>
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="442" id="ips_uid_636_6" referrerpolicy="strict-origin-when-cross-origin" src="https://academy.hsoub.com/applications/core/interface/index.html" title="دورة تطوير التطبيقات باستخدام JavaScript - أكاديمية حسوب" width="930" data-embed-src="https://www.youtube.com/embed/T6CnMmvF4e0"></iframe>
</p>

<p>
	والآن إليك قائمتنا من أطر عمل جافا سكريبت التي نُرشحها لك والتي سنعرضها بمزيد من التفصيل:
</p>

<ol>
	<li>
		ريآكت React.
	</li>
	<li>
		فيو Vue JS.
	</li>
	<li>
		أنجولار Angular JS.
	</li>
	<li>
		بوليمر Polymer.
	</li>
	<li>
		إمبر Ember.
	</li>
	<li>
		باك بون Backbone.
	</li>
	<li>
		نيتف سكريبت NativeScript.
	</li>
	<li>
		نود جي اس Node JS.
	</li>
	<li>
		ميتيور Meteor.
	</li>
	<li>
		ميثريل Mithril.
	</li>
	<li>
		أوريليا Aurelia.
	</li>
	<li>
		سوكيت Socket.
	</li>
</ol>

<h2 id="1react">
	1. ريآكت React
</h2>

<p>
	ريآكت <a href="https://academy.hsoub.com/programming/javascript/react/%D9%85%D8%A7-%D9%87%D9%8A-react%D8%9F-r773/" rel="">React</a> هو مشروع مفتوح المصدر طوره <a href="https://academy.hsoub.com/programming/general/%D9%85%D9%87%D9%86%D8%AF%D8%B3-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A7%D8%AA-%D9%85%D9%86-%D9%87%D9%88-%D9%88%D9%85%D8%A7-%D9%87%D9%8A-%D9%85%D9%87%D8%A7%D9%85%D9%87-r2264/" rel="">مهندس البرمجيات</a> جوردان ووك من شركة فيسبوك، وهو مكتبة أكثر من كونه إطار عمل متكامل تقليدي لذا يُعَدّ مناسبًا لمشاريع الويب التي تتطلب تخصيصًا عاليًّا. يستعمل ريآكت في بناء <a href="https://academy.hsoub.com/design/user-experience/%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D9%88%D8%AA%D8%AC%D8%B1%D8%A8%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-ui-ux-r831/" rel="">واجهة المستخدم UI</a> لتطبيقات الهاتف الجوّال وتطبيقات الويب، فإذا كنت تعمل في هذا المجال سيكون مهارةً ممتازة تضيفها إلى مهاراتك إذ سيُحَسِّن كفاءة مشاريعك مقارنة بغيره من الأدوات، كما أنه جيدٌ في الاختبارات ويتوافق مع أُطر جافا سكريبت الأخرى.
</p>

<p>
	كما يُعدّ إطارًا سهل التعلُّم ويستطيع أي مطوّر جافا إتقانه بسرعة والبدء بإنشاء تطبيقاته الجديدة باستخدامه ذلك أن React في جوهره هو جافا سكريبت بسيط أُضيفت له بعض الوظائف الجديدة. وبالإضافة لما سبق يوفر ريآكت React شيفراتٍ برمجية قابلة لإعادة الاستخدام تختصر عليك الوقت فلن تضطر لإعادة اختراع العجلة وكتابة الشيفرات نفسها من الصفر في كل مرة، كما أن مكونات ريآكت معزولة عن بعضها فالتحديثات التي يجريها المطوّر على أحد المكونات لن تؤثر على البقية وهذا يعطيه أريحيةً وكفاءة أكبر في إنجاز العمل.
</p>

<p>
	كما يوفر ريآكت العديد من الأدوات التي تسهّل العمل على مشروعك، خاصة في المراحل المتقدمة. من بين هذه الأدوات React Developer Tools، وهو إضافة extension متوفرة لمتصفح جوجل كروم وأيضًا لمتصفح فايرفوكس. تتيح لك هذه الإضافة فحص <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-dom-r644/" rel="">شجرة DOM</a> الخاصة بتطبيقك وتحريرها مباشرة باستخدام React، مما يمنحك تحكّمًا أكبر ويسهّل عليك اكتشاف الأخطاء وتحسين الكود.
</p>

<h2 id="2vuejs">
	2. فيو Vue JS
</h2>

<p>
	أُطلِقَ <a href="https://academy.hsoub.com/programming/javascript/vuejs/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-vuejs-r989/" rel="">إطار العمل Vue JS</a> في شهر فبراير من العام 2014، وهو من أحدث أُطر عمل جافا سكريبت، ورغم حداثة عهده فقد أصبح بسرعة واحدًا من أكثر الخيارات شعبيةً لمطوّري الويب، وذلك لعدة أسباب، نعرض أبرزها:
</p>

<p>
	السبب الأول هو صغر حجمه، وهذه صفةٌ مهمة في أي إطار عمل تنوي استخدامه ومع أي لغة برمجة، فأُطر العمل كبيرة الحجم التي تستهلك مساحةً كبيرة في الذاكرة تعمل بكفاءة أقل وتستغرق وقتًا أطول لتحميل التطبيقات وتشغيلها، أما مع إطار عمل صغير الحجم مثل Vue.js الذي يتراوح حجمه بين 20 و 25 كيلو بايت فقط سيكون أفضل أداء ويمكن تحميله بسرعة ثم البدء باستخدامه مباشرة.
</p>

<p>
	أما السبب الثاني والمميز حقًا أنك تستطيع استخدام Vue JS مع تطبيقاتك الحالية المبنية <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D9%85%D9%86-%D8%A7%D9%84%D8%B5%D9%81%D8%B1-%D8%AD%D8%AA%D9%89-%D8%A7%D9%84%D8%A7%D8%AD%D8%AA%D8%B1%D8%A7%D9%81-r2046/" rel="">بلغة جافا سكريبت</a> فقد صُمم هذا الإطار أساسًا ليُدمَج بسهولة مع التطبيقات الجاهزة وتعزيزها بميزات جديدة، على سبيل المثال إذا احتجت لإضافة وظيفة معينة إلى تطبيقك وكان Vue.js يحققها فيمكنك استخدامه بأريحية، كما أنه يتضمن العديد من المكونات القادرة على التكامل بسهولة مع أي تطبيق تقريبًا.
</p>

<p>
	وأخيرًا من الضروي التنويه لميزة البساطة التي يتمتع بها Vue JS فتعابيره البرمجية بسيطة وسهلة الفهم، ويمكنك تعلُّمه بسرعة وبأي وقت خصوصًا إذا كنت تتقن إطار Angular أو React، وفي هذا المجال ننصحك بتحميل كتاب <a href="https://academy.hsoub.com/files/22-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-vuejs/" rel="">أساسيات إطار العمل Vue.js 1.0.0</a> الذي توفره أكاديمية حسوب مجانًا وباللغة العربية.
</p>

<h2 id="3angularjs">
	3. أنجولار Angular JS
</h2>

<p>
	أطلقت جوجل إطار العمل <a href="https://academy.hsoub.com/programming/javascript/angular/%D8%A7%D9%84%D8%A8%D8%AF%D8%A1-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-angular-r1897/" rel="">أنجولار Angular</a>  عام 2012 وقد استغرق هذا الإطار بعض الوقت لينال شعبية بين المطورين وهو اليوم واحد من أكثر أُطر العمل شهرة ويستعمل في عدد كبير من مواقع الويب حول العالم، فما السر وراء انتشاره؟ وما الميزات التي يقدمها؟ سنجيبك عن هذا السؤال بخمس نقاط مختصرة:
</p>

<ul>
	<li>
		 يلائم Angular منصات متعددة cross-platform، فيمكنك استخدامه لتطوير تطبيقات سطح المكتب، وتطبيقات الهاتف الجوال، وتطبيقات الويب، وهذه من أفضل مميزاته.
	</li>
	<li>
		إطار عمل <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D8%A7%D9%84%D9%85%D9%82%D8%B5%D9%88%D8%AF-%D8%A8%D9%85%D8%B5%D8%B7%D9%84%D8%AD-%D9%85%D9%81%D8%AA%D9%88%D8%AD-%D8%A7%D9%84%D9%85%D8%B5%D8%AF%D8%B1-open-source%D8%9F-r885/" rel="">مفتوح المصدر</a> ويمكن تعديل شيفرته المصدرية حسب متطلبات العمل، وهذا يفيدنا في المشاريع المعقدة التي تحتاج متطلبات خاصة
	</li>
	<li>
		وجود مجتمع كبير من المطورين الداعمين له والساعين لتحسينه باستمرار
	</li>
	<li>
		 يتلقى Angular دعمًا مستمرًا من جوجل وتحديثات منتظمة إلى جانب الدعم الذي يتلقاه من مجتمع المطورين
	</li>
	<li>
		إطار سهل الاستخدام وهذه ميزة مهمة للمبتدئين خاصة فلن يحتاجوا وقتًا طويلًا لتعلم Angular وإتقانه مقارنة بأُطر العمل الأخرى.
	</li>
</ul>

<p>
	كانت هذه بعض من مزايا Angular التي تجعله بدايةً جيدة لأي مطوّر يرغب باستخدام أطر عمل جافا سكريبت، علمًا أنه يتمتع بمزايا أخرى عديدة ستتعرف عندما تتعامل معه، ويمكنك البدء برحلة برحلة استكشافه بقراءة مقالات <a href="https://academy.hsoub.com/programming/javascript/angular/" rel="">قسم Angular</a> على أكاديمية حسوب.
</p>

<h2 id="4polymer">
	4. بوليمر Polymer
</h2>

<p>
	أنُشِئ إطار العمل Polymer من قبل مجموعة من مطوري شركة جوجل على غرار Angular، وهو أيضًا مفتوح المصدر لكنه أحدث عهدًا منه، ومستودعه على <a href="https://academy.hsoub.com/tags/github/" rel="">GitHub</a> متاح لكل من يحب <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D8%A7%D9%84%D8%B0%D9%8A-%D9%8A%D8%AD%D9%81%D8%B2-%D8%A7%D9%84%D9%85%D8%B3%D8%A7%D9%87%D9%85%D9%8A%D9%86-%D9%84%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D8%B4%D8%A7%D8%B1%D9%8A%D8%B9-%D9%85%D9%81%D8%AA%D9%88%D8%AD%D8%A9-%D8%A7%D9%84%D9%85%D8%B5%D8%AF%D8%B1%D8%9F-r1708/" rel="">المساهمة في المشاريع مفتوحة المصدر</a>.
</p>

<p>
	يُعَدّ Polymer أقرب للمكتبة منه لإطار العمل المتكامل التقليدي لذا فهو يمنحك أريحية كبيرة في التحكم بطريقة تنظيم بيئة العمل لمشروعك، ويتمتع بمزايا أخرى عديدة أبرزها سهولة الاستخدام، فعلى سبيل المثال يكفيك ملف <a href="https://academy.hsoub.com/programming/html/%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-html-r1702/" rel="">HTML</a> واحد فقط لبناء عدة عناصر ويب في تطبيقات Polymer وتخصيصها، وهذا يجعله سهل التعلم إذا ما قارناه بأطر عمل أخرى مثل Angular الذي ستحتاج فيه عدة ملفات HTML لفهم وتطوير عنصر واحد فقط.
</p>

<p>
	كما يتميز Polymer بسهولة التعامل مع متغيرات <a href="https://academy.hsoub.com/programming/css/" rel="">CSS</a> و<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%85%D8%AE%D8%A7%D9%84%D9%8A%D8%B7-mixins-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r907/" rel="">مخاليط mixin</a> قابلة لإعادة الاستخدام، مما يزيد توافقيته مع <a href="https://academy.hsoub.com/programming/css/%D8%AA%D8%AE%D8%B7%D9%8A%D8%B7-%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AA%D9%82%D9%86%D9%8A%D8%A7%D8%AA-css3-r178/" rel="">CSS3</a> ويمنحك القدرة على استثمار كل ميزة جديدة تقدمها، وهذا ينعكس على جودة وجاذبية وتنوع تصميمات مواقع الويب التي تبنهيها يهذا الإطار.
</p>

<p>
	ونذكر من مميزاته أيضًا التوافق مع المكتبات والأدوات الأخرى المستخدمة في عملية التطوير، إذ يمكنه تبادل البيانات معها بسهولة ومباشرة من دون المرور بأي طبقة إضافية، فضلًا عمّا يوفره من توثيقات حديثة وغنية بالتفاصيل تساعدك على تعلم كافة جوانبه بسرعة وبإتقان، دون أن ننسى مجتمع المطورين الذين يستخدمون Polymer حول العالم وما يمكن أن يقدموه لك من مساعدة في إيجاد الحل لأي مشكلة تعترضك.
</p>

<h2 id="5emberjs">
	5. إمبر Ember JS
</h2>

<p>
	يستخدم إطار العمل إمبر Ember لتطوير تطبيقات الويب المعقدة والغنية بالميزات، وهو إطار عمل متكامل بالمعنى التقليدي للكلمة إذ يتضمن عددًا كبيرًا من الواجهات البرمجية <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel="">APIs</a>، ولديه طريقة محددة لإنجاز عمل، فجميع المكونات التي قد تحتاج مكتوبة وجاهزة للنشر وتستطيع الاعتماد عليها من دون كتابتها من الصفر، يُعرف هذا الأسلوب بالاتفاقيات أو التقاليد conventions وهو يتضمن الآلية المثلى لإنجاز أعمال المشروع وسيزيد إنتاجيتك في العمل، لأنك لن تضيع وقتك في إعادة برمجة المكونات الجاهزة بل ستستفيد منه للتركيز على وظائف التطبيق.
</p>

<p>
	وفي هذا المجال يساعدك موقع Ember الرسمي <a href="https://emberobserver.com/" rel="external nofollow">EmberAddons.com</a> في التعرف على جميع أنواع الإضافات plugins الجاهزة للاستخدام فهو مثل مستودع تعاوني للإضافات يساهم فيه مطورو Ember، فإذا كنت تبحث عن الشيفرة البرمجية الكاملة لإضافة معينة أو عن جزء من شيفرة برمجية معينة، يمكنك البحث في هذا المستودع وأخذ ما تحتاجه لتطبيقك، وهذا بدوره يرفع إنتاجيتك ويُسَرِّع وتيرة عملك فالإنتاجية العالية هي الميزة الأهم للإطار Ember.
</p>

<p>
	كما أن التوافقية مع الإصدارات السابقة ميزة أخرى مهمة في الإطار، فإذا كان تطبيقك يعمل باستقرار على إصدار معين من Ember لأنه يتوافق مع ميزات محددة فيه، وظهر تحديث جديد للإطار فلن تحتاج عندها لإيقاف تطبيقك وإجراء تغييرات جوهرية فيه أو حتى إعادة النظر في طريقة بنائه كما تفعل عادة مع الكثير من الأدوات البرمجية فإصدارات Ember الجديدة تعمل دائمًا بسلاسة مع التطبيقات التي ما زالت تستخدم إصدارات أقدم، وكل ما عليك الاهتمام هو إجراء بعض التغييرات لتستفيد من ميزات الإصدار الجديد حتى تُحسِّن عمل تطبيقك.
</p>

<h2 id="6backbone">
	6. باك بون Backbone
</h2>

<p>
	صدر إطار العمل باك بون Backbone عام 2016 وهو الأحدث في قائمتنا المعروضة ضمن المقال، ورغم حداثته فقد اكتسب انتشارًا واسعًا بين المطورين لما يتمتع به من ميزات أبرزها تعدد المنصات cross-platform، فيمكنك استخدامه مثلًا لبناء تطبيق يعمل على الجوال وعلى الويب في آن واحد، مما يوفر زمن التطوير والاختبار فلن تضطر لإعادة برمجة التطبيق مرتين.
</p>

<p>
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="499" id="ips_uid_636_7" referrerpolicy="strict-origin-when-cross-origin" src="https://academy.hsoub.com/applications/core/interface/index.html" title="ما هي تطبيقات الموبايل متعددة المنصات؟" width="930" data-embed-src="https://www.youtube.com/embed/nMnBfl-fQJo"></iframe>
</p>

<p>
	يساعد إطار العمل Backbone في <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%AE%D9%84%D9%81%D9%8A%D8%A9-backend-web-development/" rel="">تطوير الواجهة الخلفية back-end للتطبيق</a> ويمكنه التفاعل بسلاسة مع الواجهة الخلفية لتطبيقك أو موقعك الحالي، كما يستطيع التفاعل مع واجهات برمجة التطبيقات APIs لتنفيذ مهام القراءة والكتابة والحذف.
</p>

<p>
	ويتضمن Backbone العديد من الاتفاقيات conventions التي تناسب معظم الأغراض وإذا التزمت بها ستوفر عليك الكثير من الوقت اللازم لكتابة التعليمات البرمجية من الصفر، وستمنحك شيفرة مصدرية نظيفة وسهلة الفهم والصيانة حتى على المطورين الآخرين.
</p>

<h2 id="7nativescript">
	7. نيتف سكريبت NativeScript
</h2>

<p>
	يستخدم إطار العمل نيتف سكريبت NativeScript لتطوير تطبيقات جوال أصيلة Native لنظامي تشغيل أندرويد و IOS باستخدام جافا سكريبت، وهذه في الواقع هي ميزته الكبرى، فتطبيقات نيتف سكريبت NativeScript متعددة المنصات ستعمل على كلا نظامي التشغيل أندرويد و IOS في وقت واحد وبالتالي تحتاج لكتابة كود تطبيقك مرة واحدة فقط وسيعمل مباشرة على كلا النظامين بكل سلاسة.
</p>

<p>
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="603" id="ips_uid_636_8" referrerpolicy="strict-origin-when-cross-origin" src="https://academy.hsoub.com/applications/core/interface/index.html" title="أساليب تطوير تطبيقات الموبايل" width="930" data-embed-src="https://www.youtube.com/embed/Rn2F9bLC5UU"></iframe>
</p>

<p>
	والنقطة المهمة الأخرى التي تحتسب لصالح نيتف سكريبت أنه مفتوح المصدر، وهو ما يُسهِّل الاستفادة من تجارب الآخرين وإيجاد الحلول لمعظم المشكلات التي قد تواجهك، أضف إلى ذلك أن المصدر المفتوح يمنح نيتف سكريبت مجتمعًا نشطًا من المطورين حول العالم يضيفون إليه ميزاتٍ جديدة باستمرار، ويتيح أمامك العديد من الإضافات الجاهزة لتستخدمها في تطبيقك تمامًا كما هي من دون أي مجهود إضافي.
</p>

<h2 id="8nodejs">
	8. نود جي اس Node JS
</h2>

<p>
	يُعَدّ نود جي اس <a href="https://academy.hsoub.com/programming/javascript/nodejs/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-nodejs-r1463/" rel="">Node JS</a> إطار العمل المفضل والأكثر شيوعًا لدى مطوري جافا سكريبت، فمنذ انطلاقته الأولى في العام 2009 أصبح واحدًا من الأدوات الرائدة في مجال إنشاء <a href="https://academy.hsoub.com/devops/servers/%D8%A3%D8%B7%D8%B1-%D8%B9%D9%85%D9%84-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%AE%D8%A7%D8%AF%D9%85-r784/" rel="">تطبيقات الويب من طرف الخادم</a> وتشغيلها.
</p>

<p>
	وتعود شعبيته الكبيرة لأسباب متعددة في مقدمتها اعتماد الكثير من الشركات عليه، فكل شركة تقريبًا تستخدم <a href="https://academy.hsoub.com/programming/javascript/nodejs/" rel="">Node.js</a> في أحد جوانب عملها نحو أدوات <a href="https://academy.hsoub.com/apps/web/wordpress/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D9%85%D8%B1%D9%83%D8%B2-%D9%85%D8%B3%D8%A7%D8%B9%D8%AF%D8%A9-%D9%88%D8%AE%D8%AF%D9%85%D8%A9-%D8%AF%D8%B1%D8%AF%D8%B4%D8%A9-%D9%85%D8%A8%D8%A7%D8%B4%D8%B1%D8%A9-%D9%84%D9%85%D9%88%D9%82%D8%B9-%D9%88%D9%88%D8%B1%D8%AF%D8%A8%D8%B1%D9%8A%D8%B3-%D9%88%D9%85%D8%AA%D8%AC%D8%B1-%D9%88%D9%88%D9%83%D9%88%D9%85%D9%8A%D8%B1%D8%B3-r841/" rel="">الدردشة المباشرة live chat</a> التي تجدها في معظم مواقع الويب اليوم والتي لا بدّ ستصادفها مستقبلًا في عملك ومعرفتك بالأداة لإطار Node JS ستعينك على التعامل بكفاءة مع هذا النوع من تطبيقات الاتصال، وهناك قائمة طويلة من التطبيقات الشهيرة التي تعتمد Node.js مثل: PayPal و LinkedIn و Yahoo و Uber و eBay، وقد دفع هذا الانتشار الواسع المطورين لتعلمه والإقبال على استخدامه.
</p>

<p>
	الدور الأساسي لإطار العمل Node.js هو تمكين التطوير من جانب الخادم server-side كما ذكرنا، لكنه في واقع الأمر يعمل بكفاءة مع طرف العميل client-side أيضًا ويسهل تبادل البيانات بين الطرفين والتنسيق بينهما ليكونا متزامنين، فأي تغيير تنفذه سينعكس مباشرة على التطبيق أو صفحة الويب، ولذا يُعَرّف Node.js بأنه بيئة تشغيل لجافا سكريبت JavaScript runtime environment أكثر من كونه إطار عمل بالمعنى الدقيق بسبب قدرته على التعامل مع هذه الاتصالات المتزامنة بين الطرفين والسرعة التي يوفرها لعملية التطوير.
</p>

<p>
	بالإضافة لكل الإيجابيات السابقة يطرح نود جي اس خيارات وميزات جديدة دوريًا ليواكب اتجاهات الويب الحديثة، ويتمتع بمجتمع كبير يدعمه ويساهم في تحسينه وتطوير العديد من وحداته modules وميزاته الجديدة، وستكون منتدايات هذا المجتمع مصدرًا جيدًا لإيجاد أي معلومة تبحث عنها ولحل أي مشكلة تواجهها، كما يمكنك المساهمة بدورك في تطوير هذا المشروع مفتوح المصدر بما يحقق الفائدة لجميع المستخدمين.
</p>

<p>
	ننصحك في ختام هذه الفقرة بمراجعة مقالات <a href="https://academy.hsoub.com/programming/javascript/nodejs/" rel="">Node.js</a> أو بتحميل كتاب <a href="https://academy.hsoub.com/files/42-%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A5%D9%84%D9%89-nodejs/" rel="">دليلك إلى Node.js</a> مجانًا من أكاديمية حسوب.
</p>

<h2 id="9meteor">
	9. ميتيور Meteor
</h2>

<p>
	حظيت أداة ميتيور Meteor بشعبية كبيرة رغم حداثتها، وتكمن قوتها الأساسية بأنها لا تقتصر على تطوير تطبيقات الويب بل يمكن استخدامها <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-r1827/" rel="">لتطوير التطبيقات</a> لمختلف أنواع المنصات مثل التطبيقات التي تعمل على الويب والهاتف الجوال بذات الوقت فتوفر بذلك وقتك وجهدك.
</p>

<p>
	ومن مميزاتها أيضًا أنها تعمل في الزمن الحقيقي real-time وهذه ميزة يفضلها الكثير من المطوّرين خصوصًا في الآونة الأخيرة لأن أي تعديل تجريه على الشيفرة البرمجية ستظهر نتائجه تلقائيًا في خرج التطبيق في الزمن الحقيقي، وهذا يسهل عليك عمليات الاختبار وخاصةً تلك التي تقتصر على أجزاء معينة من التطبيق.
</p>

<p>
	تشبه Meteor إطار العمل Node.js في قدرتها على التعامل مع طرفي الخادم والعميل في عملية تطوير الويب، وتنفذ ذلك باستخدام شيفرات <a href="https://wiki.hsoub.com/JavaScript" rel="external">جافا سكريبت</a> فقط من دون الحاجة لأي لغة برمجة أخرى، وهذا يجعلها سهلة التعلم فطالما أنك تتقن جافا سكريبت يمكنك تعلم Meteor بسرعة وسهولة.
</p>

<h2 id="10mithril">
	10. ميثريل Mithril
</h2>

<p>
	يتميز إطار العمل ميثريل Mithril بحجمه الصغير فهو يبلغ حوالي 9 كيلو بايت فقط، حتى أنه أصغر من Vue.js وهذا يجعله سريعًا مقارنة بغيره من الأطر، وهو يستخدم أساسًا لتطوير تطبيقات الويب ذات الصفحة الواحدة single-page web applications لأنه على عكس Node.js و Meteor لا يستطيع التعامل مع طرفي العميل والخادم في أثناء التطوير إنما يتخصص بتطوير طرف العميل client-side فقط.
</p>

<p>
	وإطار Mithril سهل التعلم ويناسب المطورين المبتدئين الراغبين بإتقان إطار عمل جديد بسرعة، إذ يمكنك البدء باستخدامه مباشرة بمجرد تعلم بعض التوابع methods الأساسية التي يوفرها، كما أنه يتصف بسرعة الأداء مقارنة بأطر العمل الأخرى مثل ريآكت React فقد يستغرق تحميل التطبيق في Mithril نصف الزمن الذي يستغرقه في ريآكت أو غيره.
</p>

<p>
	وعندما نتحدث عن السرعة سنتسأل بالتأكيد كيف سيتعامل إطار يعمل في الزمن الحقيقي مثل Mithril مع التحديثات الدورية updates التي ينبغي تطبيقها في الزمن الحقيقي؟ وهل يستطيع الحفاظ على جودة أدائه أثناء ذلك؟ بالرغم من صعوبة الأمر لكن Mithril يتعامل جيدًا مع التحديثات ويحافظ على سرعة أدائه معها.
</p>

<h2 id="11aurelia">
	11. أوريليا Aurelia
</h2>

<p>
	يستخدم إطار العمل أوريليا Aurelia <a href="https://academy.hsoub.com/programming/workflow/%D9%81%D9%87%D9%85-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%85%D9%86-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r1462/" rel="">للتطوير من جانب العميل client-side</a>، وهو سهل التعلم ويناسب المطورين المبتدئين إذ لن تحتاج لتعلمه سوى لتعلم <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D8%B1%D9%91%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D9%85%D9%86-%D9%85%D9%86%D8%B8%D9%88%D8%B1-%D8%B9%D8%A7%D9%85-r2266/" rel="">أساسيات جافا سكريبت</a> وتعلم لغة <a href="https://wiki.hsoub.com/HTML" rel="external">HTML</a> الحديثة وهذه المعلومات متوفرة لدى جميع مطوري جافاسكريبت.
</p>

<p>
	يعتمد أوريليا مبدأ الاتفاقيات بدلاً من الإعدادات Conventions Over Configuration يعني ذلك أنه يوفر للمطور عدد من الاتفاقيات الثابتة التي سيعتمدها الإطار تلقائيًّا دون أن يكلف المطور عناء برمجة وكتابة الإعدادات لعناصر تقليدية توجد وتستخدم بكثرة في معظم التطبيقات، وهذا يجعله أسرع وأكثر كفاءة في التطوير ويمنحك شيفرة مصدرية نظيفة وفعالة.
</p>

<p>
	لا يقارن حجم المجتمع الذي يستخدم Aurelia بحجم المجتمع الذي يستخدم الأطر الأخرى مثل رياكت أو أنجولار مثلًا، لكنه مع ذلك يُعدّ مجتمعًا نشيطًا ويسعى لتطوير هذا الإطار وسيساعدك في حل مشكلاتك في التعامل معه.
</p>

<h2 id="12socketio">
	12. سوكيت Socket.io
</h2>

<p>
	صُممت Socket.io للعمل مع طرفي العميل والخادم، وهي مكتبة حديثة من مكتبات <a href="https://academy.hsoub.com/programming/javascript/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1689/" rel="">جافا سكريبت</a> اكتسبت شهرة وشعبية واسعة لأنها في المقام الأول تعمل في الزمن الحقيقي وهي ميزة مفضلة للكثير من المطورين الراغبين بتطوير هذا النوع من التطبيقات، وتتصف تطبيقات الزمن الحقيقي بأن التعديلات التي تنفذ على شيفرتها المصدرية تنعكس مباشرةً على التطبيق في اللحظة نفسها.
</p>

<p>
	ورغم العديد من الآراء التي تنادي بعدم الحاجة إلى Socket.io اليوك بسبب وجود بدائل أخرى يمكنها الحلول مكانها، لكن انتشار هذه المكتبة ما زال مستمرًا ومازالت تحظى بشعبية وبمستخدمين جدد.
</p>

<p>
	وتتميز Socket.io بسهولة تعلُّمها وتنفيذها في المشاريع، فإذا كنت ترغب بإضافة مهارة جديدة إلى قائمة مهاراتك قد تفيدك في مسيرتك المهنية مستقبلًا فهذه الأداة تُعدّ خيارًا جيدًا.
</p>

<h2 id="-2">
	الخلاصة
</h2>

<p>
	إلى هنا نكون قد وصلنا لنهاية مقالنا الذي تعرفنا فيه على أشهر أُطر عمل جافا سكريبت وأهم مميزات كل إطار، لتكون قادرًا على اختيار ما يناسب احتياجات عملك، فعلى سبيل المثال إذا كان تخصصك هو تطوير تطبيقات الجوال فقط فيمكنك اختيار NativeScript، وإذا كنت ترغب بتطوير تطبيقات متعددة المنصات تستطيع الاتجاه نحو Meteor، وإذا كانت غايتك الأولى هي السرعة فستبحث عن الإطار ذي الحجم الأصغر وما إلى ذلك، فكر في احتياجاتك ومتطلباتك أولًا ثم اتخذ قرارك.
</p>

<p>
	ترجمة -وبتصرف- لمقال <a href="https://www.binpress.com/best-javascript-frameworks/" rel="external nofollow">12 Best JavaScript Frameworks</a> لصاحبه Mark Bynum.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/javascript/" rel="">تعلم جافا سكريبت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-alpinejs-r2214/" rel="">مدخل إلى إطار عمل الويب Express وبيئة Node</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-framework/" rel="">تعرف على مفهوم إطار العمل Framework وأهميته في البرمجة</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AA%D9%89-%D9%86%D8%B3%D8%AA%D8%B9%D9%85%D9%84-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%B9%D9%85%D9%84-%D9%84%D9%84%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-javascript-r477/" rel="">متى نستعمل إطار عمل للتطوير باستخدام JavaScript</a>
	</li>
	<li>
		<a href="https://wiki.hsoub.com/JavaScript" rel="external">التوثيق العربي للغة جافا سكريبت</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2462</guid><pubDate>Thu, 05 Dec 2024 15:00:00 +0000</pubDate></item><item><title>&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x627;&#x644;&#x648;&#x627;&#x62C;&#x647;&#x629; &#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x64A;&#x629; Page Visibility &#x644;&#x62A;&#x62D;&#x633;&#x64A;&#x646; &#x62A;&#x637;&#x628;&#x64A;&#x642;&#x627;&#x62A; &#x627;&#x644;&#x648;&#x64A;&#x628;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-page-visibility-%D9%84%D8%AA%D8%AD%D8%B3%D9%8A%D9%86-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r2461/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_12/_PageVisibility.png.0bb36df955489ed5dc58e91a57b7746f.png" /></p>
<p>
	تتضمن متصفحات الويب الواجهة البرمجية Page Visibility <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> التي تسمح لنا بالكشف عن حالة ظهور الصفحة في نافذة المتصفح، ومعرفة هل هذه الصفحة مرئية وظاهرة للمستخدم أم مخفية عنه كأن تكون مصغرة أو موجودة في علامة تبويب غير نشطة، فهذه المعلومة مفيدة جدًا للمطورين وتساعد في تحسين تجربة المستخدم وأداء تطبيق الويب.
</p>

<p>
	فالتحقق من رؤية صفحة الويب يوفر لنا فكرة واضحة حول كيفية تفاعل الزوار مع صفحات الموقع، ويتيح معرفة ما إذا كانت الصفحة مرئية للمستخدم أم لا، كما يساعدنا على إعداد مستمعي الأحداث Event Listener لتنفيذ إجراءات معينة عندما تتغير حالة رؤية الصفحة. يشرح هذا المقال معنى رؤية الصفحة، ويوضح كيفية استخدام الواجهة البرمجية Page Visibility <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>، وأهم المشكلات الشائعة التي ينبغي تجنبها عند التعامل معها.
</p>

<h2 id="pagevisibility-1">
	ما هي الواجهة البرمجية Page Visibility
</h2>

<p>
	طُوِّرت واجهة برمجة التطبيقات Page Visibility في البداية كميزة مستقلة منفصلة عن مواصفات HTML الرسمية التي تتولى تحديد كيفية بناء صفحات الويب، وسرعان ما تم دمجها مع مواصفات HTML تحت <a href="https://html.spec.whatwg.org/multipage/interaction.html#page-visibility" rel="external nofollow">قسم page visibility</a>، توفر هذه الواجهة طريقة فعّالة لتحديد حالة رؤية صفحة الويب، حيث يمكن من خلالها معرفة ما إذا كانت صفحة الويب مرئية visible أو مخفية hidden.
</p>

<p>
	تكون الصفحة مخفية إذا صغرنا نافذة متصفح الويب الذي يحتوي على تلك الصفحة، أو إذا حجبنا الصفحة بشكل كامل من قبل صفحة أخرى، أو إذا انتقلنا إلى علامة تبويب أخرى، كما تتغير حالة رؤية الصفحة إلى مخفية عندما تقفل شاشة حاسوبك أو هاتفك وتنشط شاشة قفل نظام التشغيل، فعند قفل الشاشة ستعد صفحة الويب التي يشاهدها المستخدم مخفية لأن شاشة القفل تمنع من رؤية الصفحة أو التفاعل معها.
</p>

<p>
	من ناحية أخرى، تعد الصفحة مرئية طالما أنها تظهر بشكل جزئي على الشاشة، حتى إذا كانت نافذة المتصفح غير مكبرة بالكامل أو كانت هناك تطبيقات أخرى تغطي جزءًا من النافذة. ما يهم هو أن الصفحة تظل ضمن نطاق رؤية المستخدم وليست مخفية بالكامل كما يحدث عند التبديل إلى علامة تبويب أخرى أو تصغير نافذة المتصفح بالكامل.
</p>

<h2 id="">
	طريقة التحقق من تغير حالة رؤية الصفحة
</h2>

<p>
	يمكنك التحقق من حالة رؤية الصفحة باستخدام الخاصية <code><a href="https://wiki.hsoub.com/JavaScript/Document/visibilityState" rel="external">document.visibilityState</a></code>، التي تعيد إما القيمة <code>visible</code> عندما تكون الصفحة مرئية أو <code>hidden</code> عندما تكون الصفحة مخفية. كما يمكن معرفة حالة رؤية الصفحة أيضًا التحقق من قيمة الخاصية المنطقية <code><a href="https://wiki.hsoub.com/JavaScript/Document/hidden" rel="external">document.hidden</a></code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4886_11" style=""><span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">document</span><span class="pun">.</span><span class="pln">visibilityState</span><span class="pun">);</span><span class="pln"> </span><span class="com">// "visible"</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">document</span><span class="pun">.</span><span class="pln">hidden</span><span class="pun">);</span><span class="pln"> </span><span class="com">// false</span></pre>

<p>
	ومن الملائم أكثر استخدام الحدث <code><a href="https://wiki.hsoub.com/JavaScript/Document/onvisibilitychange" rel="external">visibilitychange</a></code>، حيث يمكنك من خلاله تشغيل كود معين عندما تتغير حالة رؤية الصفحة، بدلاً من التحقق من الحالة يدويًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4886_14" style=""><span class="pln">document</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"visibilitychange"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">event</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// تنفيذ الكود عند تغيير حالة رؤية الصفحة</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	بهذه الطريقة يمكنك التحقق من رؤية الصفحة عندما تتغير الحالة، وتنفيذ كود معين بالاعتماد على النتيجة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4886_16" style=""><span class="pln">document</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"visibilitychange"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">document</span><span class="pun">.</span><span class="pln">hidden</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ينفذ الكود هنا عندما تتغير حالة الرؤية إلى مخفية</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ينفذ الكود هنا عندما تتغير حالة الرؤية إلى مرئية</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	تجدر الإشارة إلى أن الخاصية <code>document.visible</code> غير موجودة في الواجهة البرمجية، وبالتالي كي تعرف إن كانت الصفحة مرئية فيمكنك استخدام الكود التالي <code>document.visibilityState === "visible"‎</code> أو استخدام الخاصية <code>‎!document.hidden‎</code> كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4886_18" style=""><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">document</span><span class="pun">.</span><span class="pln">visibilityState </span><span class="pun">===</span><span class="pln"> </span><span class="str">"visible"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// الصفحة مرئية</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">document</span><span class="pun">.</span><span class="pln">hidden</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// الصفحة مرئية</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2 id="-1">
	فائدة رؤية الصفحة
</h2>

<p>
	تفيدنا ميزة رؤية الصفحة في عدة حالات، ولكن الفائدة الأكبر تكون في التحليلات، واستخدام موارد الحاسوب، وإضافة الوظائف التي يمكن من خلالها <a href="https://academy.hsoub.com/design/user-experience/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D8%A7%D9%84%D8%B4%D8%A7%D9%85%D9%84-%D8%AE%D8%B7%D9%88%D8%A9-%D8%A8%D8%AE%D8%B7%D9%88%D8%A9-%D9%84%D8%A7%D8%AD%D8%AA%D8%B1%D8%A7%D9%81-%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D8%AA%D8%AC%D8%B1%D8%A8%D8%A9-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-ux-r836/" rel="">تحسين تجربة المستخدم</a> على مجموعة من الأجهزة، وسنناقش كل هذه الفوائد بشيء من التفصيل في الفقرات التالية.
</p>

<h3 id="-2">
	استخدام دورة حياة الجلسة Session lifecycle في التحليلات
</h3>

<p>
	عندما تجمع البيانات المتعلقة بتفاعل المستخدم مع الموقع أو التطبيق وتحللها ستحتاج لتسجيل اللحظة التي تتغير فيها حالة الصفحات من مرئية <code>visible</code> إلى مخفية <code>hidden</code>، فقد يكون تغير حالة الصفحة إلى مخفية <code>hidden</code> هو الحدث الأخير الذي يمكننا من خلاله مراقبة الصفحة، ولذلك يتعامل المطورون في أغلب الأحيان مع هذا الحدث على أنه نهاية لجلسة المستخدم user session.
</p>

<p>
	بالرغم من إمكانية ذلك، إلا أن تحديد نهاية الجلسة يعتمد بشكل كبير على تعريف مفهوم الجلسة. إذ يمكن تحديد الجلسة على أنها فترة زمنية ثابتة من عدم النشاط، بدلاً من الاعتماد فقط على حالة إخفاء الصفحة، وهذا يعتمد بالطبع على احتياجاتك عند تطوير الموقعك:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4886_22" style=""><span class="pln">document</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"visibilitychange"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">document</span><span class="pun">.</span><span class="pln">visibilityState </span><span class="pun">===</span><span class="pln"> </span><span class="str">"hidden"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    navigator</span><span class="pun">.</span><span class="pln">sendBeacon</span><span class="pun">(</span><span class="str">"/log"</span><span class="pun">,</span><span class="pln"> analyticsData</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span></pre>

<h3 id="-3">
	إدارة الموارد بفعالية
</h3>

<p>
	تفيدنا معرفة حالة رؤية الصفحة القدرة على التوقف عن إجراء شيء ما عندما يتوقف الزائر عن مشاهدة الصفحة، وهذه ميزة مفيدة للغاية وتساعد على  إدارة موارد جهاز العميل أو حتى الخادم بفعالية. فمتصفح الويب يلعب دورًا مهمًا في إدارة الموارد من خلال توفير عدة ميزات كميزة إلغاء تحميل التبويب Tab Unloading، وميزة تحسين أداء علامات التبويب الموجودة في الخلفية Background Tabs.
</p>

<p>
	وبالرغم من أن المتصفحات تقدم بعض التحسينات التلقائية في إدارة الموارد، إلا أن بإمكاننا أيضًا تحسين أداء وكفاءة تطبيقات الويب بأنفسنا من خلال دمج <a href="https://academy.hsoub.com/programming/php/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D8%A3%D8%AF%D8%A7%D8%A1-%D9%81%D9%8A-php-r1203/" rel="">اختبارات الأداء</a> في مراحل مبكرة من عملية <a href="https://academy.hsoub.com/programming/general/%D8%AA%D8%B9%D9%84%D9%85-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8/" rel="">تطوير الموقع</a>، أو تعزيز الأداء يدويًا في الجوانب التي لا تحسنها المتصفحات تلقائيًا. على سبيل المثال، عندما تكون متصلاً بالخادم في الزمن الحقيقي باستخدام تقنيات مثل WebSockets وWebRTC، فمن الأفضل أن تستخدم مقاطع فيديو منخفضة الدقة نوعًا ما لتقليل الحمل على الشبكة، كما يمكنك تحسين كيفية تعامل المتصفح مع قواعد البيانات المدمجة مثل <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%85%D8%B1%D9%83%D8%A8%D8%A9-%D9%81%D9%8A-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-indexeddb-%D9%88%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D8%AF%D9%88%D9%86-%D8%A7%D8%AA%D8%B5%D8%A7%D9%84-r2448/" rel="">IndexedDB</a> فهذا يؤدي بدوره إلى تحسين الأداء فمتصفحات الويب لا تفرض قيودًا على العمليات التي تستخدم قواعد البيانات المدمجة وتسمح بالقراءة والكتابة منها دون التأثر بالانقطاع أو البطء في الشبكة.
</p>

<h3 id="-4">
	تحسين تجربة المستخدم
</h3>

<p>
	عندما تتغير حالة الصفحة من مخفية إلى مرئية، من المفترض أن المستخدم قد عاد لزيارة الصفحة مرة أخرى. هذا يتيح لنا استئناف الأنشطة أو العمليات التي كانت متوقفة عندما كانت الصفحة مخفية في الخلفية. لكن هناك بعض التحديات التي قد تواجهها في هذه الحالة، خصوصًا عند التعامل مع الوسائط مثل مقاطع الفيديو أو ملفات الصوت، فقد لا يرغب المستخدم بإيقاف الوسائط عند إخفاء الصفحة، وسنناقش المزيد عن هذه التحديات في الفقرات التالية.
</p>

<h2 id="-5">
	حالات يجب تجنبها عند التحقق من رؤية الصفحة
</h2>

<p>
	ينبغي منح المستخدمين القدرة على التحكم في بدء أو استئناف أو تجاهل الوسائط عند تغير حالة رؤية الصفحة، فهذا أمر أساسي لتوفير تجربة مستخدم سهلة. لذا، من الضروري استخدام واجهة برمجة التطبيقات Page Visibility <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> بحذر لمراعاة راحة المستخدمين أثناء التصفح. فلا يجب أن نفترض بأن المستخدمين يرغبون في إيقاف الوسائط تلقائيًا عند تغيير حالة الصفحة من مرئية إلى مخفية، ونتيح لهم تخصيص هذه الخيارات حسب رغبتهم.
</p>

<p>
	يوضح الكود البرمجي التالي كيفية تأثير الحدث <code>visibilitychange</code> على التحكم في الوسائط. فعند تغيير حالة الصفحة إلى مخفية سيتوقف تشغيل الصوت، وعند عودة الصفحة إلى مرئية سيستأنف تشغيله.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_4886_29" style=""><span class="tag">&lt;audio</span><span class="pln">
  </span><span class="atn">controls</span><span class="pln">
  </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"https://mdn.github.io/webaudio-examples/audio-basics/outfoxing.mp3"</span><span class="tag">&gt;&lt;/audio&gt;</span></pre>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4886_31" style=""><span class="kwd">const</span><span class="pln"> audio </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"audio"</span><span class="pun">);</span><span class="pln">

document</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"visibilitychange"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">document</span><span class="pun">.</span><span class="pln">hidden</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    audio</span><span class="pun">.</span><span class="pln">pause</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    audio</span><span class="pun">.</span><span class="pln">play</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	لدينا في كود HTML السابق عنصر <a href="https://wiki.hsoub.com/HTML/audio" rel="external"><code>&lt;audio&gt;</code></a> مع عناصر تحكم تمكّن المستخدم من تشغيل أو إيقاف الصوت كما يشاء. ونحدد في كود جافا سكريبت هل سنشغل الصوت أو نوقفه استنادًا إلى حالة رؤية الصفحة عبر الحدث <code>visibilitychange</code>.
</p>

<ul>
	<li>
		إذا كانت الصفحة مخفية أي تم التبديل إلى تبويب آخر أو تم تصغير النافذة، سنوقف تشغيل الصوت
	</li>
	<li>
		إذا كانت الصفحة مرئية أي عاد المستخدم إلى التبويب أو أعاد تكبير النافذة نستأنف تشغيل الصوت
	</li>
</ul>

<p>
	على الرغم من أن هذه الطريقة قد تبدو منطقية للوهلة الأولى، إلا أن هناك بعض المشكلات التي قد تنشأ في الحالات التالية:
</p>

<ol>
	<li>
		عند التبديل بين التبويبات فإذا أراد المستخدم الانتقال إلى تبويب آخر ثم العودة إلى التبويب الذي يحتوي الصوت، قد يؤدي ذلك إلى تشغيل الصوت أو إيقافه بشكل غير متوقع، وهذا قد يكون مزعجًا للمستخدم
	</li>
	<li>
		عند تصغير نافذة المتصفح ثم تكبيرها، فقد تتغير حالة الصفحة من مرئية إلى مخفية ويتوقف الصوت دون أن يتفاعل المستخدم معه بشكل مباشر
	</li>
	<li>
		إذا كان الصوت غير مرئي عند تحميل الصفحة كأن يكون موجودًا في أسفل الصفحة وغير مرئي عند تحميلها، عندها لن يعرف المستخدم بوجود الصوت وقد يتفاجأ بأن الصوت يعمل دون أن يكون متأكدًا من مصدره ويبدأ بالبحث عن التبويب الذي يحتوي على الصوت، مما يؤدي إلى تجربة مزعجة
	</li>
</ol>

<p>
	لتجنب هذه المشكلات وتحسين تجربة الاستخدام، يُفضل أن تتحقق أولًا إذا كان المستخدم قد تفاعل مع الصوت قبل أن نقرر استئناف تشغيله عند العودة إلى الصفحة. يمكن تحقيق ذلك من خلال تخزين حالة تشغيل الصوت عندما تكون الصفحة مخفية وعند عودتها لمرئية مرة أخرى، وبهذا نستأنف تشغيل الصوت فقط إذا كان في حالة تشغيل عند إخفاء الصفحة. تضمن هذه الطريقة أن الصوت لن يبدأ بشكل غير مرغوب فيه.
</p>

<p>
	لاحظ الكود التالي كيف عدّلنا مستمع الحدث <code>visibilitychange</code> ليخزن حالة عنصر الصوت عند إخفاء الصفحة باستخدام المتغير المنطقي <code>playingOnHide</code> وكيف سنستخدمه عندما تتغير حالة رؤية الصفحة إلى مخفية <code>hidden</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4886_35" style=""><span class="com">// تعيين المتغير playingOnHide ليكون false في البداية لأنه عند تحميل الصفحة لا نشغل الصوت تلقائيًا</span><span class="pln">
</span><span class="kwd">let</span><span class="pln"> playingOnHide </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">

</span><span class="com">// إضافة مستمع للحدث visibilitychange الذي يتم إطلاقه عندما تتغير حالة الصفحة من مرئية إلى مخفية أو العكس</span><span class="pln">
document</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"visibilitychange"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// التحقق إذا أصبحت الصفحة مخفية</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">document</span><span class="pun">.</span><span class="pln">hidden</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// إذا كانت الصفحة مخفية، سنخزن حالة الصوت هل هو مشغل أم لا</span><span class="pln">
    playingOnHide </span><span class="pun">=</span><span class="pln"> </span><span class="pun">!</span><span class="pln">audio</span><span class="pun">.</span><span class="pln">paused</span><span class="pun">;</span><span class="pln"> </span><span class="com">// إذا كان الصوت مشغلاً نخزن القيمة true وإذا كان متوقفاً نخزن القيمة false</span><span class="pln">

    </span><span class="com">// نوقف الصوت مؤقتًا عندما تصبح الصفحة مخفية</span><span class="pln">
    audio</span><span class="pun">.</span><span class="pln">pause</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	<strong>ملاحظة</strong>: عند التعامل مع حالة إخفاء الصفحة، نجد أنه لا يمكننا الاعتماد على خاصية <code>audio.playing</code> لمعرفة إذا كان الصوت يعمل أم لا. بل نستخدم  بدلاً من ذلك الخاصية <code>!audio.paused‎</code> التي تخبرنا إذا كان الصوت غير متوقف مؤقتًا.
</p>

<p>
	لاحظ الكود التالي عندما تصبح الصفحة مخفية <code>document.hidden</code>، سنتحقق إذا كان الصوت يعمل باستخدام <code>!audio.paused‎</code> فإذا كان الصوت يعمل فعلًا عندها نخزّن الحالة <code>true</code> في المتغير <code>playingOnHide</code>  وإذا كان متوقف نخزن الحالة <code>false</code>. بعد ذلك، نوقف الصوت باستخدام<code>audio.pause() ‎</code>.
</p>

<p>
	وعندما تعود الصفحة مرئية مرة أخرى، نستخدم المتغير <code>playingOnHide</code> للتحقق إذا كان الصوت قد بدأ في التشغيل قبل اختفاء الصفحة. فإذا كان الصوت يعمل سنستأنف تشغيله باستخدام <code>audio.play()‎</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4886_39" style=""><span class="kwd">let</span><span class="pln"> playingOnHide </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">

document</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"visibilitychange"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">document</span><span class="pun">.</span><span class="pln">hidden</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    playingOnHide </span><span class="pun">=</span><span class="pln"> </span><span class="pun">!</span><span class="pln">audio</span><span class="pun">.</span><span class="pln">paused</span><span class="pun">;</span><span class="pln">
    audio</span><span class="pun">.</span><span class="pln">pause</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// استئنف تشغيل الصوت إذا كان تشغيله بدأ بالفعل أثناء إخفاء الصفحة</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">playingOnHide</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      audio</span><span class="pun">.</span><span class="pln">play</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	إليك الكود كاملًا، مع إضافة كود برمجي صغير يتحقق من حالة الصوت عندما تكون الصفحة مخفية:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_4886_43" style=""><span class="tag">&lt;audio</span><span class="pln">
  </span><span class="atn">controls</span><span class="pln">
  </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"https://mdn.github.io/webaudio-examples/audio-basics/outfoxing.mp3"</span><span class="tag">&gt;&lt;/audio&gt;</span></pre>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4886_41" style=""><span class="kwd">const</span><span class="pln"> audio </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"audio"</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">let</span><span class="pln"> playingOnHide </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">

document</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"visibilitychange"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">document</span><span class="pun">.</span><span class="pln">hidden</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    playingOnHide </span><span class="pun">=</span><span class="pln"> </span><span class="pun">!</span><span class="pln">audio</span><span class="pun">.</span><span class="pln">paused</span><span class="pun">;</span><span class="pln">
    audio</span><span class="pun">.</span><span class="pln">pause</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">playingOnHide</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      audio</span><span class="pun">.</span><span class="pln">play</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" frameborder="no" height="177" loading="lazy" scrolling="no" src="https://codepen.io/HsoubAcademy/embed/azovKgy?default-tab=result" style="width: 100%;" title="play">See the Pen play by Hsoub Academy (@HsoubAcademy) on CodePen.</iframe>
</p>

<h2 id="-6">
	الخلاصة
</h2>

<p>
	وصلنا لنهاية مقالنا الذي شرحنا فيه تقنية رؤية الصفحة Page Visibility وأهميتها في إدارة الموارد وتحسين تجربة المستخدم. واستعرضنا كيفية استخدام هذه التقنية والاستفادة منها في سياقات متعددة مثل تحسين أداء الصفحة وإدارة الوسائط وتعزيز تجربة المستخدم. كما تناولنا بعض التحديات التي قد تواجه المطورين عند استخدام هذه الميزة، وقدمنا بعض الحلول البرمجية لتحسين أدائها.
</p>

<p>
	ترجمة وبتصرف للمقال <a href="https://developer.mozilla.org/en-US/blog/using-the-page-visibility-api/" rel="external nofollow">Using the Page Visibility <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a> لكاتبه Brian Smith
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%84%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%B5%D9%88%D8%AA-%D9%88%D8%A7%D9%84%D9%81%D9%8A%D8%AF%D9%8A%D9%88-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2438/" rel="">الواجهات البرمجية للتعامل مع الصوت والفيديو في جافا سكريبت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/html/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D9%85%D8%AD%D8%AA%D9%88%D9%89-%D8%B3%D9%85%D8%B9%D9%8A-%D9%88%D9%85%D8%B1%D8%A6%D9%8A-%D9%81%D9%8A-%D8%B5%D9%81%D8%AD%D8%A9-html-r1825/" rel="">إضافة محتوى سمعي ومرئي في صفحة HTML</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/html/%D8%B3%D9%87%D9%88%D9%84%D8%A9-%D9%88%D8%B5%D9%88%D9%84-%D8%AC%D9%85%D9%8A%D8%B9-%D8%A7%D9%84%D8%B2%D9%88%D8%A7%D8%B1-%D9%84%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D9%88%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1323/" rel="">سهولة وصول جميع الزوار لمواقع وتطبيقات الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/html/%D8%A7%D9%84%D8%AA%D8%B1%D9%83%D9%8A%D8%B2-%D8%B9%D9%84%D9%89-%D8%B9%D9%86%D8%A7%D8%B5%D8%B1-%D8%B5%D9%81%D8%AD%D8%A9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1324/" rel="">التركيز على عناصر صفحة الويب</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2461</guid><pubDate>Wed, 04 Dec 2024 15:02:00 +0000</pubDate></item><item><title>&#x62A;&#x62E;&#x632;&#x64A;&#x646; &#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; &#x645;&#x631;&#x643;&#x628;&#x629; &#x641;&#x64A; &#x637;&#x631;&#x641; &#x627;&#x644;&#x639;&#x645;&#x64A;&#x644; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; IndexedDB &#x648;&#x627;&#x644;&#x639;&#x645;&#x644; &#x62F;&#x648;&#x646; &#x627;&#x62A;&#x635;&#x627;&#x644;</title><link>https://academy.hsoub.com/programming/javascript/%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%85%D8%B1%D9%83%D8%A8%D8%A9-%D9%81%D9%8A-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-indexeddb-%D9%88%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D8%AF%D9%88%D9%86-%D8%A7%D8%AA%D8%B5%D8%A7%D9%84-r2448/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_11/---------IndexedDB----.png.144c1326dc26b85ef70bc9932dc6e9c8.png" /></p>
<p>
	أشرنا في <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-%D9%85%D8%AE%D8%A7%D8%B2%D9%86-%D9%88%D9%8A%D8%A8-web-storage-r2443/" rel="">المقال السابق</a> إلى إمكانية استخدام قاعدة البيانات المدمجة في المتصفح IndexedDB في تخزين ما هو أعقد من النصوص واﻷرقام، بل يتعداها إلى إمكانية تخزين أي شيئ تريده بما في ذلك الكائنات ذات البنى المعقدة مثل بيانات الفيديوهات والصور الخام. مع ذلك، ليس من الصعب تخزين واسترجاع هذه البيانات مقارنة بغيرها من البيانات التي تعاملنا معها.
</p>

<p>
	ولتوضيح هذا اﻷمر، سنطور مثالًا تطبيقيًا باسم <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/client-side-storage/indexeddb/video-store" rel="external nofollow">IndexedDB video store</a> بإمكانك الاطلاع على <a href="https://mdn.github.io/learning-area/javascript/apis/client-side-storage/indexeddb/video-store/" rel="external nofollow">عمله مباشرة</a>. ينزّل هذا التطبيق عند تشغيله جميع مقاطع الفيديو من الشبكة ويخزنها في قاعدة البيانات IndexedDB، ويعرض بعدها هذه المقاطع في واجهة المستخدم ضمن العنصر <code>&lt;video&gt;</code>، وعندما تشغّل التطبيق في المرات القادمة، سيجد التطبيق المقاطع ضمن قاعدة البيانات ويعرضها بدلًا من تنزيلها مجددًا مما يجعل العملية أسرع، ويوفّر استهلاك حزمة البيانات المتاحة للاتصال باﻹنترنت.
</p>

<h2 id="indexeddb-1">
	تطبيق عملي: تخزين فيديو في قاعدة البيانات IndexedDB
</h2>

<p>
	سنعرض تاليًا اﻷجزاء اﻷكثر أهمية في تطبيقنا، ولن نستعرض كل التفاصيل طبعًا، فالكثير منها مشابه تمامًا لما غطيناه في <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-%D9%85%D8%AE%D8%A7%D8%B2%D9%86-%D9%88%D9%8A%D8%A8-web-storage-r2443/" rel="">المقال السابق</a>، إضافة إلى وجود تعليقات كافية ضمن الشيفرة توضح خطوات العمل.
</p>

<p>
	نخزن بداية أسماء مقاطع الفيديو التي نريد إحضارها ضمن مصفوفة كائنات كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6471_6" style=""><span class="kwd">const</span><span class="pln"> videos </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
  </span><span class="pun">{</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"crystal"</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">{</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"elf"</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">{</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"frog"</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">{</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"monster"</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">{</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"pig"</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">{</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"rabbit"</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
</span><span class="pun">];</span></pre>

<p>
	ننفذ الدالة <code>()init</code> عند نجاح الاتصال بقاعدة البيانات. ووظيفة هذه الدالة التنقل بين أسماء مقاطع الفيديو السابقة ومحاولة إيجاد سجل يوافق اسم المقطع ضمن قاعدة بيانات الفيديو. فإن وجد مقطع الفيديو ستكون نتيجة <code>request.result</code> هي <code>true</code> وإلا ستكون <code>undefined</code>. ستمرر الدالة بعد ذلك اسم المقطع إن وجد إلى الدالة <code>()displayVideo</code> لتضعه ضمن واجهة المستخدم وإلا تستدعي الدالة <code>()fetchVideoFromNetwork</code> ﻹحضار المقطع من اﻹنترنت.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6471_8" style=""><span class="kwd">function</span><span class="pln"> init</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">//تنقل بين أسماء مقاطع الفيديو واحدًا تلو اﻵخر</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> video </span><span class="kwd">of</span><span class="pln"> videos</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// افتح الاتصال مع قاعدة البيانات واحصل على مخزن الكائنات وكل فيديو فيه</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> objectStore </span><span class="pun">=</span><span class="pln"> db</span><span class="pun">.</span><span class="pln">transaction</span><span class="pun">(</span><span class="str">"videos_os"</span><span class="pun">).</span><span class="pln">objectStore</span><span class="pun">(</span><span class="str">"videos_os"</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> request </span><span class="pun">=</span><span class="pln"> objectStore</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="pln">video</span><span class="pun">.</span><span class="pln">name</span><span class="pun">);</span><span class="pln">
    request</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"success"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="com">// إن وجد المقطع ضمن قاعدة البيانات</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">request</span><span class="pun">.</span><span class="pln">result</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
       </span><span class="com">//displayVideo احضر المقطع واعرضه على الواجهة باستخدام الدالة</span><span class="pln">
        console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"taking videos from IDB"</span><span class="pun">);</span><span class="pln">
        displayVideo</span><span class="pun">(</span><span class="pln">
          request</span><span class="pun">.</span><span class="pln">result</span><span class="pun">.</span><span class="pln">mp4</span><span class="pun">,</span><span class="pln">
          request</span><span class="pun">.</span><span class="pln">result</span><span class="pun">.</span><span class="pln">webm</span><span class="pun">,</span><span class="pln">
          request</span><span class="pun">.</span><span class="pln">result</span><span class="pun">.</span><span class="pln">name</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">);</span><span class="pln">
      </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="com">// احضر مقطع الفيديو من الشبكة</span><span class="pln">
        fetchVideoFromNetwork</span><span class="pun">(</span><span class="pln">video</span><span class="pun">);</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تحضر الدالة <code>()fetchVideoFromNetwork</code> مقاطع فيديو من النوعين MP4 و WebM باستخدام الطلب <code>()fetch</code>، بعدها سنستخدم التابع <code>()response.blob</code> لاستخلاص جسم كل طلب على شكل <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D9%83%D8%A7%D8%A6%D9%86-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%AB%D9%86%D8%A7%D8%A6%D9%8A%D8%A9-blob-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1285/" rel="">كائن بيانات ثنائية blob</a> والذي يعطي كائنًا يمثل مقطع الفيديو، ويمكن تخزينه وعرضه لاحقًا. أما المشكلة التي تواجهنا هنا، أن هذين الطلبين غير متزامنين، لكن ما نريده فعلًا هو عرض أو تخزين المقطع فقط عندما يكتمل <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%B9%D9%88%D8%AF-promise-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r915/" rel="">الوعد promise. </a>لهذا نستخدم التابع <code>()promise.all</code> الذي يقبل معاملًا واحدًا وهو مصفوفة مراجع إلى كل الوعود التي تريد التحقق من إكتمالها، ويعيد وعدًا يتحقق عندما تتحقق كل الوعود في المصفوفة.
</p>

<p>
	نستدعي ضمن التابع <code>()then</code> المتعلق بهذا الوعد الدالة <code>()displayVideo</code> كما فعلنا سابقًا لعرض مقطع الفيديو، ثم نستدعي أيضًا الدالة <code>()storeVideo</code> لتخزين المقطع في قاعدة البيانات:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6471_10" style=""><span class="com">// fetch() إحضار مقاطع الفيديو باستخدام الدالة</span><span class="pln">
</span><span class="com">// blob تحويل أجسام الاستجابات إلى كائن</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> mp4Blob </span><span class="pun">=</span><span class="pln"> fetch</span><span class="pun">(`</span><span class="pln">videos</span><span class="pun">/</span><span class="pln">$</span><span class="pun">{</span><span class="pln">video</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}.</span><span class="pln">mp4</span><span class="pun">`).</span><span class="pln">then</span><span class="pun">((</span><span class="pln">response</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln">
  response</span><span class="pun">.</span><span class="pln">blob</span><span class="pun">(),</span><span class="pln">
</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> webmBlob </span><span class="pun">=</span><span class="pln"> fetch</span><span class="pun">(`</span><span class="pln">videos</span><span class="pun">/</span><span class="pln">$</span><span class="pun">{</span><span class="pln">video</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}.</span><span class="pln">webm</span><span class="pun">`).</span><span class="pln">then</span><span class="pun">((</span><span class="pln">response</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln">
  response</span><span class="pun">.</span><span class="pln">blob</span><span class="pun">(),</span><span class="pln">
</span><span class="pun">);</span><span class="pln">

</span><span class="com">// نفّذ الشيفرة التالية إن تحقق كلا الوعدان</span><span class="pln">
</span><span class="typ">Promise</span><span class="pun">.</span><span class="pln">all</span><span class="pun">([</span><span class="pln">mp4Blob</span><span class="pun">,</span><span class="pln"> webmBlob</span><span class="pun">]).</span><span class="pln">then</span><span class="pun">((</span><span class="pln">values</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">//displayVideo() اعرض الفيديو الذي أحضرته من الإنترنت باستخدام الدالة</span><span class="pln">
  displayVideo</span><span class="pun">(</span><span class="pln">values</span><span class="pun">[</span><span class="lit">0</span><span class="pun">],</span><span class="pln"> values</span><span class="pun">[</span><span class="lit">1</span><span class="pun">],</span><span class="pln"> video</span><span class="pun">.</span><span class="pln">name</span><span class="pun">);</span><span class="pln">
  </span><span class="com">//storeVideo() خزن مقطع الفيديو في قاعدة البيانات باستخدام</span><span class="pln">
  storeVideo</span><span class="pun">(</span><span class="pln">values</span><span class="pun">[</span><span class="lit">0</span><span class="pun">],</span><span class="pln"> values</span><span class="pun">[</span><span class="lit">1</span><span class="pun">],</span><span class="pln"> video</span><span class="pun">.</span><span class="pln">name</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<ol start="3">
	<li>
		يشبه عمل الدالة <code>()storeVideo</code> ما رأيناه في <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-%D9%85%D8%AE%D8%A7%D8%B2%D9%86-%D9%88%D9%8A%D8%A8-web-storage-r2443/" rel="">المقال السابق</a> عندما أضفنا بيانات إلى قاعدة البيانات، إذ نفتح قناة العمليات <code>readwrite</code> مع القاعدة ونتخذ مرجعًا إلى مخزن الكائن <code>video_os</code> ثم ننشئ كائنًا يمثل السجل الذي نريد إضافته إلى القاعدة ونستخدم بعدها التابع <code>()IDBObjectStore.add</code>:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6471_12" style=""><span class="com">// storeVideo() تعريف الدالة</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> storeVideo</span><span class="pun">(</span><span class="pln">mp4</span><span class="pun">,</span><span class="pln"> webm</span><span class="pun">,</span><span class="pln"> name</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// فتح قناة اتصال قراءة وكتابة مع قاعدة البيانات</span><span class="pln">
     </span><span class="kwd">const</span><span class="pln"> objectStore </span><span class="pun">=</span><span class="pln"> db
    </span><span class="pun">.</span><span class="pln">transaction</span><span class="pun">([</span><span class="str">"videos_os"</span><span class="pun">],</span><span class="pln"> </span><span class="str">"readwrite"</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">objectStore</span><span class="pun">(</span><span class="str">"videos_os"</span><span class="pun">);</span><span class="pln">

  </span><span class="com">//Add() إضافة السجل إلى قاعدة البيانات باستخدام</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> request </span><span class="pun">=</span><span class="pln"> objectStore</span><span class="pun">.</span><span class="pln">add</span><span class="pun">({</span><span class="pln"> mp4</span><span class="pun">,</span><span class="pln"> webm</span><span class="pun">,</span><span class="pln"> name </span><span class="pun">});</span><span class="pln">

  request</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"success"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Record addition attempt finished"</span><span class="pun">),</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
  request</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"error"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="pln">request</span><span class="pun">.</span><span class="pln">error</span><span class="pun">));</span><span class="pln">
</span><span class="pun">}</span></pre>

<ol start="4">
	<li>
		<p>
			تُنشئ الدالة <code>()displayVideo</code> عناصر <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%B4%D8%AC%D8%B1%D8%A9-dom-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2360/" rel="">شجرة DOM</a> اللازمة لإدراج مقطع الفيديو في واجهة المستخدم ومن ثم تلحق هذه العناصر بالصفحة. أما النقاط اﻷكثر أهمية، فهي التي نستعرضها تاليًا.<br>
			لعرض كائن البيانات الثنائية الذي يضم الفيديو داخل العنصر <code>&lt;video&gt;</code>، لا بد من إنشاء كائن عنوان URL أي عناوين داخلية تشير إلى كائن البيانات الثنائية المخزن في الذاكرة باستخدام التابع <code>()URL.creatObjectURL</code>. بعدها يمكننا أن نجعل تلك العناوين قيمًا للسمات <code>src</code> العائدة للعناصر <code>&lt;source&gt;</code> ويعمل عندها كل شيء كما هو متوقع:
		</p>
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6471_15" style=""><span class="com">//displayVideo() تعريف الدالة  </span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> displayVideo</span><span class="pun">(</span><span class="pln">mp4Blob</span><span class="pun">,</span><span class="pln"> webmBlob</span><span class="pun">,</span><span class="pln"> title</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">//blob يشير إلى الكائن URL إنشاء كائن</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> mp4URL </span><span class="pun">=</span><span class="pln"> URL</span><span class="pun">.</span><span class="pln">createObjectURL</span><span class="pun">(</span><span class="pln">mp4Blob</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> webmURL </span><span class="pun">=</span><span class="pln"> URL</span><span class="pun">.</span><span class="pln">createObjectURL</span><span class="pun">(</span><span class="pln">webmBlob</span><span class="pun">);</span><span class="pln">

  </span><span class="com">//لإدراج الفيديو في الصفحة DOM إنشاء عنصر في شجرة</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> article </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">"article"</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> h2 </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">"h2"</span><span class="pun">);</span><span class="pln">
  h2</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> title</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> video </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">"video"</span><span class="pun">);</span><span class="pln">
  video</span><span class="pun">.</span><span class="pln">controls </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> source1 </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">"source"</span><span class="pun">);</span><span class="pln">
  source1</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> mp4URL</span><span class="pun">;</span><span class="pln">
  source1</span><span class="pun">.</span><span class="pln">type </span><span class="pun">=</span><span class="pln"> </span><span class="str">"video/mp4"</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> source2 </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">"source"</span><span class="pun">);</span><span class="pln">
  source2</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> webmURL</span><span class="pun">;</span><span class="pln">
  source2</span><span class="pun">.</span><span class="pln">type </span><span class="pun">=</span><span class="pln"> </span><span class="str">"video/webm"</span><span class="pun">;</span><span class="pln">

  </span><span class="com">//في الشجرة DOM إدراج عنصر</span><span class="pln">
  section</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">article</span><span class="pun">);</span><span class="pln">
  article</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">h2</span><span class="pun">);</span><span class="pln">
  article</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">video</span><span class="pun">);</span><span class="pln">
  video</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">source1</span><span class="pun">);</span><span class="pln">
  video</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">source2</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2 id="">
	تخزين اﻷصول للعمل دون اتصال بالشبكة
</h2>

<p>
	عرضنا في المثال السابق طريقة إنشاء تطبيق يُخزّن أصولًا assets في قاعدة البيانات IndexedDB حتى لا نضطر إلى تحميلها مجددًا. ويحسن هذا اﻷمر تجربة المستخدم بشكل ملحوظ. لكن لا تزال بعض الأصول المهمة مفقودة كي يعمل التطبيق وهي ملف HTML الرئيسي وملفات <a href="https://academy.hsoub.com/programming/css/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-css/" rel="">CSS</a> و<a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D9%85%D9%86-%D8%A7%D9%84%D8%B5%D9%81%D8%B1-%D8%AD%D8%AA%D9%89-%D8%A7%D9%84%D8%A7%D8%AD%D8%AA%D8%B1%D8%A7%D9%81-r2046/" rel="">جافا سكريبت</a>، ولا بد من تنزيلها في كل مرة ندخل فيها إلى الموقع، وبالتالي لن يعمل التطبيق بدون الاتصال باﻹنترنت. وهنا يأتي دور <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B9%D9%85%D9%91%D8%A7%D9%84-workers-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2349/" rel="">عمّال الخدمة service workers</a> والواجهة البرمجية cache <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="162322" href="https://academy.hsoub.com/uploads/monthly_2024_11/01_offline.png.5691dc216bae33494732117d5a46925b.png" rel=""><img alt="01 offline" class="ipsImage ipsImage_thumbnailed" data-fileid="162322" data-unique="bf2jcezp6" style="width: 400px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2024_11/01_offline.png.5691dc216bae33494732117d5a46925b.png"> </a>
</p>

<p>
	يُعرف عامل الخدمة service worker في جافا سكريبت على أنه ملف يُسجّل تحت مصدر محدد مثل موقع ويب أو جزء من موقع ويب في نطاق معين عندما يلج إليه متصفح ويب. ويتمكن هذا الملف لكونه مسجلًا على نطاق ما أن يتحكم بالصفحات التي تنتمي إلى نفس اﻷصل أو النطاق. ويتوضع هذا الملف في مكان وسط بين الصفحة التي اكتمل تحميلها وشبكة اﻹنترنت ويعترض طلبات الشبكة التي تحوّل من وإلى ذلك المصدر أو اﻷصل.
</p>

<p>
	وعندما يعترض العامل الطلب سيكون بمقدوره تنفيذ أي شيئ تريده على هذا الطلب، لكن استخدامه النمطي هو تخزين الاستجابات على طلبات الشبكة والاستجابة لهذا الطلبات في المرات القادمة بدلًا من الاتصال بالشبكة والحصول على الاستجابة. وكنتيجة ستتمكن من بناء صفحة ويب تعمل كليًا دون اتصال بشبكة اﻹنترنت.
</p>

<p>
	أما الواجهة البرمجية Cache <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> فهي آلية أخرى لتخزين البيانات في طرف العميل، مع اختلاف بسيط هو أنها مخصصة لتخزين الاستجابات على طلبات HTTP، لهذا ستعمل جيدًا مع عمال الخدمة service workers.
</p>

<h3 id="-1">
	مثال عن عمال الخدمة
</h3>

<p>
	لنطرح مثالًا يوضح قليلًا الفكرة السابقة. إذ أنشانا نسخة أخرى من مثال تخزين ملفات الفيديو الذي فصلناه في الفقرة السابقة. وتعمل هذه النسخة بنفس اﻷسلوب ما عدا أنها تخزّن أيضًا ملفات HTML و CSS وجافا سكريبت ضمن Cache <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> من خلال عامل خدمة وبالتالي سيعمل المثال دون اتصال باﻹنترنت.
</p>

<p>
	بإمكانك تجريب <a href="https://mdn.github.io/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/" rel="external nofollow">هذه النسخة مباشرة</a> على جيت-هاب والاطلاع على <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/client-side-storage/cache-sw/video-store-offline" rel="external nofollow">الشيفرة المصدرية</a> أيضًا.
</p>

<h4 id="-2">
	تسجيل عامل الخدمة
</h4>

<p>
	أول ما تلاحظه هو وجود شيفرة إضافية في ملف <a href="https://academy.hsoub.com/javascript/" rel="">جافا سكريبت</a>. تختبر هذه الشيفرة بداية وجود العضو <code>serviceWorker</code> ضمن الكائن <code>Navigator</code>. فإن كان موجودًا (أعادت الشيفرة القيمة <code>true</code>)، نعلم حينها وجود دعم أساسي لعمال الخدمة في المتصفح. نستخدم التابع <code>()ServiceWorkerContainer.register</code> لتسجيل عامل الخدمة الموجود في الملف <code>sw.js</code> على المصدر الذي يتواجد فيه، وبالتالي سيكون قادرًا على التحكم بالصفحات الموجودة في نفس المجلد أو المجلدات الفرعية. وعندما يتحقق الوعد سيكون عامل الخدمة قد سُجِّل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6471_20" style=""><span class="com">// تسجيل عامل الخدمة لتتمكن من تشغيل المثال دون اتصال بالشبكة</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="str">"serviceWorker"</span><span class="pln"> in navigator</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  navigator</span><span class="pun">.</span><span class="pln">serviceWorker
    </span><span class="pun">.</span><span class="kwd">register</span><span class="pun">(</span><span class="pln">
      </span><span class="str">"/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/sw.js"</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">)</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Service Worker Registered"</span><span class="pun">));</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	<strong>ملاحظة</strong>: يُعطى مسار الملف <code>sw.js</code> بالنسبة إلى أصل الموقع، وليس بالنسبة إلى ملف جافا سكريبت الذي يضم الشيفرة. فالعامل موجود على العنوان:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_858_9" style=""><span class="pln">https://mdn.github.io/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/sw.js</span></pre>

<p>
	بينما عنوان اﻷصل هو
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_858_14" style=""><span class="pln">https://mdn.github.io</span></pre>

<p>
	لذا لابد أن يكون العنوان المعطى عند التسجيل هو:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_858_16" style=""><span class="pln">/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/sw.js</span></pre>

<p>
	وإذا أردت استضافة هذا المثال على حاسوبك محليًا، لا بد من تبديل هذه القيم بما يناسب. وعلى الرغم من أنه أمر مربك، لكنه ضروري لأسباب أمنية.
</p>

<h4 id="-3">
	تثبيت عامل الخدمة
</h4>

<p>
	في كل مرة ندخل فيها إلى أحد الصفحات التي تقع تحت سيطرة عامل الخدمة، يُثبَّت عامل الخدمة على هذه الصفحة أي يبدأ العامل بالتحكم فيها. وعندما يحدث ذلك، يقع الحدث <code>install</code> على عامل الخدمة، وستتمكن من كتابة الشيفرة ضمن عامل الخدمة نفسه لتستجيب إلى حدث التثبيت.
</p>

<p>
	وإذا ألقينا نظرة على الملف <code>sw.js</code> سنجد أن مترصد حدث التثبيت مسجّل وفق القيمة <code>self</code> أي على العامل نفسه. والتعليمة <code>self</code> طريقة لتشير إلى الطبيعة العامة global scope لعامل الخدمة من داخل ملف عامل الخدمة.
</p>

<p>
	نستخدم ضمن دالة المعالج <code>install</code> التابع <code>()ExtendableEvent.waitUntil</code> العائد إلى كائن الحدث لكي يبلغ المتصفح بعدم تثبيت العامل قبل أن يُنجز الوعد بنجاح.
</p>

<p>
	وهنا نجد طريقة عمل الواجهة Cache <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> الخاصة بعملية التخزين، إذ نستخدم التابع <code>()CacheStorage.open</code> لفتح كائن تخزين مؤقت جديد cache object لنخزّن ضمنه الاستجابات. وعندما يتحقق الوعد، سيعيد كائن <code>cache</code> يمثل المخزن المؤقت للفيديو <code>video-store</code>. نستخدم بعد ذلك التابع <code>()Cache.addAll</code> ﻹحضار سلسلة اﻷصول واستجاباتها إلى المخزن المؤقت:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6471_22" style=""><span class="pln">self</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"install"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  e</span><span class="pun">.</span><span class="pln">waitUntil</span><span class="pun">(</span><span class="pln">
    caches
      </span><span class="pun">.</span><span class="pln">open</span><span class="pun">(</span><span class="str">"video-store"</span><span class="pun">)</span><span class="pln">
      </span><span class="pun">.</span><span class="pln">then</span><span class="pun">((</span><span class="pln">cache</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln">
        cache</span><span class="pun">.</span><span class="pln">addAll</span><span class="pun">([</span><span class="pln">
          </span><span class="str">"/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/"</span><span class="pun">,</span><span class="pln">
          </span><span class="str">"/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/index.html"</span><span class="pun">,</span><span class="pln">
          </span><span class="str">"/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/index.js"</span><span class="pun">,</span><span class="pln">
          </span><span class="str">"/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/style.css"</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">]),</span><span class="pln">
      </span><span class="pun">),</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	وهكذا تنتهي عملية التثبيت.
</p>

<h4 id="-4">
	الاستجابة إلى طلبات أخرى
</h4>

<p>
	مع تسجيل عامل الخدمة ليتحكم بصفحة HTML وإضافة كل اﻷصول إلى المخزن المؤقت، سيكون التطبيق جاهزًا للعمل. لكن بقي هناك شيء واحد وهو كتابة شيفرة تستجيب إلى طلبات HTML اﻷخرى التي ترد، وهذا ما يفعله القسم الثاني من الشيفرة في الملف <code>sw.js</code>.
</p>

<p>
	نضيف مترصدًا آخر عامًا إلى عامل الخدمة، يعمل على تنفيذ معالج الحدث عندما يقع حدث اﻹحضار <code>fetch</code>. ويقع هذا الحدث في كل مرة يحاول فيها المتصفح إجراء طلب لأحد اﻷصول الموجودة في نفس المجلد الذي يضم عامل الخدمة.
</p>

<p>
	نسجّل بداية ضمن دالة المعالج عنوان URL للأصل المطلوب، ثم نهيئ استجابة مخصصة لهذا الطلب باستخدام التابع <code>()FetchEvent.respondWith</code>. وضمن كتلة التابع السابق نستخدم التابع <code>()CacheStorage.match</code> للتحقق من وجود طلب موافق لهذا اﻷصل ضمن المخزن المؤقت. ويتحقق الوعد الموافق لهذا الطلب في حال وجود عنوان في المخزن يطابق عنوان URL للطلب وإلا سيعيد الوعد القيمة <code>undefined</code>. نعيد بعد ذلك الطلب على شكل استجابة مخصصة في حال كان العنوان موجودًا وإلا نستخدم الواجهة <code>()fetch</code> ﻹحضاره من الشبكة كونه غير موجود في المخزن المؤقت:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6471_24" style=""><span class="pln">self</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"fetch"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">e</span><span class="pun">.</span><span class="pln">request</span><span class="pun">.</span><span class="pln">url</span><span class="pun">);</span><span class="pln">
  e</span><span class="pun">.</span><span class="pln">respondWith</span><span class="pun">(</span><span class="pln">
    caches</span><span class="pun">.</span><span class="pln">match</span><span class="pun">(</span><span class="pln">e</span><span class="pun">.</span><span class="pln">request</span><span class="pun">).</span><span class="pln">then</span><span class="pun">((</span><span class="pln">response</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> response </span><span class="pun">||</span><span class="pln"> fetch</span><span class="pun">(</span><span class="pln">e</span><span class="pun">.</span><span class="pln">request</span><span class="pun">)),</span><span class="pln">
  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	وهكذا نستخدم عامل الخدمة، علمًا أن استخداماته أوسع من ذلك ولا يسعنا تغطيها في هذا المقال.
</p>

<h4 id="-5">
	اختبار المثال دون اتصال بالشبكة
</h4>

<p>
	لاختبار عمل تطبيقنا، لا بد من تحميله عدة مرات للتأكد من تثبيت عامل الخدمة، وبعدها يمكنك:
</p>

<ul>
	<li>
		قطع الاتصال باﻹنترنت.
	</li>
	<li>
		اختيار العمل دون اتصال (اختر ملف ثم العمل دون اتصال إن كنت تستخدم فايرفوكس).
	</li>
	<li>
		الانتقال إلى أدوات مطوري ويب واختر تطبيقات ثم عمال الخدمة Service workers، ولا بد من تفعيل الخيار offline إن كنت تستخدم متصفح كروم.
	</li>
</ul>

<p>
	لو حاولت اﻵن تحديث الصفحة سترى أنها ستعيد التحميل دون أية مشكلات لأن كل أصول الصفحة قد خُزّنت في المخزن المؤقت cache، كما أن كل مقاطع الفيديو مخزنة ضمن قاعدة البيانات IndexedDB.
</p>

<h2 id="-6">
	الخلاصة
</h2>

<p>
	تعرفنا في هذا المقال على طريقة لتشغيل صفحة ويب دون اتصال باﻹنترنت عن طريقة استخدام عامل خدمة service worker مع الواجهة البرمجية <code>Cache</code> التي تخزّن الأصول ضمن مخازن مؤقتة في حاسوبك.
</p>

<p>
	ترجمة -وبتصرف- للجزء الثالث من مقال: <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Client-side_storage" rel="external nofollow">Client-side storage</a>
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق:<a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D9%81%D9%87%D8%B1%D8%B3%D8%A9-indexeddb-r2454/" rel=""> تخزين البيانات في طرف العميل باستخدام قاعدة البيانات المفهرسة IndexedDB</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-%D9%85%D8%AE%D8%A7%D8%B2%D9%86-%D9%88%D9%8A%D8%A8-web-storage-r2443/" rel="">تخزين البيانات في طرف العميل: مخازن ويب Web Storage</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%85%D8%AD%D9%84%D9%8A%D8%A7-%D9%81%D9%8A-%D9%85%D8%AA%D8%B5%D9%81%D8%AD-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%B9%D8%A8%D8%B1-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1338/" rel="">تخزين البيانات محليا في متصفح الويب عبر جافاسكربت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/html/%D8%A7%D9%84%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%AD%D9%84%D9%8A-local-storage-%D9%81%D9%8A-html5-r362/" rel="">التخزين المحلي (Local Storage) في HTML5</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/html/%D8%AA%D8%B9%D8%B1%D9%91%D9%81-%D8%B9%D9%84%D9%89-indexeddb-r67/" rel="">تعرّف على IndexedDB</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2448</guid><pubDate>Mon, 25 Nov 2024 15:00:00 +0000</pubDate></item><item><title>&#x62A;&#x62E;&#x632;&#x64A;&#x646; &#x627;&#x644;&#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; &#x641;&#x64A; &#x637;&#x631;&#x641; &#x627;&#x644;&#x639;&#x645;&#x64A;&#x644; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x642;&#x627;&#x639;&#x62F;&#x629; &#x627;&#x644;&#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; &#x627;&#x644;&#x645;&#x641;&#x647;&#x631;&#x633;&#x629; IndexedDB</title><link>https://academy.hsoub.com/programming/javascript/%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D9%81%D9%87%D8%B1%D8%B3%D8%A9-indexeddb-r2454/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_11/---------IndexedDB.png.277c0e581e4682a5e0fb37946b49e4a3.png" /></p>
<p>
	تُعد الواجه البرمجية IndexedDB أو IDB اختصارًا منظومة قواعد بيانات كاملة مضمنة في المتصفح تساعدك على تخزين بيانات مترابطة معقدة لا تقتصر فيها أنواع البيانات على قيم بسيطة مثل النصوص واﻷعداد. إذ تستطيع تخزين مقاطع الفيديو والصور وتقريبًا أي شيء في نسخ منفصلة من القاعدة IndexedDB.
</p>

<p>
	وتتيح لك هذه الواجهة البرمجية إنشاء <a href="https://academy.hsoub.com/devops/servers/databases/%D9%85%D9%83%D9%88%D9%86%D8%A7%D8%AA-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA/" rel="">قاعدة بيانات</a> ومن ثم إنشاء مخازن كائنات object stores ضمن القاعدة. وتُعد مخازن الكائنات بمثابة بنى شبيهة بالجداول الموجودة في <a href="https://academy.hsoub.com/devops/servers/databases/%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA/" rel="">قواعد البيانات العلاقية</a> relational databases، ويمكن لأي مخزن أن يضم مخازن كائنات أخرى.
</p>

<p>
	وبالطبع تأتي هذه الميزات مع آثار لا بد منها، فالواجهة IndexedDB أكثر تعقيدًا من <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-%D9%85%D8%AE%D8%A7%D8%B2%D9%86-%D9%88%D9%8A%D8%A8-web-storage-r2443/" rel="">واجهة مخازن ويب</a> Web Stores من ناحية الاستخدام. لهذا سنحاول في هذا المقال تقديم مثال يعرض جزءًا ضئيًلا جدًا من إمكانات هذه الواجهة، لكنه سيزوّدك باﻷساسيات التي تساعدك على الانطلاق.
</p>

<h2 id="">
	تطبيق عملي: تخزين ملاحظات
</h2>

<p>
	سنبني تطبيق بسيط يسمح لك بتخزين ملاحظات في المتصفح ومراجعة هذه الملاحظات وحذفها متى شئت. وسيكون البناء خطوة خطوة نشرح فيها اﻷجزاء اﻷكثر أهمية من قاعدة البيانات IndexedDB. سيبدو شكل التطبيق عند انتهائه كالتالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="163043" href="https://academy.hsoub.com/uploads/monthly_2024_11/01-idb-demo.png.6122e24e473c5b8f17ccf93cdfce9bb8.png" rel=""><img alt="01 idb demo" class="ipsImage ipsImage_thumbnailed" data-fileid="163043" data-ratio="91.67" data-unique="3jpxccbhz" style="width: 300px; height: auto;" width="300" src="https://academy.hsoub.com/uploads/monthly_2024_11/01-idb-demo.thumb.png.1af66c009a879fe7e0353ec6a727ddf9.png"> </a>
</p>

<p>
	تتكون كل ملاحظة من عنوان ومحتوى نصي يمكن تحرير أي منهما بشكل مستقل عن اﻵخر. وستجد ضمن شيفرة جافا سكريبت الخاصة بالتطبيق تعليقات كافية لشرح كل خطوة بالتفصيل.
</p>

<h3 id="-1">
	نقطة الانطلاق
</h3>

<ol>
	<li>
		انسخ بداية الملفات <a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/client-side-storage/indexeddb/notes/index.html" rel="external nofollow"><code>index.html</code></a> و <a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/client-side-storage/indexeddb/notes/style.css" rel="external nofollow"><code>style.css</code></a> و <a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/client-side-storage/indexeddb/notes/index-start.js" rel="external nofollow"><code>index-start.js</code></a> إلى مجلد جديد تُنشئه على حاسوبك.
	</li>
	<li>
		الق نظرة في البداية على تلك الملفات، وسترى أن ملف HTML يُعرّف موقع ويب له ترويسة وتذييل ومنطقة محتوى رئيسي تُعرض فيه الملاحظات، إضافة إلى نموذج ﻹدخالها في قاعدة البيانات. يقدّم ملف CSS مجموعة من قواعد التنسيق لتوضيح ما يجري، بينما يضم ملف جافا سكريبت تصريحًا عن خمسة ثوابت تضم مراجع إلى العنصر <code>&lt;ul&gt;</code> وستُعرض الملاحظات على شكل عنوان ونص ضمن عنصري إدخال <code>&lt;input&gt;</code> كما ننشئ مرجعًا إلى النموذج <code>&lt;form&gt;</code> بحد ذاته، ومرجعًا إلى زر <code>&lt;button&gt;</code>.
	</li>
	<li>
		غير اسم ملف جافا سكريبت إلى <code>index.js</code>.
	</li>
</ol>

<h3 id="-2">
	تهيئة قاعدة البيانات
</h3>

<p>
	لنلق نظرة اﻵن على ما يتوجب علبنا فعله بداية لإعداد قاعدة البيانات:
</p>

<ol>
	<li>
		أضف السطر التالي تحت التصريحات عن الثوابت:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8885_7" style=""><span class="com">// إنشاء نسخة عن كائن قاعدة البيانات</span><span class="pln">
</span><span class="kwd">let</span><span class="pln"> db</span><span class="pun">;</span></pre>

<p>
	نصرّح في هذا السطر عن متغير يُدعى <code>db</code> لنستخدمه لاحقًا في تخزين الكائن الذي يمثل قاعدة البيانات. وطالما أننا نستخدمه في عدة أماكن لذلك صرحنا عنه كمتغير عام global لتسهيل اﻷمر.
</p>

<ol start="2">
	<li>
		أضف تاليًا الشيفرة التالية:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8885_9" style=""><span class="com">// فتح قاعدة البيانات مما يؤدي إلى إنشائها إن لم تكن موجودة</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> openRequest </span><span class="pun">=</span><span class="pln"> window</span><span class="pun">.</span><span class="pln">indexedDB</span><span class="pun">.</span><span class="pln">open</span><span class="pun">(</span><span class="str">"notes_db"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span></pre>

<p>
	يُنشئ هذا السطر طلبًا لفتح النسخة 1 من قاعدة بيانات تُدعى <code>notes_db</code>، فإن لم تكن موجودة سوف ينشئها السطر الذي يليه. سترى هذا الشكل من الطلبات كثيرًا عند استخدام IndexedDB. وطالما أن العمليات على قواعد البيانات تحتاج وقتًا، فلا ينبغي ايقاف المتصفح ريثما ننتظر نتيجة الطلب، لهذا عمليات قواعد البيانات هي عمليات غير متزامنة لن تُنفّذ مباشرة، بل في فترة ما مستقبلًا وستُبلَّغ بتنفيذها. ولمعالجة اﻷمر في IndexedDB، ننشئ كائن طلب request object ندعوه مثلًا <code>openRequest</code>، ويمكنك حينها استخدام معالج حدث لتنفيذ شيفرة مخصصة عند اكتمال هذا الطلب أو فشله، وهذا ما ستراه بعد قليل.
</p>

<p>
	<strong>ملاحظة</strong>: إن رقم النسخة أمر مهم. فلو أردت تحديث قاعدة بياناتك (مثل تغيير هيكلية الجدول)، عليك تنفيذ شيفرتك مجددًا بعد زيادة رقم النسخة، وتحديد تخطيط مختلف ضمن معالج الحدث <code>upgradeneeded</code>. لكننا لن نغطي تحديث قاعدة البيانات في هذا المقال.
</p>

<ol start="3">
	<li>
		أضف اﻵن معالج الحدث التالي تحت الشيفرة السابقة:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8885_11" style=""><span class="com">// معالج خطأ يحدد حالة فشل الاتصال بقاعدة البيانات</span><span class="pln">
openRequest</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"error"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln">
 console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="str">"Database failed to open"</span><span class="pun">),</span><span class="pln">
</span><span class="pun">);</span><span class="pln">

</span><span class="com">// معالج نجاح يحدد نجاح فتح قاعدة البيانات</span><span class="pln">
openRequest</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"success"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Database opened successfully"</span><span class="pun">);</span><span class="pln">

 </span><span class="com">//db خزن قاعدة البيانات المفتوحة ضمن المتغير</span><span class="pln">
 db </span><span class="pun">=</span><span class="pln"> openRequest</span><span class="pun">.</span><span class="pln">result</span><span class="pun">;</span><span class="pln">

 </span><span class="com">//التي تعرض الملاحظات الموجودة في قاعدة البيانات displayData() نفّذ الدالة</span><span class="pln">
 displayData</span><span class="pun">();</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	يُنفَّذ معالج الحدث <code>error</code> عندما يُبلغك النظام أن طلبك قد أخفق، مما يتيح لك التعامل مع المشكلة. وما فعلناه في مثالنا هو عرض رسالة خطأ في طرفية جافا سكريبت. ويُنفَّذ معالج الحدث <code>success</code> عندما ينجح الطلب، بمعنى أن الاتصال إلى قاعدة البيانات تحقق، وأصبح الكائن الذي يمثّل قاعدة البيانات متاحًا ضمن الخاصية <code>openRequest.result</code> وبالتالي إمكانية التعامل من خلاله مع قاعدة البيانات. نخزّن النتيجة ضمن المتغيّر <code>db</code> وننفذ دالة تُدعى <code>()displayData</code> وظيفتها عرض البيانات الموجودة في قاعدة البيانات داخل العنصر <code>&lt;ul&gt;</code> بمجرد انتهاء تحميل الصفحة، وسترى تعريف هذه الدالة لاحقًا.
</p>

<ol start="4">
	<li>
		في نهاية هذا القسم سنضيف معالج الحدث <code>upgradeneeded</code> الذي يُنفَّذ إن لم تكن قاعدة البيانات قد هُيئت مسبقًا أو عندما تكون قاعدة البيانات مفتوحة. لهذا أضف اﻷسطر التالية في نهاية شيفرتك منتبهًا إلى ضرورة استخدام رقم نسخة أعلى من رقم النسخة المخزنة في قاعدة البيانات عندما تريد تحديث القاعدة:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8885_13" style=""><span class="com">// هيئ جداول قاعدة البيانات إن لم تكن مهيأة مسبقًا</span><span class="pln">
openRequest</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"upgradeneeded"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="com">// احصل على مرجع إلى قاعدة البيانات المفتوحة</span><span class="pln">
 db </span><span class="pun">=</span><span class="pln"> e</span><span class="pun">.</span><span class="pln">target</span><span class="pun">.</span><span class="pln">result</span><span class="pun">;</span><span class="pln">

 </span><span class="com">// أنشئ مخزن كائنات في قاعدة البيانات لتخزين الملاحظة مع مفتاح يزداد تلقائيًا ومخزن الكائنات مشابه للجدول في قاعدة البيانات العلاقية</span><span class="pln">

 </span><span class="kwd">const</span><span class="pln"> objectStore </span><span class="pun">=</span><span class="pln"> db</span><span class="pun">.</span><span class="pln">createObjectStore</span><span class="pun">(</span><span class="str">"notes_os"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    keyPath</span><span class="pun">:</span><span class="pln"> </span><span class="str">"id"</span><span class="pun">,</span><span class="pln">
    autoIncrement</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
 </span><span class="pun">});</span><span class="pln">

 </span><span class="com">// حدد ما ستضمه عناصر البيانات ومخازن الكائنات</span><span class="pln">
 objectStore</span><span class="pun">.</span><span class="pln">createIndex</span><span class="pun">(</span><span class="str">"title"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"title"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> unique</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pln"> </span><span class="pun">});</span><span class="pln">
 objectStore</span><span class="pun">.</span><span class="pln">createIndex</span><span class="pun">(</span><span class="str">"body"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"body"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> unique</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pln"> </span><span class="pun">});</span><span class="pln">

 console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Database setup complete"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	نعرّف هنا التخطيط الذي نعتمده لقاعدة البيانات، أي نحدد مجموعة الأعمدة أو الحقول التي تضمها. وما فعلناه أننا حددنا مرجعًا في البداية إلى قاعدة البيانات الموجودة عبر الخاصية <code>result</code> لمعالج الحدث <code>e.target.result</code> الذي يمثّل كائن الطلب <code>request</code>. وهذا الأمر مكافئ للسطر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8885_15" style=""><span class="pln">db </span><span class="pun">=</span><span class="pln"> openRequest</span><span class="pun">.</span><span class="pln">result</span><span class="pun">;</span></pre>

<p>
	داخل معالج الحدث <code>succes</code>، لكن لا بد هنا من تنفيذه بشكل مستقل لأن معالج الحدث <code>upgradeneeded</code> سيُنفذ إن احتجنا إليه قبل المعالج <code>success</code>، ولن يكون المتغير <code>db</code> متاحًا ما لم نفعل ذلك.
</p>

<p>
	نستخدم بعد ذلك التابع <code>IDBDatabase.createObjectStore</code> ﻹنشاء مخزن كائن جديد ضمن قاعدة البيانات المفتوحة يُدعى <code>notes_os</code> ويكافئ هذا المخزن جدولًا مفردًا في قواعد البيانات النمطية. كما خصصنا أيضًا حقلًا مفتاحيًا <code>autoIncrement</code> في هذا الكائن وسميناه <code>id</code> تزداد قيمة هذا الحقل كلما أضفنا ملاحظة جديدة ولا حاجة أن يفعل المطوّر هذا صراحة بل هي عملية آلية. يعمل هذا الحقل كمعرّف فريد لكل سجل وذلك عندما نريد تعديل أو حذف هذا السجل.
</p>

<p>
	وأنشأنا أيضًا حقلين مفهرسين آخرين باستخدام التابع <code>()IDBObjectStore.createIndex</code> وهما <code>title</code> و <code>body</code> كي يضما عنوان الملاحظة ونصها. وسيمثَّل كل منهما على شكل كائن له التخطيط التالي، وذلك عندما نبدأ بإضافة الملاحظات إلى قاعدة البيانات وفق التخطيط الذي وضعناه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8885_17" style=""><span class="pun">{</span><span class="pln">
 </span><span class="str">"title"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Buy milk"</span><span class="pun">,</span><span class="pln">
 </span><span class="str">"body"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Need both cows milk and soy."</span><span class="pun">,</span><span class="pln">
 </span><span class="str">"id"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">8</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3 id="-3">
	إضافة بيانات إلى قاعدة البيانات
</h3>

<p>
	لنرى اﻵن كيف يمكننا إضافة سجلات إلى قاعدة البيانات باستخدام النموذج الذي صممناه في صفحتنا. لهذا أضف الأسطر التالية تحت معالج الحدث السابق. تضبط هذه اﻷسطر معالج الحدث <code>submit</code> الذي يستدعي الدالة <code>()addData</code> عند تسليم النموذج (النقر على زر اﻹرسال):
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8885_19" style=""><span class="com">//addData() إنشاء معالج حدث لعملية تسليم النموذج يعمل عند تنفيذ الدالة</span><span class="pln">
form</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"submit"</span><span class="pun">,</span><span class="pln"> addData</span><span class="pun">);</span></pre>

<p>
	لنعرّف اﻵن الدالة <code>()addData</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8885_21" style=""><span class="com">// التصريح عن الدالة</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> addData</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

 </span><span class="com">//نمنع السلوك الافتراضي، فلا نريد تسليم النموذج بالطريقة النمطية</span><span class="pln">
 e</span><span class="pun">.</span><span class="pln">preventDefault</span><span class="pun">();</span><span class="pln">

 </span><span class="com">// الحصول على القيم التي نريد تخزينها ووضعها ضمن عنصر تخزين</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> newItem </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> title</span><span class="pun">:</span><span class="pln"> titleInput</span><span class="pun">.</span><span class="pln">value</span><span class="pun">,</span><span class="pln"> body</span><span class="pun">:</span><span class="pln"> bodyInput</span><span class="pun">.</span><span class="pln">value </span><span class="pun">};</span><span class="pln">

 </span><span class="com">// فتح قناة كتابة وقراءة إلى قاعدة البيانات</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> transaction </span><span class="pun">=</span><span class="pln"> db</span><span class="pun">.</span><span class="pln">transaction</span><span class="pun">([</span><span class="str">"notes_os"</span><span class="pun">],</span><span class="pln"> </span><span class="str">"readwrite"</span><span class="pun">);</span><span class="pln">

 </span><span class="com">// استدعاء مخزن كائنات موجود بالفعل في قاعدة البيانات</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> objectStore </span><span class="pun">=</span><span class="pln"> transaction</span><span class="pun">.</span><span class="pln">objectStore</span><span class="pun">(</span><span class="str">"notes_os"</span><span class="pun">);</span><span class="pln">

 </span><span class="com">//إلى مخزن الكائنات newItem إنشاء طلب ﻹضافة الكائن</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> addRequest </span><span class="pun">=</span><span class="pln"> objectStore</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="pln">newItem</span><span class="pun">);</span><span class="pln">

 addRequest</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"success"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// مسح النموذج استعدادًا ﻹضافة سجل آخر</span><span class="pln">
    titleInput</span><span class="pun">.</span><span class="pln">value </span><span class="pun">=</span><span class="pln"> </span><span class="str">""</span><span class="pun">;</span><span class="pln">
    bodyInput</span><span class="pun">.</span><span class="pln">value </span><span class="pun">=</span><span class="pln"> </span><span class="str">""</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">});</span><span class="pln">

 </span><span class="com">// اﻹبلاغ عن نجاح العملية على قاعدة البيانات عند اكتمالها</span><span class="pln">
 transaction</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"complete"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Transaction completed: database modification finished."</span><span class="pun">);</span><span class="pln">

 </span><span class="com">//مجددًا displayData تحديث عرض البيانات بعد إضافة السجل الجديد باستدعاء الدالة</span><span class="pln">
    displayData</span><span class="pun">();</span><span class="pln">
 </span><span class="pun">});</span><span class="pln">

 transaction</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"error"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Transaction not opened due to error"</span><span class="pun">),</span><span class="pln">
 </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لنحاول تفسير هذه الشيفرة كونها معقدة نوعًا ما:
</p>

<ul>
	<li>
		تنفيذ الدالة <code>()Event.preventDefault</code> على كائن الحدث لإيقاف إرسال بيانات النموذج بالطريقة النمطية (لأنها تدفع إلى إعادة تحديث الصفحة وإفساد التأثير المطلوب).
	</li>
	<li>
		إنشاء كائن يمثل السجل الذي نريد إدخاله إلى قاعدة البيانات ونشره بالقيم التي نحصل عليها من عناصر اﻹدخال. ولاحظ أنه لا حاجة لتزويد السجل بقيمة للمعرّف <code>id</code> كما ذكرنا سابقًا بل سيضاف تلقائيًا.
	</li>
	<li>
		فتح عملية القراءة والكتابة <code>readwrite</code> إلى مخزن الكائنات <code>notes_os</code> باستخدام التابع <code>()IDBDatabase.transaction</code>. يساعد كائن عمليات قاعدة البيانات في الوصول إلى مخزن الكائن لتنفيذ العملية المطلوبة عليه مثل إضافة سجل جديد.
	</li>
	<li>
		الوصول إلى مخزن الكائن باستخدام التابع <code>()IDBTransaction.objectStore</code> وتخزين النتيجة في المتغير <code>objectStore</code>.
	</li>
	<li>
		إضافة السجل الجديد إلى قاعدة البيانات باستخدام التابع <code>()IDBObjectStore.add</code> الذي يُنشئ كائن طلب بنفس اﻹسلوب الذي رأيناه سابقًا.
	</li>
	<li>
		إضافة مجموعة من معالجات اﻷحداث إلى الكائن <code>request</code> والكائن <code>transaction</code> لتنفيذ الشيفرة المطلوبة عند النقاط المطلوبة خلال دورة حياة التطبيق. وبمجرد نجاح الطلب، نمسح حقول اﻹدخال في النموذج لتحضيرها لعملية إدخال أخرى. وعند اكتمال العملية، ننفذ الدالة <code>()displayData</code> مجددًا لتحديث ما يُعرض من ملاحظات في الصفحة.
	</li>
</ul>

<h3 id="-4">
	عرض البيانات
</h3>

<p>
	أشرنا إلى الدالة <code>()displayData</code> مرتين في تطبيقنا، لهذا من اﻷفضل اﻵن تعريف هذه الدالة. أضف اﻷسطر البرمجية التالية بعد تعريف الدالة السابقة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8885_23" style=""><span class="com">// displayData() تعريف الدالة</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> displayData</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="com">// نمحي محتوى عناصر القائمة في كل مرة نحدّث فيها ما يُعرض</span><span class="pln">
 </span><span class="com">// وإلا ستتكرر العناصر في كل مرة نجري فيها تحديثًا</span><span class="pln">
 </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">list</span><span class="pun">.</span><span class="pln">firstChild</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    list</span><span class="pun">.</span><span class="pln">removeChild</span><span class="pun">(</span><span class="pln">list</span><span class="pun">.</span><span class="pln">firstChild</span><span class="pun">);</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">

 </span><span class="com">// افتح مخزن الكائنات واحصل على مؤشر يتنقل بين عناصره المختلفة</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> objectStore </span><span class="pun">=</span><span class="pln"> db</span><span class="pun">.</span><span class="pln">transaction</span><span class="pun">(</span><span class="str">"notes_os"</span><span class="pun">).</span><span class="pln">objectStore</span><span class="pun">(</span><span class="str">"notes_os"</span><span class="pun">);</span><span class="pln">
 objectStore</span><span class="pun">.</span><span class="pln">openCursor</span><span class="pun">().</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"success"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// احصل على مرجع إلى هذا المؤشر</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> cursor </span><span class="pun">=</span><span class="pln"> e</span><span class="pun">.</span><span class="pln">target</span><span class="pun">.</span><span class="pln">result</span><span class="pun">;</span><span class="pln">

    </span><span class="com">// استمر في تنفيذ الشيفرة طالما هناك عناصر يمكن التنقل بينها</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">cursor</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="com">//p وفقرة نصية h3 وعنوان itemlist أنشئ عنصر قائمة</span><span class="pln">
      </span><span class="com">//كي تضع ضمنها كل عنصر بيانات سيُعرض     </span><span class="pln">
     </span><span class="com">// ألحق الفقرة والعنوان بعنصر القائمة ثم ألحقه بالقائمة</span><span class="pln">

     </span><span class="kwd">const</span><span class="pln"> listItem </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">"li"</span><span class="pun">);</span><span class="pln">
     </span><span class="kwd">const</span><span class="pln"> h3 </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">"h3"</span><span class="pun">);</span><span class="pln">
     </span><span class="kwd">const</span><span class="pln"> para </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">"p"</span><span class="pun">);</span><span class="pln">

     listItem</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">h3</span><span class="pun">);</span><span class="pln">
     listItem</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">para</span><span class="pun">);</span><span class="pln">
     list</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">listItem</span><span class="pun">);</span><span class="pln">

     </span><span class="com">// ضع البيانات الموجودة في المؤشر ضمن الفقرة النصية والعنوان</span><span class="pln">
     h3</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> cursor</span><span class="pun">.</span><span class="pln">value</span><span class="pun">.</span><span class="pln">title</span><span class="pun">;</span><span class="pln">
     para</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> cursor</span><span class="pun">.</span><span class="pln">value</span><span class="pun">.</span><span class="pln">body</span><span class="pun">;</span><span class="pln">

     </span><span class="com">// خزن معرّف عنصر البيانات داخل سمة ضمن عنصر القائمة</span><span class="pln">
     </span><span class="com">//كي نعرف إلى أي عنصر تنتمي. وسيفيدنا ذلك عند حذف العناصر</span><span class="pln">

     listItem</span><span class="pun">.</span><span class="pln">setAttribute</span><span class="pun">(</span><span class="str">"data-note-id"</span><span class="pun">,</span><span class="pln"> cursor</span><span class="pun">.</span><span class="pln">value</span><span class="pun">.</span><span class="pln">id</span><span class="pun">);</span><span class="pln">

     </span><span class="com">// أنشئ زرًا وضعه ضمن كل عنصر قائمة</span><span class="pln">
     </span><span class="kwd">const</span><span class="pln"> deleteBtn </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">"button"</span><span class="pun">);</span><span class="pln">
     listItem</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">deleteBtn</span><span class="pun">);</span><span class="pln">
     deleteBtn</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Delete"</span><span class="pun">;</span><span class="pln">

     </span><span class="com">// اضبط معالج حدث يحذف عنصر القائمة عند النقر على هذا الزر</span><span class="pln">

     deleteBtn</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"click"</span><span class="pun">,</span><span class="pln"> deleteItem</span><span class="pun">);</span><span class="pln">

     </span><span class="com">// انقل المؤشر إلى العنصر التالي</span><span class="pln">
     cursor</span><span class="pun">.</span><span class="kwd">continue</span><span class="pun">();</span><span class="pln">
    </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
     </span><span class="com">//إن لم تكن هنالك أية عناصر قائمة `No notes stored` اعرض رسالة</span><span class="pln">
     </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">list</span><span class="pun">.</span><span class="pln">firstChild</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">const</span><span class="pln"> listItem </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">"li"</span><span class="pun">);</span><span class="pln">
      listItem</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="str">"No notes stored."</span><span class="pun">;</span><span class="pln">
      list</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">listItem</span><span class="pun">);</span><span class="pln">
     </span><span class="pun">}</span><span class="pln">
     </span><span class="com">// إن لم تكن هنالك أية عناصر اخرى للتنقل بينها، ابلغ عن ذلك برسالة</span><span class="pln">
     console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Notes all displayed"</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
 </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لنشرح الآن الشيفرة السابقة بشيء من التفصيل:
</p>

<ul>
	<li>
		<p>
			نمحي بداية محتوى العنصر <code>&lt;ul&gt;</code> قبل أن نملأه مجددًا بالمحتوى المُحدَّث، وإلا سينتهي بك اﻷمر إلى قائمة مليئة بالعناصر المكررة التي تُضاف عند كل تحديث.
		</p>
	</li>
	<li>
		<p>
			نتخذ مرجعًا إلى مخزن الكائن <code>notes_os</code> باستخدام التابعين <code>()IDBDatabase.transaction</code> و <code>()IDBTransaction.objectStore</code> كما فعلنا مع الدالة <code>()addData</code>، ماعدا أننا نربطهما معًا في سطر واحد هنا.
		</p>
	</li>
	<li>
		<p>
			نستخدم تاليًا التابع <code>()IDBObjectStore.openCursor</code> لطلب للحصول على مؤشر Cursor، وهي بنية تُستخدم للتنقل بين السجلات في مخزن الكائنات. كما نربط معالج حدث النجاح <code>success</code> في نهاية السطر لنجعل الشيفرة أكثر ترابطًا. وعندما يُعاد المؤشر بنجاح يُنفَّذ المعالج.
		</p>
	</li>
	<li>
		<p>
			نتخذ مرجعًا إلى المؤشر ذاته (على شكل كائن <code>IDBCursor</code>) باستخدام السطر <code>const cursor= e.target.result</code>
		</p>
	</li>
	<li>
		<p>
			نتحقق تاليًا من وجود سجل من مخزن الكائنات ضمن المؤشر (<code>{}if (cursor)</code>)، فإن كان اﻷمر كذلك، ننشئ فرعًا في شجرة DOM وننشر ضمنه بيانات السجل ومن ثم نعرضها على الصفحة ضمن العنصر <code>&lt;ul&gt;</code>. كما نضمّن الفرع زرًا يحذف عنصر القائمة الذي يضم بيانات السجل عند النقر على هذا الزر، وذلك بتنفيذ الدالة <code>()deleteItem</code> التي نتعرف عليها في الفقرة القادمة.
		</p>
	</li>
	<li>
		<p>
			في نهاية الكتلة <code>if</code> نستخدم التابع <code>()IDBCursor.continue</code> لنقل المؤشر إلى السجل التالي في المخزن وتنفيذ محتوى الكتلة <code>if</code> مجددًا إن كان هنالك سجل آخر سيعرض في الصفحة، ومن ثم يتابع المؤشر تفقد وجود سجلات أخرى.
		</p>
	</li>
	<li>
		<p>
			عند انتهاء السجلات، يعيد المؤشر القيمة <code>undefined</code> وبالتالي ستعمل الكتلة <code>else</code> هذه المرة. وتتحقق هذه الكتلة من إضافة أية ملاحظات إلى القائمة <code>&lt;ul&gt;</code>، فإن لم تُضاف أية ملاحظات، تعرض رسالة مفادها عدم وجود أية ملاحظات محفوظة.
		</p>
	</li>
</ul>

<h3 id="-5">
	حذف ملاحظة
</h3>

<p>
	أشرنا سابقا إلى أن النقر على زر الحذف الموجود إلى جوار الملاحظة المعروضة يسبب حذفها. وننفذ ذلك باستخدام الدالة <code>()deleteitem</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8885_25" style=""><span class="com">// deleteItem() تعريف الدالة</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> deleteItem</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="com">// الحصول على الملاحظة التي ينبغي حذفها على شكل عدد قبل</span><span class="pln">
 </span><span class="com">//IDB: IDB key محاولة استخدامها عن طريق الزوج</span><span class="pln">
 </span><span class="com">// والانتباه إلى أن القيم حساسة لحالة الأحرف</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> noteId </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Number</span><span class="pun">(</span><span class="pln">e</span><span class="pun">.</span><span class="pln">target</span><span class="pun">.</span><span class="pln">parentNode</span><span class="pun">.</span><span class="pln">getAttribute</span><span class="pun">(</span><span class="str">"data-note-id"</span><span class="pun">));</span><span class="pln">

 </span><span class="com">// فتح قناة العمليات مع قاعدة البيانات وحذف الملاحظة التي حصلنا</span><span class="pln">
 </span><span class="com">//على رقمها سابقًا عن طريق السمة التي خزنا فيها معرف الملاحظة</span><span class="pln">

 </span><span class="kwd">const</span><span class="pln"> transaction </span><span class="pun">=</span><span class="pln"> db</span><span class="pun">.</span><span class="pln">transaction</span><span class="pun">([</span><span class="str">"notes_os"</span><span class="pun">],</span><span class="pln"> </span><span class="str">"readwrite"</span><span class="pun">);</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> objectStore </span><span class="pun">=</span><span class="pln"> transaction</span><span class="pun">.</span><span class="pln">objectStore</span><span class="pun">(</span><span class="str">"notes_os"</span><span class="pun">);</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> deleteRequest </span><span class="pun">=</span><span class="pln"> objectStore</span><span class="pun">.</span><span class="kwd">delete</span><span class="pun">(</span><span class="pln">noteId</span><span class="pun">);</span><span class="pln">

 </span><span class="com">// اﻹبلاغ عن حذف عنصر القائمة</span><span class="pln">
 transaction</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"complete"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// حذف العنصر اﻷب للزر وهو في حالتنا عنصر القائمة ولن يُعرض بعدها</span><span class="pln">

    e</span><span class="pun">.</span><span class="pln">target</span><span class="pun">.</span><span class="pln">parentNode</span><span class="pun">.</span><span class="pln">parentNode</span><span class="pun">.</span><span class="pln">removeChild</span><span class="pun">(</span><span class="pln">e</span><span class="pun">.</span><span class="pln">target</span><span class="pun">.</span><span class="pln">parentNode</span><span class="pun">);</span><span class="pln">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(`</span><span class="typ">Note</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">noteId</span><span class="pun">}</span><span class="pln"> deleted</span><span class="pun">.`);</span><span class="pln">

    </span><span class="com">//إن لم تكن هنالك عناصر قائمة `No Notes Stored` عرض رسالة</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">list</span><span class="pun">.</span><span class="pln">firstChild</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
     </span><span class="kwd">const</span><span class="pln"> listItem </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">"li"</span><span class="pun">);</span><span class="pln">
     listItem</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="str">"No notes stored."</span><span class="pun">;</span><span class="pln">
     list</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">listItem</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
 </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span></pre>

<ul>
	<li>
		نحصل على معّرف السجل الذي نحذفه باستخدام التابع <code>Number(e.target.parentNode.getAttribute('data-note-id'))</code>، وتذكر أن معرّف السجل قد خُزِّن سابقًا ضمن السمة <code>data-note-id</code> لعنصر القائمة <code>&lt;li&gt;</code> عندما عُرض أول مرة. لكن لا بد من تمرير الدالة التي تعطينا قيمة السمة إلى التابع العام <code>()Number</code> لأنها من النوع النصي وما نريده هو المكافئ العددي للقيمة وإلا لن تميزها قاعدة البيانات التي تتوقع عددًا.
	</li>
	<li>
		نتخذ تاليًا مرجعًا إلى مخزن الكائن مستخدمين اﻷسلوب الذي خبرناه سابقًا ومن ثم التابع <code>()IDBObjectStore.delete</code> لحذف السجل من قاعدة البيانات بعد أن نمرر له معرف الملاحظة.
	</li>
	<li>
		عند اكتمال العملية على قاعدة البيانات، نحذف العنصر <code>&lt;li&gt;</code> من شجرة DOM ونتحقق مجددًا من خلو القائمة <code>&lt;ul&gt;</code> من العناصر.
	</li>
</ul>

<p>
	إن واجهت صعوبة في تطبيق المثال، قارن بين نسختك و<a href="https://mdn.github.io/learning-area/javascript/apis/client-side-storage/indexeddb/notes/" rel="external nofollow">النسخة المكتملة</a> كما يمكنك أيضًا الاطلاع على <a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/client-side-storage/indexeddb/notes/index.js" rel="external nofollow">الشيفرة المصدرية</a>.
</p>

<h2 id="-6">
	الخلاصة
</h2>

<p>
	تعرفنا في هذا المقال على قاعدة البيانات المدمجة في المتصفح IndexedDB وكيفية التعامل معها من خلال مثال تطبيقي يعرض أساسيات إضافة وحذف البيانات واسترجاعها.
</p>

<p>
	ترجمة -وبتصرف- للجزء الثاني من مقال: <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Client-side_storage" rel="external nofollow">Client-side storage</a>
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-%D9%85%D8%AE%D8%A7%D8%B2%D9%86-%D9%88%D9%8A%D8%A8-web-storage-r2443/" rel="">تخزين البيانات في طرف العميل: مخازن ويب Web Storage</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%AD%D9%82%D9%82-%D9%85%D9%86-%D8%B5%D8%AD%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D8%B3%D8%AA%D9%85%D8%A7%D8%B1%D8%A9-%D9%88%D9%8A%D8%A8-%D9%81%D9%8A-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r2406/" rel="">التحقق من صحة بيانات استمارة ويب في طرف العميل</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/%D9%86%D8%B8%D8%B1%D8%A9-%D8%B9%D9%84%D9%89-%D8%AA%D9%81%D8%A7%D8%B9%D9%84%D8%A7%D8%AA-%D8%A7%D9%84%D8%AE%D8%A7%D8%AF%D9%85-%D9%85%D8%B9-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-%D9%81%D9%8A-%D9%85%D9%88%D9%82%D8%B9-%D9%88%D9%8A%D8%A8-%D8%AF%D9%8A%D9%86%D8%A7%D9%85%D9%8A%D9%83%D9%8A-r782/" rel="">نظرة على تفاعلات الخادم مع العميل في موقع ويب ديناميكي</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%84%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%B5%D9%88%D8%AA-%D9%88%D8%A7%D9%84%D9%81%D9%8A%D8%AF%D9%8A%D9%88-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2438/" rel="">الواجهات البرمجية للتعامل مع الصوت والفيديو في جافا سكريبت</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2454</guid><pubDate>Sun, 17 Nov 2024 15:00:00 +0000</pubDate></item><item><title>&#x62A;&#x62E;&#x632;&#x64A;&#x646; &#x627;&#x644;&#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; &#x641;&#x64A; &#x637;&#x631;&#x641; &#x627;&#x644;&#x639;&#x645;&#x64A;&#x644;: &#x645;&#x62E;&#x627;&#x632;&#x646; &#x648;&#x64A;&#x628; Web Storage</title><link>https://academy.hsoub.com/programming/javascript/%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-%D9%85%D8%AE%D8%A7%D8%B2%D9%86-%D9%88%D9%8A%D8%A8-web-storage-r2443/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_11/-------Web-Storage.png.74b84ead44487119247fd8e678096383.png" /></p>
<p>
	تقدم المتصفحات الحديثة مجموعة من التقنيات المختلفة التي تسمح بتخزين بيانات تتعلق بمواقع الويب ثم استرجاعها عند الضرورة، مما يسمح لنا بالحفاظ على البيانات لفترة أطول أو تخزينها للعمل دون اتصال باﻹنترنت وغير ذلك. لهذا سنناقش في مقالنا أبسط اﻷساسيات المتعلقة بهذا اﻷمر وكيفية عملها.
</p>

<p>
	ننصحك قبل المتابعة في قراءة هذه المقالات أن:
</p>

<ul>
	<li>
		تكون ملمًا بلغتي <a href="HTTPS://wiki.hsoub.com/HTML" rel="external">HTML</a> و <a href="HTTPS://wiki.hsoub.com/CSS" rel="external">CSS</a>.
	</li>
	<li>
		تكون ملمًا بلغة <a href="HTTPS://wiki.hsoub.com/JavaScript" rel="external">جافا سكريبت</a>.
	</li>
	<li>
		تطلع على سلاسل المقالات السابقة التي ناقشت <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D8%B1%D9%91%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D9%85%D9%86-%D9%85%D9%86%D8%B8%D9%88%D8%B1-%D8%B9%D8%A7%D9%85-r2266/" rel="">أساسيات جافا سكريبت</a> و<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-prototype-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2310/" rel="">الكائنات في جافا سكريبت</a>.
	</li>
	<li>
		تطلع على <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-web-apis-r2355/" rel="">أساسيات الواجهات البرمجية في طرف العميل</a>.
	</li>
</ul>

<h2 id="">
	ماذا يعني تخزين البيانات في طرف العميل؟
</h2>

<p>
	تحدثنا في مقالات مختلفة عن الفرق بين <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D9%87%D9%8A-%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8%D8%9F-r2094/" rel="">مواقع الويب الساكنة والدينياميكية</a>، لكن لا بد من اﻹشارة إلى أن معظم مواقع الويب الحديثة ديناميكية، فهي تخزن البيانات في الخادم مستخدمة نوعًا من قواعد البيانات (تخزين في طرف الخادم)، ومن ثم تنفّذ شيفرة في طرف الخادم لاستعادتها ووضعها ضمن قوالب صفحات ساكنة، لتقدّم النتيجة بعدها إلى العميل على شكل صفحاتHTML يعرضها المتصفح.
</p>

<p>
	ويعمل التخزين في طرف العميل وفق اﻷسلوب ذاته، لكن له استخدامات خاصة. ولتنفيذ هذه العمليات نحتاج إلى واجهات جافا سكريبت التي تسمح لنا بتخزين البيانات على جهاز العميل واستعادتها عند الحاجة. وتُخّزن البيانات في طرف العميل لاستخدامات محددة مثل:
</p>

<ul>
	<li>
		إضفاء خصوصية للمستخدم في الموقع (مثل عرض خيارات يفضلها المستخدم أو اختيار اللون أو حجم الخط).
	</li>
	<li>
		تخزين النشاطات السابقة للمستخدم مثل تخزين محتوى قائمة مشتريات من جلسة سابقة أو تذكر تسجيل الدخول السابق).
	</li>
	<li>
		تخزين البيانات واﻷصول محليًا لتسريع تحميل الموقع (مع احتمال انخفاض كلفة التصفح) وإمكانية التصفح دون الاتصال بالانترنت.
	</li>
	<li>
		تخزين صفحات الويب التي تولدها تطبيقات الويب ديناميكيًا محليًا لاستخدامها دون اتصال بالانترنت.
	</li>
</ul>

<p>
	تُستخدم الطريقتان السابقتان في التخزين معًا عادةً، فقد تحمّل مثلًا ملف موسيقى (يُستخدم مع لعبة ويب أو مشغّل موسيقى) ومن ثم تخزّنه في قاعدة بيانات طرف العميل ومن ثم تشغيله عند الحاجة. وهكذا يمكن للمستخدم تحميل الملف لمرة واحدة، وعند الزيارات اللاحقة للموقع يستخرج الملف من قاعدة بياناته المحلية مما يسرع العملية ويقلل تكلفة التصفح.
</p>

<p>
	<strong>ملاحظة</strong>: هناك حد لكمية البيانات التي يمكن تخزينها في طرف العميل عبر الواجهات البرمجية (منفصلة أو مجتمعة) ويختلف هذا الحد وفقًا للمتصفح، وقد يعتمد على اﻹعدادات التي يضبطها المستخدم.
</p>

<h3 id="cookies">
	استخدام الطريقة التقليدية: ملفات تعريف الارتباط Cookies
</h3>

<p>
	تُعد تقنية تخزين البيانات في طرف العميل تقنية قديمة، فقد استخدمت المواقع <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D8%AA%D8%B9%D8%B1%D9%8A%D9%81-%D8%A7%D9%84%D8%A7%D8%B1%D8%AA%D8%A8%D8%A7%D8%B7-%D9%88%D8%B6%D8%A8%D8%B7%D9%87%D8%A7-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1330/" rel="">ملفات تعريف الارتباط cookies</a> منذ البدايات الأولى للويب، وذلك لتخزين البيانات وإعطاء طابع شخصي للموقع. وقد كانت أولى أشكال تخزين البيانات في طرف العميل.
</p>

<p>
	أما حاليًا، فقد ظهرت تقنيات أفضل وأحدث لتخزين البيانات في طرف العميل، لذلك لن نتحدث عن استخدام ملفات تعريف اﻹرتباط في مقالنا الحالي. ولا يعني ذلك بالطبع أن ملفات تعريف الارتباط عديمة الفائدة في عالم الويب المعاصر، إذ لا تزال شائعة الاستخدام في تخزين البيانات المتعلقة بمعلومات المستخدم الشخصية وحالته مثل معرفات الجلسة session IDs ومفاتيح الوصول المشفرة access token.
</p>

<h2 id="indexeddb">
	التقنية الجديدة: مخازن ويب وقاعدة البيانات IndexedDB
</h2>

<p>
	من الميزات السهلة للتقنيتين اللتين يشير إليهما العنوان نجد:
</p>

<ul>
	<li>
		اﻵلية التي تقدمها واجهة مخازن ويب البرمجية Web Storage <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> في تخزين واسترجاع عناصر البيانات صغيرة الحجم والمكونة من اسم وقيمة موافقة. ولهذا اﻷمر أهميته عندما تحتاج إلى تخزين بيانات بسيطة مثل اسم المستخدم وتاريخ تسجيل الدخول إلى موقع الويب واللون الذي يفضله للخلفية وهكذا.
	</li>
	<li>
		قاعدة البيانات المتكاملة التي تقدمها الواجهة البرمجية IndexedDB <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> للمتصفح لتخزين البيانات الأكثر تعقيدًا. يمكن استخدام هذه القاعدة مثلًا في تخزين بيانات مجموعة كاملة من سجلات المستخدمين وحتى أنواع معقدة من البيانات مثل ملفات الصوت والفيديو.
	</li>
</ul>

<h3 id="cache">
	الواجهة البرمجية <code>Cache</code>
</h3>

<p>
	صُممت هذه الواجهة لتحزين الاستجابات الناتجة عن طلبات HTTP محددة، وهي مفيدة خصوصًا في أمور مثل تخزين أصول موقع ويب محليًا ليتمكن الموقع من استخدامها باستمرار دون اتصال مع شبكة اﻹنترنت. تُستخدم واجهة التخزين المؤقت <code>cache</code> عادةً بمرافقة <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%B9%D8%A7%D9%85%D9%84-%D8%A7%D9%84%D8%AE%D8%AF%D9%85%D8%A9-service-worker-%D9%84%D8%AA%D8%B3%D8%B1%D9%8A%D8%B9-%D9%85%D9%88%D9%82%D8%B9%D9%83-r1542/" rel="">واجهة عمال الخدمة Service Worker <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a> على الرغم من عدم الحاجة إلى ذلك فعليًا.
</p>

<p>
	ويُعد استخدام واجهة التخزين المؤقت مع واجهة عمال الخدمة موضوعًا متقدمًا لن نغطيه في سلسلة مقالاتنا بالتفصيل، مع ذلك، سنعرض في آخر مقال من هذه السلسلة مثالًا عنها.
</p>

<h2 id="webstorage-1">
	الواجهة <code>Web Storage</code> وتخزين بيانات بسيطة
</h2>

<p>
	من السهل جدًا استخدام هذه الواجهة، إذ تخزن البيانات البسيطة على شكل أزواج مكونة من اسم name وقيمة value (محدودة بأنواع مخصصة مثل النصوص واﻷعداد وغيرها)، ومن ثم استرجاع تلك القيم عند الحاجة.
</p>

<h3 id="-1">
	الصياغة القواعدية الأساسية
</h3>

<p>
	لنلق نظرة على ذلك:
</p>

<ol>
	<li>
		انتقل بداية إلى <a href="https://mdn.github.io/learning-area/javascript/apis/client-side-storage/web-storage/index.html" rel="external nofollow">القالب</a> الموجود على جيت-هاب وافتحه في نافذة جديدة.
	</li>
	<li>
		افتح طرفية جافا سكريبت في المتصفح.
	</li>
	<li>
		توضع مخازن ويب ضمن بُنى تشبه الكائنات في المتصفح هي <code>sessionStorge</code> و <code>localStorage</code>. تبقى البيانات المخزنة في البنية اﻷولى طالما أن المتصفح يعمل (تُحذف هذه البيانات عند إغلاق المتصفح)، بينما تبقى البيانات في البنية الثانية مقيمة في الذاكرة حتى بعد إغلاق المتصفح. سنستخدم في مقالنا البنية الثانية لأنها أكثر فائدة عمومًا. إذ يسمح التابع <code>()Strorage.setItem</code> بتخزين البيانات في البنية Storge، وله معاملان: اﻷول هو اسم العنصر، والثاني هو القيمة. جرّب كتابة ما يلي في طرفية جافا سكريبت:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_143_9" style=""><span class="pln">localStorage</span><span class="pun">.</span><span class="pln">setItem</span><span class="pun">(</span><span class="str">"name"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Chris"</span><span class="pun">);</span></pre>

<ol start="4">
	<li>
		يأخذ التابع <code>()Storage.getItem</code> معاملًا واحدًا يمثل عنصر البيانات الذي تريد استرجاع قيمته. جرّب اﻵن الشيفرة التالية:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_143_12" style=""><span class="kwd">let</span><span class="pln"> myName </span><span class="pun">=</span><span class="pln"> localStorage</span><span class="pun">.</span><span class="pln">getItem</span><span class="pun">(</span><span class="str">"name"</span><span class="pun">);</span><span class="pln">
myName</span><span class="pun">;</span></pre>

<p>
	سترى عند كتابتك الشيفرة السابقة كيف سيضم المتغير <code>myName</code> قيمة عنصر البيانات <code>name</code>.
</p>

<ol start="5">
	<li>
		يأخذ التابع <code>()removeItem</code> معاملًا واحدًا هو اسم عنصر البيانات التي تريد إزالته ومن ثم يزيله من مخزن ويب. جرّب الشيفرة التالية في طرفية جافا سكريبت:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_143_14" style=""><span class="pln">localStorage</span><span class="pun">.</span><span class="pln">removeItem</span><span class="pun">(</span><span class="str">"name"</span><span class="pun">);</span><span class="pln">
myName </span><span class="pun">=</span><span class="pln"> localStorage</span><span class="pun">.</span><span class="pln">getItem</span><span class="pun">(</span><span class="str">"name"</span><span class="pun">);</span><span class="pln">
myName</span><span class="pun">;</span></pre>

<p>
	من المفترض أن يعيد تنفيذ السطر الثالث القيمة <code>null</code> للعنصر <code>name</code> لأنه لم يعد موجودًا في مخزن ويب.
</p>

<h3 id="-2">
	البيانات المقيمة في الذاكرة
</h3>

<p>
	من الميزات المهمة لمخازن ويب أن البيانات تبقى موجودة في الفترة التي تُحمّل فيها الصفحات وحتى بعد إغلاق المتصفح عند استخدام <code>local Storage</code>، لنلق نظرة على هذا اﻷمر:
</p>

<ol>
	<li>
		افتح مجددًا قالب مخازن ويب السابق لكن في متصفح يختلف عن المتصفح الذي تقرأ فيه هذا المقال.
	</li>
	<li>
		اكتب الشيفرة التالية في طرفية جافا سكريبت لهذا المتصفح:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_143_16" style=""><span class="pln">localStorage</span><span class="pun">.</span><span class="pln">setItem</span><span class="pun">(</span><span class="str">"name"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Chris"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">let</span><span class="pln"> myName </span><span class="pun">=</span><span class="pln"> localStorage</span><span class="pun">.</span><span class="pln">getItem</span><span class="pun">(</span><span class="str">"name"</span><span class="pun">);</span><span class="pln">
myName</span><span class="pun">;</span></pre>

<p>
	من المفترض أن ترى قيمة عنصر البيانات <code>name</code>.
</p>

<ol start="3">
	<li>
		اغلق اﻵن المتصفح ثم افتحه مجددًا.
	</li>
	<li>
		اكتب الشيفرة التالية في طرفية جافا سكريبت:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_143_18" style=""><span class="kwd">let</span><span class="pln"> myName </span><span class="pun">=</span><span class="pln"> localStorage</span><span class="pun">.</span><span class="pln">getItem</span><span class="pun">(</span><span class="str">"name"</span><span class="pun">);</span><span class="pln">
myName</span><span class="pun">;</span></pre>

<p>
	سترى أن قيمة عنصر البيانات لا تزال متوفرة على الرغم من إغلاق المتصفح وفتحه مجددًا.
</p>

<h3 id="-3">
	مخزن منفصل لكل نطاق
</h3>

<p>
	يُوجد مخزن بيانات منفصل لكل نطاق (لكل عنوان ويب حمّله المتصفح)، وسترى ذلك إن حمّلت موقعين وحاولت تخزين عنصر بيانات في أحدهما، فلن يكون هذا العنصر متاحًا للموقع اﻵخر. وهذا اﻷمر منطقي، فرؤية بيانات موقع من موقع آخر مصدر للكثير من الثغرات اﻷمنية.
</p>

<h3 id="-4">
	مثال على مخزن ويب بتفاصيل أكثر
</h3>

<p>
	سنبني في هذه الفقرة مثالًا نطبق فيه ما تعلمناه ويعطيك فكرة عن كيفية استخدام مخزن ويب. ندخل في هذا المثال اسمًا ثم نُحدّث الصفحة بعد ذلك لترحب بصاحب الاسم شخصيًا. وستبقى هذه الحالة خلال إعادة تحميل الصفحات أو المتصفح لأننا سنخزن الاسم في مخزن ويب.
</p>

<p>
	بإمكانك إيجاد <a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/client-side-storage/web-storage/personal-greeting.html" rel="external nofollow">نسخة عن ملف HTML المستخدم</a> على جيت-هاب، ويتضمن موقع ويب يتكون من ترويسة ومحتوى وتذييل ونموذج ﻹدخال الاسم.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="161677" href="https://academy.hsoub.com/uploads/monthly_2024_11/01_web-storage-demo.png.3db6064638dc001a7fa1bacaca0d25f9.png" rel=""><img alt="01 web storage demo" class="ipsImage ipsImage_thumbnailed" data-fileid="161677" data-unique="j37cfctqq" src="https://academy.hsoub.com/uploads/monthly_2024_11/01_web-storage-demo.thumb.png.d7aba42a363ec8b651ffd6ce1833f5c4.png"> </a>
</p>

<p>
	سنبني المثال اﻵن حتى نفهم آلية عمله:
</p>

<ol>
	<li>
		انسخ ملف المثال إلى مجلد على حاسوبك.
	</li>
	<li>
		لاحظ كيف يشير ملف HTML إلى ملف جافا سكريبت يُدعى <code>index.js</code> من خلال سطر يشبه السطر<br>
		<code>&lt;script src="index.js" defer&gt;&lt;/script&gt;</code>.<br>
		علينا إذًا إنشاء الملف <code>index.js</code> ضمن نفس المجلد الذي يضم ملف HTML وكتابة شيفرة جافا سكريبت ضمنه.
	</li>
	<li>
		نبدأ الشيفرة ببناء مراجع إلى جميع عناصر HTML التي نريد التعامل معها في مثالنا، وستكون هذه المراجع على شكل ثوابت لأننا لن نغيرها خلال دورة حياة التطبيق. أضف اﻵن الشيفرة التالية:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_143_20" style=""><span class="com">// إنشاء الثوابت المطلوبة</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> rememberDiv </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">".remember"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> forgetDiv </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">".forget"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> form </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"form"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> nameInput </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"#entername"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> submitBtn </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"#submitname"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> forgetBtn </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"#forgetname"</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> h1 </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"h1"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> personalGreeting </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">".personal-greeting"</span><span class="pun">);</span></pre>

<ol start="4">
	<li>
		علينا اﻵن كتابة مترصد أحداث بسيط لمنع النموذج من تسليم محتوياته عند النقر على زر اﻹرسال، فهذا ليس السلوك الذي نريده. أضف الشيفرة التالية تحت الشيفرة السابقة:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_143_22" style=""><span class="com">// Stop the form from submitting when a button is pressed</span><span class="pln">
form</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"submit"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> e</span><span class="pun">.</span><span class="pln">preventDefault</span><span class="pun">());</span></pre>

<ol start="5">
	<li>
		يجب إضافة الدالة التي تتعامل مع حدث النقر على الزر "Say hello"، وستجد شرحًا وافيًا ضمن تعليقات الشيفرة لكل خطوة، لكن ما تفعله الشيفرة عمومًا هو الحصول على الاسم الذي ندخله ضمن صندوق اﻹدخال النصي وتخزينه في مخزن ويب باستخدام الدالة <code>()setItem</code> ثم تنفيذ الدالة <code>()nameDisplayCheck</code> التي تعالج عملية تحديث النص المطلوب من الصفحة. أضف الشيفرة التالية أسفل الشيفرة السابقة:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_143_24" style=""><span class="com">//`Say hello` نفذ الدالة عند النقر على الزر</span><span class="pln">
submitBtn</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"click"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="com">// احفظ الاسم في مخزن ويب</span><span class="pln">
 localStorage</span><span class="pun">.</span><span class="pln">setItem</span><span class="pun">(</span><span class="str">"name"</span><span class="pun">,</span><span class="pln"> nameInput</span><span class="pun">.</span><span class="pln">value</span><span class="pun">);</span><span class="pln">
 </span><span class="com">//لعرض التحية المخصصة nameDisplayCheck استخدم الدالة</span><span class="pln">
 nameDisplayCheck</span><span class="pun">();</span><span class="pln">
</span><span class="pun">});</span></pre>

<ol start="6">
	<li>
		نحتاج إلى معالج حدث للتعامل مع النقر على الزر "Forget" الذي يظهر فقط بعد النقر على الزر "Say hello". كما نزيل في دالة معالج الحدث العنصر <code>name</code> من مخزن ويب باستخدام التابع <code>()removeItem</code> ثم ننفذ مجددًا الدالة <code>()nameDisplayCheck</code> لتحديث ما يُعرض. أضف الآن الشيفرة التالية:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_143_26" style=""><span class="com">//`Forget` نفّذ الدالة عند النقر على الزر</span><span class="pln">
forgetBtn</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"click"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="com">//إزالة الاسم المخزن في مخزن ويب</span><span class="pln">
 localStorage</span><span class="pun">.</span><span class="pln">removeItem</span><span class="pun">(</span><span class="str">"name"</span><span class="pun">);</span><span class="pln">
</span><span class="com">//لعرض التحية الأصلية وتحديث ما يُعرض nameDisplayCheck استخدم الدالة</span><span class="pln">
 nameDisplayCheck</span><span class="pun">();</span><span class="pln">
</span><span class="pun">});</span></pre>

<ol start="7">
	<li>
		نعرّف اﻵن الدالة <code>()nameDisplayCheck</code> التي نتحقق فيها فيما لو خُزِّن العنصر <code>name</code> في مخزن ويب باستخدام التابع <code>('name('localStorage.getItem</code> من خلال عبارة شرطية. فإن وُجد في المخزن، ستكون نتيجة الشرط <code>true</code> وإلا ستكون <code>false</code>. نعرض في الحالة اﻷولى رسالة الترحيب الخاصة ونعرض الجزء "forget" من النموذج ونخفي الجزء "Say hello"، أما في الحالة الثانية، سنعرض الرسالة الأصلية ونجري عكس ما فعلناه في الحالة الأولى:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_143_28" style=""><span class="com">// nameDisplayCheck() نعرّف الدالة</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> nameDisplayCheck</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="com">// نتحقق من تخزين عنصر الاسم في مخزن ويب</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">localStorage</span><span class="pun">.</span><span class="pln">getItem</span><span class="pun">(</span><span class="str">"name"</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// نعرض رسالة الترحيب المخصصة إن كان الأمر كذلك</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> name </span><span class="pun">=</span><span class="pln"> localStorage</span><span class="pun">.</span><span class="pln">getItem</span><span class="pun">(</span><span class="str">"name"</span><span class="pun">);</span><span class="pln">
    h1</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="typ">Welcome</span><span class="pun">,</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">name</span><span class="pun">}`;</span><span class="pln">
    personalGreeting</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="typ">Welcome</span><span class="pln"> to our website</span><span class="pun">,</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">name</span><span class="pun">}!</span><span class="pln"> </span><span class="typ">We</span><span class="pln"> hope you have fun </span><span class="kwd">while</span><span class="pln"> you are here</span><span class="pun">.`;</span><span class="pln">
    </span><span class="com">//`forget` من الاستمارة ونعرض الجزء `remember` نخفي الجزء</span><span class="pln">
    forgetDiv</span><span class="pun">.</span><span class="pln">style</span><span class="pun">.</span><span class="pln">display </span><span class="pun">=</span><span class="pln"> </span><span class="str">"block"</span><span class="pun">;</span><span class="pln">
    rememberDiv</span><span class="pun">.</span><span class="pln">style</span><span class="pun">.</span><span class="pln">display </span><span class="pun">=</span><span class="pln"> </span><span class="str">"none"</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// إن لم يكن الايم مخزنًانعرض الرسالة اﻷصلية</span><span class="pln">
    h1</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Welcome to our website "</span><span class="pun">;</span><span class="pln">
    personalGreeting</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln">
     </span><span class="str">"Welcome to our website. We hope you have fun while you are here."</span><span class="pun">;</span><span class="pln">
     </span><span class="com">//`remember` من الاستمارة ونعرض الجزء `forget` نخفي الجزء</span><span class="pln">
    forgetDiv</span><span class="pun">.</span><span class="pln">style</span><span class="pun">.</span><span class="pln">display </span><span class="pun">=</span><span class="pln"> </span><span class="str">"none"</span><span class="pun">;</span><span class="pln">
    rememberDiv</span><span class="pun">.</span><span class="pln">style</span><span class="pun">.</span><span class="pln">display </span><span class="pun">=</span><span class="pln"> </span><span class="str">"block"</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<ol start="8">
	<li>
		ننفذ الدالة <code>()nameDisplayCheck</code> عند اكتمال تحميل الصفحة. لأن الرسالة المخصصة لن تظهر إن لم نفعل ذلك خلال تحميل الصفحة بشكل متكرر. أضف ما يلي إلى آخر الشيفرة:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_143_30" style=""><span class="pln">nameDisplayCheck</span><span class="pun">();</span></pre>

<p>
	وهكذا يكون مثالنا قد انتهي، وبإمكان الاطلاع في أي وقت على <a href="https://mdn.github.io/learning-area/javascript/apis/client-side-storage/web-storage/personal-greeting.html" rel="external nofollow">النسخة المكتملة</a> منه على جيت-هاب.
</p>

<p>
	<strong>ملاحظة</strong>: تمنع السمة <code>defer</code> في السطر التالي من تنفيذ شيفرة جافا سكريبت حتى اكتمال تحميل الصفحة<br>
	<code>&lt;script src="index.js" defer&gt;&lt;/script&gt;</code>.
</p>

<h2 id="-5">
	الخلاصة
</h2>

<p>
	تعرفنا في هذا المقال على أساسيات تخزين البيانات في طرف العميل من خلال واجهات برمجية مخصصة مثل Web Storage <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> و IndexedDB. كما شرحنا مخازن ويب Web Storage وطريقة العمل معها من خلال مثال تطبيقي بسيط يعرض أساسيات العمل مع هذه الواجهة البرمجية.
</p>

<p>
	ترجمة -وبتصرف- للجزء الأول من مقال: <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Client-side_storage" rel="external nofollow">Client-side storage</a>
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%84%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%B5%D9%88%D8%AA-%D9%88%D8%A7%D9%84%D9%81%D9%8A%D8%AF%D9%8A%D9%88-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2438/" rel="">الواجهات البرمجية للتعامل مع الصوت والفيديو في جافا سكريبت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D9%85%D8%B4%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%B4%D8%A7%D8%A6%D8%B9%D8%A9-%D9%84%D9%84%D8%AA%D9%88%D8%A7%D9%81%D9%82-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D9%81%D9%8A-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1972/" rel="">معالجة المشاكل الشائعة للتوافق مع المتصفحات في شيفرة جافاسكربت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-fetch-%D9%81%D9%8A-javascript-r739/" rel="">الواجهة البرمجية fetch في JavaScript</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%85%D8%AD%D9%84%D9%8A%D9%8B%D8%A7-%D9%81%D9%8A-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD-%D8%B9%D8%A8%D8%B1-%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-indexeddb-r1337/" rel="">تخزين البيانات محليًا في المتصفح عبر قاعدة البيانات IndexedDB</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2443</guid><pubDate>Sat, 09 Nov 2024 15:03:01 +0000</pubDate></item><item><title>&#x627;&#x644;&#x648;&#x627;&#x62C;&#x647;&#x627;&#x62A; &#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x64A;&#x629; &#x644;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x627;&#x644;&#x635;&#x648;&#x62A; &#x648;&#x627;&#x644;&#x641;&#x64A;&#x62F;&#x64A;&#x648; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627; &#x633;&#x643;&#x631;&#x64A;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%84%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%B5%D9%88%D8%AA-%D9%88%D8%A7%D9%84%D9%81%D9%8A%D8%AF%D9%8A%D9%88-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2438/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_10/--------.png.d260a2a6914fc3ce562ee11cb83d991b.png" /></p>
<p>
	تضم لغة HTML عناصر مخصصة لتضمين الوسائط المتعددة إلى صفحاتك مثل <code>&lt;audio&gt;</code> و <code>&lt;video&gt;</code> والتي تأتي مزوّدة بواجهة برمجية مخصصة للتحكم بتشغيلها وتقديمها وتأخيرها. لهذا سنتعرف في هذا المقال على طرق تنفيذ بعض المهام الشائعة كإنشاء أدوات تحكم متخصصة باستخدام الواجهة البرمجية HTMLMediaElement التي توفر عدة ميزات للتحكم في تشغيل الصوت و الفيديو برمجيًا .
</p>

<p>
	ننصحك قبل المتابعة في قراءة هذه المقالات أن:
</p>

<ul>
	<li>
		تكون ملمًا بلغتي <a href="HTTPS://wiki.hsoub.com/HTML" rel="external">HTML</a> و <a href="HTTPS://wiki.hsoub.com/CSS" rel="external">CSS</a>.
	</li>
	<li>
		تكون ملمًا بلغة <a href="HTTPS://wiki.hsoub.com/JavaScript" rel="external">جافا سكريبت</a>.
	</li>
	<li>
		تطلع على سلاسل المقالات السابقة التي ناقشت <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D8%B1%D9%91%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D9%85%D9%86-%D9%85%D9%86%D8%B8%D9%88%D8%B1-%D8%B9%D8%A7%D9%85-r2266/" rel="">أساسيات جافا سكريبت</a> و<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-prototype-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2310/" rel="">الكائنات Prototype في جافا سكريبت</a>.
	</li>
	<li>
		تطلع على <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-web-apis-r2355/" rel="">أساسيات الواجهات البرمجية في طرف العميل</a>.
	</li>
</ul>

<h2 id="html">
	عناصر HTML الخاصة بالصوت والفيديو
</h2>

<p>
	يسمح العنصران <code>&lt;video&gt;</code> و <code>&lt;audio&gt;</code> <a href="https://academy.hsoub.com/programming/html/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D9%85%D8%AD%D8%AA%D9%88%D9%89-%D8%B3%D9%85%D8%B9%D9%8A-%D9%88%D9%85%D8%B1%D8%A6%D9%8A-%D9%81%D9%8A-%D8%B5%D9%81%D8%AD%D8%A9-html-r1825/" rel="">بإدراج مقاطع الصوت و الفيديو في صفحات الويب</a>، وستبدو الطريقة النمطية ﻹنجاز اﻷمر كالتالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9427_11" style=""><span class="tag">&lt;video</span><span class="pln"> </span><span class="atn">controls</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;source</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"rabbit320.mp4"</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"video/mp4"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;source</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"rabbit320.webm"</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"video/webm"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;p&gt;</span><span class="pln">
    Your browser doesn't support HTML video. Here is a
    </span><span class="tag">&lt;a</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"rabbit320.mp4"</span><span class="tag">&gt;</span><span class="pln">link to the video</span><span class="tag">&lt;/a&gt;</span><span class="pln"> instead.
  </span><span class="tag">&lt;/p&gt;</span><span class="pln">
</span><span class="tag">&lt;/video&gt;</span></pre>

<p>
	تعرض الشيفرة السابقة مشغل فيديو في صفحتك كالتالي:
</p>

<p>
	<iframe height="380" loading="lazy" src="https://mdn.github.io/learning-area/html/multimedia-and-embedding/video-and-audio-content/multiple-video-formats.html" width="100%"></iframe>
</p>

<p>
	وأكثر ما يثير الاهتمام في الشيفرة السابقة هي السمة <code>controls</code> التي تعرض أدوات التحكم الافتراضية مع مشغل الفيديو، وإن لم تستخدم هذه السمة فلن ترى عناصر التحكم على المشغل:
</p>

<p>
	<iframe height="380" loading="lazy" src="https://mdn.github.io/learning-area/html/multimedia-and-embedding/video-and-audio-content/multiple-video-formats-no-controls.html" width="100%"></iframe>
</p>

<p>
	لهذه اﻷدوات إيجابيات، لكن من أبرز مشكلاتها هي اختلافها من متصفح إلى آخر وهذا أمر مربك إن حاولت دعم عدة متصفحات في شيفرتك. ومن المشكلات الكبيرة أيضًا أن أدوات التحكم اﻷصلية في معظم المتصفحات لا تدعم التحكم من خلال لوحة المفاتيح. ويمكن حل كلتا المشكلتين السابقتين بإخفاء أدوات التحكم الأصلية (عن طريق إزالة السمة <code>controls</code>) وبرمجة أدوات تحكم خاصة بك باستخدام HTML و CSS وجافا سكريبت. وسنلقي نظرة في اﻷقسام التالية على اﻷدوات البسيطة المتاحة لهذا الغرض.
</p>

<h2 id="htmlmediaelement">
	الواجهة البرمجية HTMLMediaElement
</h2>

<p>
	تزّودك بعض مواصفات الواجهة البرمجية HTMLMediaElement بميزات تسمح لك بالتحكم في مشغلات الصوت و الفيديو برمجيًا مثل التوابع <code>()HTMLMediaElement.play</code> و <code>()HTMLMediaElement.pause</code> وغيرها. وهاتان الواجهتان متاحتان للاستخدام مع العنصرين <code>&lt;audio&gt;</code> و<code>&lt;video&gt;</code> فهما متطابقان من ناحية العمل. لهذا سنوضح طريقة استخدام هذه الواجهات من خلال المثال التالي:
</p>

<p>
	يبدو مثالنا عندما يكتمل مشابهًا للتالي:
</p>

<p>
	<iframe height="360" loading="lazy" src="https://mdn.github.io/learning-area/javascript/apis/video-audio/finished/" width="100%"></iframe>
</p>

<h3 id="-1">
	نقطة الانطلاق
</h3>

<p>
	حتى نبدأ العمل، عليك تنزيل <a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/video-audio/start/media-player-start.zip" rel="external nofollow">الملف المضغوط</a> الخاص بالمثال ثم تستخرج محتوياته في مجلد جديد على حاسوبك. أما إن حمّلت <a href="https://github.com/mdn/learning-area" rel="external nofollow">مستودع اﻷمثلة بأكمله</a>، ستجد المثال في المسار <code>javascript/apis/video-audio/start/</code>.
</p>

<p>
	إن حمّلت المثال على متصفحك سترى مشغل فيديو HTML نمطي مع أدوات التحكم الافتراضية.
</p>

<h4 id="html-1">
	التعرف على ملف HTML
</h4>

<p>
	عندما تفتح الملف index.html سترى عددًا من العناصر، وستلاحظ أن معظم الشيفرة تدور حول مشغل الفيديو وأدوات التحكم الخاصة به:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9427_13" style=""><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"player"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;video</span><span class="pln"> </span><span class="atn">controls</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;source</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"video/sintel-short.mp4"</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"video/mp4"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
    </span><span class="tag">&lt;source</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"video/sintel-short.webm"</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"video/webm"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
    </span><span class="com">&lt;!-- fallback content here --&gt;</span><span class="pln">
  </span><span class="tag">&lt;/video&gt;</span><span class="pln">
  </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"controls"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"play"</span><span class="pln"> </span><span class="atn">data-icon</span><span class="pun">=</span><span class="atv">"P"</span><span class="pln"> </span><span class="atn">aria-label</span><span class="pun">=</span><span class="atv">"play pause toggle"</span><span class="tag">&gt;&lt;/button&gt;</span><span class="pln">
    </span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"stop"</span><span class="pln"> </span><span class="atn">data-icon</span><span class="pun">=</span><span class="atv">"S"</span><span class="pln"> </span><span class="atn">aria-label</span><span class="pun">=</span><span class="atv">"stop"</span><span class="tag">&gt;&lt;/button&gt;</span><span class="pln">
    </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"timer"</span><span class="tag">&gt;</span><span class="pln">
      </span><span class="tag">&lt;div&gt;&lt;/div&gt;</span><span class="pln">
      </span><span class="tag">&lt;span</span><span class="pln"> </span><span class="atn">aria-label</span><span class="pun">=</span><span class="atv">"timer"</span><span class="tag">&gt;</span><span class="pln">00:00</span><span class="tag">&lt;/span&gt;</span><span class="pln">
    </span><span class="tag">&lt;/div&gt;</span><span class="pln">
    </span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"rwd"</span><span class="pln"> </span><span class="atn">data-icon</span><span class="pun">=</span><span class="atv">"B"</span><span class="pln"> </span><span class="atn">aria-label</span><span class="pun">=</span><span class="atv">"rewind"</span><span class="tag">&gt;&lt;/button&gt;</span><span class="pln">
    </span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"fwd"</span><span class="pln"> </span><span class="atn">data-icon</span><span class="pun">=</span><span class="atv">"F"</span><span class="pln"> </span><span class="atn">aria-label</span><span class="pun">=</span><span class="atv">"fast forward"</span><span class="tag">&gt;&lt;/button&gt;</span><span class="pln">
  </span><span class="tag">&lt;/div&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span></pre>

<ul>
	<li>
		وضعنا المشغل بأكمله داخل العنصر <code>&lt;div&gt;</code> حتى يُنسّق بالكامل عند الحاجة.
	</li>
	<li>
		يحتوي العنصر <code>&lt;video&gt;</code> على عنصرين من النوع <code>&lt;source&gt;</code> كي نتمكن من تحميل تنسيقات مختلفة لمقطع الفيديو وفقًا للمتصفح الذي نستخدمه.
	</li>
	<li>
		وربما تكون أدوات تحكم HTML هي اﻷكثر أهمية هنا:
	</li>
	<li>
		لدينا أربعة أزرار <code>&lt;button&gt;</code> لتشغيل وإيقاف العرض مؤقتًا واﻹطفاء والتقديم للأمام والعودة للخلف.
	</li>
	<li>
		خصصنا لكل زر سمات هي اسم صنف التنسيق <code>class</code> و <code>data-icon</code> لتحديد اﻷيقونة التي تُعرض على الزر (سنرى كيف ننفذ ذلك لاحقًا) و <code>aria-label</code> لتقديم وصف مفهوم عن عمل كل زر، وذلك لأننا لا نقدم عنوانًا مقروءًا ضمن وسم العنصر. ويُقرأ محتوى السمة <code>aria-label</code> من قبل قارئات الشاشة عندما ينتقل التركيز إلى العناصر التي تمتلك هذه السمة.
	</li>
	<li>
		تضم الصفحة أيضًا مؤقتًا ضمن عنصر <code>&lt;div&gt;</code> يعرض الوقت المنقضي من مقطع الفيديو، ولتحسين تجربة المستخدم، زودنا المثال بآليتين لتحديد الوقت المنقضي: الأولى ضمن عنصر <code>&lt;span&gt;</code> يعطي الوقت المنقضي بالدقائق والثواني، والثانية ضمن عنصر <code>&lt;div&gt;</code> يضم شريط أفقي يزداد طوله عندما يتقدم عرض الفيديو. ولكي تأخذ فكرة عما ستكونه الصفحة بشكلها الكامل <a href="https://mdn.github.io/learning-area/javascript/apis/video-audio/finished/" rel="external nofollow">ألق نظرة هنا.</a>.
	</li>
</ul>

<h4 id="css">
	التعرف على ملف CSS
</h4>

<p>
	افتح اﻵن ملف CSS وألقِ نظرة عليه. لا يبدو هذا الملف معقدًا، لكننا سنشير إلى النقاط المهمة فيه. لاحظ بداية القاعدة <code>controls.</code>:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_9427_16" style=""><span class="pun">.</span><span class="pln">controls </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">visibility</span><span class="pun">:</span><span class="pln"> hidden</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">opacity</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0.5</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">400px</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">border-radius</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10px</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">position</span><span class="pun">:</span><span class="pln"> absolute</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">bottom</span><span class="pun">:</span><span class="pln"> </span><span class="lit">20px</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">left</span><span class="pun">:</span><span class="pln"> </span><span class="lit">50%</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">margin-left</span><span class="pun">:</span><span class="pln"> </span><span class="pun">-</span><span class="lit">200px</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">background-color</span><span class="pun">:</span><span class="pln"> black</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">box-shadow</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3px</span><span class="pln"> </span><span class="lit">3px</span><span class="pln"> </span><span class="lit">5px</span><span class="pln"> black</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">transition</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1s</span><span class="pln"> all</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">display</span><span class="pun">:</span><span class="pln"> flex</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="kwd">player</span><span class="pun">:</span><span class="pln">hover </span><span class="pun">.</span><span class="pln">controls</span><span class="pun">,</span><span class="pln">
</span><span class="pun">.</span><span class="kwd">player</span><span class="pun">:</span><span class="pln">focus-within </span><span class="pun">.</span><span class="pln">controls </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">opacity</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<ul>
	<li>
		بدأنا بالخاصية <code>visibility</code> لمجموعة أدوات التحكم المخصصة وضبطناها على <code>hidden</code>، لكننا سنضبطها لاحقًا عبر جافا سكريبت لتكون <code>visible</code> ونزيل السمة <code>controls</code> من العنصر <code>&lt;video&gt;</code>. وذلك كي يبقى المستخدم قادرًا على تشغيل الفيديو باستخدام أدوات التحكم الافتراضية في حال فشل تحميل شيفرة جافا سكريبت لسبب ما.
	</li>
	<li>
		منحنا أدوات التحكم قتامة افتراضية <code>opacity</code> قيمتها 0.5، كي لا تشتت الانتباه عند عرض الفيديو، لكن عندما تمرر الفأرة فوق المشغّل أو تمنحه تركيز الدخل ستكون اﻷدوات كاملة القتامة.
	</li>
	<li>
		نرتب اﻷزرار ضمن شريط التحكم باستخدام <a href="https://academy.hsoub.com/programming/css/%D8%AA%D8%AE%D8%B7%D9%8A%D8%B7-%D8%A7%D9%84%D8%B5%D9%86%D8%AF%D9%88%D9%82-%D8%A7%D9%84%D9%85%D8%B1%D9%86-flexbox-%D9%81%D9%8A-%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r2279/" rel="">تخطيط الصندوق المرن display: flex </a>لتسهيل ضبط مواقعها.
	</li>
</ul>

<p>
	لنلق نظرة تاليًا على أيقونات اﻷزرار:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_9427_20" style=""><span class="pun">@</span><span class="pln">font-face </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">font-family</span><span class="pun">:</span><span class="pln"> </span><span class="str">"HeydingsControlsRegular"</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">src</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">url</span><span class="pun">(</span><span class="str">"fonts/heydings_controls-webfont.eot"</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">src</span><span class="pun">:</span><span class="pln">
    </span><span class="kwd">url</span><span class="pun">(</span><span class="str">"fonts/heydings_controls-webfont.eot?#iefix"</span><span class="pun">)</span><span class="pln"> format</span><span class="pun">(</span><span class="str">"embedded-opentype"</span><span class="pun">),</span><span class="pln">
    </span><span class="kwd">url</span><span class="pun">(</span><span class="str">"fonts/heydings_controls-webfont.woff"</span><span class="pun">)</span><span class="pln"> format</span><span class="pun">(</span><span class="str">"woff"</span><span class="pun">),</span><span class="pln">
    </span><span class="kwd">url</span><span class="pun">(</span><span class="str">"fonts/heydings_controls-webfont.ttf"</span><span class="pun">)</span><span class="pln"> format</span><span class="pun">(</span><span class="str">"truetype"</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">font-weight</span><span class="pun">:</span><span class="pln"> normal</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">font-style</span><span class="pun">:</span><span class="pln"> normal</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">button</span><span class="pun">:</span><span class="pln">before </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">font-family</span><span class="pun">:</span><span class="pln"> HeydingsControlsRegular</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">font-size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">20px</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">position</span><span class="pun">:</span><span class="pln"> relative</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">content</span><span class="pun">:</span><span class="pln"> attr</span><span class="pun">(</span><span class="pln">data-icon</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#aaa</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">text-shadow</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1px</span><span class="pln"> </span><span class="lit">1px</span><span class="pln"> </span><span class="lit">0px</span><span class="pln"> black</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	استخدمنا بداية في أعلى ملف <a href="https://academy.hsoub.com/programming/css/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-css/" rel="">CSS</a> الكتلة <code>font-face@</code> لاستيراد خط ويب مخصص، وهذا الخط هو عبارة عن أيقونات بدلًا من الحرف اﻷبجدية وتستخدم لعرض أيقونات مختلفة يشيع استخدامها في التطبيقات.
</p>

<p>
	نولد بعد ذلك محتوىً خاصًا لعرض اﻷيقونات على كل زر:
</p>

<ul>
	<li>
		نستخدم المحدد <code>before::</code> لعرض المحتوى قبل كل زر <code>&lt;button&gt;</code>.
	</li>
	<li>
		نستخدم الخاصية <code>content</code> لضبط المحتوى الذي يعرض في كل حالة ليكون نفسه محتوى السمة <code>data-icon</code>. ففي حالة زر التشغيل مثلًا، سيكون محتوى السمة <code>data-icon</code> هو المحرف <code>P</code> (بشكله الكبير).
	</li>
	<li>
		نطبق خط ويب السابق على اﻷزرار باستخدام الخاصية <code>font-family</code>، وسيكون الحرف <code>P</code> في هذا الخط عمليًا أيقونة التشغيل، وهكذا ستظهر على زر التشغيل أيقونة التشغيل.
	</li>
</ul>

<p>
	إن الخطوط التي تعرض أيقونات جميلة ومفيدة ﻷسباب عديدة منها تقليل عدد طلبات HTTP لانك لن تحتاج إلى تحميل تلك اﻷيقونات على شكل ملفات صور، إضافة إلى إمكانية تكبير وتصغير اﻷيقونات بدقة وكذلك إمكانية استخدام خاصيات نصية لتنسيق تلك اﻷيقونات مثل <code>color</code> و <code>text-shadow</code>.
</p>

<p>
	لنلق نظرة أيضًا على تنسيق المؤقت الزمني:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_9427_23" style=""><span class="pun">.</span><span class="pln">timer </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">line-height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">38px</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">font-size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10px</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">font-family</span><span class="pun">:</span><span class="pln"> monospace</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">text-shadow</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1px</span><span class="pln"> </span><span class="lit">1px</span><span class="pln"> </span><span class="lit">0px</span><span class="pln"> black</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">color</span><span class="pun">:</span><span class="pln"> white</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">flex</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">position</span><span class="pun">:</span><span class="pln"> relative</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">timer div </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">position</span><span class="pun">:</span><span class="pln"> absolute</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">background-color</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">rgb</span><span class="pun">(</span><span class="lit">255</span><span class="pln"> </span><span class="lit">255</span><span class="pln"> </span><span class="lit">255</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">20%</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">left</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">top</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">38px</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">z-index</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">timer span </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">position</span><span class="pun">:</span><span class="pln"> absolute</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">z-index</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">left</span><span class="pun">:</span><span class="pln"> </span><span class="lit">19px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<ul>
	<li>
		ضبطنا قيمة الخاصية <code>flex</code> للعنصر <code>timer.</code> الخارجي على القيمة 5 لتشغل أكبر مساحة من شريط التحكم. كما ضبطنا خاصية الموقع بالشكل <code>position:relative</code> كي نتمكن من ضبط العناصر ضمن العنصر الخارجي كما نشاء وبالنسبة إلى حدوده وليس حدود العنصر <code>&lt;body&gt;</code>.
	</li>
	<li>
		ضبطنا موقع العنصر <code>&lt;div&gt;</code> الداخلي ليكون مطلقًا <code>position:absolute</code> لكي يظهر مباشرة في أعلى العنصر <code>&lt;div&gt;</code> الخارجي. كما ضبطنا قيمة اتساع العنصر على الشكل <code>width:0</code> كي لا يُرى العنصر إطلاقًا. وعندما يبدأ العرض نستخدم جافا سكريبت لزيادة اتساع العنصر.
	</li>
	<li>
		ضطنا موقع العنصر <code>&lt;span&gt;</code> ليكون مطلقًا وبالتالي سيكون بالقرب من الطرف اﻷيسر لشريط المؤقت.
	</li>
	<li>
		ضبطنا خاصية العلو <code>z-index</code> للكائن <code>&lt;div&gt;</code> الداخلي والكائن <code>&lt;span&gt;</code> كي يُعرض الشريط الزمني في الأعلى وتحته العنصر <code>&lt;div&gt;</code> الداخلي، ونضمن بذلك أنك سترى كل المعلومات ولن يَحجِب صندوق آخر.
	</li>
</ul>

<h3 id="-2">
	إنجاز شيفرة جافا سكريبت
</h3>

<p>
	بعد أن حضرنا واجهتي HTML و CSS، لا بد من كتاب شيفرة اﻷزرار المخصصة للتحكم بمشغل الفيديو.
</p>

<ol>
	<li>
		أنشئ ملف جافا سكريبت جديد في نفس المجلد الذي يضم الملف index.html وسمِّه <code>custom-player.js</code>.
	</li>
	<li>
		ضع الشيفرة التالية أعلى الملف:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9427_25" style=""><span class="kwd">const</span><span class="pln"> media </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"video"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> controls </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">".controls"</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> play </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">".play"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> stop </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">".stop"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> rwd </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">".rwd"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> fwd </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">".fwd"</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> timerWrapper </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">".timer"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> timer </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">".timer span"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> timerBar </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">".timer div"</span><span class="pun">);</span></pre>

<p>
	أنشأنا في الشيفرة السابقة ثوابت لتكون مراجع إلى الكائنات التي نريد التعامل معها، ولدينا ثلاثة مجموعات:
</p>

<ul>
	<li>
		العنصر <code>&lt;video&gt;</code> وشريط التحكم.
	</li>
	<li>
		أزرار التحكم "تشغيل/إيقاف مؤقت play/pause" و "للأمام rewind" و "للخلف fast forward".
	</li>
	<li>
		غلاف المؤقت الخارجي <code>&lt;div&gt;</code> والعنصر <code>&lt;span&gt;</code> الذي يعرض المؤقت والعنصر <code>&lt;div&gt;</code> الخارجي الذي يزداد اتساعه عندما يتقدم الفيديو.
	</li>
</ul>

<ol>
	<li>
		ضع اﻵن الشيفرة التالية تحت سابقتها:
	</li>
</ol>

<pre class="ipsCode">media.removeAttribute("controls");
controls.style.visibility = "visible";
</pre>

<p>
	تزيل الشيفرة السابقة مشغل الفيديو الافتراضي الخاص بالمتصفح ويُظهر أدوات التحكم المخصصة:
</p>

<h4 id="-3">
	تشغيل وإيقاف الفيديو مؤقتًا
</h4>

<p>
	سننجز اﻵن شيفرة التحكم بزر التشغيل و اﻹيقاف المؤقت:
</p>

<ol>
	<li>
		أضف بداية الشيفرة التالية في أسفل الشيفرة كي تُستدعى الدالة <code>()playPauseMedia</code> عند النقر على زر التشغيل:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9427_27" style=""><span class="pln">play</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"click"</span><span class="pun">,</span><span class="pln"> playPauseMedia</span><span class="pun">);</span></pre>

<ol start="2">
	<li>
		ولتعريف الدالة <code>()playPauseMedia</code>، أضف الشيفرة التالية إلى أسفل الشيفرة السابقة:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9427_29" style=""><span class="kwd">function</span><span class="pln"> playPauseMedia</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">media</span><span class="pun">.</span><span class="pln">paused</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    play</span><span class="pun">.</span><span class="pln">setAttribute</span><span class="pun">(</span><span class="str">"data-icon"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"u"</span><span class="pun">);</span><span class="pln">
    media</span><span class="pun">.</span><span class="pln">play</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    play</span><span class="pun">.</span><span class="pln">setAttribute</span><span class="pun">(</span><span class="str">"data-icon"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"P"</span><span class="pun">);</span><span class="pln">
    media</span><span class="pun">.</span><span class="pln">pause</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نستخدم هنا عبارة <code>if</code> للتحقق من توقف تشغيل الفيديو، وتعيد الخاصية <code>HTMLMediaElement.paused</code> القيمة <code>true</code> عند توقف التشغيل مؤقتًا بما في ذلك عند ضبطته على القيمة 0 بعد تحميله أول مرة. عند ذلك نضبط قيمة السمة <code>data-icon</code> لزر التشغيل على <code>u</code> التي تعرض بدورها أيقونة التشغيل المؤقت عليه، وتستدعي التابع <code>()HTMLMediaElement.play</code> لتشغيل الفيديو. وعند النقر على الزر مرة ثانية سيعود الزر كما كان، إذ تظهر أيقونة التشغيل وسيتوقف الفيديو بتنفيذ التابع <code>()HTMLMediaElement.paused</code>.
</p>

<h4 id="-4">
	إيقاف عرض الفيديو
</h4>

<ol>
	<li>
		نضيف بداية الشيفرة التي تتعامل مع إيقاف تشغيل الفيديو تحت الشيفرة السابقة:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9427_31" style=""><span class="pln">stop</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"click"</span><span class="pun">,</span><span class="pln"> stopMedia</span><span class="pun">);</span><span class="pln">
media</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"ended"</span><span class="pun">,</span><span class="pln"> stopMedia</span><span class="pun">);</span></pre>

<p>
	يضيف سطري الشيفرة مترصدي أحداث <code>addEventListener</code> للتعامل مع الحدث <code>click</code> الذي يوقف تشغيل الفيديو بتنفيذ الدالة <code>()stopMedia</code> عند النقر على زر اﻹيقاف. ولا بد من إيقاف التشغيل أيضًا عند إنتهاء المقطع، لهذا نترصد أيضًا الحدث <code>ended</code> من خلال مترصد الحدث الثاني والذي ينفذ أيضًا الدالة <code>()stopMedia</code> عند انتهاء مقطع الفيديو.
</p>

<ol start="2">
	<li>
		نعرّف تاليًا الدالة <code>()stopMedia</code>، بإضاف اﻷسطر التالية بعد الدالة <code>()playpauseMedia</code>:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9427_33" style=""><span class="kwd">function</span><span class="pln"> stopMedia</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  media</span><span class="pun">.</span><span class="pln">pause</span><span class="pun">();</span><span class="pln">
  media</span><span class="pun">.</span><span class="pln">currentTime </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  play</span><span class="pun">.</span><span class="pln">setAttribute</span><span class="pun">(</span><span class="str">"data-icon"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"P"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	وبما أن الواجهة البرمجية HTMLMediaElement لا تقدم تابعًا مخصصًا ﻹيقاف عرض الفيديو، سنستخدم التابع <code>()pause</code> ﻹيقاف التشغيل مؤقتًا ثم نضبط قيمة الخاصية <code>currentTime</code> على القيمة 0 ليعود الفيديو إلى البداية. فضبط قيمة هذه الخاصية (بالثواني) سينقل الموقع الحالي للفيديو إلى النقطة الزمنية المحددة. يبقى علينا فقط إظهار أيقونة التشغيل على زر التشغيل. وبصرف النظر عن وضع الفيديو سواءً كان قيد التشغيل أو أوقف مؤقتًا عند النقر على زر إيقاف التشغيل "Stop"، لابد أن تُظهر أن المشغل جاهز للعمل مجددًا.
</p>

<h4 id="-5">
	التنقل بالفيديو إلى اﻷمام والخلف
</h4>

<p>
	ستجد العديد من الطرق لتقديم أو إعادة المشغل إلى نقطة زمنية محددة، وما سنعرضه حاليًا طريقة معقدة نوعًا ما في تنفيذ الأمر لتفادي اﻷخطاء التي قد تحدث عند النقر على أزرار مختلفة بترتيب غير متوقع.
</p>

<ol>
	<li>
		أضف مترصدي الحدث التاليين تحت تعريف المترصدين السابقين:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9427_35" style=""><span class="pln">rwd</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"click"</span><span class="pun">,</span><span class="pln"> mediaBackward</span><span class="pun">);</span><span class="pln">
fwd</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"click"</span><span class="pun">,</span><span class="pln"> mediaForward</span><span class="pun">);</span></pre>

<ol start="2">
	<li>
		أضف الدالتين <code>()mediaBackWard</code> و <code>()mediaForWard</code> التاليتين تحت الدوال السابقة، وستصبح الشيفرة كالتالي:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9427_37" style=""><span class="kwd">let</span><span class="pln"> intervalFwd</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">let</span><span class="pln"> intervalRwd</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> mediaBackward</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  clearInterval</span><span class="pun">(</span><span class="pln">intervalFwd</span><span class="pun">);</span><span class="pln">
  fwd</span><span class="pun">.</span><span class="pln">classList</span><span class="pun">.</span><span class="pln">remove</span><span class="pun">(</span><span class="str">"active"</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">rwd</span><span class="pun">.</span><span class="pln">classList</span><span class="pun">.</span><span class="pln">contains</span><span class="pun">(</span><span class="str">"active"</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    rwd</span><span class="pun">.</span><span class="pln">classList</span><span class="pun">.</span><span class="pln">remove</span><span class="pun">(</span><span class="str">"active"</span><span class="pun">);</span><span class="pln">
    clearInterval</span><span class="pun">(</span><span class="pln">intervalRwd</span><span class="pun">);</span><span class="pln">
    media</span><span class="pun">.</span><span class="pln">play</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    rwd</span><span class="pun">.</span><span class="pln">classList</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="str">"active"</span><span class="pun">);</span><span class="pln">
    media</span><span class="pun">.</span><span class="pln">pause</span><span class="pun">();</span><span class="pln">
    intervalRwd </span><span class="pun">=</span><span class="pln"> setInterval</span><span class="pun">(</span><span class="pln">windBackward</span><span class="pun">,</span><span class="pln"> </span><span class="lit">200</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> mediaForward</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  clearInterval</span><span class="pun">(</span><span class="pln">intervalRwd</span><span class="pun">);</span><span class="pln">
  rwd</span><span class="pun">.</span><span class="pln">classList</span><span class="pun">.</span><span class="pln">remove</span><span class="pun">(</span><span class="str">"active"</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">fwd</span><span class="pun">.</span><span class="pln">classList</span><span class="pun">.</span><span class="pln">contains</span><span class="pun">(</span><span class="str">"active"</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fwd</span><span class="pun">.</span><span class="pln">classList</span><span class="pun">.</span><span class="pln">remove</span><span class="pun">(</span><span class="str">"active"</span><span class="pun">);</span><span class="pln">
    clearInterval</span><span class="pun">(</span><span class="pln">intervalFwd</span><span class="pun">);</span><span class="pln">
    media</span><span class="pun">.</span><span class="pln">play</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fwd</span><span class="pun">.</span><span class="pln">classList</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="str">"active"</span><span class="pun">);</span><span class="pln">
    media</span><span class="pun">.</span><span class="pln">pause</span><span class="pun">();</span><span class="pln">
    intervalFwd </span><span class="pun">=</span><span class="pln"> setInterval</span><span class="pun">(</span><span class="pln">windForward</span><span class="pun">,</span><span class="pln"> </span><span class="lit">200</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هيأنا أولًا متغيرين <code>intervalFwd</code> و <code>intervlRwd</code> وسترى عملهما لاحقًا، كما ستلاحظ أن عمل الدالتين <code>()mediaBackWard</code> و <code>()mediaForWard</code> متطابق لكن بترتيب معكوس:
</p>

<ol>
	<li>
		يجب تصفير اﻷصناف والمجالات التي ضبطناها عند تنفيذ وظيفة التقديم السريع للأمام، لأننا لو نقرنا على زر <code>rwd</code> بعد النقر على الرز <code>fwd</code> من المفترض أن نلغي أي إعدادات خاصة بالتقديم السريع للمشغل <code>fwd</code> واستبدالها بإعدادت التراجع <code>rwd</code>، لأن المشغل سيخفق لو حاولنا النقر على كلا الزرين في نفس الوقت.
	</li>
	<li>
		استخدمنا عبارة <code>if</code> للتحقق من ضبط صنف الزر <code>rwd</code> ليكون <code>active</code> في إشارة إلى أن الزر قد نُقر للتو. ويتمتع كل عنصر بالخاصية <code>classlist</code>، وهي خاصية مفيدة تضم كل الأصناف التي يمتلكها العنصر وتقدم توابع ﻹزالة وإضافة اﻷصناف. وقد استخدمنا التابع <code>()classList.contains</code> للتحقق من جود الصنف <code>active</code> ضمن أصناف الزر، وتعيد قيمة منطقية <code>true/false</code>.
	</li>
	<li>
		في حال كان <code>active</code> أحد أصناف العنصر <code>rwd</code> نزيله باستخدام التابع <code>()classList.remove</code> ثم نلغي قيمة الفاصل الزمني الذي ضُبط مسبقًا عندما نقرنا على الزر ومن ثم نستخدم التابع <code>()HTMLMediaElement.play</code> ﻹلغاء العودة للخلف وتشغيل الفيديو بشكل طبيعي.
	</li>
	<li>
		إن لم يمتلك الزر تلك الخاصية نضيفها إليه باستخدام التابع <code>()clasList.add</code> ومن ثم نوقف الفيديو مؤقتًا باستخدام التابع <code>()HTMLMediaElement.pause</code>. نضبط بعدها قيمة المتغير <code>intervalRwd</code> ليعادل القيمة المعادة من استدعاء الدالة <code>()setInterval</code>. تُحدد هذه الدالة فترة زمنية معينة تنفذ بعد انقضائها الدالة التي تُمرر إليها كمعامل أول أما الفترة الزمنية فيحددها المعامل الثاني بالميلي ثانية. وهنا ننفذ الدالة كل 200 ميلي ثانية كي نعيد مشغل الفيديو إلى الخلف بوتيرة ثابتة. ولكي نوقف تنفيذ الدالة <code>()setInterval</code> نستدعي الدالة <code>()clearIterval</code> ممرين لها المتغير <code>intervalRwd</code> (الذي أسندت إليه الدالة <code>()setInterval</code>).
	</li>
	<li>
		عرفنا أخيرًا الدالة <code>()windBackwrd</code> والدالة <code>()windForward</code> اللتان تمررا إلى <code>()setInterval</code>، لهذا أضف الشيفرة التالية تحت الدوال السابقة:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9427_39" style=""><span class="kwd">function</span><span class="pln"> windBackward</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">media</span><span class="pun">.</span><span class="pln">currentTime </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    rwd</span><span class="pun">.</span><span class="pln">classList</span><span class="pun">.</span><span class="pln">remove</span><span class="pun">(</span><span class="str">"active"</span><span class="pun">);</span><span class="pln">
    clearInterval</span><span class="pun">(</span><span class="pln">intervalRwd</span><span class="pun">);</span><span class="pln">
    stopMedia</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    media</span><span class="pun">.</span><span class="pln">currentTime </span><span class="pun">-=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> windForward</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">media</span><span class="pun">.</span><span class="pln">currentTime </span><span class="pun">&gt;=</span><span class="pln"> media</span><span class="pun">.</span><span class="pln">duration </span><span class="pun">-</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    fwd</span><span class="pun">.</span><span class="pln">classList</span><span class="pun">.</span><span class="pln">remove</span><span class="pun">(</span><span class="str">"active"</span><span class="pun">);</span><span class="pln">
    clearInterval</span><span class="pun">(</span><span class="pln">intervalFwd</span><span class="pun">);</span><span class="pln">
    stopMedia</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    media</span><span class="pun">.</span><span class="pln">currentTime </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">3</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سنشرح تاليًا الدالة الأولى فقط لكون الدالتين متطابقتان من ناحية الشيفرة ومتعاكستان عملًا. وما فعلناه في الدالة <code>()windBackward</code> هو التالي (تذكر أنه بمجرد تفعيل الفاصل الزمني الذي سيتراجع فيه المشغل إلى الخلف ستُستدعى هذه الدالة كل 200 ميلي ثانية):
</p>

<ol>
	<li>
		نبدأ الشيفرة بالعبارة <code>if</code> التي تتحقق أن المدة المنقضية من المقطع أقل من 3 ثانية، أي سيعود المشغل عند تراجعه إلى ما قبل نقطة البداية، وهذا ما يسبب سلوكًا غريبًا للمشغل. فلو كانت الحالة كذلك، نوقف تشغيل المقطع باستدعاء الدالة <code>()stopMedia</code> ومن ثم نزيل الصنف <code>active</code> من قائمة أصناف الزر <code>rwd</code> ونمحي قيمة المتغير <code>intervalRwd</code> ﻹيقاف عملية التراجع. وفي حال أهملنا هذه الخطوة اﻷخيرة سيستمر المشغل بالتراجع إلى ما لا نهاية.
	</li>
	<li>
		إن كان الوقت المنقضي أكبر من 3 ثانية، نزيل ثلاث ثوانٍ من الوقت الحالي باستخدام التعليمة <code>media.currentTime -=3</code>، أي نعيد مشغل الفيديو إلى ما قبل ثلاث ثوان وذلك كل 200 ميلي ثانية.
	</li>
</ol>

<h4 id="-6">
	تحديث الوقت المنقضي
</h4>

<p>
	آخر ما سننفذه ﻹنجاز أدوات التحكم المخصصة بمشغل الفيديو هو تحديد الوقت المنقضي من زمن المقطع. لذا نشغّل دالة تحدّث الوقت الذي نعرضه في كل مرة يقع فيها الحدث <code>timeupdate</code> المرتبط بالعنصر <code>&lt;video&gt;</code>. أما تواتر عملية وقوع هذا الحدث، فتعتمد على المتصفح وقوة معالج جهازك.
</p>

<p>
	أضف اﻵن السطر التالي الذي يعرّف مترصد تحديث زمن التشغيل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9427_41" style=""><span class="pln">media</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"timeupdate"</span><span class="pun">,</span><span class="pln"> setTime</span><span class="pun">);</span></pre>

<p>
	ولتعريف الدالة <code>()setTime</code>، أضف مايلي في أسفل ملف جافا سكريبت:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9427_43" style=""><span class="kwd">function</span><span class="pln"> setTime</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> minutes </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">floor</span><span class="pun">(</span><span class="pln">media</span><span class="pun">.</span><span class="pln">currentTime </span><span class="pun">/</span><span class="pln"> </span><span class="lit">60</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> seconds </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">floor</span><span class="pun">(</span><span class="pln">media</span><span class="pun">.</span><span class="pln">currentTime </span><span class="pun">-</span><span class="pln"> minutes </span><span class="pun">*</span><span class="pln"> </span><span class="lit">60</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> minuteValue </span><span class="pun">=</span><span class="pln"> minutes</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">().</span><span class="pln">padStart</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="str">"0"</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> secondValue </span><span class="pun">=</span><span class="pln"> seconds</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">().</span><span class="pln">padStart</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="str">"0"</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> mediaTime </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">minuteValue</span><span class="pun">}:</span><span class="pln">$</span><span class="pun">{</span><span class="pln">secondValue</span><span class="pun">}`;</span><span class="pln">
  timer</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> mediaTime</span><span class="pun">;</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> barLength </span><span class="pun">=</span><span class="pln">
    timerWrapper</span><span class="pun">.</span><span class="pln">clientWidth </span><span class="pun">*</span><span class="pln"> </span><span class="pun">(</span><span class="pln">media</span><span class="pun">.</span><span class="pln">currentTime </span><span class="pun">/</span><span class="pln"> media</span><span class="pun">.</span><span class="pln">duration</span><span class="pun">);</span><span class="pln">
  timerBar</span><span class="pun">.</span><span class="pln">style</span><span class="pun">.</span><span class="pln">width </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">barLength</span><span class="pun">}</span><span class="pln">px</span><span class="pun">`;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هذه الدالة طويلة، لهذا سنناقشها خطوة خطوة:
</p>

<ol>
	<li>
		نعمل بداية على تحديد الدقائق والثواني المنقضية من خلال قيمة <code>HTMLMediaElement.currentTime</code>.
	</li>
	<li>
		نهيئ بعد ذلك متغيرين إضافيين هما <code>minuteValue</code> و <code>secondValue</code>، ثم نستخدم التابع <code>()padStart</code> لكي نمثّل قيمة الدقائق والثواني على شكل محرفين فقط حتى لو كانت القيمة رقمًا وحيدًا.
	</li>
	<li>
		أما الوقت الفعلي الذي سيُعرض فهو قيمة المتغير <code>minuteValue</code> تليه نقطتان متعامدتان ثم قيمة المتغير <code>secondValue</code>.
	</li>
	<li>
		نضبط قيمة المؤقت <code>Node.textContent</code> لتعادل قيمة الوقت الحالي وبالتالي ستُعرض هذه القيمة على واجهة المشغّل.
	</li>
	<li>
		نحدد طول عنصر <code>&lt;div&gt;</code> الداخلي (الذي سيعرض شريط تقدم مقطع الفيديو) من خلال تحديد اتساع عنصر <code>&lt;div&gt;</code> الخارجي (نأخذها من الخاصية <code>clientWidth</code>) ومن ثم ضرب هذه القيمة بالوقت الحالي <code>HTMLMediaElement.currentTime</code> ونقسم على المدة الكلية لمقطع الفيديو <code>HTMLMediaElement.duration</code>.
	</li>
	<li>
		نضبط قيمة اتساع العنصر <code>&lt;div&gt;</code> الداخلي ليعادل طول شريط تتبع تقدم الفيديو بعد إضافة القيمة "px" كي تشير إلى الاتساع مقدرًا بالبكسل.
	</li>
</ol>

<h4 id="-7">
	إصلاح مشكلات التشغيل واﻹيقاف المؤقت للفيديو
</h4>

<p>
	هنالك مشكلة واحدة ولا بد من حلها. فعند النقر على زر التشغيل أو إيقاف الفيديو وزر التقديم أو التراجع، فلن يعمل هذا الزر! وما علينا إصلاحه هنا هو إلغاء وظائف التقدم أو التراجع عند النقر على زر التشغيل لمتابع العمل كما هو متوقع، وهذا أمر سهل.
</p>

<p>
	أضف بداية الشيفرة التالية ضمن الدالة <code>()stopMedia</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9427_45" style=""><span class="pln">rwd</span><span class="pun">.</span><span class="pln">classList</span><span class="pun">.</span><span class="pln">remove</span><span class="pun">(</span><span class="str">"active"</span><span class="pun">);</span><span class="pln">
fwd</span><span class="pun">.</span><span class="pln">classList</span><span class="pun">.</span><span class="pln">remove</span><span class="pun">(</span><span class="str">"active"</span><span class="pun">);</span><span class="pln">
clearInterval</span><span class="pun">(</span><span class="pln">intervalRwd</span><span class="pun">);</span><span class="pln">
clearInterval</span><span class="pun">(</span><span class="pln">intervalFwd</span><span class="pun">);</span></pre>

<p>
	أضف اﻵن نفس اﻷسطر في بداية الدالة <code>()playpauseMedia</code> (وقبل عبارة <code>if</code>).
</p>

<p>
	يمكنك اﻵن ازالة نفس الأسطر من الدالتين <code>()windBackwrd</code> و <code>()windForward</code> لأننا وضعنا هذه الوظيفة المشتركة بينهما في الدالة <code>()stopMedia</code>.
</p>

<p>
	<strong>ملاحظة</strong>: يمكنك تحسين فعالية الشيفرة أكثر من خلال إنشاء دالة منفصلة تضم اﻷسطر السابقة ومن ثم استدعاء هذه الدالة عند الحاجة بدلًا من تكرار اﻷسطر عدة مرات في الشيفرة.
</p>

<h2 id="-8">
	الخلاصة
</h2>

<p>
	تعلمنا في هذا المقال ما يكفي عن الواجهة <code>HTMLMediaElement</code> التي تقدم كما كبيرًا من الوظائف ﻹنشاء مشغل وسائط متعددة، وما رأيناه هو مجرد جزء ضئيل من إمكانياتها.
</p>

<p>
	إليك أخيرًا بعض الاقتراحات التي تساعد في تحسين مثالنا:
</p>

<ol>
	<li>
		يختل عمل المؤقت إن كانت مدة المقطع أكثر من ساعة (فلن يعرض الساعات بل الدقائق والثواني فقط). هل يمكنك تعديل الشيفرة لتعرض الساعات أيضًا؟
	</li>
	<li>
		يمتلك العنصر <code>&lt;audio&gt;</code> نفس وظائف <code>HTMLMediaElement</code> وبالتالي يمكنك تشغيل المقاطع الصوتية بسهولة، جرب لك.
	</li>
	<li>
		هل يمكنك إيجاد طريقة الانتقال إلى مكان ما من المقطع بالنقر على شريط تقدم الفيديو (العنصر <code>&lt;div&gt;</code> الداخلي). وكتلميح يمكنك إيجاد x و y لزوايا الشريط من خلال التابع <code>()getBoundingClientRect</code> وإيجاد إحداثيي موقع مؤشر الفأرة من خلال كائن الحدث الذي ينتج عن حدث النقر على المستند. إليك مثالًا:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9427_47" style=""><span class="pln">document</span><span class="pun">.</span><span class="pln">onclick </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">e</span><span class="pun">.</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> e</span><span class="pun">.</span><span class="pln">y</span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	ترجمة -وبتصرف- لمقال: <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs" rel="external nofollow">Video and audio APIs</a>
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%85%D8%B9-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D8%A7%D9%84%D8%AD%D9%84%D9%82%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85-%D8%A7%D9%84%D9%85%D8%AA%D8%AD%D8%B1%D9%83%D8%A9-r2435/" rel="">العمل مع واجهات الرسوميات البرمجية في جافا سكريبت: الحلقات والرسوم المتحركة</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/html/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D9%85%D9%82%D8%A7%D8%B7%D8%B9-%D8%A7%D9%84%D9%81%D9%8A%D8%AF%D9%8A%D9%88-%D8%B9%D8%A8%D8%B1-%D8%A7%D9%84%D8%B9%D9%86%D8%B5%D8%B1-%D9%81%D9%8A-html5-r356/" rel="">إضافة مقاطع الفيديو عبر العنصر &lt;video&gt; في HTML5</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/html/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D9%85%D8%AD%D8%AA%D9%88%D9%89-%D8%B3%D9%85%D8%B9%D9%8A-%D9%88%D9%85%D8%B1%D8%A6%D9%8A-%D9%81%D9%8A-%D8%B5%D9%81%D8%AD%D8%A9-html-r1825/" rel="">إضافة محتوى سمعي ومرئي في صفحة HTML</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%A3%D8%AB%D9%8A%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D9%85%D8%B1%D9%8A%D8%B1-%D9%81%D9%8A-%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-javascript-%D9%88css%E2%80%93-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%A3%D9%88%D9%84-r400/" rel="">تأثيرات التمرير في صفحات الويب باستخدام Javascript وCSS</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2438</guid><pubDate>Sat, 02 Nov 2024 15:06:05 +0000</pubDate></item><item><title>&#x627;&#x644;&#x639;&#x645;&#x644; &#x645;&#x639; &#x648;&#x627;&#x62C;&#x647;&#x627;&#x62A; &#x627;&#x644;&#x631;&#x633;&#x648;&#x645;&#x64A;&#x627;&#x62A; &#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x64A;&#x629; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627; &#x633;&#x643;&#x631;&#x64A;&#x628;&#x62A;: &#x627;&#x644;&#x62D;&#x644;&#x642;&#x627;&#x62A; &#x648;&#x627;&#x644;&#x631;&#x633;&#x648;&#x645; &#x627;&#x644;&#x645;&#x62A;&#x62D;&#x631;&#x643;&#x629;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%85%D8%B9-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D8%A7%D9%84%D8%AD%D9%84%D9%82%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85-%D8%A7%D9%84%D9%85%D8%AA%D8%AD%D8%B1%D9%83%D8%A9-r2435/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_10/----------.png.b1dad5a221e043f9bf4e31acdc9c82cf.png" /></p>
<p>
	غطينا في مقالنا <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D8%A7%D8%AA-%D8%AB%D9%86%D8%A7%D8%A6%D9%8A%D8%A9-%D8%A7%D9%84%D8%A8%D8%B9%D8%AF-%D8%B6%D9%85%D9%86-%D8%A7%D9%84%D8%B9%D9%86%D8%B5%D8%B1-canvas-r2431/" rel="">السابق</a> بعض أساسيات الرسوم ثنائية البعد ضمن العنصر <code>&lt;canvas&gt;</code>، لكنك لن تلمس عمليًا فعالية هذا العنصر ما لم ترى قدرته على تحريك الرسوم. إذ يقدم هذا العنصر إمكانية إنشاء صور ورسومات باستخدام سكربتات مخصصة، لكن إن لم يكن هدفك تحريك أي شيء، عليك استخدام صور ثابتة لتوفر على نفسك عناء العمل.
</p>

<h2 id="canvas">
	إنشاء الحلقات في Canvas
</h2>

<p>
	لن يكون صعبًا التعامل مع الحلقات في <code><a href="https://wiki.hsoub.com/HTML/canvas" rel="external">&lt;canvas&gt;</a></code>، وما عليك سوى استخدام تعليمات هذا العنصر (التوابع والخاصيات) داخل <a href="https://wiki.hsoub.com/JavaScript/for" rel="external">حلقة for</a> أو غيرها من الحلقات كغيرها من شيفرات جافا سكريبت. لهذا سنعطي مثالًا تطبيقيًا عن الموضوع:
</p>

<ol>
	<li>
		أنشئ نسخة جديدة عن القالب الرسومي الذي أنشأناه في <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/1_canvas_template" rel="external nofollow">المقال السابق</a> وافتحه ضمن محرر الشيفرة الذي تستخدمه.
	</li>
	<li>
		أضف السطر التالي إلى أسفل ملف جافا سكريبت، ويتضمن هذا السطر تابعًا جديدًا هو <code>()translate</code> الذي يحرّك نقطة المبدأ في لوحة الرسم:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4803_8" style=""><span class="pln">ctx</span><span class="pun">.</span><span class="pln">translate</span><span class="pun">(</span><span class="pln">width </span><span class="pun">/</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> height </span><span class="pun">/</span><span class="pln"> </span><span class="lit">2</span><span class="pun">);</span></pre>

<p>
	يسبب ذلك تحريك نقطة المبدأ (0,0) إلى مركز اللوحة بدلًا من كونها في الزاوية العليا اليسارية. ولهذا اﻷمر فائدته في الكثير من الحالات كما في مثالنا، إذ نريد أن يكون التصميم منسوبًا إلى مركز اللوحة.
</p>

<p>
	أضف اﻵن الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4803_10" style=""><span class="kwd">function</span><span class="pln"> degToRad</span><span class="pun">(</span><span class="pln">degrees</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">degrees </span><span class="pun">*</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">PI</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">180</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> rand</span><span class="pun">(</span><span class="pln">min</span><span class="pun">,</span><span class="pln"> max</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">floor</span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">(</span><span class="pln">max </span><span class="pun">-</span><span class="pln"> min </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">))</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> min</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">let</span><span class="pln"> length </span><span class="pun">=</span><span class="pln"> </span><span class="lit">250</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">let</span><span class="pln"> moveOffset </span><span class="pun">=</span><span class="pln"> </span><span class="lit">20</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">let</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> length</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{}</span></pre>

<p>
	سننجز هنا نفس الدالة <code>()degToRad</code> التي رأيناها في مثال المثلث في <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D8%A7%D8%AA-%D8%AB%D9%86%D8%A7%D8%A6%D9%8A%D8%A9-%D8%A7%D9%84%D8%A8%D8%B9%D8%AF-%D8%B6%D9%85%D9%86-%D8%A7%D9%84%D8%B9%D9%86%D8%B5%D8%B1-canvas-r2431/" rel="">مقالنا السابق</a>، ونستخدم الدالة التي تعيد رقمًا عشوائيًا بين حدين علوي وسفلي معينين. إضافة إلى ذلك ننشئ المتغيرين <code>length</code> و <code>moveOffset</code> (سنرحهما لاحقًا)، كما نستخدم حلقة <code>for</code> فارغة.
</p>

<p>
	ما سنفعله هنا هو رسم شيء ما ضمن اللوحة لكن ضمن الحلقة <code>for</code> ثم نكرر ما نفعله عدة مرات. أضف اﻵن الشيفرة التالية داخل الحلقة <code>for</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4803_13" style=""><span class="pln">ctx</span><span class="pun">.</span><span class="pln">fillStyle </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">rgb</span><span class="pun">(</span><span class="pln">$</span><span class="pun">{</span><span class="lit">255</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> length</span><span class="pun">}</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> $</span><span class="pun">{</span><span class="lit">255</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> length</span><span class="pun">}</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">90</span><span class="pun">%)`;</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">beginPath</span><span class="pun">();</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">moveTo</span><span class="pun">(</span><span class="pln">moveOffset</span><span class="pun">,</span><span class="pln"> moveOffset</span><span class="pun">);</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">lineTo</span><span class="pun">(</span><span class="pln">moveOffset </span><span class="pun">+</span><span class="pln"> length</span><span class="pun">,</span><span class="pln"> moveOffset</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> triHeight </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">length </span><span class="pun">/</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">tan</span><span class="pun">(</span><span class="pln">degToRad</span><span class="pun">(</span><span class="lit">60</span><span class="pun">));</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">lineTo</span><span class="pun">(</span><span class="pln">moveOffset </span><span class="pun">+</span><span class="pln"> length </span><span class="pun">/</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> moveOffset </span><span class="pun">+</span><span class="pln"> triHeight</span><span class="pun">);</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">lineTo</span><span class="pun">(</span><span class="pln">moveOffset</span><span class="pun">,</span><span class="pln"> moveOffset</span><span class="pun">);</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">fill</span><span class="pun">();</span><span class="pln">

length</span><span class="pun">--;</span><span class="pln">
moveOffset </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">0.7</span><span class="pun">;</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">rotate</span><span class="pun">(</span><span class="pln">degToRad</span><span class="pun">(</span><span class="lit">5</span><span class="pun">));</span></pre>

<p>
	في كل تكرار:
</p>

<ul>
	<li>
		نضبط <code>fillStyle</code> ليكون ظلًا بنفسجيًا شفافًا قليلًا ويتغير كل مرة وفقًا لقيمة المتغير <code>length</code>. وكما سترى سيقل الطول في كل تكرار وبالتالي سيكون أثر ذلك على اللون الذي يصبح أكثر لمعانًا مع كل مثلث يُرسم على التتابع.
	</li>
	<li>
		نبدأ مسار الرسم.
	</li>
	<li>
		ننقل قلم الرسم إلى اﻹحداثي <code>(moveOffset, moveOffset)</code> ويُعرّف هذا المتغير المسافة التي يجب أن نحركها في كل مرة نرسم فيها مثلًا جديدًا.
	</li>
	<li>
		نرسم خطًا إلى اﻹحداثي <code>(moveOffset+length, moveOffset)</code> وهذا الخط طوله قيمة المتغير <code>length</code> ويوازي المحور x.
	</li>
	<li>
		نحسب ارتفاع المثلث كما فعلنا في المقال السابق.
	</li>
	<li>
		نرسم خطًا نحو رأس المثلث المتجه نحو الأسفل ومن ثم خطا إلى نقطة بداية المثلث.
	</li>
	<li>
		نستدعى التابع <code>()fill</code> لملء المثلث.
	</li>
	<li>
		نحدّث قيمة المتغيرات التي تصف سلسلة المثلثات التي نرسمها كي نتمكن من رسم المثلث التالي. نخفض قيمة المتغير <code>length</code> بمقدار 1 وبالتالي سيصغر المثلث كل مرة. كما نزيد قيمة <code>moveOffset</code> بمقدار صغير كي يكون كل مثلث أبعد قليلًا عن سابقه. ونستخدم التابع <code>()rotate</code> الذي يسمح لنا تدوير اللوحة بأكملها، حيث ندورها بمقدار خمس درجات قبل أن نرسم المثلث التالي.
	</li>
</ul>

<p>
	هذا كل ما في اﻷمر، وستبدو نتيجة مثالنا كالتالي:
</p>

<p>
	<iframe height="550" loading="lazy" src="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/loops_animation/6_canvas_for_loop/index.html" width="100%"></iframe>
</p>

<p>
	نشجعك اﻵن على إجراء تغييرات في هذا المثال وتجرّب ما تعلمته. إذ يمكنك مثلًا:
</p>

<ul>
	<li>
		أن ترسم مربعًا أو قوسًا بدلًا من المثلث.
	</li>
	<li>
		أن تغير قيمة المتغير <code>length</code> أو <code>moveOffset</code>.
	</li>
	<li>
		تغيير اﻷرقام العشوائية التي نولدها باستخدام الدالة <code>()rand</code> التي وضعناها في الشيفرة السابقة ولم نستخدمها.
	</li>
</ul>

<p>
	<strong>ملاحظة</strong>: بإمكانك الاطلاع على <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/6_canvas_for_loop" rel="external nofollow">الشيفرة كاملة</a> لهذا المثال على جت-هب.
</p>

<h2 id="-1">
	الرسوم المتحركة
</h2>

<p>
	ما فعلناه في مثالنا السابق أمر جميل، لكن ما تحتاجه حقيقة حلقة ثابتة تستمر وتستمر في أي تطبيق فعلي يعتمد على عنصر <code>&lt;canvs&gt;</code> (مثل <a href="https://academy.hsoub.com/programming/game-development/%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A3%D9%84%D8%B9%D8%A7%D8%A8-%D8%A7%D9%84%D8%A5%D9%84%D9%83%D8%AA%D8%B1%D9%88%D9%86%D9%8A%D8%A9/" rel="">اﻷلعاب الإلكترونية</a>، والعرض البصري المباشر). فلو فكرت بلوحة الرسم على أنها فيلم سينمائي، ستعرف أنه عليك تحديث اﻹطارات المعروضة بشكل مستمر وبمعدل 60 إطار في الثانية (القيمة المثالية) كي يبدو المشهد المتحرك ناعمًا ومريحًا للعين البشرية.
</p>

<p>
	تقدم لك جافا سكريبت مجموعة من الدوال التي تسمح لك بتنفيذ دوال أخرى بشكل متكرر عدة مرات في الثانية الواحدة. ونجد أنسب هذه الدوال لمثالنا الدالة <code>()window.requestAnimationFrame</code> التي تأخذ معاملًا واحدًا وهو اسم الدالة التي نريد استدعاءها. فإن رسمت هذه الدالة تحديثًا جديدًا من سلسلة الرسوم المتحركة التي سنعرضها، عليك حينها استدعاء الدالة <code>()window.requestAnimationFrame</code> مجددًا قبل نهاية الدالة المنفذة للرسم كي تستمر حلقة الرسم. تنتهي الحلقة عندما تتوقف عن استدعاء <code>()window.requestAnimationFrame</code> أو عند استدعاء الدالة <code>()window.caancelAnimationFrame</code> بعد استدعاء <code>()window.requestAnimationFrame</code> وقبل البدء برسم اﻹطار (الذي سيكون اﻷخير حينها).
</p>

<p>
	<strong>ملاحظة</strong>:من الممارسات الجيدة استدعاء الدالة <code>()window.caancelAnimationFrame</code> من شيفرتك الرئيسية عند الانتهاء من الرسم، كي تضمن عدم وجود أية تحديثات أخرى يمكن أن تُعرض على اللوحة.
</p>

<p>
	يُنفّذ المتصفح التفاصيل المعقدة للعملية مثل تحريك الرسوم بمعدل ثابت، والتأكد من عدم تنفيذ رسوميات لا تُرى. ولكي تتعرف على عمل المتصفح، سنلقي نظرة على مثالنا السابق "الكرات القافزة المرتدة" (يمكنك تجربتها <a href="https://mdn.github.io/learning-area/javascript/oojs/bouncing-balls/index-finished.html" rel="external nofollow">مباشرة</a> أو الاطلاع على <a href="https://github.com/mdn/learning-area/tree/main/javascript/oojs/bouncing-balls" rel="external nofollow">الشيفرة المصدرية</a><span class="ipsEmoji">😞</span>
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4803_16" style=""><span class="kwd">function</span><span class="pln"> loop</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  ctx</span><span class="pun">.</span><span class="pln">fillStyle </span><span class="pun">=</span><span class="pln"> </span><span class="str">"rgb(0 0 0 / 25%)"</span><span class="pun">;</span><span class="pln">
  ctx</span><span class="pun">.</span><span class="pln">fillRect</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> width</span><span class="pun">,</span><span class="pln"> height</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> ball </span><span class="kwd">of</span><span class="pln"> balls</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    ball</span><span class="pun">.</span><span class="pln">draw</span><span class="pun">();</span><span class="pln">
    ball</span><span class="pun">.</span><span class="pln">update</span><span class="pun">();</span><span class="pln">
    ball</span><span class="pun">.</span><span class="pln">collisionDetect</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  requestAnimationFrame</span><span class="pun">(</span><span class="pln">loop</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

loop</span><span class="pun">();</span></pre>

<p>
	نشغل الدالة<code>()loop</code> مرة واحدة في آخر الشيفرة لنبدأ دورة الرسوميات برسم اﻹطار المتحرك الأول ومن ثم تتولى الدالة <code>()loop</code> مسؤولية استدعاء الدالة <code>(loop(requestAnimationframe</code> التي ستحضر وترسم اﻹطار الثاني من الرسوم المتحركة وتكرر اﻷمر حتى النهاية.
</p>

<p>
	وتجدر ملاحظة أننا نمسح اللوحة تمامًا عند رسم كل إطار ومن ثم نعيد رسم كل شيء. إذ نرسم كل كرة ونحدّث موقعها ونتحقق فيما لو اصطدمت بكرة أخرى. وبمجرد أن ترسم شيئًا الى اللوحة، لن تتمكن من تعديله بشكل مستقل كما هو الحال مع عناصر <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%B4%D8%AC%D8%B1%D8%A9-dom-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2360/" rel="">شجرة DOM</a>. ولن تستطيع أيضًا تحريك كل كرة بمفردها ضمن اللوحة، لأنك بمجرد رسم الكرة ستصبح جزءًا من اللوحة وليست كائنًا مستقلًا يمكنك التعامل معه. لهذا عليك مسح وإعادة رسم العناصر، إما بمسح اﻹطار بأكمله وإعادة رسم كل شيء أو كتابة شيفرة تحد تمامًا الجزء الذي يجب مسحه وبالتالي إعادة الرسم في المنطقة المحددة من اللوحة.
</p>

<p>
	لهذا يُعد تحسين الرسوم المتحركة اختصاصًا برمجيًا بحد ذاته، ويتطلب استعمال العديد من التقنيات الذكية المتاحة. لكن هذا اﻷمر خارج إطار مقالنا والمثال الذي نعمل عليه. وعمومًا، تتطلب عملية تنفيذ رسوم متحركة ضمن اللوحة الخطوات التالية:
</p>

<ol>
	<li>
		مسح محتوى اللوحة باستخدام <code>()fillRect</code> أو <code>()clearRect</code>.
	</li>
	<li>
		تخزين الحالة عند الضرورة باستخدام <code>()save</code>، وذلك عندما تحتاج إلى حفظ اﻹعدادات التي حدّثتها في اللوحة قبل الاستمرار، وللأمر فائدته في التطبيقات المتقدمة.
	</li>
	<li>
		رسم الأشياء التي تريد تحريكها.
	</li>
	<li>
		استعادة الإعدادات التي خزنتها في الخطوة الثانية باستخدام <code>()restore</code>.
	</li>
	<li>
		استدعاء الدالة <code>()requestAnimationFrame</code> لجدولة رسم اﻹطار التالي من الرسم المتحرك.
	</li>
</ol>

<p>
	<strong>ملاحظة</strong>: لن نغطي الدالتين <code>()save</code> و <code>()restore</code> في مقالنا.
</p>

<h2 id="-2">
	تحريك شخصية بسيطة
</h2>

<p>
	سننشئ اﻵن رسمًا متحركًا خاصًا بنا، تمشي الشخصية المقتبسة من أحد ألعاب الحاسوب القديمة خلال عرض الرسم المتحرك عبر الشاشة.
</p>

<ol>
	<li>
		أنشئ نسخة جديدة من القالب الذي نستخدمه في أمثلتنا وافتحه في محرر اﻷلعاب.
	</li>
	<li>
		حدّث شيفرة HTML حتى تعكس الصورة:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4803_19" style=""><span class="pun">&lt;</span><span class="pln">canvas </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"myCanvas"</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="pln">p</span><span class="pun">&gt;</span><span class="pln">A man walking</span><span class="pun">.&lt;/</span><span class="pln">p</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">canvas</span><span class="pun">&gt;</span></pre>

<p>
	أضف اﻷسطر التالية إلى نهاية ملف جافا سكريبت كي تكون نقطة المبدأ منتصف لوحة الرسم.:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4803_21" style=""><span class="pln">ctx</span><span class="pun">.</span><span class="pln">translate</span><span class="pun">(</span><span class="pln">width </span><span class="pun">/</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> height </span><span class="pun">/</span><span class="pln"> </span><span class="lit">2</span><span class="pun">);</span></pre>

<p>
	ننشئ تاليًا كائن <code>HTMLImgeElement</code> ونضبط قيمة الخاصية <code>src</code> له كي تكون عنوان الصورة التي نريد تحميلها ثم نضيف معالجًا للحدث <code>onload</code> الذي يستدعي الدالة <code>()draw</code> عند اكتمال تحميل الصورة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4803_23" style=""><span class="kwd">const</span><span class="pln"> image </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Image</span><span class="pun">();</span><span class="pln">
image</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> </span><span class="str">"walk-right.png"</span><span class="pun">;</span><span class="pln">
image</span><span class="pun">.</span><span class="pln">onload </span><span class="pun">=</span><span class="pln"> draw</span><span class="pun">;</span></pre>

<p>
	سنضيف اﻵن بعض المتغيرات التي تتعقب موقع الشخصية في اللوحة وعدد الشخصيات التي نرسمها على اللوحة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4803_25" style=""><span class="kwd">let</span><span class="pln"> sprite </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">let</span><span class="pln"> posX </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span></pre>

<p>
	سنشرح تاليًا صورة الشخصية المأخوذة من التطبيق <a href="https://codepen.io/mikethomas/pen/kQjKLW" rel="external nofollow"> Walking cycle using CSS animation</a>
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="160438" href="https://academy.hsoub.com/uploads/monthly_2024_10/01_walk-right.png.fd952f503e79216431b15054dc412045.png" rel=""><img alt="01 walk right" class="ipsImage ipsImage_thumbnailed" data-fileid="160438" data-unique="ifdqvf9md" src="https://academy.hsoub.com/uploads/monthly_2024_10/01_walk-right.png.fd952f503e79216431b15054dc412045.png"> </a>
</p>

<p>
	تتضمن الصورة ست شخصيات تمثل تسلسل حركة الشخصية. عرض صورة كل شخصية 102 بكسل وارتفاعها 148 بكسل. ولرسم كل شخصية على حدة، علينا استخدام التابع <code>()drawImage</code> لاقتصاص صورة واحدة للشخصية وعرض هذا الجزء فقط، كما فعلنا مع شعار فايرفوكس في مثال سابق. وينبغي ضرب اﻹحداثي X للشريحة بالعدد 102 ويبقى اﻹحداثي Y مساويًا للصفر، وستبقى أبعاد الشريحة دائمًا 102x148 بكسل.
</p>

<p>
	سنضع اﻵن شيفرة الدالة <code>()draw</code> في اﻷسفل لكي نزودها بالشيفرة اللازمة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4803_27" style=""><span class="kwd">function</span><span class="pln"> draw</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{}</span></pre>

<p>
	أما بقية الشيفرة في هذا القسم فستكون ضمن الدالة <code>()draw</code>. لهذا أضف اﻷسطر التالية التي تمسح اللوحة وتعدها لرسم كل إطار. وانتبه إلى ضرورة تخصيص الزاوية العليا اليسارية من المربع لتكون <code>(width/2, height/2)</code> لأننا اتخذنا مركز اللوحة نقطة البداية.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4803_29" style=""><span class="pln">ctx</span><span class="pun">.</span><span class="pln">fillRect</span><span class="pun">(-(</span><span class="pln">width </span><span class="pun">/</span><span class="pln"> </span><span class="lit">2</span><span class="pun">),</span><span class="pln"> </span><span class="pun">-(</span><span class="pln">height </span><span class="pun">/</span><span class="pln"> </span><span class="lit">2</span><span class="pun">),</span><span class="pln"> width</span><span class="pun">,</span><span class="pln"> height</span><span class="pun">);</span></pre>

<p>
	نرسم تاليًا الصورة باستخدام الدالة <code>drawImage</code> التي تقبل تسع معاملات:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4803_31" style=""><span class="pln">ctx</span><span class="pun">.</span><span class="pln">drawImage</span><span class="pun">(</span><span class="pln">image</span><span class="pun">,</span><span class="pln"> sprite </span><span class="pun">*</span><span class="pln"> </span><span class="lit">102</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">102</span><span class="pun">,</span><span class="pln"> </span><span class="lit">148</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> posX</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">74</span><span class="pun">,</span><span class="pln"> </span><span class="lit">102</span><span class="pun">,</span><span class="pln"> </span><span class="lit">148</span><span class="pun">);</span></pre>

<p>
	وكما ترى:
</p>

<ul>
	<li>
		خصصنا <code>image</code> لتكون الصورة التي نرسمها.
	</li>
	<li>
		يحدد المعاملان 2 و 3 إحداثيا الزاوية العليا اليسارية من الشريحة التي نريد اقتصاصها من الصورة المصدرية، ويكون X هو قيمة المتغير <code>sprite</code> مضروبًا بالعدد 102 (حيث يمثل المتغير عدد الشخصيات الموجودة في الصورة من 0 إلى 5) بينما تبقى قيمة Y هي <code>0</code>.
	</li>
	<li>
		يحدد المعاملان 4 و 5 أبعاد الشريحة التي نقتصها وهي 102x148 بكسل.
	</li>
	<li>
		يحدد المعاملان 6 و 7 الزاوية العليا اليسارية من الصندوق الذي نرسم ضمنه الشخصية، وتكون قيمة اﻹحداثي X هي <code>0 + posX</code> وبالتالي نستطيع تغيير مكان رسم الخصية بتغيير قيمة <code>posX</code>.
	</li>
	<li>
		يحدد المعاملان 8 و 9 أبعاد الصورة على اللوحة، وعلينا هنا المحافظة على اﻷبعاد اﻷصلية لهذا كانت قمة المعاملين 102 و 148 على التتالي:
	</li>
</ul>

<p>
	علينا تعديل قيمة المتغير <code>sprite</code> عند كل رسم
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4803_33" style=""><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">posX </span><span class="pun">%</span><span class="pln"> </span><span class="lit">13</span><span class="pln"> </span><span class="pun">===</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">sprite </span><span class="pun">===</span><span class="pln"> </span><span class="lit">5</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    sprite </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    sprite</span><span class="pun">++;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لاحظ كيف وضعنا الشيفرة السابقة ضمن الكتلة <code>({}if (posX % 13 === 0</code> واستخدمنا العامل <code>%</code> (عامل باقي القسمة) للتحقق من إمكانية قابلية قسمة قيمة المتغير <code>posX</code> على 13. فإن كان الوضع كذلك ننتقل إلى الشخصية التالية بزيادة قيمة المتغير <code>sprite</code> بمقدار 1 (ثم نعود إلى 0 عندما تصبح قيمته 5). ويعني ذلك فعليًا أننا نغير الشخصية عند اﻹطار 13 وتقريبًا حوالي 5 إطارات في الثانية (تكرر الدالة <code>()requestAnimationFrame</code> العملية بمعدل 60 إطار في الثانية إن أمكن). وعندما نعرض أخر شخصية نعود بعدها إلى الشخصية 0 وإلا سنزيد المتغير <code>sprite</code> بمقدار 1.
</p>

<p>
	سنعمل اﻵن على آلية تغيير قيمة <code>posX</code> مع كل إطار، لهذا عليك إضافة الشيفرة التالية تحت الشيفرة السابقة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4803_35" style=""><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">posX </span><span class="pun">&gt;</span><span class="pln"> width </span><span class="pun">/</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">let</span><span class="pln"> newStartPos </span><span class="pun">=</span><span class="pln"> </span><span class="pun">-(</span><span class="pln">width </span><span class="pun">/</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">102</span><span class="pun">);</span><span class="pln">
  posX </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">ceil</span><span class="pun">(</span><span class="pln">newStartPos</span><span class="pun">);</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">posX</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  posX </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	نستخدم عبارة <code><a href="https://wiki.hsoub.com/JavaScript/if...else" rel="external">if...else</a></code> للتحقق من تجاوز قيمة المتغير <code>posX</code> القيمة <code>width/2</code> والذي يعني خروج الشخصية من يمين لوحة الرسم، وعندها نحسب موقعًا جديدًا للشخصية يضعها على يسار الحافة اليسرى للوحة. بينما إن لم تتجاوز قيمة المتغير <code>posX</code> تلك القيمة نزيد قيمته بمقدار 2. وبالتالي ستتحرك الشخصية إلى اليمين قليلًا في الإطار التالي.
</p>

<p>
	ولا بد أخيرًا من تنفيذ الحركة السابقة باستمرار عن طريق استدعاء <code>()requestAnimationFrame</code> في نهاية الدالة <code>()draw</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4803_38" style=""><span class="pln">window</span><span class="pun">.</span><span class="pln">requestAnimationFrame</span><span class="pun">(</span><span class="pln">draw</span><span class="pun">);</span></pre>

<p>
	ستبدو نتيجة الشيفرة اﻵن كالتالي:
</p>

<p>
	<iframe height="260" loading="lazy" src="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/loops_animation/7_canvas_walking_animation/index.html" width="100%"></iframe>
</p>

<p>
	<strong>ملاحظة</strong>: بإمكانك الاطلاع على <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/loops_animation/7_canvas_walking_animation" rel="external nofollow">الشيفرة كاملة</a> لهذا المثال على جت-هب.
</p>

<h2 id="-3">
	تطبيق رسومي بسيط
</h2>

<p>
	كمثال أخير عن الرسوميات، سنعرض تطبيقًا بسيطًا جدًا للرسم نجمع فيه بين الاستجابة لمدخلات المستخدم (حركة الفأرة في هذا المثال) والحلقة التي تبني الرسم المتحرك. لن نشرح بالتفصيل خطوات بناء التطبيق بل سنلقي نظرة على الشيفرة اﻷكثر أهمية.
</p>

<p>
	بإمكانك الاطلاع على <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/loops_animation/8_canvas_drawing_app" rel="external nofollow">شيفرة التطبيق</a> من خلال المستودع المخص له على جت-هب.
</p>

<p>
	<iframe height="600" loading="lazy" src="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/loops_animation/8_canvas_drawing_app/index.html" width="100%"></iframe>
</p>

<p>
	لنلق نظرة على بعض اﻷجزاء المهمة:
</p>

<ul>
	<li>
		أولًا: نتتبع موقع الفأرة من خلال إحداثيات x و y، كما نترصد حدث نقر الفأرة وذلك من خلال المتغيرات <code>curX</code> و <code>curY</code> و <code>pressed</code>. وعندما تتحرك الفأرة يقع الحدث <code>onmousemove</code> وننفذ معالجه الذي يلتقط الإحداثيات الحالية لموقع الفأرة. كما نستخدم أيضًا معالجي الحدثين <code>onmousedown</code> و <code>onmouseup</code> لتغيير قيمة المتغير <code>pressed</code> إلى <code>true</code> عندما نضغط زر الفأرة وإلى <code>false</code> عندما نحرر الزر.
	</li>
</ul>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4803_40" style=""><span class="kwd">let</span><span class="pln"> curX</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">let</span><span class="pln"> curY</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">let</span><span class="pln"> pressed </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">

</span><span class="com">// حدّث إحداثيات موقع الفأرة</span><span class="pln">
document</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"mousemove"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  curX </span><span class="pun">=</span><span class="pln"> e</span><span class="pun">.</span><span class="pln">pageX</span><span class="pun">;</span><span class="pln">
  curY </span><span class="pun">=</span><span class="pln"> e</span><span class="pun">.</span><span class="pln">pageY</span><span class="pun">;</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

canvas</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"mousedown"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">pressed </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">));</span><span class="pln">

canvas</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"mouseup"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">pressed </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">));</span></pre>

<p>
	عند النقر على الزر "مسح اللوحة Clean canvas" ننفذ دالة بسيطة تمحي اللوحة بأكملها وتعيدها إلى اللون اﻷسود:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4803_42" style=""><span class="pln">clearBtn</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"click"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  ctx</span><span class="pun">.</span><span class="pln">fillStyle </span><span class="pun">=</span><span class="pln"> </span><span class="str">"rgb(0 0 0)"</span><span class="pun">;</span><span class="pln">
  ctx</span><span class="pun">.</span><span class="pln">fillRect</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> width</span><span class="pun">,</span><span class="pln"> height</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	وحلقة الرسم بسيطة هنا، فعندما تكون قيمة المتغير <code>pressed</code> هي <code>true</code>، نرسم دائرة لها اللون الذي يحدده منتقي اﻷلوان color picker ونصف قطر يحدده عنصر تحديد المجال range input. وعلينا رسم الدائرة فوق النقطة المحددة بمقدار 85 بكسل، ذلك أن القياس مأخوذ بالنسبة إلى أعلى شاشة العرض (أعلى نافذة المتصفح) لكننا نرسم الدائرة بالنسبة ﻷعلى اللوحة التي تبدأ تحت شريط التحكم (الذي يضم منتقي اﻷلوان ومحدد نصف القطر) ذو الارتفاع 85 بكسل. ولو رسمنا الدائرة اعتمادًا على قيمة <code>curY</code> ستبدو الدائرة تحت النقطة المحددة للرسم بحدود 85 بكسل.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4803_44" style=""><span class="kwd">function</span><span class="pln"> draw</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">pressed</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    ctx</span><span class="pun">.</span><span class="pln">fillStyle </span><span class="pun">=</span><span class="pln"> colorPicker</span><span class="pun">.</span><span class="pln">value</span><span class="pun">;</span><span class="pln">
    ctx</span><span class="pun">.</span><span class="pln">beginPath</span><span class="pun">();</span><span class="pln">
    ctx</span><span class="pun">.</span><span class="pln">arc</span><span class="pun">(</span><span class="pln">
      curX</span><span class="pun">,</span><span class="pln">
      curY </span><span class="pun">-</span><span class="pln"> </span><span class="lit">85</span><span class="pun">,</span><span class="pln">
      sizePicker</span><span class="pun">.</span><span class="pln">value</span><span class="pun">,</span><span class="pln">
      degToRad</span><span class="pun">(</span><span class="lit">0</span><span class="pun">),</span><span class="pln">
      degToRad</span><span class="pun">(</span><span class="lit">360</span><span class="pun">),</span><span class="pln">
      </span><span class="kwd">false</span><span class="pun">,</span><span class="pln">
    </span><span class="pun">);</span><span class="pln">
    ctx</span><span class="pun">.</span><span class="pln">fill</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  requestAnimationFrame</span><span class="pun">(</span><span class="pln">draw</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

draw</span><span class="pun">();</span></pre>

<p>
	جميع أنواع عنصر الدخل <code>&lt;input&gt;</code> مدعومة جيدًا من قبل المتصفحات، وإن لم يدعمها متصفح سيعرض حقل نصي نمطي بدلًا عنه.
</p>

<h2 id="webgl">
	الواجهة WebGL
</h2>

<p>
	لنترك اﻵن البيئة الرسومية ثنائية البعد ونلقي نظرة سريعة على لوحات الرسم ثلاثية اﻷبعاد. تُستخدم الواجهة البرمجية <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API" rel="external nofollow">WebGL <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a> للعمل مع الرسومات ثلاثية البعد، وهي واجهة منفصلة تمامًا عن واجهة البيئة الرسومية ثنائية البعد مع أن شيفرتهما تُصيّر ضمن العنصر نفسه <code>&lt;canvas&gt;</code>.
</p>

<p>
	بنيت WebGL على أساس OpenGL (مكتبة الرسوميات المفتوحة Open Graphics Library) وتسمح لك بالتواصل مباشرة مع المعالج الرسومي للحاسوب GPU. لهذا فكتابة شيفرة WebGL خام أشبه بكتابة شيفرات لغات منخفضة المستوى مثل ++C مقارنة بشيفرة جافا سكريبت، فهي معقدة لكنها قوية جدًا.
</p>

<h3 id="-4">
	استخدام مكتبة جافا سكريبت خارجبة
</h3>

<p>
	يستخدم معظم المطورون مكتبات يقدمها طرف آخر عند العمل مع الرسوميات ثلاثية البعد نظرًا لتعقيد WebGL مثل <a href="https://developer.mozilla.org/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Three.js" rel="external nofollow">Three.js</a> أو <a href="https://developer.mozilla.org/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_PlayCanvas" rel="external nofollow">PlayCanvas</a> أو <a href="https://developer.mozilla.org/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Babylon.js" rel="external nofollow">Babylon.js</a>. تعمل هذه المكتبات عومًا على نحو متشابه، فهي تقدم دوال أولية وأشكال مخصصة وكاميرات لعرض الموقع وطرق لتطبيق اﻹضاءة والظل ولتغطية السطوح بخامات مختلفة وغيرها. فهذه المكتبات تتعامل مباشرة مع WebGL بدلًا منك متيحة لك المجال للعمل وفق سوية برمجية أعلى. ويعني هذا بالطبع تعلم واجهات برمجية أخرى (واجهات يقدمها طرف آخر في حالتنا) لكنها أبسط بكثير من التعامل مع شيفرة WebGL الخام.
</p>

<h3 id="-5">
	إعادة إنشاء مكعب
</h3>

<p>
	لنلق نظرة على مثال بسيط يشرح استخدام المكتبة WebGL، وسنختار فيه المكتبة Three.js كونها من أكثر المكتبات استخدامًا. وسنبني في مثالنا مكعب ثلاثي اﻷبعاد يدور حول نفسه.
</p>

<ol>
	<li>
		أنشئ نسخة عن <a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/drawing-graphics/threejs-cube/index.html" rel="external nofollow">ملف المثال</a>) ضمن مجلد جديد ثم احفظ نسخة من الملف <a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/drawing-graphics/threejs-cube/metal003.png" rel="external nofollow">metal003.png</a> في المجلد نفسه. ويمثل الملف اﻷخير الصورة التي نستخدمها لتغطية سطح المكعب لاحقًا.
	</li>
	<li>
		أنشئ ملفًا جديدًا باسم <code>script.js</code> في نفس المجلد السابق.
	</li>
	<li>
		نزّل <a href="https://raw.githubusercontent.com/mrdoob/three.js/dev/build/three.min.js" rel="external nofollow">المكتبة Three.min.js</a> وخزنها في نفس المجلد السابق.
	</li>
	<li>
		لدينا اﻵن الملف three.js الذي يرتبط بصفحتنا، ويمكننا كتابة الشيفرة الذي تستخدمه ضمن الملف script.js.
	</li>
</ol>

<p>
	لنبدأ بإنشاء مشهد جديد عن طريق إضافة الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4803_46" style=""><span class="kwd">const</span><span class="pln"> scene </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> THREE</span><span class="pun">.</span><span class="typ">Scene</span><span class="pun">();</span></pre>

<p>
	تُنشئ الدالة البانية<code>()scene</code> مشهدًا جديدًا يمثل بيئة عمل ثلاثية اﻷبعاد التي نريد عرضها. ونضيف بعد ذلك كاميرا لرؤية المشهد. ووفق مصطلحات التصميم ثلاثي اﻷبعاد، تمثل الكاميرا موقع المراقب، وﻹنشائها أضف اﻷسطر التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4803_48" style=""><span class="kwd">const</span><span class="pln"> camera </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> THREE</span><span class="pun">.</span><span class="typ">PerspectiveCamera</span><span class="pun">(</span><span class="pln">
  </span><span class="lit">75</span><span class="pun">,</span><span class="pln">
  window</span><span class="pun">.</span><span class="pln">innerWidth </span><span class="pun">/</span><span class="pln"> window</span><span class="pun">.</span><span class="pln">innerHeight</span><span class="pun">,</span><span class="pln">
  </span><span class="lit">0.1</span><span class="pun">,</span><span class="pln">
  </span><span class="lit">1000</span><span class="pun">,</span><span class="pln">
</span><span class="pun">);</span><span class="pln">
camera</span><span class="pun">.</span><span class="pln">position</span><span class="pun">.</span><span class="pln">z </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">;</span></pre>

<p>
	تأخذ الدالة البانية <code>PrespectiveCamera</code> أربع وسطاء:
</p>

<ul>
	<li>
		حقل الرؤية: ويدل على اتساع المساحة أمام الكاميرا التي يجب عرضها على الشاشة مقدرة بالدرجات.
	</li>
	<li>
		نسبة العرض aspect ratio: وهي عادة نسبة اتساع الشاشة مقسومًا على ارتفاعها، واستخدام نسب أخرى ستشوه المشهد.
	</li>
	<li>
		مستوي البعد: وتمثل البعد عن الكاميرا الذي لن تصير بعده اﻷشياء.
	</li>
</ul>

<p>
	نضبط أيضًا موقع الكاميرا ليكون على بعد خمس وحدات قياس بعيدًا عن المحور z، وهذا مشابه للخاصية <code>z-index</code> في CSS التي تمثل موقع العنصر بعيدًا عن الشاشة باتجاهك.
</p>

<p>
	أما المكون الحيوي الثالث فهو المصيّر renderer، وهو كائن يصير المشهد كما يُرى من الكاميرا. وسننشئ اﻵن مصيّرًا باستخدام الدالة البانية <code>()WebGLRenderer</code> لكننا لن نستخدمه حاليًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4803_50" style=""><span class="kwd">const</span><span class="pln"> renderer </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> THREE</span><span class="pun">.</span><span class="typ">WebGLRenderer</span><span class="pun">();</span><span class="pln">
renderer</span><span class="pun">.</span><span class="pln">setSize</span><span class="pun">(</span><span class="pln">window</span><span class="pun">.</span><span class="pln">innerWidth</span><span class="pun">,</span><span class="pln"> window</span><span class="pun">.</span><span class="pln">innerHeight</span><span class="pun">);</span><span class="pln">
document</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">renderer</span><span class="pun">.</span><span class="pln">domElement</span><span class="pun">);</span></pre>

<p>
	يُنشئ السطر الأول مصيرًا جديدًا والثاني يضبط الأبعاد التي سيرسم المصير ضمنها ما تعرضه الكاميرا، بينما يربط السطر الثالث العنصر <code>&lt;canvas&gt;</code> الذي يُنشئه المصيّر بجسم مستند HTML وسيُعرض كل ما يرسمه المصيّر في نافذة المتصفح.
</p>

<p>
	وﻹنشاء المكعب الذي نريد رسمه في اللوحة، عليك إضافة اﻷسطر التالية إلى نهاية ملف جافا سكريبت:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4803_52" style=""><span class="kwd">let</span><span class="pln"> cube</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> loader </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> THREE</span><span class="pun">.</span><span class="typ">TextureLoader</span><span class="pun">();</span><span class="pln">

loader</span><span class="pun">.</span><span class="pln">load</span><span class="pun">(</span><span class="str">"metal003.png"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">texture</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  texture</span><span class="pun">.</span><span class="pln">wrapS </span><span class="pun">=</span><span class="pln"> THREE</span><span class="pun">.</span><span class="typ">RepeatWrapping</span><span class="pun">;</span><span class="pln">
  texture</span><span class="pun">.</span><span class="pln">wrapT </span><span class="pun">=</span><span class="pln"> THREE</span><span class="pun">.</span><span class="typ">RepeatWrapping</span><span class="pun">;</span><span class="pln">
  texture</span><span class="pun">.</span><span class="pln">repeat</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> geometry </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> THREE</span><span class="pun">.</span><span class="typ">BoxGeometry</span><span class="pun">(</span><span class="lit">2.4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2.4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2.4</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> material </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> THREE</span><span class="pun">.</span><span class="typ">MeshLambertMaterial</span><span class="pun">({</span><span class="pln"> map</span><span class="pun">:</span><span class="pln"> texture </span><span class="pun">});</span><span class="pln">
  cube </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> THREE</span><span class="pun">.</span><span class="typ">Mesh</span><span class="pun">(</span><span class="pln">geometry</span><span class="pun">,</span><span class="pln"> material</span><span class="pun">);</span><span class="pln">
  scene</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="pln">cube</span><span class="pun">);</span><span class="pln">

  draw</span><span class="pun">();</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	هناك نقاط عديدة يجدر شرحها في الشيفرة السابقة:
</p>

<ul>
	<li>
		<p>
			ننشئ أولًا المتغير العام <code>cube</code> لكي نصل إلى المكعب في أي مكان من الشيفرة.
		</p>
	</li>
	<li>
		<p>
			ننشئ تاليًا كائن <code>TextureLoader</code> جديد ونستدعي التابع <code>()load</code> العائد له. ويأخذ هذا التابع معاملين في حالتنا (علمًا أنه يأخذ أكثر): الخامة التي نريد أن نحمّلها (صورة PNG) ودالة ننفذها عند اكتمال تحميل الخامة.
		</p>
	</li>
	<li>
		<p>
			نستخدم داخل الدالة السابقة خاصيات الكائن لتكرار الصورة التي تغلف جميع أوجه المكعب بمقدار 2x2. ومن ثم ننشئ كائن <code>BoxGeometry</code> وكائن <code>MeshLambertMaterial</code> جديدان ونربطهما معًا ضمن شبكة <code>Mesh</code> ﻹنشاء المكعب. ويحتاج أي كائن نمطيًا إلى بنية هندسية (الشكل الذي سيكون عليه) ومظهر مادي (كيف سيبدو السطح الخارجي).
		</p>
	</li>
	<li>
		<p>
			وفي النهاية، نضيف المكعب إلى المشهد ومن ثم نستدعي الدالة <code>()draw</code> لتبدأ عملية تحريك الرسم.
		</p>
	</li>
</ul>

<p>
	وقبل أن نعرّف الدالة <code>()draw</code>، نضيف زوجًا من الأضواء إلى المشهد، ليبدو المشهد أكثر حيوية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4803_54" style=""><span class="kwd">const</span><span class="pln"> light </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> THREE</span><span class="pun">.</span><span class="typ">AmbientLight</span><span class="pun">(</span><span class="str">"rgb(255 255 255)"</span><span class="pun">);</span><span class="pln"> </span><span class="com">// soft white light</span><span class="pln">
scene</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="pln">light</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> spotLight </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> THREE</span><span class="pun">.</span><span class="typ">SpotLight</span><span class="pun">(</span><span class="str">"rgb(255 255 255)"</span><span class="pun">);</span><span class="pln">
spotLight</span><span class="pun">.</span><span class="pln">position</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(</span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">);</span><span class="pln">
spotLight</span><span class="pun">.</span><span class="pln">castShadow </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
scene</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="pln">spotLight</span><span class="pun">);</span></pre>

<p>
	يُعد الكائن <code>AmbientLight</code> نوعًا من اﻷضواء البرمجية التي تضيئ المشهد بأكمله بما يشبه الشمس التي تضيء عليك وأنت في الخارج. بينما يمثل الكائن <code>spotLight</code> شعاع ضوئي وفق اتجاه محدد مثل مشعل أو بقعة ضوء.
</p>

<p>
	لنضف اﻵن الدالة <code>()draw</code> إلى أسفل شيفرة جافا سكريبت:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4803_56" style=""><span class="kwd">function</span><span class="pln"> draw</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  cube</span><span class="pun">.</span><span class="pln">rotation</span><span class="pun">.</span><span class="pln">x </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">0.01</span><span class="pun">;</span><span class="pln">
  cube</span><span class="pun">.</span><span class="pln">rotation</span><span class="pun">.</span><span class="pln">y </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">0.01</span><span class="pun">;</span><span class="pln">
  renderer</span><span class="pun">.</span><span class="pln">render</span><span class="pun">(</span><span class="pln">scene</span><span class="pun">,</span><span class="pln"> camera</span><span class="pun">);</span><span class="pln">

  requestAnimationFrame</span><span class="pun">(</span><span class="pln">draw</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	الشيفرة السابقة واضحة عمومًا، إذ ندوّر المكعّب قليلًا في كل إطار حول محوريه اﻷفقي والشاقولي ونصيّر المشهد كما يُرى من الكاميرا ونستدعي أخيرًا الدالة <code>()requestAnimationFrame</code> لتحضير رسم اﻹطار التالي:
</p>

<p>
	إليك المشهد بشكله النهائي:
</p>

<p>
	<iframe height="500" loading="lazy" src="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/threejs-cube/index.html" width="100%"></iframe>
</p>

<p>
	<strong>ملاحظة</strong>: بإمكانك الاطلاع على <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/threejs-cube" rel="external nofollow">الشيفرة كاملة</a> لهذا المثال على جت-هب.
</p>

<h2 id="-6">
	الخلاصة
</h2>

<p>
	لا بد وأن تكون في نهاية هذا المقال قد امتلكت فكرة لا بأس بها عن أساسيات برمجة الرسوميات باستخدام الواجهة البرمجية Canvas و المكتبة WebGL وما يمكن فعله باستخدام هاتين الواجهتين، وامتلكت فكرة جيدًا عن الأماكن التي تقصدها لتحصل على معلومات أكثر.
</p>

<p>
	ترجمة -وبتصرف- للقسم الثاني من مقال <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics" rel="external nofollow">Drawing graphics</a>
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D8%A7%D8%AA-%D8%AB%D9%86%D8%A7%D8%A6%D9%8A%D8%A9-%D8%A7%D9%84%D8%A8%D8%B9%D8%AF-%D8%B6%D9%85%D9%86-%D8%A7%D9%84%D8%B9%D9%86%D8%B5%D8%B1-canvas-r2431/" rel="">العمل مع الرسوميات في جافا سكريبت: الرسومات ثنائية البعد ضمن العنصر Canvas</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-webgl-%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%AA%D9%81%D8%A7%D8%B5%D9%8A%D9%84-%D8%A5%D9%84%D9%89-%D8%B3%D8%B7%D8%AD-%D9%85%D8%AC%D8%B3%D9%91%D9%8E%D9%85-r489/" rel="">مقدمة إلى WebGL - إضافة التفاصيل إلى سطح مجسَّم</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/game-development/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B5%D9%86%D8%A7%D8%B9%D8%A9-%D8%A3%D9%84%D8%B9%D8%A7%D8%A8-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD-r767/" rel="">مدخل إلى صناعة ألعاب المتصفح</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%B1%D8%B3%D9%85-%D8%B9%D9%84%D9%89-%D9%84%D9%88%D8%AD%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1364/" rel="">الرسم على لوحة في جافاسكربت</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2435</guid><pubDate>Fri, 25 Oct 2024 15:02:01 +0000</pubDate></item><item><title>&#x627;&#x644;&#x639;&#x645;&#x644; &#x645;&#x639; &#x627;&#x644;&#x631;&#x633;&#x648;&#x645;&#x64A;&#x627;&#x62A; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627; &#x633;&#x643;&#x631;&#x64A;&#x628;&#x62A;: &#x627;&#x644;&#x631;&#x633;&#x648;&#x645;&#x627;&#x62A; &#x62B;&#x646;&#x627;&#x626;&#x64A;&#x629; &#x627;&#x644;&#x628;&#x639;&#x62F; &#x636;&#x645;&#x646; &#x627;&#x644;&#x639;&#x646;&#x635;&#x631; Canvas</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D8%A7%D8%AA-%D8%AB%D9%86%D8%A7%D8%A6%D9%8A%D8%A9-%D8%A7%D9%84%D8%A8%D8%B9%D8%AF-%D8%B6%D9%85%D9%86-%D8%A7%D9%84%D8%B9%D9%86%D8%B5%D8%B1-canvas-r2431/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_10/-------------Canvas.png.c655dfa2c42c7463ad2af433b3164b7a.png" /></p>
<p>
	يتضمن المتصفح مجموعة أدوات برمجية فعّالة للتعامل الرسوميات ابتداءً من لغة إنشاء الرسوميات الشعاعية <a href="https://academy.hsoub.com/programming/html/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%B4%D8%B9%D8%A7%D8%B9%D9%8A%D8%A9-svg-%D8%A5%D9%84%D9%89-%D8%B5%D9%81%D8%AD%D8%A9-html-r1833/" rel="">SVG</a>، إلى الواجهات التي تسمح لك بالرسم ضمن العنصر <code>&lt;canvas&gt;</code>. لهذا سنقدم في هذا المقال مدخلًا إلى الوجهة البرمجية Canvas، إضافة إلى بعض الموارد اﻷخرى لتزيد من معارفك في هذا المجال.
</p>

<p>
	ننصحك قبل المتابعة في قراءة هذه المقالات أن:
</p>

<ul>
	<li>
		تكون ملمًا بلغتي <a href="HTTPS://wiki.hsoub.com/HTML" rel="external">HTML</a> و <a href="HTTPS://wiki.hsoub.com/CSS" rel="external">CSS</a>.
	</li>
	<li>
		تكون ملمًا بلغة <a href="HTTPS://wiki.hsoub.com/JavaScript" rel="external">جافا سكريبت</a>.
	</li>
	<li>
		تطلع على سلاسل المقالات السابقة التي ناقشت <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D8%B1%D9%91%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D9%85%D9%86-%D9%85%D9%86%D8%B8%D9%88%D8%B1-%D8%B9%D8%A7%D9%85-r2266/" rel="">أساسيات جافا سكريبت</a> و<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-prototype-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2310/" rel="">الكائنات في جافا سكريبت</a>.
	</li>
	<li>
		تطلع على <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-web-apis-r2355/" rel="">أساسيات الواجهات البرمجية في طرف العميل</a>.
	</li>
</ul>

<h2 id="">
	الرسوميات في الويب
</h2>

<p>
	ذكرنا في <a href="https://academy.hsoub.com/programming/html/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%B5%D9%88%D8%B1-%D9%81%D9%8A-%D8%B5%D9%81%D8%AD%D8%A9-html-r1824/" rel="">مقالات سابقة</a> أن الويب كان بداية نصيًا -أي يعرض محتوى نصي فقط- بشكل كامل مما جعله ضعيف الجاذبية، لذلك ظهرت <a href="https://academy.hsoub.com/programming/html/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%B5%D9%88%D8%B1-%D9%81%D9%8A-%D8%B5%D9%81%D8%AD%D8%A9-html-r1824/" rel="">الصور</a> بداية من خلال <a href="https://wiki.hsoub.com/HTML/img" rel="external">العنصر &lt;img&gt;</a> ولاحقًا من خلال خاصيات <a href="https://academy.hsoub.com/programming/css/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-css/" rel="">لغة التنسيق CSS</a> مثل الخاصية <code><a href="https://wiki.hsoub.com/CSS/background-image" rel="external">background-image</a></code> و من ثم بدأ استخدام الصور الشعاعية أو المتجهة <a href="https://academy.hsoub.com/programming/html/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%B4%D8%B9%D8%A7%D8%B9%D9%8A%D8%A9-svg-%D8%A5%D9%84%D9%89-%D8%B5%D9%81%D8%AD%D8%A9-html-r1833" rel="">SVG</a>.
</p>

<p>
	مع ذلك لم يكن كل هذا كافيًا. وعلى الرغم من إمكانية استخدام CSS وجافا سكريبت لتحريك الصور الشعاعية SVG vector images والتعامل معها كونها تُكتب باستخدام تعليمات ترميز markup ولم تكن هناك طريقة لعمل المثل على الصور النقطية bitmap images وكانت الأدوات المتوفرة محدودة. ولم توجد طريقة أصيلة في الويب ﻹنشاء الرسوميات المتحركة أو اﻷلعاب أو المشاهد ثلاثية الأبعاد والتي تحتاج متطلبات خاصة تتعامل معها <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%B3%D8%AA%D9%88%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/" rel="">لغات برمجة منخفضة المستوى</a> مثل ++C وجافا.
</p>

<p>
	بدأ الوضع بالتحسن عندما دعمت المتصفحات العنصر والواجهة البرمجية المتعلقة به في عام 2004. وكما سنرى تاليًا، تقدم <a href="https://academy.hsoub.com/programming/javascript/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%B9%D9%86%D8%B5%D8%B1-canvas-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r207/" rel="">عناصر canvas</a> بعض اﻷدوات المفيدة التي تساعد في إنشاء رسوميات متحركة ثنائية البعد وألعاب، وعرض البيانات وغيرها من اﻹمكانات وخاصة عندما تتكامل مع واجهات برمجية أخرى تقدمها منصة الويب. لكن كان من الصعب إعدادها للوصول السهل accessibility.
</p>

<p>
	سترى في المثال التالي الكرات القافزة المرتدة التي عملنا عليها في <a href="https://academy.hsoub.com/programming/javascript/%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%A7%D9%84%D9%83%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B1%D8%AA%D8%AF%D8%A9-%D8%A7%D9%84%D9%85%D9%84%D9%88%D9%86%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%A3%D9%88%D9%84-r2323/" rel="">مقال سابق</a> وهي مشهد ثنائي البعد مبني على أساس العنصر canvas.
</p>

<p>
	<iframe height="500" loading="lazy" src="https://mdn.github.io/learning-area/javascript/oojs/bouncing-balls/index-finished.html" width="100%"></iframe>
</p>

<p>
	وفي الفترة الممتدة بين 2006 إلى 2007 عملت موزيللا على إنجاز عناصر لوحات رسومية canvas ثلاثية اﻷبعاد، وتحولت فيما بعد إلى <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-webgl-%D8%AA%D8%B9%D8%B1%D9%8A%D9%81-r481/" rel="">WebGL</a> التي حظيت باهتمام مطوري المتصفحات وقد جرى توصيفها لتكون معيارًا بين عامي 2009-2010. وتتيح لك الواجهة WebGL إنشاء رسوميات ثلاثية اﻷبعاد ضمن المتصفح. بقدم المثال التالي مكعبًا يدور باستخدام هذه الواجهة:
</p>

<p>
	<iframe height="500" loading="lazy" src="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/threejs-cube/index.html" width="100%"></iframe>
</p>

<p>
	نركز في مقالنا على لوحات الرسم ثنائية البعد، وبما أن شيفرة WebGL الخام شديدة التعقيد، سنعرض طريقة استخدام المكتبة <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-webgl-%D8%AA%D8%B9%D8%B1%D9%8A%D9%81-r481/" rel="">WebGL</a> ﻹنشاء مشهد ثلاثي اﻷيعاد بسهولة أكبر.
</p>

<h2 id="canvas-1">
	تطبيق عملي: ابدأ العمل مع لوحة الرسم canvas
</h2>

<p>
	إن أردت إنشاء رسوميات ثنائية وثلاثية البعد على صفحة ويب عليك أن تنطلق من عنصر HTML الذي يُمثّل لوحة الرسم <code><a href="https://wiki.hsoub.com/HTML/canvas" rel="external">&lt;canvas&gt;</a></code>. ويُستخدم هذا العنصر في تحديد منطقة من الصفحة للرسم فيها. واﻷمر بسيط ويتم بإضافة عنصر <code>&lt;canvas&gt;</code> إلى الصفحة كما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7011_8" style=""><span class="tag">&lt;canvas</span><span class="pln"> </span><span class="atn">width</span><span class="pun">=</span><span class="atv">"320"</span><span class="pln"> </span><span class="atn">height</span><span class="pun">=</span><span class="atv">"240"</span><span class="tag">&gt;&lt;/canvas&gt;</span></pre>

<p>
	تنشئ الشيفرة السابقة لوحة رسم أبعادها 230 و 240 بكسل. ولا بد أن تضع شيئًا ما ضمن وسمي البداية والنهاية للعنصر كي يصف محتوى اللوحة لمستخدمي المتصفحات التي لا تدعم العنصر أو لمستخدمي قارئات الشاشة:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7011_10" style=""><span class="tag">&lt;canvas</span><span class="pln"> </span><span class="atn">width</span><span class="pun">=</span><span class="atv">"320"</span><span class="pln"> </span><span class="atn">height</span><span class="pun">=</span><span class="atv">"240"</span><span class="tag">&gt;</span><span class="pln">
 </span><span class="tag">&lt;p&gt;</span><span class="pln">اكتب هنا وصف اللوحة للمستخدمين الذين لا يمكنهم رؤيتها </span><span class="tag">&lt;/p&gt;</span><span class="pln">
</span><span class="tag">&lt;/canvas&gt;</span></pre>

<p>
	و لابد أن يعتبر ما تضعه ضمن وسمي العنصر بديلًا مفيدًا عن محتوى اللوحة، فإن كنت تصيّر أو تعرض رسمًا يتغير بشكل مستمر ليعبر عن أسعار البورصة مثلًا ، ينبغي أن يكون المحتوى البديل صورة تتضمن آخر تحديث للرسم مع نص بديل عنها <code>alt</code> يتحدث عن اﻷسعار أو قائمة من الروابط المستقلة لكل صفحة من صفحات هذه البورصة.
</p>

<p>
	<strong>ملاحظة</strong>: لا يمكن الوصول إلى محتوى لوحة الرسم من خلال قارئات الشاشات، لهذا عليك وضع نص يصف محتواها على شكل قيمة<br>
	للسمة <code>arial-label</code> ضمن العنصر <code>&lt;canvas&gt;</code> نفسه أو استخدام محتوى مستقل ضمن وسمي البداية والنهاية للعنصر. وتذكر أن محتوى <code>&lt;canvas&gt;</code> ليس جزءًا من <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%B4%D8%AC%D8%B1%D9%8A%D8%A9-%D9%84%D9%80-dom-r646/" rel="">شجرة DOM</a> لكن العنصر الذي تضعه ضمنه كذلك.
</p>

<h3 id="-1">
	إنشاء لوحة رسم وتحديد أبعادها
</h3>

<p>
	لنبدأ بإنشاء لوحة رسم خاصة بتطبيقنا، لهذا اتبع الخطوات التالية:
</p>

<ol>
	<li>
		انسخ <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/0_canvas_start" rel="external nofollow">مجلد المشروع</a> الذي يتضمن الملفات التالية:
	</li>
</ol>

<ul>
	<li>
		index.html
	</li>
	<li>
		script.js
	</li>
	<li>
		style.css
	</li>
</ul>

<ol>
	<li>
		افتح الملف index.html ثم أضف الشيفرة التالية ضمنه تحت الوسم <code>&lt;body&gt;</code>:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7011_13" style=""><span class="tag">&lt;canvas</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"myCanvas"</span><span class="tag">&gt;</span><span class="pln">
 </span><span class="tag">&lt;p&gt;</span><span class="pln">Add suitable fallback here.</span><span class="tag">&lt;/p&gt;</span><span class="pln">
</span><span class="tag">&lt;/canvas&gt;</span></pre>

<p>
	أضفنا في الكود أعلاه صنفًا إلى العنصر <code>&lt;canvas&gt;</code> حتى يسهل الوصول إليه عن طريق جافا سكريبت في حال كان هناك أكثر من لوحة نريد العمل معها، لكننا أزلنا السمتين <code>width</code> و <code>height</code> حاليًا (بإمكانك إعادتهما إن أردت، لكننا سنضبطهما لاحقًا باستخدام جافا سكريبت). وستأخذ اللوحات افتراضيًا ارتفاعًا مقداره 150 بكسل واتساعًا مقداره 300 بكسل.
</p>

<ol start="3">
	<li>
		افتح الملف scripts.js ثم أضف شيفرة جافا سكريبت التالية:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7011_15" style=""><span class="kwd">const</span><span class="pln"> canvas </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">".myCanvas"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> width </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">canvas</span><span class="pun">.</span><span class="pln">width </span><span class="pun">=</span><span class="pln"> window</span><span class="pun">.</span><span class="pln">innerWidth</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> height </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">canvas</span><span class="pun">.</span><span class="pln">height </span><span class="pun">=</span><span class="pln"> window</span><span class="pun">.</span><span class="pln">innerHeight</span><span class="pun">);</span></pre>

<p>
	خزّنا هنا مرجعًا إلى لوحة الرسم ضمن الثابت <code>canvas</code> ومن ثم أنشأنا ثابتًا آخر <code>width</code> وضبطنا قيمته وقيمة اتساع اللوحة لتكون مساوية لاتساع نافذة المتصفح <code>Window.innerWidth</code>، وكررنا ما فعلناه في السطر الثالث لكن مع ارتفاع اللوحة.
</p>

<p>
	وهكذا ستملأ لوحة الرسم نافذة المتصفح (تذكر أن الارتفاع هنا هو ارتفاع نافذة العرض viewport). لاحظ أيضًا كيف نفذنا سلسلة من اﻹسنادات باستخدام عدة عوامل مساواة <code>=</code>، وهذا أمر مسموح في جافا سكريبت ويُعد مفيدًا إن أردت إسناد القيمة ذاتها إلى عدة متغيرات. كما حرصنا على تأمين طريقة للوصول إلى أبعاد اللوحة بإسنادها إلى متغيرات، وتأتي فائدة هذه الفكرة إن احتجنا لاحقًا على سبيل المثال إلى رسم شيء ما في وسط اللوحة تمامًا.
</p>

<p>
	<strong>ملاحظة</strong>: علينا غالبًا ضبط أبعاد الصور باستخدام سمات HTML أو خاصيات شجرة DOM كما شرحنا في اﻷعلى. كما يمكنك استخدام CSS لكن تطبيق اﻷبعاد الجديدة سيكون بعد تصيير لوحة الرسم وهكذا قد تتعرض لوحة الرسم كغيرها من الصورة إلى التشوه.
</p>

<h3 id="-2">
	ضبط مسار العمل على اللوحة وإنهاء اﻹعداد
</h3>

<p>
	نحتاج إلى مرجع خاص إلى منطقة العمل حتى نستطيع الرسم على اللوحة يُعرف بمسار العمل context. وننفذ هذا اﻷمر باستخدام التابع <code>()HTMLCanvasElement.getContext</code> الذي يأخذ معاملًا واحدًا بأبسط حالات استخدامه تمثل نوع مسار العمل الذي نريده. وما نحتاجه في تطبيقنا لوحة ثنائية البعد، لهذا سنضيف شيفرة جافا سكريبت التالية في آخر الشيفرة الموجودة في الملف script.js:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7011_17" style=""><span class="kwd">const</span><span class="pln"> ctx </span><span class="pun">=</span><span class="pln"> canvas</span><span class="pun">.</span><span class="pln">getContext</span><span class="pun">(</span><span class="str">"2d"</span><span class="pun">);</span></pre>

<p>
	<strong>ملاحظة</strong>: بإمكانك اختيار مسارات عمل أخرى مثل <code>webgl</code> من أجل WebGL و <code>webgl2</code> من أجل 2 WebGL لكننا لن تحتاج هذه المسارات في مقالنا.
</p>

<p>
	وهكذا تصبح لوحة الرسم جاهزة في تطبيقنا، وسيحمل المتغير <code>ctx</code> الكائن <a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D" rel="external nofollow"><code>CanvasRenderingContext2D</code></a><code> </code>وسيكون الرسم على اللوحة من خلال التعامل مع هذا الكائن.
</p>

<p>
	دعونا قبل إكمال العمل ننفذ شيئًا أخيرًا وهو تلوين خلفية الصفحة لتأخذ فكرة بسيطة عن الواجهة البرمجية Canvas. أضف الشيفرة التالية إلى شيفرة الملف <code>script.js</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7011_19" style=""><span class="pln">ctx</span><span class="pun">.</span><span class="pln">fillStyle </span><span class="pun">=</span><span class="pln"> </span><span class="str">"rgb(0 0 0)"</span><span class="pun">;</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">fillRect</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> width</span><span class="pun">,</span><span class="pln"> height</span><span class="pun">);</span></pre>

<p>
	نضبط في هذه الشيفرة لون الخلفية باستخدام الخاصية <code>fillStyle</code> التي تأخذ قيمًا لونية كما هو حال خاصيات CSS المشابهة، ثم نرسم مربعًا يغطي كامل لوحة الرسم باستخدام التابع <code>fillRect</code>، ويمثل أول معاملين له الزاوية العليا اليسارية والمعاملين الباقين اتساع وارتفاع المربع الذي نريد رسمه (أخبرناك أن للمتغيرين <code>width</code> و <code>height</code> فوائد لاحقة).
</p>

<h2 id="canvas-2">
	أساسيات الرسوميات ثنائية البعد ضمن العنصر <code>&lt;canvas&gt;</code>
</h2>

<p>
	ذكرنا سابقًا أن جميع عمليات الرسم تجري من خلال التعامل مع الكائن <code>CanvasRenderingContext2D</code> (وهو <code>ctx</code> في تطبيقنا). وتحتاج الكثير من العمليات إلى إحداثيات لتحديد المكان الذي نرسم فيه بدقة، وتكون الزاوية العليا اليسارية بمثابة مبدأ الجملة اﻹحداثية وتمثل النقطة (0,0)، بينما يتجه المحور الأفقي (x) من اليسار نحو اليمين والعمودي (y) من اﻷعلى إلى اﻷسفل.
</p>

<p style="text-align: center;">
	<img alt="01_canvas_default_grid.png" class="ipsImage ipsImage_thumbnailed" data-fileid="159968" data-ratio="100.00" data-unique="6niesdts3" width="220" src="https://academy.hsoub.com/uploads/monthly_2024_10/01_canvas_default_grid.png.119410aed9549c373d90a0bd686245fd.png">
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" href="" rel=""> </a>
</p>

<p>
	تميل معظم الرسوميات إلى استخدام المربع البدائي primitive rectangle (الذي يمثل شكل أساسي يستخدم لبناء رسوميات أكثر تعقيدًا) أو تتبع خط عبر مسار محدد ومن ثم ملء الشكل الناتج. وسنشرح تاليًا كيف يجري اﻷمر.
</p>

<h3 id="-3">
	مربعات بسيطة
</h3>

<p>
	سنبدأ برسم بعض المربعات البسيطة، لهذا:
</p>

<ol>
	<li>
		انسخ شيفرة قالب لوحة الرسم الذي حضرناه سابقًا (كما يمكنك إنشاء نسخة عن <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/1_canvas_template" rel="external nofollow">مجلد التطبيق</a> إن لم تتابع معنا الخطوات السابقة).
	</li>
	<li>
		أضف الأسطر التالية من الشيفرة في أسفل شيفرة جافا سكريبت الموجودة:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7011_22" style=""><span class="pln">ctx</span><span class="pun">.</span><span class="pln">fillStyle </span><span class="pun">=</span><span class="pln"> </span><span class="str">"rgb(255 0 0)"</span><span class="pun">;</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">fillRect</span><span class="pun">(</span><span class="lit">50</span><span class="pun">,</span><span class="pln"> </span><span class="lit">50</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="lit">150</span><span class="pun">);</span></pre>

<p>
	إن حفظت التغيرات وأعدت تحميل الصفحة سترى مربعًا أحمر اللون ضمن اللوحة، تبعد زاويته العليا اليسارية مقدار 50 بكسل عن الحافتين العليا واليسارية للوحة (كما حددهما أول معاملين) وله اتساع مقداره 100 بكسل وارتفاع 150 بكسل (كما حددهما المعاملان اﻷخيران).
</p>

<p>
	لنضف اﻵن مربعًا آخر أخضر هذه المرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7011_24" style=""><span class="pln">ctx</span><span class="pun">.</span><span class="pln">fillStyle </span><span class="pun">=</span><span class="pln"> </span><span class="str">"rgb(0 255 0)"</span><span class="pun">;</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">fillRect</span><span class="pun">(</span><span class="lit">75</span><span class="pun">,</span><span class="pln"> </span><span class="lit">75</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">);</span></pre>

<p>
	احفظ التغييرات وأعد تحميل الصفحة لترى النتيجة.
</p>

<p>
	تطرح الشيفرة السابقة نقطة هامة وهي أن جميع عمليات الرسم مثل رسم مربع أو خط وغيرها تُنفّذ وفق تسلسل ورودها في الشيفرة. فكّر بالأمر وكأنك ترسم على جدار، حيث تغطي كل طبقة ما تحتها. وبالطبع لا يمكن أن نغيّر هذا اﻷمر، لهذا عليك أن تفكّر مليًا بترتيب ما ترسمه على اللوحة.
</p>

<p>
	كما يمكنك إنشاء رسومات شبه شفافة عند اختيارك لونًا شبه شفاف باستخدام التابع <code>()rgb</code> مثلًا. إذ تُعرّف القناة ألفا alpha channel مقدار الشفافية في اللون، وكلما كانت قيمتها أكبر كلما زادت قتامة اللون وغطّى ما تحته. أضف السطرين التاليين إلى الشيفرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7011_26" style=""><span class="pln">ctx</span><span class="pun">.</span><span class="pln">fillStyle </span><span class="pun">=</span><span class="pln"> </span><span class="str">"rgb(255 0 255 / 75%)"</span><span class="pun">;</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">fillRect</span><span class="pun">(</span><span class="lit">25</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="lit">175</span><span class="pun">,</span><span class="pln"> </span><span class="lit">50</span><span class="pun">);</span></pre>

<p>
	جرب أن ترسم مربعات لتختبر قدرتك!
</p>

<h3 id="-4">
	الإطارات وسماكة الخطوط
</h3>

<p>
	رسمنا حتى اللحظة مربعات ممتلئة، لكنك تستطيع أيضًا رسم إطارات مربعة strokes. ولضبط لون اﻹطار نستخدم الخاصية <code>strokeStyle</code> ونرسمه باستخدام التابع <code>strokeRect</code>.
</p>

<p>
	أضف السطرين التاليين إلى الشيفرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7011_28" style=""><span class="pln">ctx</span><span class="pun">.</span><span class="pln">strokeStyle </span><span class="pun">=</span><span class="pln"> </span><span class="str">"rgb(255 255 255)"</span><span class="pun">;</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">strokeRect</span><span class="pun">(</span><span class="lit">25</span><span class="pun">,</span><span class="pln"> </span><span class="lit">25</span><span class="pun">,</span><span class="pln"> </span><span class="lit">175</span><span class="pun">,</span><span class="pln"> </span><span class="lit">200</span><span class="pun">);</span></pre>

<p>
	للإطارات سماكة افتراضية قيمتها 1 بكسل، لكنك تستطيع تعديل السماكة باستخدام الخاصية <code>lineWidth</code> التي تأخذ قيمة تمثل سماكة اﻹطار مقدرة بالبكسل. أضف اﻵن السطر التالي إلى الشيفرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7011_30" style=""><span class="pln">ctx</span><span class="pun">.</span><span class="pln">lineWidth </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pun">;</span></pre>

<p>
	لاحظ كيف سيبدو اﻹطار أكثر سماكة. وسيبدو مثالنا حتى اللحظة كالتالي:
</p>

<p>
	<iframe height="250" loading="lazy" src="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/getting-started/2_canvas_rectangles/index.html" width="100%"></iframe>
</p>

<p>
	<strong>ملاحظة</strong>: ستجد <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/2_canvas_rectangles" rel="external nofollow">الشيفرة الكاملة</a> لهذا المثال على جت-هب.
</p>

<h3 id="-5">
	رسم المسارات
</h3>

<p>
	لو أردت رسم ما هو أعقد من مربع، لا بد حينها من رسم مسار. ويقتضي اﻷمر بأبسط أشكاله كتابة شيفرة تحدد تمامًا المسار الذي تريد أن يتحرك قلم الرسم عليه ضمن اللوحة حتى يرسم الشكل المطلوب. وتضم الواجهة Canvas دوال لرسم خطوط مستقيمة ودوائر و<a href="https://www.google.com/url?sa=t&amp;rct=j&amp;q=&amp;esrc=s&amp;source=web&amp;cd=&amp;cad=rja&amp;uact=8&amp;ved=2ahUKEwjZsb-TzfmEAxW1UaQEHaH_D-sQFnoECBIQAQ&amp;url=https%3A%2F%2Facademy.hsoub.com%2Fprogramming%2Fjavascript%2F%25D9%2585%25D9%2586%25D8%25AD%25D9%2586%25D9%2589-%25D8%25A8%25D9%258A%25D8%25B2%25D9%258A%25D9%2587-%25D9%2588%25D8%25A3%25D9%2587%25D9%2585%25D9%258A%25D8%25AA%25D9%2587-%25D9%2581%25D9%258A-%25D8%25A7%25D9%2584%25D8%25B1%25D8%25B3%25D9%2588%25D9%2585%25D9%258A%25D8%25A7%25D8%25AA-%25D9%2588%25D8%25B5%25D9%2586%25D8%25A7%25D8%25B9%25D8%25A9-%25D8%25A7%25D9%2584%25D8%25AD%25D8%25B1%25D9%2583%25D8%25A7%25D8%25AA-%25D9%2581%25D9%258A-%25D8%25AC%25D8%25A7%25D9%2581%25D8%25A7%25D8%25B3%25D9%2583%25D8%25B1%25D8%25A8%25D8%25AA-r1339%2F&amp;usg=AOvVaw1DbGWk4McP9dLiXKdYPAgU&amp;opi=89978449" rel="external nofollow">منحنيات بيزيه</a> وغيرها الكثير.
</p>

<p>
	لنبدأ اﻵن هذا القسم بنسخة جديدة من قالب المثال الذي أعددناه سابقًا، وسنستخدم بعض التوابع والخاصيات الشائعة خلال الأقسام التالية:
</p>

<ul>
	<li>
		<code>()beginPath</code>: يبدأ رسم مسار من النقطة التي يكون عندها القلم حاليًا في اللوحة وستكون هذه النقطة مبدأ اﻹحداثيات إن كانت اللوحة جديدة.
	</li>
	<li>
		<code>()moveTo</code>: ينقل القم إلى نقطة أخرى من اللوحة دون رسم أو تسجيل المسار بل يقفز القلم إلى النقطة المختارة.
	</li>
	<li>
		<code>()fill</code>: يرسم شكلًا يملأ المسار الذي رسمه القلم.
	</li>
	<li>
		<code>()stroke</code>: يرسم إطارًا مبنيًا على المسار الذي يرسمه القلم.
	</li>
	<li>
		باﻹمكان استخدام الخاصيات <code>lineWidth</code> و <code>fillStyle</code> أو <code>strokeStyle</code> مع المسارات أيضًا.
	</li>
</ul>

<p>
	تبدو شيفرة رسم مسار نمطي قريبة من التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7011_32" style=""><span class="pln">ctx</span><span class="pun">.</span><span class="pln">fillStyle </span><span class="pun">=</span><span class="pln"> </span><span class="str">"rgb(255 0 0)"</span><span class="pun">;</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">beginPath</span><span class="pun">();</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">moveTo</span><span class="pun">(</span><span class="lit">50</span><span class="pun">,</span><span class="pln"> </span><span class="lit">50</span><span class="pun">);</span><span class="pln">
</span><span class="com">// ارسم مسارك</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">fill</span><span class="pun">();</span></pre>

<h4 id="-6">
	رسم الخطوط
</h4>

<p>
	لنرسم اﻵن مثلث متساوي الأضلاع ضمن اللوحة:
</p>

<ol>
	<li>
		أضف بداية الدالة المساعدة التالية في أسفل الشيفرة، مهمة هذه الدالة تحويل قيم الزوايا من درجات إلى راديان. تكمن فائدة هذه الدالة في أن جافا سكريبت تفهم قيم الزوايا بالراديان لكننا كبشر نفكر طبيعيًا بالدرجات.
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7011_34" style=""><span class="kwd">function</span><span class="pln"> degToRad</span><span class="pun">(</span><span class="pln">degrees</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">degrees </span><span class="pun">*</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">PI</span><span class="pun">)</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">180</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<ol start="2">
	<li>
		ابدأ المسار بإضافة الشيفرة التالية تحت الشيفرة السابقة، وفيها نضبط لون المثلث ونبدأ رسم المسار ثم ننتقل مباشرة إلى النقطة (0,0) دون رسم أي شيء ومن هذه النقطة نبدأ رسم المثلث:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7011_36" style=""><span class="pln">ctx</span><span class="pun">.</span><span class="pln">fillStyle </span><span class="pun">=</span><span class="pln"> </span><span class="str">"rgb(255 0 0)"</span><span class="pun">;</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">beginPath</span><span class="pun">();</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">moveTo</span><span class="pun">(</span><span class="lit">50</span><span class="pun">,</span><span class="pln"> </span><span class="lit">50</span><span class="pun">);</span></pre>

<ol start="3">
	<li>
		أضف اﻷسطر التالية في نهاية الشيفرة السابقة:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7011_38" style=""><span class="pln">ctx</span><span class="pun">.</span><span class="pln">lineTo</span><span class="pun">(</span><span class="lit">150</span><span class="pun">,</span><span class="pln"> </span><span class="lit">50</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> triHeight </span><span class="pun">=</span><span class="pln"> </span><span class="lit">50</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">tan</span><span class="pun">(</span><span class="pln">degToRad</span><span class="pun">(</span><span class="lit">60</span><span class="pun">));</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">lineTo</span><span class="pun">(</span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="lit">50</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> triHeight</span><span class="pun">);</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">lineTo</span><span class="pun">(</span><span class="lit">50</span><span class="pun">,</span><span class="pln"> </span><span class="lit">50</span><span class="pun">);</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">fill</span><span class="pun">();</span></pre>

<p>
	نرسم بداية خطًا من نقطة البداية إلى النقطة (150,50) وسيتجه مسارنا 100 بكسل إلى اليمين وفق المحور x. نحسب بعد ذلك ارتفاع المثلث متساوي اﻷضلاع باستخدام قواعد مثلثية بسيطة إذ نعلم أن زوايا المثلث هي 60 درجة. لهذا نستطيع تقسيم المثلث المتساوي اﻷضلاع الذي نوجهه للأسفل إلى مثلثين قائمين لكل منهما زاويتين حادتين قياسهما 30 و60 درجة. ونعرّف في المثلث القائم:
</p>

<ul>
	<li>
		<strong>الوتر hypotenuse</strong>: وهو أطول أضلاع المثلث القائم.
	</li>
	<li>
		<strong>المجاور adjacent</strong>: وهو هنا الضلع المجاور للزاوية 60 وطوله 50 بكسل لأنه يمثل نصف طول المسار الذي رسمناه سابقًا.
	</li>
	<li>
		<strong>المقابل opposite</strong>: وهو هنا الضلع المقابل للزاوية 60 ويمثل ارتفاع المثلث المتساوي اﻷضلاع الذي ننوي رسمه.
	</li>
</ul>

<p>
	يُعطى طول المجاور رياضيًا من خلال جداء المقابل بظل الزاوية tan:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7011_40" style=""><span class="lit">50</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">tan</span><span class="pun">(</span><span class="pln">degToRad</span><span class="pun">(</span><span class="lit">60</span><span class="pun">))</span></pre>

<p>
	نستخدم هنا الدالة <code>()degToRad</code> التي بنيناها سابقًا لتحويل الزاوية 60 درجة إلى راديان وهي القيمة التي يتوقعها التابع <code><a href="https://wiki.hsoub.com/JavaScript/Math/tan" rel="external">()Math.tan</a></code> الذي يحسب ظل الزاوية.
</p>

<ol start="4">
	<li>
		بعد حساب اﻹرتفاع، نرسم خطًا آخر إلى النقطة <code>(100, 50+triHeight)</code> إلى نقطة أخرى لها إحداثي X يعادل نصف طول المسار المستقيمة السابق وإحداثي Y قيمته تعادل 50 زائدًا طول اﻹرتفاع، ذلك أن قاعدة المثلث تنزاح إلى داخل اللوحة مقدار 50 بكسل عن الحافة العليا لها.
	</li>
	<li>
		أما الخطوة التالية فهي رسم خط من آخر نقطة إلى نقطة البداية ليتكون المثلث.
	</li>
	<li>
		نستدعي في النهاية التابع <code>()ctx.fill</code> ﻹنهاء المسار وملئ الشكل الناتج.
	</li>
</ol>

<h4 id="-7">
	رسم الدوائر
</h4>

<p>
	لنلق نظرة على طريقة رسم الدوائر في اللوحة. تُنفّذ هذه العملية من خلال التابع <code>()arc</code> الذي يرسم جزءًا من قوس الدائرة أو قوس الدائرة بأكمله ابتداءًا من نقطة محددة:
</p>

<ol>
	<li>
		ﻹضافة دائرة إلى لوحتنا ضع الشيفرة التالية في نهاية الشيفرة السابقة:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7011_43" style=""><span class="pln">ctx</span><span class="pun">.</span><span class="pln">fillStyle </span><span class="pun">=</span><span class="pln"> </span><span class="str">"rgb(0 0 255)"</span><span class="pun">;</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">beginPath</span><span class="pun">();</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">arc</span><span class="pun">(</span><span class="lit">150</span><span class="pun">,</span><span class="pln"> </span><span class="lit">106</span><span class="pun">,</span><span class="pln"> </span><span class="lit">50</span><span class="pun">,</span><span class="pln"> degToRad</span><span class="pun">(</span><span class="lit">0</span><span class="pun">),</span><span class="pln"> degToRad</span><span class="pun">(</span><span class="lit">360</span><span class="pun">),</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">);</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">fill</span><span class="pun">();</span></pre>

<p>
	يأخذ التابع <code>()arc</code> ست معاملات، يحدد اﻷول والثاني اﻹحداثيين x و y لمركز الدائرة والثالث هو نصف قطر الدائرة، بينما يحدد المعاملين الخامس والسادس زاويتي البداية والنهاية لقوس الدائرة (0 و 360 يرسمان دائرة كاملة) ويحدد المعامل اﻷخير إذا ما كانت الدائرة سترسم باتجاه عقارب الساعة أو عكسها (تعني القيمة <code>false</code> أن الرسم باتجاه عقارب الساعة)
</p>

<p>
	<strong>ملاحظة</strong>: الزاوية 0 هي الزاوية الأفقية إلى اليمين.
</p>

<p>
	لنجرب إضافة قوس آخر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7011_45" style=""><span class="pln">ctx</span><span class="pun">.</span><span class="pln">fillStyle </span><span class="pun">=</span><span class="pln"> </span><span class="str">"yellow"</span><span class="pun">;</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">beginPath</span><span class="pun">();</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">arc</span><span class="pun">(</span><span class="lit">200</span><span class="pun">,</span><span class="pln"> </span><span class="lit">106</span><span class="pun">,</span><span class="pln"> </span><span class="lit">50</span><span class="pun">,</span><span class="pln"> degToRad</span><span class="pun">(-</span><span class="lit">45</span><span class="pun">),</span><span class="pln"> degToRad</span><span class="pun">(</span><span class="lit">45</span><span class="pun">),</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">);</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">lineTo</span><span class="pun">(</span><span class="lit">200</span><span class="pun">,</span><span class="pln"> </span><span class="lit">106</span><span class="pun">);</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">fill</span><span class="pun">();</span></pre>

<p>
	هناك اختلافان بسيطان عن النمط السابق:
</p>

<ul>
	<li>
		ضبطنا قيمة المعامل الأخير للتابع <code>()arc</code> على القيمة <code>true</code> أي سيرسم القوس بعكس اتجاه عقارب الساعة، فحتى لو كانت زاوية البداية هي 45- وزاوية النهاية 45 درجة فإن القوس يغطي زاوية 270 درجة وليس 90 درجة والتي يمكن أن تحصل عليها إن كانت قيمة المعامل <code>false</code>.
	</li>
	<li>
		رسمنا خطًا إلى مركز الدائرة قبل استدعاء <code>()fill</code> كي نحصل على دائرة اقتطع منها مثلث. وإن لم نرسم هذا الخط سيصل المتصفح نقطة البداية ونقطة النهاية ويملأ الشكل الناتج وهو دائرة اقتطع منها طرف.
	</li>
</ul>

<p>
	ستبدو نتيجة المثال السابق قريبة من التالي:
</p>

<p>
	<iframe height="200" loading="lazy" src="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/getting-started/3_canvas_paths/index.html" width="100%"></iframe>
</p>

<p>
	<strong>ملاحظة</strong>: بإمكانك الاطلاع على <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/3_canvas_paths" rel="external nofollow">الشيفرة كاملة</a> لهذا المثال على جت-هب.
</p>

<h3 id="-8">
	رسم النصوص
</h3>

<p>
	يتيح لك العنصر <code>&lt;canvas&gt;</code> رسم عبارات نصية، وهذا ما سنتعلمه بإيجاز تاليًا. لنبدأ بإنشاء نسخة جديدة عن قالب التطبيق كي نرسم المثال الجديد. ونستخدم في هذا المثال التابعين:
</p>

<ul>
	<li>
		<code>()fillText</code>: الذي يملأ النص.
	</li>
	<li>
		<code>()strokeText</code>: الذي يرسم الحواف الخارجية للنص.
	</li>
</ul>

<p>
	يأخذ كل تابع منهما ثلاثة خاصيات بشكله البسيط: النص الذي سيُرسم واﻹحداثيين x و y للنقطة التي يبدأ الرسم عندها. هذه النقطة هي عمليًا الزاوية السفلى اليسارية لصندوق النص الذي نرسمه (الصندوق الذي يحيط بالنص). قد يسبب اﻷمر إرباكًا أحيانًا بالنظر إلى أن عمليات الرسم اﻷخرى تميل إلى البدء من الزاويا العليا اليسارية، تذكر ذلك جيدًا.
</p>

<p>
	وهنالك أيضًا عدد من الخاصيات التي تساعد في إدارة تصيير النص مثل <code>font</code> التي تسمح بتخصيص عائلة الخط وحجمه وغيرها، وتأخذ قيمها وفق الصيغة نفسها التي نستخدمها مع <a href="https://wiki.hsoub.com/CSS/font" rel="external">خاصية CSS</a> التي تحمل نفس الاسم.
</p>

<p>
	ولا يمكن لقارئات الشاشة الوصول إلى محتوى العنصر <code>&lt;canvas&gt;</code> لأن النص الذي يُرسم في اللوحة لا يُعد جزءًا من شجرة DOM، لهذا لا بد من جعله متاحًا لذوي الاحتياجات الخاصة. وفي مثالنا جعلنا النص المكتوب ضمن اللوحة قيمة للسمة <code>aria-label</code>.
</p>

<p>
	أضف اﻵن الشيفرة التالية إلى نهاية شيفرة جافا سكريبت:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7011_49" style=""><span class="pln">ctx</span><span class="pun">.</span><span class="pln">strokeStyle </span><span class="pun">=</span><span class="pln"> </span><span class="str">"white"</span><span class="pun">;</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">lineWidth </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">font </span><span class="pun">=</span><span class="pln"> </span><span class="str">"36px arial"</span><span class="pun">;</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">strokeText</span><span class="pun">(</span><span class="str">"Canvas text"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">50</span><span class="pun">,</span><span class="pln"> </span><span class="lit">50</span><span class="pun">);</span><span class="pln">

ctx</span><span class="pun">.</span><span class="pln">fillStyle </span><span class="pun">=</span><span class="pln"> </span><span class="str">"red"</span><span class="pun">;</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">font </span><span class="pun">=</span><span class="pln"> </span><span class="str">"48px georgia"</span><span class="pun">;</span><span class="pln">
ctx</span><span class="pun">.</span><span class="pln">fillText</span><span class="pun">(</span><span class="str">"Canvas text"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">50</span><span class="pun">,</span><span class="pln"> </span><span class="lit">150</span><span class="pun">);</span><span class="pln">

canvas</span><span class="pun">.</span><span class="pln">setAttribute</span><span class="pun">(</span><span class="str">"aria-label"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Canvas text"</span><span class="pun">);</span></pre>

<p>
	رسمنا باستخدام الشيفرة السابقة سطرين أولهما مفرّغ واﻵخر ممتلئ، ويبدو الشكل النهائي للوحة شبيهًا بالتالي:
</p>

<p>
	<iframe height="180" loading="lazy" src="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/getting-started/4_canvas_text/index.html" width="100%"></iframe>
</p>

<p>
	<strong>ملاحظة</strong>: بإمكانك الاطلاع على <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/4_canvas_text" rel="external nofollow">الشيفرة كاملة</a> لهذا المثال على جت-هب.
</p>

<h3 id="-9">
	رسم صور ضمن اللوحة
</h3>

<p>
	بإمكانك أيضًا تصيّر صور خارجية كي تُرسم ضمن العنصر <code>&lt;canvas&gt;</code>، ويمكن أن تكون الصور بسيطةً أو إطارات من فيديو أو غير ذلك. وسنلقي نظرة على رسم صور بسيطة ضمن اللوحة.
</p>

<ol>
	<li>
		أنشئ نسخة جديدة من قالب التطبيق الذي نستخدمه لتنفيذ الرسوميات. إذ ترسم الصور ضمن اللوحة باستخدام التابع <code>()drawImage</code>. ويأخذ التابع بأبسط أشكاله ثلاث معاملات هي مرجع إلى الصورة واﻹحداثيين x و y للزاوية العليا اليسارية من الصورة.
	</li>
	<li>
		لنبدأ بتحديد مصدر للصورة التي نريد رسمها، لهذا أضف الشيفرة التالية إلى ملف جافا سكريبت:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7011_51" style=""><span class="kwd">const</span><span class="pln"> image </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Image</span><span class="pun">();</span><span class="pln">
image</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> </span><span class="str">"firefox.png"</span><span class="pun">;</span></pre>

<p>
	أنشأنا في الشيفرة السابقة كائن <code>HTMLImageElement</code> جديد باستخدام الدالة البانية <code>()Image</code>. وللكائن المعاد النوع ذاته الذي يُعاد عندما ننشئ مرجعًا إلى العنصر <code>&lt;img&gt;</code>، لهذا يمكن ضبط السمة <code>src</code> له كي تكون عنوان URL لصورة شعار فايرفوكس، وفي هذه المرحلة يبدأ المتصفح تحميل الصورة.
</p>

<p>
	يمكن اﻵن رسم الصورة ضمن اللوحة باستخدام <code>()drawImage</code>، لكن علينا أولًا التأكد من اكتمال تحميل الصورة وإلا ستخفق العملية. نتحقق من ذلك عن طريق الحدث <code>load</code> الذي يقع فقط عند إنتهاء تحميل الصورة، لهذا أضف الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7011_53" style=""><span class="pln">image</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"load"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> ctx</span><span class="pun">.</span><span class="pln">drawImage</span><span class="pun">(</span><span class="pln">image</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">));</span></pre>

<p>
	سترى إن أعدت تحميل اللوحة كيف رُسمت الصورة ضمن اللوحة. لكن بالطبع هناك المزيد. فماذا لو أردت رسم جزء من الصورة فقط أو أردت تغيير أبعادها؟ يمكننا بالطبع تنفيذ كلا اﻷمرين باستخدام صيغة أعقد للتابع <code>()drawImage</code>. لهذا عدّل استدعاء التابع <code>()ctx.drawImage</code> ليصبح كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7011_55" style=""><span class="pln">ctx</span><span class="pun">.</span><span class="pln">drawImage</span><span class="pun">(</span><span class="pln">image</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">,</span><span class="pln"> </span><span class="lit">20</span><span class="pun">,</span><span class="pln"> </span><span class="lit">185</span><span class="pun">,</span><span class="pln"> </span><span class="lit">175</span><span class="pun">,</span><span class="pln"> </span><span class="lit">50</span><span class="pun">,</span><span class="pln"> </span><span class="lit">50</span><span class="pun">,</span><span class="pln"> </span><span class="lit">185</span><span class="pun">,</span><span class="pln"> </span><span class="lit">175</span><span class="pun">);</span></pre>

<ul>
	<li>
		المعامل اﻷول هو مرجع إلى الصورة.
	</li>
	<li>
		يحدد المعاملان 2 و 3 إحداثيات الزاوية العليا اليسارية من المنطقة التي تريد اقتطاعها من الصورة المحمّلة، ولن يُرسم أي شئ أعلى أو إلى يسار قيمتي المعاملين السابقين.
	</li>
	<li>
		يحدد المعاملان 4 و 5 اتساع وارتفاع المنطقة التي تريد اقتطاعها من الصورة التي حملتها.
	</li>
	<li>
		يحدد المعاملان 6 و 7 إحداثيا النقطة التي نريد أن نبدأ فيها رسم الصورة المقتطعة انطلاقًا من الزاوية العليا اليسارية لها نسبة إلى الزاوية العليا اليسارية للوحة.
	</li>
	<li>
		يحدد المعاملان 8 و 9 اتساع وارتفاع المنطقة التي نريد أن نرسم فيها الصورة المقتطعة. وقد حددنا في مثالنا نفس أبعاد الصورة المقتطعة، لكن باﻹمكان إعادة تحجيم الصورة باستخدام قيم مختلفة للمعاملين.
	</li>
</ul>

<p>
	في حال غيّرت في الصورة تغييرًا واضحًا لابد من تحديث توصيف الصورة الخاص بسهولة الوصول <a href="https://academy.hsoub.com/programming/html/%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%B3%D9%87%D9%88%D9%84%D8%A9-%D8%A7%D9%84%D9%88%D8%B5%D9%88%D9%84-accessibility-%D8%A7%D9%84%D9%84%D8%A7%D8%B2%D9%85%D8%A9-%D9%81%D9%8A-%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1984/" rel="">accessibility</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7011_57" style=""><span class="pln">canvas</span><span class="pun">.</span><span class="pln">setAttribute</span><span class="pun">(</span><span class="str">"aria-label"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Firefox Logo"</span><span class="pun">);</span></pre>

<p>
	ستبدو نتيجة المثال قريبة من التالي:
</p>

<p>
	<iframe height="260" loading="lazy" src="https://mdn.github.io/learning-area/javascript/apis/drawing-graphics/getting-started/5_canvas_images/index.html" width="100%"></iframe>
</p>

<p>
	<strong>ملاحظة</strong>: بإمكانك الاطلاع على <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/drawing-graphics/getting-started/5_canvas_images" rel="external nofollow">الشيفرة كاملة</a> لهذا المثال على جت-هب.
</p>

<h2 id="-10">
	الخلاصة
</h2>

<p>
	تعرفنا في هذا المقال على أساسيات الرسم ضمن العنصر <code>&lt;canvas&gt;</code> من حيث إعداد العنصر وضبط معاملاته. ثم تدربنا على رسم الخطوط والمسارات والدوائر والنصوص والصور في بيئة ثنائية البعد. وسنتابع في الجزء الثاني من هذا المقال العمل مع الرسومات المتحركة ثنائية وثلاثية البعد.
</p>

<p>
	ترجمة -وبتصرف- للقسم اﻷول من مقال: <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics" rel="external nofollow">Drawing graphics</a>
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق:  <a href="https://academy.hsoub.com/programming/javascript/%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D8%AE%D8%A7%D8%B1%D8%AC%D9%8A%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-third-party-apis-r2423/" rel="">واجهات برمجية خارجية في جافا سكريبت Third Party APIs</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/html/%D8%A7%D9%84%D8%B1%D8%B3%D9%85-%D8%B9%D8%A8%D8%B1-%D8%B9%D9%86%D8%B5%D8%B1-canvas-%D9%81%D9%8A-html5-r345/" rel="">الرسم عبر عنصر canvas في HTML5</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%B9%D9%86%D8%B5%D8%B1-canvas-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-%D8%B1%D8%B3%D9%85-%D8%A7%D9%84%D8%A3%D8%B4%D9%83%D8%A7%D9%84-r209/" rel="">التعامل مع عنصر Canvas باستخدام جافاسكربت (رسم الأشكال)</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%B9%D9%86%D8%B5%D8%B1-canvas-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-%D8%B1%D8%B3%D9%85-%D8%A7%D9%84%D8%A3%D8%B4%D9%83%D8%A7%D9%84-r209/" rel="">التعامل مع العنصر Canvas باستخدام جافاسكربت (رسم الصور )</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%AA%D8%B5%D8%A7%D9%85%D9%8A%D9%85%D8%8C-%D8%A7%D9%84%D8%A3%D9%84%D9%88%D8%A7%D9%86-%D9%88%D8%A7%D9%84%D8%AE%D8%B7%D9%88%D8%B7-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-canvas-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r239/" rel="">التعامل مع التصاميم، الألوان والخطوط باستخدام Canvas في جافاسكربت</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2431</guid><pubDate>Thu, 17 Oct 2024 15:04:01 +0000</pubDate></item><item><title>&#x648;&#x627;&#x62C;&#x647;&#x627;&#x62A; &#x628;&#x631;&#x645;&#x62C;&#x64A;&#x629; &#x62E;&#x627;&#x631;&#x62C;&#x64A;&#x629; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627; &#x633;&#x643;&#x631;&#x64A;&#x628;&#x62A; Third Party APIs</title><link>https://academy.hsoub.com/programming/javascript/%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D8%AE%D8%A7%D8%B1%D8%AC%D9%8A%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-third-party-apis-r2423/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_10/-------.png.aa1e470e82c8561959135882832557cc.png" /></p>
<p>
	تُعد الواجهات البرمجية التي ذكرناها في <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-web-apis-r2355/" rel="">مقالات سابقة</a> واجهات مُضمَّنة في المتصفح، لكن ليست كل الواجهات البرمجية كذلك. إذ تقدم الكثير من الشركات مثل فيسبوك وجوجل و PayPal وغيرها، واجهات برمجية مخصصة تسمح للمطورين باستخدام بياناتها أو خدماتها (مثل عرض خريطة جوجل محددة في موقعك أو استخدام حساب فيسبوك لتسجيل المستخدمين في موقعك). لهذا نلقي نظرة في هذا المقال على الاختلافات بين الواجهات المضمنة في المتصفح والواجهات التي تقدمها أطراف أخرى والتي تعرف باسم third party APIs ونستعرض بعض الحالات النمطية لاستخدامها.
</p>

<p>
	ننصحك قبل المتابعة في قراءة هذه المقالات أن:
</p>

<ul>
	<li>
		تكون ملمًا بلغتي <a href="HTTPS://wiki.hsoub.com/HTML" rel="external" target="_blank">HTML</a> و <a href="HTTPS://wiki.hsoub.com/CSS" rel="external" target="_blank">CSS</a>.
	</li>
	<li>
		تكون ملمًا بلغة <a href="HTTPS://wiki.hsoub.com/JavaScript" rel="external" target="_blank">جافا سكريبت</a>.
	</li>
	<li>
		تطّلع على سلاسل المقالات السابقة التي ناقشت <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D8%B1%D9%91%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D9%85%D9%86-%D9%85%D9%86%D8%B8%D9%88%D8%B1-%D8%B9%D8%A7%D9%85-r2266/" rel="">أساسيات جافا سكريبت</a> و<a href="https://academy.hsoub.com/programming/javascript/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2305/" rel="">الكائنات في جافا سكريبت.</a>
	</li>
	<li>
		تطّلع على <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-web-apis-r2355/" rel="">أساسيات الواجهات البرمجية في طرف العميل</a>.
	</li>
</ul>

<h2 id="">
	الواجهات البرمجية التي يقدمها طرف خارجي
</h2>

<p>
	وهي واجهات يقدمها طرف خارجي (ليس أنت وليس المتصفح)، تسمح لك شركات مثل فيسبوك وتويتر وجوجل وغيرها بالوصول إلى بعض وظائف منتجاتها عبر جافا سكريبت واستخدامها في موقعك. ومن أكثر اﻷمثلة وضوحًا نجد الواجهات البرمجية للخرائط Maps <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> التي تعرض خرائط أماكن مخصصة على موقع الويب الخاص بك.
</p>

<p>
	دعنا نلقي نظرة على <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/third-party-apis/mapquest" rel="external nofollow" target="_blank">مثال بسيط يتعلق باستخدام الواجهة البرمجية Mapquest <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a> يشرح الاختلاف بين الواجهات البرمجية التي يقدمها طرف آخر وتلك المضمنة في المتصفح.
</p>

<p>
	<strong>ملاحظة</strong>: بإمكانك <a href="https://developer.mozilla.org/en-US/docs/Learn#getting_our_code_examples" rel="external nofollow" target="_blank">تنزيل جميع ملفات اﻷمثلة</a> دفعة واحدة ومن ثم البحث عن ملف المثال المطلوب الذي تحتاجه في كل قسم من المقال.
</p>

<h3 id="-1">
	الواجهات موجودة على خوادم الطرف الذي يقدمها
</h3>

<p>
	تُضمن واجهات المتصفح البرمجية ضمن المتصفح وستتعامل معها من خلال جافا سكريبت مباشرة. ولقد رأيت في <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-web-apis-r2355/" rel="">مقالنا التمهيدي</a> كيف تعاملنا مع الواجهة البرمجية Web Audio <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> من خلال كائن جافا سكريبت اﻷصلي <code>AudioContext</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9137_12" style=""><span class="kwd">const</span><span class="pln"> audioCtx </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">AudioContext</span><span class="pun">();</span><span class="pln">
</span><span class="com">// …</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> audioElement </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"audio"</span><span class="pun">);</span><span class="pln">
</span><span class="com">// …</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> audioSource </span><span class="pun">=</span><span class="pln"> audioCtx</span><span class="pun">.</span><span class="pln">createMediaElementSource</span><span class="pun">(</span><span class="pln">audioElement</span><span class="pun">);</span><span class="pln">
</span><span class="com">// etc.</span></pre>

<p>
	تتواجد الواجهات البرمجية التي يقدمها طرف خارجي على خوادم هذا الطرف، لهذا عليك أولًا الاتصال بتلك الواجهات حتى تتمكن من استخدامها في صفحاتك. وتقتضي هذه العملية بداية ربط صفحتك بمكتبة جافا سكريبت على ذلك الخادم عبر العنصر <code>&lt;script&gt;</code> كما في مثالنا عن الواجهة mapquest <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>.
</p>

<p>
	إليك شيفرة HTML:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9137_14" style=""><span class="tag">&lt;script</span><span class="pln">
 </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"https://api.mqcdn.com/sdk/mapquest-js/v1.3.2/mapquest.js"</span><span class="pln">
 </span><span class="atn">defer</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
</span><span class="tag">&lt;link</span><span class="pln">
 </span><span class="atn">rel</span><span class="pun">=</span><span class="atv">"stylesheet"</span><span class="pln">
 </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"https://api.mqcdn.com/sdk/mapquest-js/v1.3.2/mapquest.css"</span><span class="pln"> </span><span class="tag">/&gt;</span></pre>

<p>
	يمكنك اﻵن استخدام الكائنات التي تقدمها المكتبة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9137_16" style=""><span class="kwd">const</span><span class="pln"> map </span><span class="pun">=</span><span class="pln"> L</span><span class="pun">.</span><span class="pln">mapquest</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="str">"map"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 center</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="lit">53.480759</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">2.242631</span><span class="pun">],</span><span class="pln">
 layers</span><span class="pun">:</span><span class="pln"> L</span><span class="pun">.</span><span class="pln">mapquest</span><span class="pun">.</span><span class="pln">tileLayer</span><span class="pun">(</span><span class="str">"map"</span><span class="pun">),</span><span class="pln">
 zoom</span><span class="pun">:</span><span class="pln"> </span><span class="lit">12</span><span class="pun">,</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	تُنشئ الشيفرة السابقة متغيرًا لتخزين معلومات الخريطة، ثم تنشئ خريطة جديدة باستخدام التابع <code>()mapquest.map</code> الذي يأخذ المعاملات التالية:
</p>

<ul>
	<li>
		معرّف العنصر <code>&lt;div&gt;</code> الذي تعرض الخريطة ضمنه وهو في مثالنا "map".
	</li>
	<li>
		كائن خيارات يضم تفاصيل الخريطة التي نريد عرضها، نذكر فيه إحداثيات الموقع وطبقة خريطة map layer نبنيها باستخدام التابع <code>()mapquest.titleLayer</code> ومستوى تكبير الخريطة Zoom.
	</li>
</ul>

<p>
	هذه هي المعلومات التي تحتاجها الواجهة البرمجية mapquest <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> لعرض خريطة بسيطة، بينما يتكفل الخادم بمعالجة كل العمليات المعقدة مثل اختيار الخريطة الصحيحة للمنطقة المطلوبة وعرضها.
</p>

<p>
	<strong>ملاحظة</strong>: يختلف أسلوب الوصول إلى وظائف بعض الواجهات البرمجية عما عرضنا، إذ يطلب بعضها المطور الاتصال عن طريق طلب HTTP إلى عنوان URL معين للوصول إلى البيانات وتُدعى عندها RESTful APIs.
</p>

<div class="ipsEmbeddedVideo" contenteditable="false">
	<div>
		<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="113" id="ips_uid_2587_6" referrerpolicy="strict-origin-when-cross-origin" src="https://academy.hsoub.com/applications/core/interface/index.html" title="شرح مفاهيم RESTful - تعلم كيف تبني واجهات REST برمجية API" width="200" data-embed-src="https://www.youtube-nocookie.com/embed/MgWJympekBU?feature=oembed"></iframe>
	</div>
</div>

<h3 id="accesskeys">
	تحتاج الواجهات البرمجية إلى مفاتيح وصول Access keys عادة
</h3>

<p>
	تتعامل واجهات المتصفح البرمجية مع الأمور اﻷمنية من خلال عرض رسائل لتحديد السماحيات، والغاية من ذلك هو إعلام المستخدم بما يحصل على موقع الويب الذي يزوره وبالتالي لن يكون ضحية لاستخدامات مشبوهة للواجهة البرمجية.
</p>

<p>
	ويجري اﻷمر في الواجهات البرمجية التي يقدمها طرف خارجي على نحو مختلف قليلا، إذ تميل هذه الواجهات إلى استخدام مفاتيح لوصول المطورين إلى وظائف الواجهة، وهي مصممة غالبًا لحماية مقدّم الواجهة البرمجية بدلًا من المستخدم. إذ ستجد سطرًا برمجيًا مشابهًا للسطر التالي في مثالنا عن استخدام Mapquest <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9137_18" style=""><span class="pln">L</span><span class="pun">.</span><span class="pln">mapquest</span><span class="pun">.</span><span class="pln">key </span><span class="pun">=</span><span class="pln"> </span><span class="str">"YOUR-API-KEY-HERE"</span><span class="pun">;</span></pre>

<p>
	يخصص هذا السطر مفتاح وصول إلى الواجهة لتستخدمه في تطبيقك. وينبغي أن يقدّم المطور طلبًا للحصول على مفتاح، ويمكنه بعدها تضمينه في الشيفرة كي يتمكن من الوصول إلى وظائف الواجهة البرمجية المطلوبة. وما عرضناه في السطر السابق هو بمثابة مفتاح افتراضي غير صحيح لتوضيح طريقة كتابته.
</p>

<p>
	<strong>ملاحظة</strong>: ستستخدم مفتاحك الخاص عندما تكتب تطبيقاتك الخاصة مكان المفتاح الافتراضي. وقد تطلب بعض الواجهات طريقة أخرى لتقديم مفتاحك، لكن الطرق متشابهة تقريبًا.
</p>

<p>
	إن الغاية من طلب مفتاح وصول وهو السماح لمقدمي الواجهة البرمجية معرفة مستخدمي الواجهة البرمجية وكيف يستخدمونها. فعندما يُمنح المطوّر مفتاحًا سيتعرف عليه مزوّد الواجهة، وسيتمكن المزود من اتخاذ الإجراءات المناسبة إن استخدم الواجهة بطريقة مشبوهة (مثل تعقب موقع شخص، أو محاولة إغراق الواجهة بالطلبات ﻹيقافها عن العمل). وستكون الطريق اﻷسهل للتعامل مع المطور هي إلغاء الامتيازات الخاصة به.
</p>

<h2 id="mapquest">
	توسيع مثال Mapquest
</h2>

<p>
	لنضف اﻵن بعض الوظائف الجديدة إلى مثالنا الذي يستخدم الواجهة البرمجية mapquest وذلك لاستعراض بعض مزايا الواجهة:
</p>

<ol>
	<li>
		حتى نبدأ العمل في هذا القسم انسخ <a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/third-party-apis/mapquest/start/index.html" rel="external nofollow" target="_blank">ملف المثال</a> إلى مجلد جديد. وإن حضرت مسبقًا نسخة عن المستودع الذي يضم أمثلة المقال فستجد نسخة عن الملف المطلوب هنا في المجلد <code>javascript/apis/mapquest/start</code>.
	</li>
	<li>
		عليك تاليًا زيارة الموقع <a href="https://developer.mapquest.com/" rel="external nofollow" target="_blank">Mapquest developer site</a> ثم إنشاء حساب والحصول على مفتاح مطوّر (يُدعى هذا المفتاح حتى لحظة كتابة هذه اﻷسطر "مفتاح المستهلك consumer key"، كما يُطلب إليك أثناء اﻹجراء تقديم عنوان رد النداء callback URL، لكن لا حاجة هنا لتقديم أي شيء لذا اتركه فارغًا).
	</li>
	<li>
		افتح ملف المثال واستبدل المفتاح الافتراضي بالمفتاح الذي حصلت عليه.
	</li>
</ol>

<h3 id="-2">
	تغيير نوع الخريطة
</h3>

<p>
	هنالك أنواع مختلفة من الخرائط التي يمكن عرضها باستخدام الواجهة البرمجية mapquest <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>، ولاكتشاف الأمر، انتقل إلى السطر التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9137_20" style=""><span class="pln">layers</span><span class="pun">:</span><span class="pln"> L</span><span class="pun">.</span><span class="pln">mapquest</span><span class="pun">.</span><span class="pln">tileLayer</span><span class="pun">(</span><span class="str">"map"</span><span class="pun">);</span></pre>

<p>
	وجرّب تغيير <code>'map'</code> إلى <code>'hybrid'</code> لعرض خريطة هجينة، وجرّب قيمًا أخرى أيضًا، من خلال الاطلاع على <a href="https://developer.mapquest.com/documentation/mapquest-js/v1.3/l-mapquest-tile-layer/" rel="external nofollow" target="_blank">توثيق الخاصية <code>titleLayer</code></a> والخيارات المتاحة وغيرها من المعلومات.
</p>

<h3 id="-3">
	إضافة أدوات تحكم مختلفة
</h3>

<p>
	يمكن استخدام عدة أدوات للتحكم بالخريطة، لكنها تعرض افتراضيًا فقط أدوات التكبير والتصغير. ولتوسيع أدوات التحكم المتاحة نستخدم التابع <code>()map.addControl</code>، لهذا أضف السطر التالي إلى الشيفرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9137_22" style=""><span class="pln">map</span><span class="pun">.</span><span class="pln">addControl</span><span class="pun">(</span><span class="pln">L</span><span class="pun">.</span><span class="pln">mapquest</span><span class="pun">.</span><span class="pln">control</span><span class="pun">());</span></pre>

<p>
	يُنشئ التابع لوحة تحكم بسيطة كاملة الوظائف ويعرضها في الزاوية العليا اليمنى بشكل افتراضي. لكن بإمكانك تعديل موقع اللوحة بتخصيص كائن خيارات وتمريره كمعامل إلي التابع <code>()map.addControl</code> بعد تحديد الموقع المطلوب. جرّب ما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9137_24" style=""><span class="pln">map</span><span class="pun">.</span><span class="pln">addControl</span><span class="pun">(</span><span class="pln">L</span><span class="pun">.</span><span class="pln">mapquest</span><span class="pun">.</span><span class="pln">control</span><span class="pun">({</span><span class="pln"> position</span><span class="pun">:</span><span class="pln"> </span><span class="str">"bottomright"</span><span class="pln"> </span><span class="pun">}));</span></pre>

<p>
	وهنالك أنواع أخرى من أدوات التحكم مثل اﻷدوات التي يقدمها التابعان <code>()mapquest.searchControl</code> و <code>()mapquest.satelliteControl</code> وبعضها معقدة وفعّالة. جرّب هذه اﻷدوات واكتشف إمكانياتها.
</p>

<h3 id="-4">
	إضافة علامة خاصة
</h3>

<p>
	من السهل أيضًا إضافة علامة خاصة بك أو أيقونة إلى الخريطة، وذلك باستخدام التابع <code>()L.marker</code>. أضف اﻵن الشيفرة التالية ضمن <code>window.onload</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9137_26" style=""><span class="pln">L</span><span class="pun">.</span><span class="pln">marker</span><span class="pun">([</span><span class="lit">53.480759</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">2.242631</span><span class="pun">],</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 icon</span><span class="pun">:</span><span class="pln"> L</span><span class="pun">.</span><span class="pln">mapquest</span><span class="pun">.</span><span class="pln">icons</span><span class="pun">.</span><span class="pln">marker</span><span class="pun">({</span><span class="pln">
    primaryColor</span><span class="pun">:</span><span class="pln"> </span><span class="str">"#22407F"</span><span class="pun">,</span><span class="pln">
    secondaryColor</span><span class="pun">:</span><span class="pln"> </span><span class="str">"#3B5998"</span><span class="pun">,</span><span class="pln">
    shadow</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
    size</span><span class="pun">:</span><span class="pln"> </span><span class="str">"md"</span><span class="pun">,</span><span class="pln">
    symbol</span><span class="pun">:</span><span class="pln"> </span><span class="str">"A"</span><span class="pun">,</span><span class="pln">
 </span><span class="pun">}),</span><span class="pln">
</span><span class="pun">})</span><span class="pln">
 </span><span class="pun">.</span><span class="pln">bindPopup</span><span class="pun">(</span><span class="str">"This is Manchester!"</span><span class="pun">)</span><span class="pln">
 </span><span class="pun">.</span><span class="pln">addTo</span><span class="pun">(</span><span class="pln">map</span><span class="pun">);</span></pre>

<p>
	وكما ترى يأخذ التابع بأبسط أشكاله معاملين: مصفوفة تضم الإحداثيات التي تريد عرض العلامة عندها، وكائن خيارات يضم الخاصية <code>icon</code> التي تحدد اﻷيقونة التي تُعرض في هذا المكان.
</p>

<p>
	تُعرّف الأيقونة باستخدام التابع <code>()mapquest.icons.marker</code> الذي يتضمن معلومات مثل لون وحجم العلامة. وفي نهاية التابع نربط تابعًا آخر <code>('bindPopup('This is Manchester.</code> يعرّف المحتوى الذي يُعرض عند النقر على العلامة. ثم نربط أخيرًا التابع <code>(addTo(map.</code> إلى نهاية السلسلة ﻹضافة العلامة فعليًا إلى الخريطة.
</p>

<p>
	اقرأ توثيق هذه الواجهة البرمجية وجرّب خيارات أخرى وراقب ما ستحصل عليه. إذ تقدم mapquest وظائف متقدمة مثل الاتجاهات والبحث وغيرها.
</p>

<p>
	<strong>ملاحظة</strong>: إن واجهتك أية مشاكل في تجربة المثال، قارن أمثلتك <a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/third-party-apis/mapquest/finished/script.js" rel="external nofollow" target="_blank">بالنسخة المكتملة منه</a>.
</p>

<h2 id="nytimesrestful">
	الواجهة البرمجية اﻹخبارية NYTimes (واجهة وفق معيار RESTful)
</h2>

<p>
	لنلقِ نظرة اﻵن إلى مثال جديد مبني على الواجهة البرمجية لمجلة New York Times التي تسمح لك استخلاص اﻷخبار من المجلة وعرضها على صفحتك. يُعرف هذا النوع من الواجهات بواجهات <a href="https://academy.hsoub.com/programming/general/%D8%B4%D8%B1%D8%AD-%D9%81%D9%84%D8%B3%D9%81%D8%A9-restful-%D8%AA%D8%B9%D9%84%D9%85-%D9%83%D9%8A%D9%81-%D8%AA%D8%A8%D9%86%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-rest-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-r635/" rel="">RESTful</a> والتي تحصل فيها على البيانات من خلال إرسال طلبات HTTP إلى عنوان URL محدد، وتُنفَّّذ عمليات البحث وغيرها من الخاصيات عن طريق تشفيرها ضمن عنوان URL (على شكل معاملات غالبًا). وهذا النوع شائع كثيرًا في الواجهات البرمجية إضافة إلى الواجهات التي تعتمد على ميزات مكتبات جافا سكريبت مثل mapquest.
</p>

<h2 id="-5">
	نهج لاستخدام الواجهات البرمجية التي يقدمها طرف خارجي
</h2>

<p>
	سننتقل في المثال التالي خطوة خطوة لعرض طريقة استخدام الواجهة NYTimes والذي يقدم لك أيضًا خطوات عامة لتتبعها في العمل مع الواجهات البرمجية الجديدة.
</p>

<h3 id="-6">
	البحث عن التوثيق
</h3>

<p>
	عندما تقرر العمل مع واجهة برمجية من طرف خارجي، عليك بداية إيجاد توثيق الواجهة والاطلاع على الميزات التي تقدمها وكيفية استخدامها. لهذا عليك الاطلاع على <a href="https://developer.nytimes.com" rel="external nofollow" target="_blank">توثيق الواجهة البرمجية NYTimes</a> قبل العمل على مثالنا.
</p>

<h3 id="-7">
	الحصول على مفتاح مطوّر
</h3>

<p>
	تحتاج معظم الواجهات إلى استخدام مفتاح من نوع معين لأسباب إحصائية وآمنة. وللحصول على مفتاح للعمل على واجهة NYTimes اطلع على <a href="https://developer.nytimes.com/get-started" rel="external nofollow" target="_blank">الخطوات اللازمة الواردة في صفحة المطورين</a>.
</p>

<ol>
	<li>
		لنطلب مفتاحًا لاستخدام الواجهة في البحث عن مقال، لهذا أنشئ تطبيقًا جديدًا، واختر هذا الاستخدام ليكون الواجهة المطلوبة للتطبيق (املأ في النموذج اسم التطبيق ووصفًا له ثم اختر "Article search <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>" ثم انقر "Create").
	</li>
	<li>
		انسخ المفتاح من الصفحة الناتجة عن التسجيل.
	</li>
	<li>
		حتى نبدأ المثال، انسخ جميع الملفات الموجودة في <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/third-party-apis/nytimes/start" rel="external nofollow" target="_blank">مجلد المثال</a> إلى حاسوبك. وإن حضرت مسبقًا نسخة عن <a href="https://developer.mozilla.org/en-US/docs/Learn#getting_our_code_examples" rel="external nofollow" target="_blank">المستودع الذي يضم أمثلة المقال</a> فستجد نسخة عن الملف المطلوب هنا في المجلد <code>javascript/apis/nytimes/start</code>. ستجد في الملف بعض المتغيرات التي تحتاجها ﻹعداد المثال، وسنملأ الملف بالشيفرة اللازمة لتزويده بالوظائف المطلوبة.
	</li>
</ol>

<p>
	سينتهي بك المطاف إلى تطبيق تكتب فيه معايير البحث مع إمكانية اختيار تاريخ بداية ونهاية فترة البحث، ومن ثم تستخدم هذه البيانات لاستعلام الواجهة NYTimes والبحث عن المطلوب.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="159337" href="https://academy.hsoub.com/uploads/monthly_2024_10/01_nytimes-example.png.b70f6d3907f8a6515ae035ecf32425b5.png" rel=""><img alt="01 nytimes example" class="ipsImage ipsImage_thumbnailed" data-fileid="159337" data-unique="nwlb5fbv2" src="https://academy.hsoub.com/uploads/monthly_2024_10/01_nytimes-example.png.b70f6d3907f8a6515ae035ecf32425b5.png"> </a>
</p>

<h3 id="-8">
	ربط الواجهة البرمجية مع التطبيق
</h3>

<p>
	عليك أولًا ربط الواجهة البرمجية مع تطبيقك، وفي حالتنا، عليك أن تضيف المفتاح كمعامل للطلب <code>get</code> في كل مرة تطلب فيها بيانات من الخدمة على عنوان URL الصحيح.
</p>

<ol>
	<li>
		ابحث أولًا عن السطر التالي:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9137_29" style=""><span class="kwd">const</span><span class="pln"> key </span><span class="pun">=</span><span class="pln"> </span><span class="str">"INSERT-YOUR-API-KEY-HERE"</span><span class="pun">;</span></pre>

<p>
	وأضف السطر التالي في ملف جافا سكريبت تحت التعليق: "<code>// Event listeners to control the functionality</code>". ومهمة هذا السطر تنفيذ الدالة <code>()submitSearch</code> عند النقر على زر إرسال النموذج:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9137_31" style=""><span class="pln">searchForm</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"submit"</span><span class="pun">,</span><span class="pln"> submitSearch</span><span class="pun">);</span></pre>

<ol start="2">
	<li>
		أضف اﻵن تعريفي الدالتين <code>()submitSearch</code> و <code>()fetchResults</code> تحت السطر السابق:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9137_33" style=""><span class="kwd">function</span><span class="pln"> submitSearch</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 pageNumber </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
 fetchResults</span><span class="pun">(</span><span class="pln">e</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> fetchResults</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="com">// Use preventDefault() to stop the form submitting</span><span class="pln">
 e</span><span class="pun">.</span><span class="pln">preventDefault</span><span class="pun">();</span><span class="pln">

 </span><span class="com">// Assemble the full URL</span><span class="pln">
 </span><span class="kwd">let</span><span class="pln"> url </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">baseURL</span><span class="pun">}?</span><span class="pln">api</span><span class="pun">-</span><span class="pln">key</span><span class="pun">=</span><span class="pln">$</span><span class="pun">{</span><span class="pln">key</span><span class="pun">}&amp;</span><span class="pln">page</span><span class="pun">=</span><span class="pln">$</span><span class="pun">{</span><span class="pln">pageNumber</span><span class="pun">}&amp;</span><span class="pln">q</span><span class="pun">=</span><span class="pln">$</span><span class="pun">{</span><span class="pln">searchTerm</span><span class="pun">.</span><span class="pln">value</span><span class="pun">}&amp;</span><span class="pln">fq</span><span class="pun">=</span><span class="pln">document_type</span><span class="pun">:(</span><span class="str">"article"</span><span class="pun">)`;</span><span class="pln">

 </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">startDate</span><span class="pun">.</span><span class="pln">value </span><span class="pun">!==</span><span class="pln"> </span><span class="str">""</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    url </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">url</span><span class="pun">}&amp;</span><span class="pln">begin_date</span><span class="pun">=</span><span class="pln">$</span><span class="pun">{</span><span class="pln">startDate</span><span class="pun">.</span><span class="pln">value</span><span class="pun">}`;</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">

 </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">endDate</span><span class="pun">.</span><span class="pln">value </span><span class="pun">!==</span><span class="pln"> </span><span class="str">""</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    url </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">url</span><span class="pun">}&amp;</span><span class="pln">end_date</span><span class="pun">=</span><span class="pln">$</span><span class="pun">{</span><span class="pln">endDate</span><span class="pun">.</span><span class="pln">value</span><span class="pun">}`;</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تضبط الدالة <code>submitSearch</code> رقم الصفحة على القيمة <code>0</code> ثم تستدعي الدالة <code>()fetchResults</code>. نستخدم الدالة <code>()preventDefault</code> العائدة لكائن الحدث كي لا تجري عملية إرسال مباشر للطلب قبل أن ننهي برمجة المثال. بعد ذلك، نشكل عنوان URL مكتمل بالعمل على بعض القيم النصية كي نستخدمه عند إرسال الطلب، وسنبدأ بالأجزاء الضرورية:
</p>

<ul>
	<li>
		قاعدة عنوان URL (تُؤخذ من المتغير <code>baseURL</code>).
	</li>
	<li>
		مفتاح الوصول إلى الواجهة البرمجية الذي يجب أن يُسند إلى المعامل <code>api-key</code> لعنوان URL (تؤخذ القيمة من المتغير <code>key</code>).
	</li>
	<li>
		رقم الصفحة الذي ينبغي إسناده إلى المعامل <code>page</code> لعنوان URL (تؤخذ القيمة من المتغير <code>pageNumber</code>).
	</li>
	<li>
		العبارة التي نبحث عنها، وتُسند إلى المعامل <code>q</code> لعنوان URL (تؤخذ القيمة من قيمة عنصر اﻹدخال <code>&lt;input&gt;</code> الذي يُدعى <code>searchTerm</code>).
	</li>
	<li>
		نوع المستند الذي نريد الحصول عليه، ويحدد من خلال التعبير الذي يُمرر إلى المعامل <code>fq</code> لعنوان URL. وفي حالتنا نريد أن يعيد البحث مقالًا.
	</li>
</ul>

<p>
	نستخدم تاليًا عبارتي <code>()if</code> للتحقق من وجود قيم المتغيرين <code>startDate</code> و <code>endDate</code>. فإن كان اﻷمر كذلك وضعنا القيمتين في عنوان URL ضمن المعاملين الاختياريين <code>begin_date</code> و <code>end_date</code>. وسيبدو الشكل الكامل لعنوان URL شبيهًا بالعنوان التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9137_35" style=""><span class="pln">https</span><span class="pun">:</span><span class="com">//api.nytimes.com/svc/search/v2/articlesearch.json?api-key=YOUR-API-KEY-HERE&amp;page=0&amp;q=cats&amp;fq=document_type:("article")&amp;begin_date=20170301&amp;end_date=20170312</span></pre>

<p>
	<strong>ملاحظة</strong>: بإمكانك الاطلاع على المعاملات اﻷخرى التي يمكن تضمينها ضمن عنوان URL في <a href="https://developer.nytimes.com/apis" rel="external nofollow" target="_blank">توثيق الواجهة البرمجية NYTimes</a>.
</p>

<p>
	<strong>ملاحظة</strong>: يتحقق المثال بشكل مبسط من القيم المُدخلة. فيجب بداية إدخال نص البحث قبل إرسال الاستعلام باستخدام السمة <code>required</code> (مطلوب). كما يجب أن يضم حقل التاريخ 8 أرقام حتى يُرسل الطلب وذلك من خلال استخدام السمة <code>pattern</code> لتكون قيمتها <code>{pattern=[0-9]{8</code>.
</p>

<h3 id="-9">
	طلب البيانات من الواجهة البرمجية
</h3>

<p>
	بعد أن شكلنا عنوان URL الخاص بالطلب لننفذ الطلب باستعمال الواجهة Fetch <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>. لهذا أضف الشيفرة التالية ضمن كتلة الدالة <code>()fetchResults</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9137_38" style=""><span class="com">// لاستعلام الواجهة البرمجية fetch() استخدم</span><span class="pln">
fetch</span><span class="pun">(</span><span class="pln">url</span><span class="pun">)</span><span class="pln">
 </span><span class="pun">.</span><span class="pln">then</span><span class="pun">((</span><span class="pln">response</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">())</span><span class="pln">
 </span><span class="pun">.</span><span class="pln">then</span><span class="pun">((</span><span class="pln">json</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> displayResults</span><span class="pun">(</span><span class="pln">json</span><span class="pun">))</span><span class="pln">
 </span><span class="pun">.</span><span class="kwd">catch</span><span class="pun">((</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`</span><span class="typ">Error</span><span class="pln"> fetching data</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">error</span><span class="pun">.</span><span class="pln">message</span><span class="pun">}`));</span></pre>

<p>
	ننفذ الاستعلام بتمرير قيمة المتغير إلى الدالة ثم نحوّل الاستجابة إلى صيغة JSON عبر الدالة <code>()json</code> ونمرر النتيجة إلى الدالة <code>()displayResults</code> كي تُعرض البيانات على واجهة المستخدم. وبعدها يُعالج أي خطأ قد يقع باستخدام التابع <code>()catch.</code>.
</p>

<h3 id="-10">
	عرض البيانات
</h3>

<p>
	لننظر إلى الطريقة التي نعرض فيها البيانات على شاشة المستخدم. لهذا، أضف الدالة التالية تحت الدالة <code>()fetchResults</code> مباشرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9137_40" style=""><span class="kwd">function</span><span class="pln"> displayResults</span><span class="pun">(</span><span class="pln">json</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">section</span><span class="pun">.</span><span class="pln">firstChild</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    section</span><span class="pun">.</span><span class="pln">removeChild</span><span class="pun">(</span><span class="pln">section</span><span class="pun">.</span><span class="pln">firstChild</span><span class="pun">);</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">

 </span><span class="kwd">const</span><span class="pln"> articles </span><span class="pun">=</span><span class="pln"> json</span><span class="pun">.</span><span class="pln">response</span><span class="pun">.</span><span class="pln">docs</span><span class="pun">;</span><span class="pln">

 nav</span><span class="pun">.</span><span class="pln">style</span><span class="pun">.</span><span class="pln">display </span><span class="pun">=</span><span class="pln"> articles</span><span class="pun">.</span><span class="pln">length </span><span class="pun">===</span><span class="pln"> </span><span class="lit">10</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> </span><span class="str">"block"</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="str">"none"</span><span class="pun">;</span><span class="pln">

 </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">articles</span><span class="pun">.</span><span class="pln">length </span><span class="pun">===</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> para </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">"p"</span><span class="pun">);</span><span class="pln">
    para</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="str">"No results returned."</span><span class="pun">;</span><span class="pln">
    section</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">para</span><span class="pun">);</span><span class="pln">
 </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> current </span><span class="kwd">of</span><span class="pln"> articles</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
     </span><span class="kwd">const</span><span class="pln"> article </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">"article"</span><span class="pun">);</span><span class="pln">
     </span><span class="kwd">const</span><span class="pln"> heading </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">"h2"</span><span class="pun">);</span><span class="pln">
     </span><span class="kwd">const</span><span class="pln"> link </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">"a"</span><span class="pun">);</span><span class="pln">
     </span><span class="kwd">const</span><span class="pln"> img </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">"img"</span><span class="pun">);</span><span class="pln">
     </span><span class="kwd">const</span><span class="pln"> para1 </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">"p"</span><span class="pun">);</span><span class="pln">
     </span><span class="kwd">const</span><span class="pln"> keywordPara </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">"p"</span><span class="pun">);</span><span class="pln">
     keywordPara</span><span class="pun">.</span><span class="pln">classList</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="str">"keywords"</span><span class="pun">);</span><span class="pln">

     console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">current</span><span class="pun">);</span><span class="pln">

     link</span><span class="pun">.</span><span class="pln">href </span><span class="pun">=</span><span class="pln"> current</span><span class="pun">.</span><span class="pln">web_url</span><span class="pun">;</span><span class="pln">
     link</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> current</span><span class="pun">.</span><span class="pln">headline</span><span class="pun">.</span><span class="pln">main</span><span class="pun">;</span><span class="pln">
     para1</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> current</span><span class="pun">.</span><span class="pln">snippet</span><span class="pun">;</span><span class="pln">
     keywordPara</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Keywords: "</span><span class="pun">;</span><span class="pln">
     </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">const</span><span class="pln"> keyword </span><span class="kwd">of</span><span class="pln"> current</span><span class="pun">.</span><span class="pln">keywords</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">const</span><span class="pln"> span </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">"span"</span><span class="pun">);</span><span class="pln">
      span</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">keyword</span><span class="pun">.</span><span class="pln">value</span><span class="pun">}</span><span class="pln"> </span><span class="pun">`;</span><span class="pln">
      keywordPara</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">span</span><span class="pun">);</span><span class="pln">
     </span><span class="pun">}</span><span class="pln">

     </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">current</span><span class="pun">.</span><span class="pln">multimedia</span><span class="pun">.</span><span class="pln">length </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      img</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">http</span><span class="pun">:</span><span class="com">//www.nytimes.com/${current.multimedia[0].url}`;</span><span class="pln">
      img</span><span class="pun">.</span><span class="pln">alt </span><span class="pun">=</span><span class="pln"> current</span><span class="pun">.</span><span class="pln">headline</span><span class="pun">.</span><span class="pln">main</span><span class="pun">;</span><span class="pln">
     </span><span class="pun">}</span><span class="pln">

     article</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">heading</span><span class="pun">);</span><span class="pln">
     heading</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">link</span><span class="pun">);</span><span class="pln">
     article</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">img</span><span class="pun">);</span><span class="pln">
     article</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">para1</span><span class="pun">);</span><span class="pln">
     article</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">keywordPara</span><span class="pun">);</span><span class="pln">
     section</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">article</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سنشرح فيما يلي النقاط التي عالجتها الشيفرة السابقة:
</p>

<ul>
	<li>
		تُستخدم الحلقة <code>while</code> عادة لحذف محتوى أي عنصر من عناصر <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%B4%D8%AC%D8%B1%D9%8A%D8%A9-%D9%84%D9%80-dom-r646/" rel="">شجرة DOM</a>، وفي حالتنا لمسح محتوى العنصر <code>&lt;section&gt;</code>. إذ نتحقق في هذه الحلقة من وجد ابن أول first child للعنصر باستمرار ونحذفه إن وجد، ثم تنتهي الحلقة عندما لا يتبقى أبناء لهذا العنصر.
	</li>
	<li>
		نضبط قيمة المتغير <code>articles</code> لتكون قيمة <code>json.reponse.docs</code> وهي المصفوفة التي تضم كل الكائنات التي تمثل المقالات التي يعيدها البحث، وذلك لجعل لتبسيط الشيفرة التي تأتي لاحقًا.
	</li>
	<li>
		تتحقق الكتلة <code>()if</code> اﻷولى من وجود 10 نتائج (لأن الواجهة تعيد حتى 10 نتائج في كل مرة)، فإن كان اﻷمر كذلك، تعرض الشيفرة العنصر <code>&lt;nav&gt;</code> الذي يضم زري التنقل بين الصفحات <em>Previous 10</em> و <em>Next 10</em>. إما إن كان عدد النتائج أقل من عشرة فلن يُعرض الزران السابقان لأن الصفحة ستتسع للنتائج. ونناقش شيفرة زري التنقل في فقرة قادمة.
	</li>
	<li>
		تتحقق الكتلة <code>()if</code> الثانية من عدم وجود مقالات يعيدها البحث، فإن كان اﻷمر كذلك، لن نعرض أي شيء، بل ننشئ عنصر فقرة <code>&lt;p&gt;</code> يضم النص "لا توجد نتائج No results return"، ونلحقها بالعنصر <code>&lt;section&gt;</code>.
	</li>
	<li>
		في حال وجود نتائج، ننشئ بداية العناصر اللازمة لعرض نتائج كل مقال إخباري ومن ثم ترتيب هذه العناصر ضمن بعضها بالشكل الصحيح وإلحاقها بشجرة DOM في المكان المناسب. ولمعرفة أية خاصيات لكائنات المقالات تحتوي على المعلومات التي نريد عرضها، عُد إلى مراجع البحث عن مقالات باستخدام الواجهة NYTimes.
	</li>
</ul>

<p>
	إن معظم العمليات السابقة واضحة، لكن بعضها يستحق التوقف والشرح:
</p>

<ul>
	<li>
		استخدمنا <code>()for ...of</code> للتنقل بين جميع المفاتيح المرتبطة بكل مقال ومن ثم وضع كل مفتاح ضمن عنصر <code>&lt;span&gt;</code> مخصص داخل فقرة نصية <code>&lt;p&gt;</code> ليسهل تنسيق البيانات.
	</li>
	<li>
		استخدمنا الكتلة <code>{ }if (current.multimedia.length &gt; 0)</code> للتحقق إن احتوى أي مقال على صور، لأن بعضها لا يمتلك أيًا منها، ونعرض الصورة اﻷولى إن وجدت، وإلا سيُرمى خطأ.
	</li>
</ul>

<h3 id="-11">
	كتابة شيفرة أزرار التنقل بين الصفحات
</h3>

<p>
	حتى يعمل زرا التنقل بين الصفحات لابد من زيادة قيمة المتغير <code>pageNumber</code> أو إنقاصها ومن ثم إعادة تنفيذ طلب إحضار البيانات بعد تحديث قيمة المتغير في عنوان URL. ويعمل هذا لأن الواجهة البرمجية تعيد 10 نتائج فقط في كل مرة، فإن توفّر أكثر من ذلك تعيد العشرة الأولى (من 0 إلى 9) إن كانت قيمة المعامل <code>page</code> هي 0 (أو لم يستخدم هذا المعامل في العنوان أصلًا) وستعيد المجموعة الثانية من النتائج (من 10-19) عندما تكون قيمة المعامل <code>page</code> هي 1.
</p>

<p>
	يتيح لنا ذلك كتابة دالة بسيطة للتنقل بين الصفحات.
</p>

<ol>
	<li>
		أضف الشيفرة التالية بعد الدالة <code>()addEventListener</code> لكي تستدعي الدالتين <code>()nextPage</code> و <code>()previousPage</code> عند النقر على الزر الموافق:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9137_43" style=""><span class="pln">nextBtn</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"click"</span><span class="pun">,</span><span class="pln"> nextPage</span><span class="pun">);</span><span class="pln">
previousBtn</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"click"</span><span class="pun">,</span><span class="pln"> previousPage</span><span class="pun">);</span></pre>

<p>
	لنعرّف اﻵن بعد إضافة الشيفرة السابقة الدالتين السابقتين:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9137_45" style=""><span class="kwd">function</span><span class="pln"> nextPage</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 pageNumber</span><span class="pun">++;</span><span class="pln">
 fetchResults</span><span class="pun">(</span><span class="pln">e</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> previousPage</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">pageNumber </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    pageNumber</span><span class="pun">--;</span><span class="pln">
 </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
 fetchResults</span><span class="pun">(</span><span class="pln">e</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تزيد الدالة اﻷولى قيمة المتغير <code>pageNumber</code> ومن ثم تنفّذ الدالة مجددًا لعرض نتائج الصفحة التالية. وتعمل الدالة الثانية بنفس الطريقة تمامًا لكن بالعكس، وعلينا اتخاذ خطوة إضافية للتحقق أن قيمة المتغير <code>pageNumber</code> ليست صفرًا قبل إنقاص القيمة، فقد يسبب تنفيذ طلب إحضار البيانات بقيمة سالبة لهذا المتغير خطأً. فإن كانت قيمته بالفعل <code>0</code> ننهي الدالة مباشرة بتنفيذ <code>return</code>، ولا حاجة ﻹعادة طلب نفس النتائج الموجودة مرة أخرى.
</p>

<p>
	<strong>ملاحظة</strong>: بإمكانك الاطلاع على <a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/third-party-apis/nytimes/finished/index.html" rel="external nofollow" target="_blank">النسخة المكتملة</a> من التطبيق على جت-هب (وبإمكانك <a href="https://mdn.github.io/learning-area/javascript/apis/third-party-apis/nytimes/finished/" rel="external nofollow" target="_blank">تجربته مباشرة</a> أيضًا).
</p>

<h2 id="-12">
	مثال عن استخدام واجهة يوتيوب البرمجية
</h2>

<p>
	نقدم لك مثالًا أيضًا عن واجهة يوتيوب البرمجية لتدرسه وتتعلم منه. لهذا الق نظرة على مثال <a href="https://mdn.github.io/learning-area/javascript/apis/third-party-apis/youtube/" rel="external nofollow" target="_blank">YouTube video search example</a> الذي يتضمن واجهتين مرتبطتين ببعضهما:
</p>

<ul>
	<li>
		الواجهة <a href="https://developers.google.com/youtube/v3/docs/" rel="external nofollow" target="_blank">YouTube Data <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a> لليحث عن فيديوهات على يوتيوب وإعادة النتائج.
	</li>
	<li>
		الواجهة <a href="https://developers.google.com/youtube/iframe_api_reference" rel="external nofollow" target="_blank">YouTube IFrame Player <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a> لعرض الفيديوهات التي يعيدها البحث ضمن إطار IFrame لتشغيل فيديو.
	</li>
</ul>

<p>
	تأتي أهمية هذا المثال من كونه يعرض طريقة للربط بين واجهتين برمجيتين يقدمهما طرف خارجي وتعملان معًا. اﻷولى تتوافق مع واجهات RESTful وتعمل اﻷخرى بطريقة مشابهة للواجهة mapquest (تستخدم توابع ودوال خاصة). وتجدر اﻹشارة إلى أن كلتا الواجهتين تتطلب استخدام مكتبة جافا سكريبت في الصفحة، وأن للواجهة RESTfull دوال للتعامل مع تحضير طلبات HTTP وإعادة النتائج.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="159338" href="https://academy.hsoub.com/uploads/monthly_2024_10/02_youtube-example.png.ee342cb9646283bf308e473124489d35.png" rel=""><img alt="02 youtube example" class="ipsImage ipsImage_thumbnailed" data-fileid="159338" data-unique="wijjj6vhj" src="https://academy.hsoub.com/uploads/monthly_2024_10/02_youtube-example.png.ee342cb9646283bf308e473124489d35.png"> </a>
</p>

<p>
	لن نعرض الكثير عن هذا المثال في مقالنا، لكنك ستجد الكثير من التفاصيل في التعليقات التي تشرح طريقة العمل.
</p>

<p>
	ولكي تشغّل المثال تحتاج إلى:
</p>

<ul>
	<li>
		قراءة <a href="https://developers.google.com/youtube/v3/getting-started" rel="external nofollow" target="_blank">توثيق الواجهة البرمجية</a>.
	</li>
	<li>
		زيارة الصفحة <a href="https://console.cloud.google.com/apis/enabled" rel="external nofollow" target="_blank">Enabled APIs page</a> والتأكد من حالة الواجهة YouTube Data <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> v3 (هل هي ON) ضمن قائمة الواجهات المعروضة.
	</li>
	<li>
		الحصول على مفتاح لاستخدام الواجهة من خلال <a href="https://cloud.google.com/" rel="external nofollow" target="_blank">Google Cloud</a>.
	</li>
	<li>
		استبدال القيمة <code>ENTER-<abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>-KEY-HERE</code> في الشيفرة المصدرية بالمفتاح الذي حصلت عليه.
	</li>
	<li>
		تنفيذ المثال من خلال خادم ويب، فلن يعمل إن شغّلته في متصفحك مباشرة. (من خلال العنوان <code>//:file</code>).
	</li>
</ul>

<h2 id="-13">
	الخلاصة
</h2>

<p>
	يقدم هذا المقال مدخلًا مفيدة عن استخدام الواجهات البرمجية التي يقدمها طرف خارجي ﻹضافة وظائف جديدة إلى موقعك.
</p>

<p>
	ترجمة -وبتصرف- لمقال: <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Third_party_APIs" rel="external nofollow" target="_blank">Third-party APIs</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/%D8%A5%D8%AD%D8%B6%D8%A7%D8%B1-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%85%D9%86-%D8%A7%D9%84%D8%AE%D8%A7%D8%AF%D9%85-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2419/" rel="">إحضار البيانات من الخادم باستخدام جافا سكريبت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%BA%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1304/" rel="">البرمجة غير المتزامنة في جافاسكريبت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A5%D8%B1%D8%B3%D8%A7%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%88%D8%A7%D8%B3%D8%AA%D9%84%D8%A7%D9%85%D9%87%D8%A7-%D8%B9%D8%A8%D8%B1-%D8%A7%D9%84%D8%B4%D8%A8%D9%83%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1294/" rel="">إرسال البيانات واستلامها عبر الشبكة في جافاسكربت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-fetch-%D9%85%D8%B9-%D8%A7%D9%84%D8%B7%D9%84%D8%A8%D8%A7%D8%AA-%D8%B0%D8%A7%D8%AA-%D8%A7%D9%84%D8%A3%D8%B5%D9%84-%D8%A7%D9%84%D9%85%D8%AE%D8%AA%D9%84%D8%B7-cross-origin-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1296/" rel="">استخدام Fetch مع الطلبات ذات الأصل المختلط Cross-Origin في جافاسكربت</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2423</guid><pubDate>Wed, 09 Oct 2024 15:07:04 +0000</pubDate></item><item><title>&#x625;&#x62D;&#x636;&#x627;&#x631; &#x627;&#x644;&#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; &#x645;&#x646; &#x627;&#x644;&#x62E;&#x627;&#x62F;&#x645; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x62C;&#x627;&#x641;&#x627; &#x633;&#x643;&#x631;&#x64A;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A5%D8%AD%D8%B6%D8%A7%D8%B1-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%85%D9%86-%D8%A7%D9%84%D8%AE%D8%A7%D8%AF%D9%85-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2419/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_10/------.png.a1fbb3d0de589edb4f6f6d6fcaccbe60.png" /></p>
<p>
	تطرقنا سابقًا للحديث عن <span ipsnoautolink="true">واجهة برمجة التطبيقات</span> واستخدامها في مهام مختلفة مثل <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%B4%D8%AC%D8%B1%D8%A9-dom-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2360/" rel="">معالجة مستندات الويب وشجرة DOM</a>، ومن المهام اﻷخرى الشائعة لتطبيقات ومواقع الويب إحضار بيانات محددة من الخادم لتحديث بعض أجزاء صفحة الويب دون الحاجة إلى تحميل صفحة جديدة. وكان لهذا اﻷمر البسيط أثر هائل على أداء وسلوك صفحات الويب. سنشرح في هذا المقال التقنيات التي تسمح بتنفيذ هذه اﻷمور مثل وعلى وجه الخصوص الواجهة <code>Fetch</code>.
</p>

<p>
	ننصحك قبل المتابعة في قراءة هذه المقالات أن:
</p>

<ul>
	<li>
		تكون ملمًا بلغتي <a href="HTTPS://wiki.hsoub.com/HTML" rel="external">HTML</a> و <a href="HTTPS://wiki.hsoub.com/CSS" rel="external">CSS</a>.
	</li>
	<li>
		تكون ملمًا بلغة <a href="HTTPS://wiki.hsoub.com/JavaScript" rel="external">جافا سكريبت</a>.
	</li>
	<li>
		تطلع على سلاسل المقالات السابقة التي ناقشت <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D8%B1%D9%91%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D9%85%D9%86-%D9%85%D9%86%D8%B8%D9%88%D8%B1-%D8%B9%D8%A7%D9%85-r2266/" rel="">أساسيات جافا سكريبت</a> و<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%81%D8%A7%D9%87%D9%8A%D9%85-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%83%D8%A7%D8%A6%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%AC%D9%87-%D9%88%D8%AA%D8%AD%D9%82%D9%8A%D9%82%D9%87%D8%A7-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2316/" rel="">الكائنات في جافا سكريبت</a>.
	</li>
	<li>
		تطلع على أساسيات <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-web-apis-r2355/" rel="">الواجهات البرمجية في طرف العميل.</a>
	</li>
</ul>

<h2 id="-1">
	ما الذي يحدث عند طلب مورد من الخادم؟
</h2>

<p>
	تتكون صفحة الويب من صفحة HTML إضافة إلى عدة ملفات أخرى كملفات التنسيق <a href="https://academy.hsoub.com/programming/css/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-css/" rel="">CSS</a> والسكريبتات والصور. وما يحدث وفق النموذج النمطي لتحميل الصفحة، أن متصفحك سيرسل عدة طلبات إلى الخادم لإحضار تلك الملفات اللازمة لعرض الصفحة بالشكل المطلوب، ومن المفترض أن يستجيب الخادم مرسلًا تلك الملفات. ويتكرر اﻷمر في كل مرة تزور فيها صفحة جديدة.
</p>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="159061" href="https://academy.hsoub.com/uploads/monthly_2024_10/01-traditional-loading-(1).png.358adc2c27005b21455d3296eaf4d7d5.png" rel=""><img alt="01-traditional-loading-(1).png" class="ipsImage ipsImage_thumbnailed" data-fileid="159061" data-ratio="38.89" data-unique="r4x6nltrp" width="900" src="https://academy.hsoub.com/uploads/monthly_2024_10/01-traditional-loading-(1).thumb.png.eb1409eb7444f22d962bbd0a2c48ff69.png"></a>
</p>

<p>
	يعمل هذا النموذج جيدًا في العديد من المواقع، لكن ماذا لو كان الموقع مخصصًا ﻹحضار بيانات من الخادم مثل موقع مكتبة؟ ومن الممكن أن تفكّر في هذا النوع من المواقع على أنه واجهة تربط المستخدم بقاعدة بيانات. فقد يسمح لك بالبحث عن نوع معين من الكتب أو قد ينصحك بكتب تستهويك وفقًا لقائمة الكتب التي استعرتها سابقًا. وعندما تفعل ذلك، لا بد من تحديث محتوى الصفحة بمجموعة الكتب الجديدة التي بحثت عنها أو نصحك بها الموقع. مع هذا، سيبقى جُل محتوى الصفحة كما هو دون تغيير مثل الترويسة والتذييل والأشرطة الجانبية.
</p>

<p>
	إن مشكلة النموذج النمطي في هذه الحالة هو أنه سيحضر البيانات ويعيد تحميل الصفحة بأكملها حتى لو أردنا تحديث جزء صغير منها، وهذا أمر عديم الجدوى كما يقدم تجربة سيئة لمستخدمي الموقع. ولتجنب هذا اﻷمر، تستخدم العديد من مواقع الويب واجهات جافا سكريبت البرمجية <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel="">APIs</a> لطلب البيانات من الخادم وتحديث محتوى الصفحة دون إعادة تحميلها. فعندما يبحث المستخدم عن منتج جديد مثلًا، سيطلب المتصفح فقط البيانات التي يحتاجها لتحديث الصفحة كأن يطلب قائمة بالكتب التي يجب عرضها.
</p>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="159062" href="https://academy.hsoub.com/uploads/monthly_2024_10/02-fetch-update.png.fe5e79c573a4489d315e4ada8a32b9d4.png" rel=""><img alt="02-fetch-update.png" class="ipsImage ipsImage_thumbnailed" data-fileid="159062" data-ratio="46.22" data-unique="f23oy4i0u" width="900" src="https://academy.hsoub.com/uploads/monthly_2024_10/02-fetch-update.thumb.png.bcdb9f20e35cadede8f23e1d76f0daaf.png"></a>
</p>

<p>
	إن الواجهة البرمجية الرئيسية في هذه الحالة هي <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-fetch-api-r1297/" rel="">الواجهة Fetch</a> التي تسمح لشيفرة جافا سكريبت في صفحة الويب بإرسال طلبات إلى الخادم لإحضار مورد محدد. وعندما يقدم الخادم البيانات المطلوبة، يمكن للشيفرة أن تستخدمها لتحديث محتوى الصفحة من خلال واجهة برمجية أخرى هي شجرة DOM عادة. وغالبًا ما تكون البيانات المطلوبة محضّرة وفق تنسيق <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A8%D8%B5%D9%8A%D8%BA%D8%A9-json-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2318/" rel="">JSON</a> وهي صيغة مناسبة لنقل البيانات المهيكلة، لكن البيانات قد تكون أيضًا شيفرة HTML أو مجرد نص نمطي. وستجد هذا النموذج في الكثير من العديد من المواقع المصممة لتبادل البيانات مثل أمازون ويوتيوب وإي باي وغيرها. ومن خلال هذا النموذج:
</p>

<ul>
	<li>
		سيكون تحديث الصفحة أسرع بكثير ولن تُضطر إلى الانتظار حتى تُحمَّل الصفحة ككل، وهذا ما يعطي شعورًا بان الموقع أسرع وأكثر تجاوبًا.
	</li>
	<li>
		يُنزَّل كم قليل من البيانات عند كل تحديث للمحتوى وبالتالي هدرًا أقل لحزمة البيانات المخصصة للمستخدم. وعلى الرغم أن هذا الأمر لن يكون مشكلة في اﻷجهزة المكتبية المتصلة بحزمة اتصال عريضة broadband لكنها مهمة جدًا في اﻷجهزة المحمولة أو في الدول التي لا تمتلك خدمة انترنت سريعة.
	</li>
</ul>

<p>
	<strong>ملاحظة</strong>: عُرفت هذه التقنية في بداياتها باسم "<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%BA%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1304/" rel="">جافا سكريبت غير المتزامنة</a> و <a href="https://academy.hsoub.com/programming/java/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%85%D8%AE%D8%AA%D8%B5%D8%B1%D8%A9-%D9%84%D9%84%D8%BA%D8%A9-xml-%D9%88%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84%D9%87%D8%A7-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%AC%D8%A7%D9%81%D8%A7-r1475/" rel="">XML</a>" واختصارًا أجاكس Ajax لأنها تميل إلى إحضار البيانات على شكل بيانات XML. وعلى الرغم أن البيانات المطلوبة حاليًا هي بيانات JSON، لكن الطريقة تبقى ذاتها ولازال المصطلح Ajax يشير إلى هذه التقنية
</p>

<p>
	ولتسريع اﻷمر أكثر، تُخزّن بعض المواقع أيضًا بيانات وأصولًا ضمن حاسوب المستخدم عند طلبها للمرة اﻷولى، وبالتالي ستُستخدم هذه النسخة المخزنة عند الزيارات اللاحقة بدلًا من تنزيل نسخ جديدة في كل مرة تُحمّل فيها الصفحة. ولن يُعاد تحميل المحتوى من الخادم إلا عندما يُحدّث هذا المحتوى على الخادم.
</p>

<h2 id="fetch">
	الواجهة البرمجية Fetch
</h2>

<p>
	سنتعلم أكثر عن هذه الواجهة من خلال المثاليين اﻵتيين.
</p>

<h3 id="-2">
	إحضار محتوى نصي
</h3>

<p>
	سنطلب في هذا المثال بيانات نصية من عدة ملفات ونستخدمها لتحديث الجزء الذي يضم محتوى الصفحة. ستعمل سلسلة الملفات النصية السابقة كقاعدة بيانات مفترضة، لكن تجدر اﻹشارة إلا أننا نستخدم لطلب البيانات عادة لغة برمجة من طرف الخادم في حالات كهذه مثل <a href="https://wiki.hsoub.com/PHP" rel="external">PHP</a> أو <a href="https://wiki.hsoub.com/Python" rel="external">بايثون</a> أو <a href="https://wiki.hsoub.com/Node.js" rel="external">Node.js</a> من قاعدة بيانات حقيقية. مع ذلك، نتوخى في مثالنا البساطة ونحاول التركيز على تقنية إحضار البيانات من طرف العميل.
</p>

<p>
	وحتى تبدأ العمل معنا حمّل نسختك من الملفات <a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/fetching-data/fetch-start.html" rel="external nofollow">fetch-start.html</a> و <a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/fetching-data/verse1.txt" rel="external nofollow">verse1.txt</a> و <a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/fetching-data/verse2.txt" rel="external nofollow">verse2.txt</a> و <a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/fetching-data/verse3.txt" rel="external nofollow">verse3.txt</a> <a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/fetching-data/verse4.txt" rel="external nofollow">verse4.txt</a>، ثم ضعها في مجلد جديد على حاسوبك. وما سنفعله لاحقًا هو إحضار أبيات محددة من قصيدة عندما نختار هذه اﻷبيات من قائمة منسدلة.
</p>

<p>
	أضف الشيفر التالية ضمن العنصر <code>&lt;script&gt;</code>. إذ تُخزّن هذه الشيفرة مرجعين إلى العنصرين <code>&lt;select&gt;</code> و <code>&lt;pre&gt;</code>، وعندما يختار المستخدم قيمةً، تمرر هذه القيمة كمعامل إلى الدالة <code>()updateDisplay</code>:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7435_19" style=""><span class="kwd">const</span><span class="pln"> verseChoose </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"select"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> poemDisplay </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"pre"</span><span class="pun">);</span><span class="pln">

verseChoose</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"change"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> verse </span><span class="pun">=</span><span class="pln"> verseChoose</span><span class="pun">.</span><span class="kwd">value</span><span class="pun">;</span><span class="pln">
 updateDisplay</span><span class="pun">(</span><span class="pln">verse</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	لنعرّف بداية الدالة <code>()updateDisplay</code> بوضع الشيفرة التالية تحت الشيفرة السابقة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7435_21" style=""><span class="kwd">function</span><span class="pln"> updateDisplay</span><span class="pun">(</span><span class="pln">verse</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

</span><span class="pun">}</span></pre>

<p>
	نبدأ كتابة شيفرة الدالة بإنشاء عنوان URL نسبي يشير إلى الملف النصي الذي نريد تحميله، إذ نحتاجه لاحقًا. وستكون قيمة العنصر <code>&lt;select&gt;</code> مطابقة دائمًا لمحتوى هذا العنصر ما لم نسند إليه قيمة أخرى من خلال السمة <code>value</code> مثل "Verse 1". في هذه الحالة سيكون الملف النصي الموافق هو الملف "verse1.txt" الموجود في نفس المجلد الذي يضم ملف HTML، لهذا يكفي استخدام اسم الملف كعنوان URL نسبي.
</p>

<p>
	وانتبه إلى أن الخوادم تتحس حالة اﻷحرف غالبًا لهذا السبب علينا إزالة الفراغ من القيمة "Verse 1" وكذلك تحويل الحرف "V" إلى الشكل الصغير "v" ومن ثم إضافة اللاحقة "txt.". ولتنفيذ ذلك، استخدم التابعين النصيين <code>()replace</code> و <code>()toLowerCase</code> إضافة إلى قالب حرفي template literal <code>{...}$</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7435_23" style=""><span class="pln">verse </span><span class="pun">=</span><span class="pln"> verse</span><span class="pun">.</span><span class="pln">replace</span><span class="pun">(</span><span class="str">" "</span><span class="pun">,</span><span class="pln"> </span><span class="str">""</span><span class="pun">).</span><span class="pln">toLowerCase</span><span class="pun">();</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> url </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">verse</span><span class="pun">}.</span><span class="pln">txt</span><span class="pun">`;</span></pre>

<p>
	وأخيرًا أصبحنا مستعدين لاستخدام الواجهة البرمجية Fetch:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7435_25" style=""><span class="com">//URL ومرر إليها عنوان `fetch()`استدع</span><span class="pln">
fetch</span><span class="pun">(</span><span class="pln">url</span><span class="pun">)</span><span class="pln">
 </span><span class="com">//وعدًا، وعندما يستجيب الخادم يًستدعى التابع fetch() تعيد الدالة</span><span class="pln">
 </span><span class="com">//`then()`</span><span class="pln">
 </span><span class="pun">.</span><span class="pln">then</span><span class="pun">((</span><span class="pln">response</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// يرمي معالج الحدث خطأً إن أخفق الوعد</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">response</span><span class="pun">.</span><span class="pln">ok</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
     </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(`</span><span class="pln">HTTP error</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">response</span><span class="pun">.</span><span class="pln">status</span><span class="pun">}`);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="com">// وإن نحج الوعد، يعيد معالج الحدث الاستجابة على شكل نص باستدعاء التابع</span><span class="pln">
    </span><span class="com">//الذي يعيد بدوره وعدًا، وعند إنجاز الوعد اﻷخير response.text()</span><span class="pln">

 </span><span class="pun">})</span><span class="pln">
 </span><span class="com">//`poemDisplay` الذي يعيد النص فننسخه إلى مربع النص `then()` يُستدعى</span><span class="pln">
 </span><span class="pun">.</span><span class="pln">then</span><span class="pun">((</span><span class="pln">text</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    poemDisplay</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> text</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">})</span><span class="pln">
 </span><span class="com">//`poemDisplay` التقاط أية أخطاء أخرى وعرضها برسالة ضمن الصندوق</span><span class="pln">
 </span><span class="pun">.</span><span class="kwd">catch</span><span class="pun">((</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    poemDisplay</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="typ">Could</span><span class="pln"> not fetch verse</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">error</span><span class="pun">}`;</span><span class="pln">
 </span><span class="pun">});</span></pre>

<p>
	وإليك توضيحًا للشيفرة السابقة:
</p>

<ul>
	<li>
		إن مدخل الواجهة البرمجية Fetch هو الدالة العامة <code>()fetch</code> التي تأخذ عنوان URL معاملًا لها (ولها أيضًا معامل آخر اختياري لأغراض خاصة، لكننا لم نستخدمه).
	</li>
	<li>
		الدالة <code>()fetch</code> هي دالة غير متزامنة تعيد وعدًا <code>Promise</code>، بإمكانك مراجعة مقال <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%88%D8%B9%D9%88%D8%AF-promises-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2341/" rel="">استخدام الوعود في جافا سكريبت غير المتزامنة</a> إن لم تكن على دراية بمفهوم الوعود، ثم العودة والمتابعة معنا، وستلاحظ أن هذا المقال يتحدث أيضًا عن الواجهة Fetch.
	</li>
	<li>
		تعيد الدالة <code>()fetch</code> وعدًا، لهذا نمرر دالة إلى التابع <code>()then</code> المرتبط بهذا الوعد. يُستدعى هذا التابع حالما يتلقى طلب HTTP ردًا من الخادم. ومن ثم نتحقق من نجاح الوعد (إنجازه) ضمن دالة معالج الحدث ونرمي رسالة خطأ إن لم ينجح. وعند النجاح، نستدعي الدالة <code>()response.text</code> للحصول على جسم الاستجابة على شكل نص.
	</li>
	<li>
		إن الدالة <code>()response.text</code> هي أيضًا دالة غير متزامنة، لهذا نعيد الوعد الذي تعيده ونمرره إلى التابع <code>()then</code> المرتبطة بالوعد الجديد. يُستدعى هذا التابع عندما يجهز نص الاستجابة، ونضع ضمنه شيفرة تحديث محتوى العنصر <code>&lt;pre&gt;</code>.
	</li>
	<li>
		نربط أخيرًا دالة المعالجة <code>()catch</code> في النهاية لالتقاط أية أخطاء ترميها أيًا من الدوال غير المتزامنة التي استدعيناها أو معالجات اﻷحداث المتعلقة بها.
	</li>
</ul>

<p>
	أحد مشكلات هذا المثال أنه لن يعرض أية قصيدة عندما يُحمّل للمرة الأولى. وﻹصلاح اﻷمر، أضف السطرين التاليين في نهاية شيفرتك (قبل وسم النهاية <code>&lt;script/&gt;</code>) لتحميل verse 1 افتراضيًا ولكي يأخذ العنصر <code>&lt;select&gt;</code> القيمة الصحيحة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7435_28" style=""><span class="pln">updateDisplay</span><span class="pun">(</span><span class="str">"Verse 1"</span><span class="pun">);</span><span class="pln">
verseChoose</span><span class="pun">.</span><span class="pln">value </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Verse 1"</span><span class="pun">;</span></pre>

<h4 id="-3">
	تشغيل المثال على الخادم
</h4>

<p>
	لن تسمح المتصفحات الحديثة بتنفيذ طلبات HTTP إن كنت تشغّل المثال على حاسوبك الشخصي بسبب قيود أمنية. وللالتفاف على الموضوع، عليك اختبار المثال على خادم ويب محلي. للمزيد من المعلومات اطلع على مقال <a href="https://academy.hsoub.com/devops/servers/%D8%AF%D9%84%D9%8A%D9%84-%D8%A5%D8%B9%D8%AF%D8%A7%D8%AF-%D8%AE%D8%A7%D8%AF%D9%85-%D9%88%D9%8A%D8%A8-%D9%85%D8%AD%D9%84%D9%8A-%D8%AE%D8%B7%D9%88%D8%A9-%D8%A8%D8%AE%D8%B7%D9%88%D8%A9-r422/" rel="">دليل إعداد خادم ويب محلي خطوة بخطوة</a> الذي نشرته <a href="https://academy.hsoub.com/" rel="">أكاديمية حسوب</a>.
</p>

<h3 id="-4">
	متجر معلبات
</h3>

<p>
	أنشأنا في هذا المثال موقعًا بسيطًا يُدعى متجر المعلبات The Can Store، وهو سوبر ماركت يبيع المعلبات فقط. بإمكانك <a href="https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/" rel="external nofollow">تجربة المثال مباشرة</a> على جت-هب والاطلاع على <a href="https://github.com/mdn/learning-area/tree/main/javascript/apis/fetching-data/can-store" rel="external nofollow">شيفرته المصدرية</a>.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="159063" href="https://academy.hsoub.com/uploads/monthly_2024_10/03_can-store.png.aed9ea11396295544004b552e5a18016.png" rel=""><img alt="03 can store" class="ipsImage ipsImage_thumbnailed" data-fileid="159063" data-unique="5drzlqm4n" style="width: 500px; height: auto;" src="https://academy.hsoub.com/uploads/monthly_2024_10/03_can-store.thumb.png.3d9a76c6fc13cf9da61b7043934d3c18.png"> </a>
</p>

<p>
	يعرض هذا الموقع افتراضيًا جميع المنتجات، لكنك تستطيع ترشيح أو فلترة هذه المنتجات والبحث عنها باستخدام أدوات التحكم الموجودة ضمن العمود اليساري للصفحة.
</p>

<p>
	قد تجد بعض التعقيد في شيفرة ترشيح المنتجات وفقًا للتصنيف مثل معايير البحث ومعالجة النصوص لعرض البيانات بشكل صحيح على واجهة المستخدم وغيرها. لن نشرح بالطبع كل التفاصيل في هذا المقال لكنك ستجد كما كبيرًا من التعليقات التي تشرح الشيفرة ضمن الملف <a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/fetching-data/can-store/can-script.js" rel="external nofollow">can-script.js</a>، مع ذلك سنشرح شيفرة الواجهة fetch.
</p>

<p>
	ستجد أولى الكتل البرمجية التي تستخدم fetch في مقدمة الملف:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7435_30" style=""><span class="pln">fetch</span><span class="pun">(</span><span class="str">"products.json"</span><span class="pun">)</span><span class="pln">
 </span><span class="pun">.</span><span class="pln">then</span><span class="pun">((</span><span class="pln">response</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">response</span><span class="pun">.</span><span class="pln">ok</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
     </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(`</span><span class="pln">HTTP error</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">response</span><span class="pun">.</span><span class="pln">status</span><span class="pun">}`);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">json</span><span class="pun">();</span><span class="pln">
 </span><span class="pun">})</span><span class="pln">
 </span><span class="pun">.</span><span class="pln">then</span><span class="pun">((</span><span class="pln">json</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> initialize</span><span class="pun">(</span><span class="pln">json</span><span class="pun">))</span><span class="pln">
 </span><span class="pun">.</span><span class="kwd">catch</span><span class="pun">((</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`</span><span class="typ">Fetch</span><span class="pln"> problem</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">err</span><span class="pun">.</span><span class="pln">message</span><span class="pun">}`));</span></pre>

<p>
	تعيد الدالة <code>()fetch</code> وعدًا، فإن أنجز هذا الوعد بنجاح، ستعاد الدالة الموجودة ضمن أول كتلة <code>()then</code> تضم الاستجابة <code>response</code> من الشبكة، وما نفعله ضمن هذه الدالة هو:
</p>

<ul>
	<li>
		التحقق من عدم إرسال الخادم خطأً (مثل <code>Not Found 404</code>) وإن حدث ذلك، نرمي الخطأ.
	</li>
	<li>
		استدعاء التابع <code>()json</code> للعمل على الاستجابة واستخلاص البيانات منها على شكل كائن JSON، ثم نعيد الوعد الذي يعيده <code>response.json</code>.
	</li>
</ul>

<p>
	نمرر دالة إلى التابع <code>()then</code> المرتبط بالوعد المعاد، كما نمرر إلى هذه الدالة كائنًا يتضمن بيانات الاستجابة وفق تنسيق JSON بعد تمريرها إلى الدالة <code>()initialize</code> التي تبدأ عملية عرض جميع المنتجات على واجهة المستخدم.
</p>

<p>
	ولمعالجة اﻷخطاء، نربط كتلة <code>()catch.</code> في نهاية السلسلة، وستعمل شيفرة هذه الكتلة إذا وقع خطأ لسبب ما. نضع ضمن هذه الكتلة دالة يُمرر إليها الكائن <code>err</code> كمعامل ويُستخدم لتسجيل طبيعة الخطأ الذي حصل ونعرضه من خلال الدالة <code>()console.error</code>.
</p>

<p>
	تتعامل المواقع المكتملة مع اﻷخطاء بطريقة شمولية أفضل، وذلك بعرض رسالة على شاشة المستخدم. كما قد تعرض أيضًا خيارات لحل المشكلة، لكننا لن نحتاج هنا إلا للتابع <code>()console.error</code>.
</p>

<p>
	بإمكانك أيضًا اختبار حالات الفشل بنفسك:
</p>

<ol>
	<li>
		انسخ ملفات التمرين على حاسوبك.
	</li>
	<li>
		شغل الشيفرة باستخدام خادم ويب محلي.
	</li>
	<li>
		عدّل مسار الملف الذي نحضره مثل "produc.json" بدلًا من "product.json" (تأكد من ارتكاب خطأ كتابي).
	</li>
	<li>
		حمّل اﻵن الملف index.html في المتصفح (<code>localhost:8000/index.html</code>) ثم ألق نظرة على طرفية جافا سكريبت، وستجد رسالة خطأ مشابهة للرسالة "Fetch problem: HTTP error: 404".
	</li>
</ol>

<p>
	ستجد كتلة fetch الثانية ضمن الدالة <code>()fetchBlob</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7435_32" style=""><span class="pln">fetch</span><span class="pun">(</span><span class="pln">url</span><span class="pun">)</span><span class="pln">
 </span><span class="pun">.</span><span class="pln">then</span><span class="pun">((</span><span class="pln">response</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">response</span><span class="pun">.</span><span class="pln">ok</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
     </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Error</span><span class="pun">(`</span><span class="pln">HTTP error</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">response</span><span class="pun">.</span><span class="pln">status</span><span class="pun">}`);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> response</span><span class="pun">.</span><span class="pln">blob</span><span class="pun">();</span><span class="pln">
 </span><span class="pun">})</span><span class="pln">
 </span><span class="pun">.</span><span class="pln">then</span><span class="pun">((</span><span class="pln">blob</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> showProduct</span><span class="pun">(</span><span class="pln">blob</span><span class="pun">,</span><span class="pln"> product</span><span class="pun">))</span><span class="pln">
 </span><span class="pun">.</span><span class="kwd">catch</span><span class="pun">((</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`</span><span class="typ">Fetch</span><span class="pln"> problem</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">err</span><span class="pun">.</span><span class="pln">message</span><span class="pun">}`));</span></pre>

<p>
	تعمل هذه الشيفرة تمامًا كسابقتها ما عدا أننا استخدمنا التابع <code>()blob</code> بدلًا من <code>()json</code> لأننا نريد في هذه الحالة الحصول على ملف صورة في الاستجابة وسيكون حينها تنسيق البيانات على شكل كائن بيانات ثنائية <a href="https://academy.hsoub.com/programming/javascript/%D9%83%D8%A7%D8%A6%D9%86-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D8%AB%D9%86%D8%A7%D8%A6%D9%8A%D8%A9-blob-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1285/" rel="">Blob</a> (اختصارًا للعبارة "كائن ضخم ثنائي Binry Large Object"). ويُستخدم هذا الكائن لتمثيل كائنات ضخمة مشابهة للملفات مثل الصور والفيديو.
</p>

<p>
	وبمجرد أن نحصل على الكائن blob، نمرره إلى الدالة <code>()showProduct</code> التي تعرضه.
</p>

<h2 id="xmlhttprequest">
	الواجهة البرمجية XMLHttpRequest
</h2>

<p>
	سترى في بعض اﻷحيان وخاصة في الشيفرة اﻷقدم واجهة برمجية أخرى تُدعى <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86-xmlhttprequest-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1299/" rel=""><code>XMLHttpRequest</code></a> (وتختصر أحيانًا إلى "XHR") تُستخدم في إجراء طلبات HTTP. وقد سبقت هذه الواجهة الواجهة البرمجية Fetch وكانت أولى الواجهات التي استخدمت على نطاق واسع لتنفيذ تقنية AJAX. لكن ننصحك باستخدام Fetch إن أمكن فهي واجهة أبسط وتضم ميزات أكثر من الواجهة XMLHttpRequest. لن نقدم مثالًا عن استخدام الواجهة <code>XMLHttpRequest</code>، لكننا سنعرض نسخة <code>XMLHttpRequest</code> من مثال متجر المعلبات. سيبدو الطلب كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7435_34" style=""><span class="kwd">const</span><span class="pln"> request </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">XMLHttpRequest</span><span class="pun">();</span><span class="pln">

</span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 request</span><span class="pun">.</span><span class="pln">open</span><span class="pun">(</span><span class="str">"GET"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"products.json"</span><span class="pun">);</span><span class="pln">

 request</span><span class="pun">.</span><span class="pln">responseType </span><span class="pun">=</span><span class="pln"> </span><span class="str">"json"</span><span class="pun">;</span><span class="pln">

 request</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"load"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> initialize</span><span class="pun">(</span><span class="pln">request</span><span class="pun">.</span><span class="pln">response</span><span class="pun">));</span><span class="pln">
 request</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"error"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="str">"XHR error"</span><span class="pun">));</span><span class="pln">

 request</span><span class="pun">.</span><span class="pln">send</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`</span><span class="pln">XHR error $</span><span class="pun">{</span><span class="pln">request</span><span class="pun">.</span><span class="pln">status</span><span class="pun">}`);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هناك خمس مراحل:
</p>

<ol>
	<li>
		إنشاء كائن <code>XMLHttpRequest</code>. جديد.
	</li>
	<li>
		استدعاء التابع <code>()open</code> لتهيئة الكائن الجديد.
	</li>
	<li>
		إضافة مترصد للحدث <code>load</code> يُنفَّذ عند إكتمال الطلب بنجاح. ونستدعي الدالة <code>()initialize</code> بعد تزويدها بالبيانات ضمن دالة مترصد الحدث.
	</li>
	<li>
		إضافة مترصد حدث إلى الحدث <code>error</code> الذي يقع عندما يواجهة الطلب خطأً.
	</li>
	<li>
		إرسال الطلب
	</li>
</ol>

<p>
	ولا بد من تغليف الشيفرة السابقة ضمن كتلة <code>try...catch</code> للتعامل مع الأخطاء التي قد تحدث عند استخدام التابعين <code>()open</code> أو <code>()send</code>.
</p>

<p>
	ومن الجيد أن تدرك أن <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API" rel="external nofollow">الواجهة Fetch</a> هي تطوير للواجهة <a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest" rel="external nofollow">XMLHttpRequest</a>، وأن تفهم الطريقة المتبعة في التعامل مع اﻷخطاء في كلتا الواجهتين.
</p>

<h2 id="-5">
	الخلاصة
</h2>

<p>
	شرحنا في هذا المقال طريقة استخدام الواجهة البرمجية Fetch في إحضار البيانات من الخادم، وسنتناول بعض المواضيع التي وردت في المقال بمزيد من التفصيل في مقالاتنا التالية.
</p>

<p>
	ترجمة-وبتصرف- للمقال: <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Fetching_data" rel="external nofollow">Fetching data from the server</a>.
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%B4%D8%AC%D8%B1%D8%A9-dom-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2360/" rel="">الواجهات البرمجية والتعامل مع شجرة DOM في جافا سكريبت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A5%D8%B1%D8%B3%D8%A7%D9%84-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%88%D8%A7%D8%B3%D8%AA%D9%84%D8%A7%D9%85%D9%87%D8%A7-%D8%B9%D8%A8%D8%B1-%D8%A7%D9%84%D8%B4%D8%A8%D9%83%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1294/" rel="">إرسال البيانات واستلامها عبر الشبكة في جافاسكربت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/react/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1071/" rel="">أساسيات بناء تطبيقات الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%B7%D9%84%D8%A8%D8%A7%D8%AA-http-%D9%81%D9%8A-nodejs-r1868/" rel="">التعامل مع طلبات HTTP في Node.js</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/php/%D8%A7%D9%84%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%A4%D9%82%D8%AA-cache-%D9%88%D9%85%D9%82%D8%A7%D8%A8%D8%B3-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-webscockets-%D9%81%D9%8A-php-r1180/" rel="">التخزين المؤقت Cache ومقابس الويب Webscockets في PHP</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2419</guid><pubDate>Wed, 02 Oct 2024 15:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x62D;&#x642;&#x642; &#x645;&#x646; &#x635;&#x62D;&#x629; &#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; &#x627;&#x633;&#x62A;&#x645;&#x627;&#x631;&#x629; &#x648;&#x64A;&#x628; &#x641;&#x64A; &#x637;&#x631;&#x641; &#x627;&#x644;&#x639;&#x645;&#x64A;&#x644;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%AD%D9%82%D9%82-%D9%85%D9%86-%D8%B5%D8%AD%D8%A9-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D8%B3%D8%AA%D9%85%D8%A7%D8%B1%D8%A9-%D9%88%D9%8A%D8%A8-%D9%81%D9%8A-%D8%B7%D8%B1%D9%81-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-r2406/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_09/--------(1).png.2a9920cef3b9542da3f701ea8964d1f0.png" /></p>
<p>
	من الضروري التحقق من ملء بيانات جميع عناصر تحكم استمارة ويب وفق التنسيق الصحيح قبل إرسالها إلى الخادم، وتُدعى هذه العملية التحقق من الاستمارة في طرف العميل <strong>client-side form validation</strong>، وهي تساعد على ضمان توافق البيانات مع المتطلبات المخصصة لكل عنصر تحكم من عناصر الاستمارة قبل <a href="https://academy.hsoub.com/programming/advanced/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%B7%D8%B1%D9%82-%D9%85%D8%AA%D8%B9%D8%AF%D8%AF%D8%A9-%D9%84%D8%A5%D8%B1%D8%B3%D8%A7%D9%84-%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D8%B3%D8%AA%D9%85%D8%A7%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AE%D8%A7%D8%AF%D9%85-r2407/" rel="">إرسالها للخادم</a>. لهذا سيقودك هذا المقال عبر المفاهيم اﻷساسية التي تحتاجها للتحقق من البيانات في طرف العميل.
</p>

<p>
	ننصحك قبل المتابعة في قراءة هذا المقال الاطلاع على <a href="https://academy.hsoub.com/programming/html/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-html-r1687/" rel="">أساسيات HTML</a> وعلى <a href="https://academy.hsoub.com/programming/css/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-css/" rel="">أساسيات تنسيق الصفحات باستخدام CSS</a> وكذلك <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D8%B1%D9%91%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D9%85%D9%86-%D9%85%D9%86%D8%B8%D9%88%D8%B1-%D8%B9%D8%A7%D9%85-r2266/" rel="">أساسيات لغة جافا سكريبت</a>.
</p>

<p>
	إن التحقق من صحة البيانات التي يدخلها المستخدم أثناء كتابتها في المتصفح ميزة هامة، وتقدم للمستخدم تجربة أفضل، فهي تنبهه إلى اﻷخطاء التي ارتكبها كي يصححها مباشرة. فإرسال بيانات خاطئة إلى الخادم ستؤدي إلى رفضها إضافة إلى التأخير الذي يحدث نتيجة إرسال البيانات ومن ثم الانتظار حتى يتحقق منها الخادم ويعيدها طالبًا منك تصحيحها.
</p>

<p>
	مع ذلك لا يمكن أن نعتمد كليًا على التحقق من طرف العميل فقط وذلك لأسباب أمنية، فمن السهل جدًا تجاوز التحقق من طرف العميل من قبل مستخدمين مشبوهين وإرسال بيانات غير صحيحة أو ضارة إلى الخادم. لهذا يجب التحقق من صحة البيانات في كلا الطرفين. وعلى الرغم من أننا لن نتحدث عن حاليًا عن التحقق من البيانات في طرف الخادم، لكن عليك تذكر ذلك جيدًا.
</p>

<h2 id="-1">
	ما الذي نعنيه بالتحقق من الاستمارة؟
</h2>

<p>
	جرّب أن تزور أي موقع ويب مشهور يعرض استمارة تسجيل وستلاحظ أنه يقدم توصيات بتصحيح البيانات المدخلة إن لم تكتبها وفق الصيغة المطلوبة الذي تتوقعه الاستماره. فقد تتلقى رسائل مثل
</p>

<ul>
	<li>
		"هذا الحقل مطلوب": أي لا يمكنك ترك هذا الحقل فارغًا.
	</li>
	<li>
		"الرجاء إدخال رقم الهاتف وفق التنسيق xxx-xxxx": أي ينبغي إدخال البيانات بتنسيق معين حتى تُعد صالحة كرقم هاتف.
	</li>
	<li>
		"الرجاء إدخال عنوان بريد إلكتروني صالح": أي لم تدخل البريد الإلكتروني وفق الصيغة المطلوبة.
	</li>
	<li>
		"كلمة السر يجب أن تكون بين 8 إلى 30 محرفًا، وتضم على اﻷقل حرفًا كبيرًا ورمزًا ورقمًا": أي المطلوب إدخال بيانات وفق تنسيق مخصص ومحدد تمامًا.
	</li>
</ul>

<p>
	يُدعى ذلك التحقق من الاستمارة form validation. فعندما تُدخل البيانات إلى الاستمارة يتحقق المتصفح (والخادم) من أن هذه البيانات وفق الصيغة الصالحة أو الحدود التي يفرضها التطبيق. وعندما يتحقق المتصفح من البيانات ندعوه التحقق من طرف العميل client-side validation، بينما ندعوه تحقق من طرف الخادم server-side validation عندما ينفذ الخادم عملية التحقق، وسنقتصر في مقالنا كما وضحنا سابقًا شرح آلية التحقق من طرف العميل.
</p>

<p>
	إن كانت البيانات المدخلة إلى الاستمارة مطابقة للصيغة أو الحدود المطلوبة، ستُسلَّم الاستمارة إلى الخادم وقد تحفظ البيانات في قاعدة بيانات، بينما إن لم تكن البينات مطابقة لما هو مطلوب، فلن تّرسل إلى الخادم ويعرض المتصفح رسالة خطأ تشير إلى ما يجب عليك تصحيحه ويتيح لك المحاولة مجددًا.
</p>

<p>
	وطالما أننا نريد من عملية ملء الاستمارة أن تكون سهلة إلى أبعد الحدود، قد تتساءل لماذا نربك أنفسنا بالتحقق من كل البيانات التي نُدخلها؟ إليك ثلاثة أسباب أساسية:
</p>

<ul>
	<li>
		الحاجة إلى بيانات صحيحة وفق الصيغة الصحيحة. فلن يعمل التطبيق بالشكل المطلوب إن خُزّنت بيانات المستخدم بصيغة خاطئة أو كانت غير صالحة أو لم تكن موجودة أصلًا.
	</li>
	<li>
		الحاجة إلى حماية بيانات المستخدم. فإجبار المستخدم على إدخال كلمة سر قوية وفق صيغة محددة تُسهِّل حماية بيانات حسابه.
	</li>
	<li>
		الحاجة إلى حماية الموقع. فهناك طرق كثيرة يمكن فيها للمخترقين من إساءة استعمال الاستمارات غير المحمية لتخريب <a href="https://academy.hsoub.com/programming/advanced/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D9%85%D8%A7%D9%86-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r2020/" rel="">أمان مواقع الويب</a>.
	</li>
</ul>

<p>
	<strong>تحذير</strong>: لا تثق أبدًا بالبيانات التي يرسلها المستخدم إلى الخادم. وحتى لو تحققت الاستمارة من صحة البيانات المدخلة ومنعت المستخدم من إرسال بيانات غير واضحة، يمكن للمخترقين تغيير طلبات الشبكة التي تصل المتصفح بالخادم.
</p>

<h2 id="-2">
	اﻷنواع المختلفة للتحقق في طرف العميل
</h2>

<p>
	هناك نوعان مختلفان للتحقق في طرف العميل ستواجههما في الويب:
</p>

<ol>
	<li>
		<strong>التحقق المدمج built-in validation</strong>: يستخدم وسيلة التحقق التي تقدمها بعض عناصر HTML، وقد رأينا العديد منها سابقًا مثل <a href="https://wiki.hsoub.com/HTML/input/email" rel="external">حقل إدخال البريد اﻹلكتروني</a>. إذ لا يتطلب هذا النوع من التحقق الكثير من شيفرات جافا سكريبت، وله أداء أفضل من التحقق باستخدام جافا سكريبت لكن الأسلوب الثاني أكثر قابلية للتخصيص.
	</li>
	<li>
		<strong>التحقق باستخدام جافا سكريبت</strong> J<strong>avaScript validation</strong>: يستخدم شيفرة جافا سكريبت للتحقق من بيانات الاستمارة، وهو أسلوب قابل للتخصيص بالكامل، لكن عليك كتابة جميع تفاصيل العملية بنفسك (أو استخدام مكتبات جاهزة بالطبع).
	</li>
</ol>

<h2 id="-3">
	استخدام التحقق المدمج من بيانات الاستمارة
</h2>

<p>
	من أبرز ميزات عناصر التحكم الحديثة في الاستمارات هي قدرتها على التحقق من صلاحية البيانات المدخلة إليها دون الاعتماد على جافا سكريبت. يتحقق العنصر من البيانات باستخدام خاصيات التحقق لعناصر تحكم النموذج، وقد رأينا سابقًا العديد منها، سنحاول مراجعتها تاليًا:
</p>

<ul>
	<li>
		<code>required</code>: تحدد هذه الخاصية إذا ما كان على المستخدم ملء عنصر التحكم قبل إرسال البيانات أم لا.
	</li>
	<li>
		<code>minlength</code> و <code>maxlength</code>: تحددان الطول اﻷدنى واﻷعلى للبيانات النصية المدخلة.
	</li>
	<li>
		<code>min</code> و <code>max</code>: تحددان الحد اﻷدنى واﻷعلى لقيم مدخلات عددية.
	</li>
	<li>
		<code>type</code>: تحدد نوع عنصر اﻹدخال سواء نص أو بريد إلكتروني أو قيمة عددية وهكذا.
	</li>
	<li>
		<code>pattern</code>: تقبل <a href="https://academy.hsoub.com/programming/javascript/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%AD%D8%AB-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1399/" rel="">تعبير نمطي</a> regular expression يعرف نمط أو صيغة المدخلات التي يجب اتباعها عند إدخال البيانات إلى عنصر التحكم.
	</li>
</ul>

<p>
	فإن خضعت البيانات المدخلة إلى عنصر تحكم لجميع القواعد التي تحددها تلك الخاصيات، ستُعد بيانات صالحة وإلا تًعد العكس. وعندما تكون قيمة أي مُدخل صحيحة، ستكون النقاط التالية محققة:
</p>

<ul>
	<li>
		يمكن أن تستهدف العنصر عبر صنف التنسيق الزائف <code>valid:</code> لتطبيق <a href="https://academy.hsoub.com/programming/css/%D9%87%D9%8A%D9%83%D9%84%D9%8A%D8%A9-%D9%84%D8%BA%D8%A9-css-r2002/" rel="">تنسيق CSS </a>محدد عليه.
	</li>
	<li>
		إن حاول المستخدم إرسال البيانات، فسيعمل المتصفح على تسليمها إن لم يكن هناك أمر آخر يمنع ذلك (مثل شيفرة جافا سكريبت).
	</li>
</ul>

<p>
	وعندما تكون البيانات المدخلة غير صالحة، فالنقاط التالية محققة:
</p>

<ul>
	<li>
		يمكن أن تستهدف العنصر من خلال الصنف الزائف <code><a href="https://wiki.hsoub.com/CSS/:invalid" rel="external">invalid</a>:</code> وأصناف أخرى أحيانًا مثل <code>out-of-range:</code> وفقًا للخطأ في البيانات المدخلة، مما يسمح بتطبيق تنسيق مخصص على العنصر.
	</li>
	<li>
		سيمنع المتصفح المستخدم من تسليم الاستمارة إن حاول ذلك، ويعرض رسالة خطأ.
	</li>
</ul>

<p>
	<strong>ملاحظة</strong>: هناك الكير من اﻷخطاء التي تمنع تسليم الاستمارة مثل <code>badInput</code> و <code>patternMismatch</code> و <code>rangeOverflow</code> أو <code>rangeUnderflow</code> و <code>stepMismatch</code> و <code>tooLong</code> أو <code>tooShort</code> و <code>typeMismatch</code> و <code>valueMissing</code> أو <code>customError</code>.
</p>

<h2 id="-4">
	أمثلة عن استخدام التحقق المدمج من صحة بيانات عنصر التحكم
</h2>

<p>
	نناقش في هذا القسم استخدام بعض الخاصيات التي ذكرناها سابقًا.
</p>

<h3 id="-5">
	ملف بسيط للبدء
</h3>

<p>
	لنبدأ أمثلتنا بمثال بسيط يضم عنصر إدخال تختار فيه الموز أو الكرز. يستخدم الملف عنصر إدخال نصي <code><a href="https://wiki.hsoub.com/HTML/input" rel="external">&lt;input&gt;</a></code> مع عنوان مرافق <code><a href="https://wiki.hsoub.com/HTML/label" rel="external">&lt;label&gt;</a></code> وزر إرسال <code><a href="https://wiki.hsoub.com/HTML/button" rel="external">&lt;button&gt;</a></code>. بإمكانك إيجاد <a href="https://github.com/mdn/learning-area/blob/main/html/forms/form-validation/fruit-start.html" rel="external nofollow">الشيفرة المصدرية</a> للمثال على جيت-هاب:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6431_9" style=""><span class="tag">&lt;form&gt;</span><span class="pln">
 </span><span class="tag">&lt;label</span><span class="pln"> </span><span class="atn">for</span><span class="pun">=</span><span class="atv">"choose"</span><span class="tag">&gt;</span><span class="pln">Would you prefer a banana or cherry?</span><span class="tag">&lt;/label&gt;</span><span class="pln">
 </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"choose"</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"i-like"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
 </span><span class="tag">&lt;button&gt;</span><span class="pln">Submit</span><span class="tag">&lt;/button&gt;</span><span class="pln">
</span><span class="tag">&lt;/form&gt;</span></pre>

<p>
	وإليك شيفرة تنسيق CSS:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6431_11" style=""><span class="kwd">input</span><span class="pun">:</span><span class="pln">invalid </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">border</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2px</span><span class="pln"> dashed red</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">input</span><span class="pun">:</span><span class="pln">valid </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">border</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2px</span><span class="pln"> solid black</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" frameborder="no" height="200" loading="lazy" scrolling="no" src="https://codepen.io/HsoubAcademy/embed/rNERpEv?default-tab=" style="width: 100%;" title="fruit-start">See the Pen fruit-start by Hsoub Academy (@HsoubAcademy) on CodePen.</iframe>
</p>

<p>
	وحتى تتابع معنا، انسخ <a href="https://github.com/mdn/learning-area/blob/main/html/forms/form-validation/fruit-start.html" rel="external nofollow">ملف المثال</a> على جهازك وضعه في مجلد جديد.
</p>

<h3 id="-6">
	الخاصيات المطلوبة
</h3>

<p>
	تُعد الخاصية <span ipsnoautolink="true">required</span> من أبسط طرق التحقق في HTML. ولكي تجعل إدخال بيانات ضمن عنصر تحكمًا إجباريًا، أضف هذه الخاصية إلى العنصر، وعندها يمكنك استهداف العنصر باستخدام <a href="https://wiki.hsoub.com/CSS/:required" rel="external">الصنف required:</a>، ولن تُرسل الاستمارة وسيعيد المتصفح رسالة خطأ عندما تحاول تسليم الاستمارة وهذا العنصر فارغ. وسيعدُّ المتصفح العنصر غير صالح طالما أنه فارغ ويمكن استهدافه حينها بالصنف <code>invalid:</code>:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6431_16" style=""><span class="tag">&lt;form&gt;</span><span class="pln">
 </span><span class="tag">&lt;label</span><span class="pln"> </span><span class="atn">for</span><span class="pun">=</span><span class="atv">"choose"</span><span class="tag">&gt;</span><span class="pln">Would you prefer a banana or cherry? (required)</span><span class="tag">&lt;/label&gt;</span><span class="pln">
 </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"choose"</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"i-like"</span><span class="pln"> </span><span class="atn">required</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
 </span><span class="tag">&lt;button&gt;</span><span class="pln">Submit</span><span class="tag">&lt;/button&gt;</span><span class="pln">
</span><span class="tag">&lt;/form&gt;</span></pre>

<p>
	ستكون شيفرة التنسيق مضمّنه في ملف HTML:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6431_18" style=""><span class="kwd">input</span><span class="pun">:</span><span class="pln">invalid </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">border</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2px</span><span class="pln"> dashed red</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">input</span><span class="pun">:</span><span class="kwd">invalid</span><span class="pun">:</span><span class="pln">required </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">background-image</span><span class="pun">:</span><span class="pln"> linear-gradient</span><span class="pun">(</span><span class="pln">to right</span><span class="pun">,</span><span class="pln"> pink</span><span class="pun">,</span><span class="pln"> lightgreen</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">input</span><span class="pun">:</span><span class="pln">valid </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">border</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2px</span><span class="pln"> solid black</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يسبب التنسيق السابق ظهور حواف متقطعة باللون الأحمر عندما تكون القيمة المدخلة غير صالحة، وحواف متصلة باللون الأسود عندما تكون صحيحة. كما أضفنا خلفية لونية متدرجة عندما يكون عنصر اﻹدخال مطلوبًا وقيمته غير صالحة. جرّب المثال مباشرة في المحرر التفاعلي التالي:
</p>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" frameborder="no" height="200" loading="lazy" scrolling="no" src="https://codepen.io/HsoubAcademy/embed/mdZoXdJ?default-tab=" style="width: 100%;" title="Untitled">See the Pen Untitled by Hsoub Academy (@HsoubAcademy) on CodePen.</iframe>
</p>

<p>
	 
</p>

<p>
	جرب النقر على زر اﻹرسال عندما يكون المربع النصي فارغًا، سيكتسب عندها عنصر اﻹدخال تركيز الدخل وتظهر لك رسالة الخطأ التالية:<br>
	"Please fill out this field" ولن تُسلًم بيانات الاستمارة.
</p>

<p>
	إن وجود الخاصية <code>required</code> ضمن أي عنصر يدعم هذه الخاصية، يعني إمكانية استهداف العنصر من قبل الصنف <code>required:</code> سواء كان العنصر فارغًا أو لا. بينما إن كان العنصر فارغًا، سيستهدفه أيضًا الصنف <code>invalid:</code>.
</p>

<p>
	<strong>ملاحظة</strong>: لتقديم تجربة مستخدم جيدة، بيّن للمستخدم الحقول المطلوبة. هذا اﻷمر مطلوب أيضًا وفق <a href="https://academy.hsoub.com/programming/html/%D8%B3%D9%87%D9%88%D9%84%D8%A9-%D9%88%D8%B5%D9%88%D9%84-%D8%AC%D9%85%D9%8A%D8%B9-%D8%A7%D9%84%D8%B2%D9%88%D8%A7%D8%B1-%D9%84%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D9%88%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r1323/" rel="">توجيهات WCAG لسهولة الوصول</a>. وانتبه دائمًا لعدم إجبار المستخدم على إدخال بيانات غير ضرورية، فلا معنى أن تجبره على إدخال جنسه أو لقبه.
</p>

<h3 id="regex">
	تقييم صلاحية مدخلات باستخدام التعابير النمطية regex
</h3>

<p>
	من الخاصيات المفيدة أيضًا نجد <code>pattern</code> التي تتوقع <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-regular-expressions-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1278/" rel="">تعبيرًا نمطيًا regex</a> قيمةً لها. والتعبير النمطي هو سلسلة من الرموز التي تستخدم ﻹيجاد تشكيلة محددة من المحارف ضمن سلسلة نصية، فهو مثالي جدًا للتحقق من المدخلات المختلفة، وله استخدامات كثيرة في جافا سكريبت.
</p>

<p>
	و التعابير النمطية هي بالفعل نماذج معقدة، ولا ننوي أن نشرحها في المقال، لكن إليك بعض اﻷمثلة البسيطة التي توضح مفهومها:
</p>

<ul>
	<li>
		<code>a</code>: تعبير نمطي يطابق الحرف <code>a</code> (وليس <code>b</code> أو <code>aa</code> وهكذا).
	</li>
	<li>
		<code>abc</code>: يطابق سلسلة من اﻷحرف تبدأ ب <code>a</code> يليه <code>b</code> يليه <code>c</code>.
	</li>
	<li>
		<code>ab?c</code>: يطابق سلسلة من اﻷحرف تبدأ ب <code>a</code> وقد يليه <code>b</code> فقط ثم <code>c</code> (يطابق "ac" و "abc" وليس "aac").
	</li>
	<li>
		<code>ab*c</code>: يطابق سلسلة من اﻷحرف تبدأ ب <code>a</code> وقد يليه عدة محارف <code>b</code> فقط ثم <code>c</code> (يطابق "ac" و "abc" و "abbbc").
	</li>
	<li>
		<code>a|b</code>: يبحث عن أحد المحرفين <code>a</code> أو <code>b</code>.
	</li>
	<li>
		<code>abc|xyz</code>: يطابق <code>abc</code> تمامًا أو <code>xyz</code> تمامًا.
	</li>
</ul>

<p>
	بالطبع هناك الكثير والكثير من اﻷنماط التي لم نذكرها، وللاطلاع على هذه اﻷنماط بأكملها وطريقة استخدامها ننصح بمطالعة مقال <a href="https://academy.hsoub.com/devops/linux/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D9%81%D9%8A-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D8%A8%D9%8A%D8%B1-%D8%A7%D9%84%D9%86%D9%85%D8%B7%D9%8A%D8%A9-regular-expressions-r63/" rel="">مقدمة في التعابير النمطية Regular Expressions</a>.
</p>

<p>
	لنحاول تحديث المثال السابق من خلال إضافة الخاصية <code>pattern</code> واستخدام تعبير نمطي مناسب:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6431_22" style=""><span class="tag">&lt;form&gt;</span><span class="pln">
 </span><span class="tag">&lt;label</span><span class="pln"> </span><span class="atn">for</span><span class="pun">=</span><span class="atv">"choose"</span><span class="tag">&gt;</span><span class="pln">Would you prefer a banana or a cherry?</span><span class="tag">&lt;/label&gt;</span><span class="pln">
 </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"choose"</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"i-like"</span><span class="pln"> </span><span class="atn">required</span><span class="pln"> </span><span class="atn">pattern</span><span class="pun">=</span><span class="atv">"[Bb]anana|[Cc]herry"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
 </span><span class="tag">&lt;button&gt;</span><span class="pln">Submit</span><span class="tag">&lt;/button&gt;</span><span class="pln">
</span><span class="tag">&lt;/form&gt;</span></pre>

<p>
	إليك نتيجة العمل:
</p>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" frameborder="no" height="180" loading="lazy" scrolling="no" src="https://codepen.io/HsoubAcademy/embed/MWMxQYQ?default-tab=" style="width: 100%;" title="fruit-pattern">See the Pen fruit-pattern by Hsoub Academy (@HsoubAcademy) on CodePen.</iframe>
</p>

<p>
	يقبل عنصر اﻹدخال في هذه النسخة من المثال أربع احتمالات هي: "banana" أو "Banana" أو "cherry" أو "Cherry". ولأن التعبير النمطي حساس لحالة اﻷحرف، فقد جعلناه يدعم الكلمة التي تبدأ بحرف كبير أو صغير، من خلال وضع الخيارين ضمن قوسين مربعين <code>[Bb]</code>.
</p>

<p>
	حاول تغيير التعبير النمطي ضمن الخاصية <code>pattern</code> ليماثل بعض القواعد التي أشرنا إليها سابقًا وراقب تأثيرها على القيمة التي تُدخلها ضمن عنصر اﻹدخال. إن لم تتطابق القيمة المدخلة مع تسلسل التعبير النمطي في الخاصية <code>pattern</code> يتطابق عندها عنصر اﻹدخال والصنف <code>invalid:</code>.
</p>

<p>
	<strong>ملاحظة</strong>: لا تحتاج بعض عناصر اﻹدخال إلى الخاصية <code>pattern</code> حتى تُقيّم مُدخلاتها بالاعتماد على التعابير النمطية مثل البريد اﻹلكتروني <code>email</code>، إذ تُقيَّم المدخلات بالمقارنة مع نمط خاص بالبريد اﻹلكتروني، مع أو بدون فواصل في حال استُخدمت الخاصية <code>multiple</code>.
</p>

<p>
	<strong>ملاحظة</strong>: لا يدعم العنصر <code><a href="https://wiki.hsoub.com/HTML/textarea" rel="external">&lt;textarea&gt;</a></code> الخاصية <code>pattern</code>.
</p>

<h3 id="-7">
	تحديد طول المدخلات
</h3>

<p>
	بإمكانك تحديد عدد المحارف في حقل إدخال نصي من خلال الخاصيتين <code>minlength</code> و <code>maxlength</code>. وسيكون المُدخل غير صالح إن كان عدد المحارف أدنى من <code>minlength</code> أو أكثر من <code>maxlength</code>.
</p>

<p>
	لا تسمح المتصفحات للمستخدم عادة بكتابة عدد محارف أكثر من الحد الأعلى المسموح. ولتزيد من جودة تجربة المستخدم يمكنك -إضافة إلى استخدام الخاصية <code>maxlength</code>- إظهار عدّاد للمحارف المتبقية التي يمكن للمستخدم إدخالها، مثل تلك العدادات التي تظهر عندما تحاول كتابة منشور على وسائط التواصل الاجتماعي. وهنا يأتي دور جافا سكريبت مع الخاصية <code>maxlength</code> لتنفيذ اﻷمر.
</p>

<h3 id="-8">
	تحديد مجالات القيم للمدخلات
</h3>

<p>
	تُستخدم الخاصيتان <code>min</code> و <code>max</code> لتحديد أعلى وأدنى قيمة يمكن إدخالها ضمن عنصر إدخال عددي مثل <code>&lt;"input type= "number&gt;</code>، وستكون القيمة خاطئة إن خرجت عن هذا المجال.
</p>

<p>
	لنلق نظرة على مثال آخر، ولنبدأ بإنشاء نسخة جديدة عن <a href="https://github.com/mdn/learning-area/blob/main/html/forms/form-validation/fruit-start.html" rel="external nofollow">ملف مثال الفواكه</a> السابق، ثم حذف محتوى العنصر <code>&lt;body&gt;</code> واستبداله بالشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6431_27" style=""><span class="tag">&lt;form&gt;</span><span class="pln">
 </span><span class="tag">&lt;div&gt;</span><span class="pln">
    </span><span class="tag">&lt;label</span><span class="pln"> </span><span class="atn">for</span><span class="pun">=</span><span class="atv">"choose"</span><span class="tag">&gt;</span><span class="pln">Would you prefer a banana or a cherry?</span><span class="tag">&lt;/label&gt;</span><span class="pln">
    </span><span class="tag">&lt;input</span><span class="pln">
     </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text"</span><span class="pln">
     </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"choose"</span><span class="pln">
     </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"i-like"</span><span class="pln">
     </span><span class="atn">required</span><span class="pln">
     </span><span class="atn">minlength</span><span class="pun">=</span><span class="atv">"6"</span><span class="pln">
     </span><span class="atn">maxlength</span><span class="pun">=</span><span class="atv">"6"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
 </span><span class="tag">&lt;/div&gt;</span><span class="pln">
 </span><span class="tag">&lt;div&gt;</span><span class="pln">
    </span><span class="tag">&lt;label</span><span class="pln"> </span><span class="atn">for</span><span class="pun">=</span><span class="atv">"number"</span><span class="tag">&gt;</span><span class="pln">How many would you like?</span><span class="tag">&lt;/label&gt;</span><span class="pln">
    </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"number"</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"number"</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"amount"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"1"</span><span class="pln"> </span><span class="atn">min</span><span class="pun">=</span><span class="atv">"1"</span><span class="pln"> </span><span class="atn">max</span><span class="pun">=</span><span class="atv">"10"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
 </span><span class="tag">&lt;/div&gt;</span><span class="pln">
 </span><span class="tag">&lt;div&gt;</span><span class="pln">
    </span><span class="tag">&lt;button&gt;</span><span class="pln">Submit</span><span class="tag">&lt;/button&gt;</span><span class="pln">
 </span><span class="tag">&lt;/div&gt;</span><span class="pln">
</span><span class="tag">&lt;/form&gt;</span></pre>

<ul>
	<li>
		حددنا عدد المحارف اﻷعلى واﻷدنى في حقل إدخال النصي ليكون 6 وهو نفس عدد الحروف في الكلمتين banana و cherry.
	</li>
	<li>
		وحددنا قيمة عنصر اﻹدخال العددي ليكون الحد اﻷدنى <code>min</code> هو 1 والحد اﻷعلى <code>max</code> هو 10، وسيكون أي عدد خارج هذا المجال غير صالح ولن يتمكن المستخدم من استخدام زر الزيادة واﻹنقاص خارجه. وإن أدخل المستخدم عدد خارج هذا المجال يدويًا سيكون غير صالح أيضًا. وطالما أن هذا العنصر غير مطلوب، فإزالة هذه القيمة ستُبقي قيمة العنصر صالحة!
	</li>
</ul>

<p>
	إليك نتيجة المثال:
</p>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" frameborder="no" height="250" loading="lazy" scrolling="no" src="https://codepen.io/HsoubAcademy/embed/WNqmMvj?default-tab=" style="width: 100%;" title="fruit-length">See the Pen fruit-length by Hsoub Academy (@HsoubAcademy) on CodePen.</iframe>
</p>

<p>
	<strong>ملاحظة</strong>: يأخذ العنصر <code>&lt;"input type="number&gt;</code> وغيره من العناصر مثل (<code>range</code> و <code>date</code>) الخاصية <code>step</code> التي تحدد مقدار الزيادة واﻹنقاص عند استخدام زر التحكم المخصص. لكن في مثالنا السابق لم نستخدم هذه الخاصية وستكون القيمة الافتراضية لها هي <code>1</code> وبالتالي لن تكون القيم ذات الفاصلة صالحة مثل <code>3.4</code>.
</p>

<h3 id="-9">
	المثال كاملًا
</h3>

<p>
	إليك المثال الكامل الذي يعرض استخدام ميزات التحقق المضمنة مع عناصر HTML:
</p>

<ul>
	<li>
		شيفرة HTML:
	</li>
</ul>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6431_29" style=""><span class="tag">&lt;form&gt;</span><span class="pln">
 </span><span class="tag">&lt;fieldset&gt;</span><span class="pln">
    </span><span class="tag">&lt;legend&gt;</span><span class="pln">
     Do you have a driver's license?</span><span class="tag">&lt;span</span><span class="pln"> </span><span class="atn">aria-label</span><span class="pun">=</span><span class="atv">"required"</span><span class="tag">&gt;</span><span class="pln">*</span><span class="tag">&lt;/span&gt;</span><span class="pln">
    </span><span class="tag">&lt;/legend&gt;</span><span class="pln">
    </span><span class="com">&lt;!-- While only one radio button in a same-named group can be selected at a time,
       and therefore only one radio button in a same-named group having the "required"
       attribute suffices in making a selection a requirement --&gt;</span><span class="pln">
    </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"radio"</span><span class="pln"> </span><span class="atn">required</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"driver"</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"r1"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"yes"</span><span class="pln"> </span><span class="tag">/&gt;&lt;label</span><span class="pln">
     </span><span class="atn">for</span><span class="pun">=</span><span class="atv">"r1"</span><span class="pln">
     </span><span class="tag">&gt;</span><span class="pln">Yes</span><span class="tag">&lt;/label</span><span class="pln">
    </span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"radio"</span><span class="pln"> </span><span class="atn">required</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"driver"</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"r2"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"no"</span><span class="pln"> </span><span class="tag">/&gt;&lt;label</span><span class="pln">
     </span><span class="atn">for</span><span class="pun">=</span><span class="atv">"r2"</span><span class="pln">
     </span><span class="tag">&gt;</span><span class="pln">No</span><span class="tag">&lt;/label</span><span class="pln">
    </span><span class="tag">&gt;</span><span class="pln">
 </span><span class="tag">&lt;/fieldset&gt;</span><span class="pln">
 </span><span class="tag">&lt;p&gt;</span><span class="pln">
    </span><span class="tag">&lt;label</span><span class="pln"> </span><span class="atn">for</span><span class="pun">=</span><span class="atv">"n1"</span><span class="tag">&gt;</span><span class="pln">How old are you?</span><span class="tag">&lt;/label&gt;</span><span class="pln">
    </span><span class="com">&lt;!-- The pattern attribute can act as a fallback for browsers which
       don't implement the number input type but support the pattern attribute.
       Please note that browsers that support the pattern attribute will make it
       fail silently when used with a number field.
       Its usage here acts only as a fallback --&gt;</span><span class="pln">
    </span><span class="tag">&lt;input</span><span class="pln">
     </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"number"</span><span class="pln">
     </span><span class="atn">min</span><span class="pun">=</span><span class="atv">"12"</span><span class="pln">
     </span><span class="atn">max</span><span class="pun">=</span><span class="atv">"120"</span><span class="pln">
     </span><span class="atn">step</span><span class="pun">=</span><span class="atv">"1"</span><span class="pln">
     </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"n1"</span><span class="pln">
     </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"age"</span><span class="pln">
     </span><span class="atn">pattern</span><span class="pun">=</span><span class="atv">"\d+"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
 </span><span class="tag">&lt;/p&gt;</span><span class="pln">
 </span><span class="tag">&lt;p&gt;</span><span class="pln">
    </span><span class="tag">&lt;label</span><span class="pln"> </span><span class="atn">for</span><span class="pun">=</span><span class="atv">"t1"</span><span class="pln">
     </span><span class="tag">&gt;</span><span class="pln">What's your favorite fruit?</span><span class="tag">&lt;span</span><span class="pln"> </span><span class="atn">aria-label</span><span class="pun">=</span><span class="atv">"required"</span><span class="tag">&gt;</span><span class="pln">*</span><span class="tag">&lt;/span&gt;&lt;/label</span><span class="pln">
    </span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;input</span><span class="pln">
     </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text"</span><span class="pln">
     </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"t1"</span><span class="pln">
     </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"fruit"</span><span class="pln">
     </span><span class="atn">list</span><span class="pun">=</span><span class="atv">"l1"</span><span class="pln">
     </span><span class="atn">required</span><span class="pln">
     </span><span class="atn">pattern</span><span class="pun">=</span><span class="atv">"[Bb]anana|[Cc]herry|[Aa]pple|[Ss]trawberry|[Ll]emon|[Oo]range"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
    </span><span class="tag">&lt;datalist</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"l1"</span><span class="tag">&gt;</span><span class="pln">
     </span><span class="tag">&lt;option&gt;</span><span class="pln">Banana</span><span class="tag">&lt;/option&gt;</span><span class="pln">
     </span><span class="tag">&lt;option&gt;</span><span class="pln">Cherry</span><span class="tag">&lt;/option&gt;</span><span class="pln">
     </span><span class="tag">&lt;option&gt;</span><span class="pln">Apple</span><span class="tag">&lt;/option&gt;</span><span class="pln">
     </span><span class="tag">&lt;option&gt;</span><span class="pln">Strawberry</span><span class="tag">&lt;/option&gt;</span><span class="pln">
     </span><span class="tag">&lt;option&gt;</span><span class="pln">Lemon</span><span class="tag">&lt;/option&gt;</span><span class="pln">
     </span><span class="tag">&lt;option&gt;</span><span class="pln">Orange</span><span class="tag">&lt;/option&gt;</span><span class="pln">
    </span><span class="tag">&lt;/datalist&gt;</span><span class="pln">
 </span><span class="tag">&lt;/p&gt;</span><span class="pln">
 </span><span class="tag">&lt;p&gt;</span><span class="pln">
    </span><span class="tag">&lt;label</span><span class="pln"> </span><span class="atn">for</span><span class="pun">=</span><span class="atv">"t2"</span><span class="tag">&gt;</span><span class="pln">What's your email address?</span><span class="tag">&lt;/label&gt;</span><span class="pln">
    </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"email"</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"t2"</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"email"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
 </span><span class="tag">&lt;/p&gt;</span><span class="pln">
 </span><span class="tag">&lt;p&gt;</span><span class="pln">
    </span><span class="tag">&lt;label</span><span class="pln"> </span><span class="atn">for</span><span class="pun">=</span><span class="atv">"t3"</span><span class="tag">&gt;</span><span class="pln">Leave a short message</span><span class="tag">&lt;/label&gt;</span><span class="pln">
    </span><span class="tag">&lt;textarea</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"t3"</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"msg"</span><span class="pln"> </span><span class="atn">maxlength</span><span class="pun">=</span><span class="atv">"140"</span><span class="pln"> </span><span class="atn">rows</span><span class="pun">=</span><span class="atv">"5"</span><span class="tag">&gt;&lt;/textarea&gt;</span><span class="pln">
 </span><span class="tag">&lt;/p&gt;</span><span class="pln">
 </span><span class="tag">&lt;p&gt;</span><span class="pln">
    </span><span class="tag">&lt;button&gt;</span><span class="pln">Submit</span><span class="tag">&lt;/button&gt;</span><span class="pln">
 </span><span class="tag">&lt;/p&gt;</span><span class="pln">
</span><span class="tag">&lt;/form&gt;</span></pre>

<ul>
	<li>
		شيفرة CSS:
	</li>
</ul>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6431_31" style=""><span class="pln">form </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">font</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1em</span><span class="pln"> sans-serif</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">max-width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">320px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

p </span><span class="pun">&gt;</span><span class="pln"> label </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">display</span><span class="pun">:</span><span class="pln"> block</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

input</span><span class="pun">[</span><span class="pln">type</span><span class="pun">=</span><span class="str">"text"</span><span class="pun">],</span><span class="pln">
input</span><span class="pun">[</span><span class="pln">type</span><span class="pun">=</span><span class="str">"email"</span><span class="pun">],</span><span class="pln">
input</span><span class="pun">[</span><span class="pln">type</span><span class="pun">=</span><span class="str">"number"</span><span class="pun">],</span><span class="pln">
textarea</span><span class="pun">,</span><span class="pln">
fieldset </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100%</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">border</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1px</span><span class="pln"> solid </span><span class="lit">#333</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">box-sizing</span><span class="pun">:</span><span class="pln"> border-box</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">input</span><span class="pun">:</span><span class="pln">invalid </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">box-shadow</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">5px</span><span class="pln"> </span><span class="lit">1px</span><span class="pln"> red</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">input</span><span class="pun">:</span><span class="kwd">focus</span><span class="pun">:</span><span class="pln">invalid </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">box-shadow</span><span class="pun">:</span><span class="pln"> none</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<ul>
	<li>
		نتيجة عرض الشيفرة السابقة:
	</li>
</ul>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" frameborder="no" height="600" loading="lazy" scrolling="no" src="https://codepen.io/HsoubAcademy/embed/LYKaQJV?default-tab=" style="width: 100%;" title="Validation-related attributes">See the Pen Validation-related attributes by Hsoub Academy (@HsoubAcademy) on CodePen.</iframe>
</p>

<h2 id="-10">
	التحقق من الاستمارة باستخدام جافا سكريبت
</h2>

<p>
	لا بد من استخدام جافا سكريبت إن أردت التحكم الكامل بمظهر وشكل رسائل الخطأ، وهذا ما نناقشه في هذا القسم من المقال.
</p>

<h3 id="-11">
	الواجهة البرمجية لتحديد صلاحية المُدخلات
</h3>

<p>
	تتكون هذه الواجهة البرمجية من مجموعة من التوابع والخاصيات تقدمها واجهات عناصر <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-dom-r644/" rel="">DOM</a> التالية:
</p>

<ul>
	<li>
		<code>HTMLButtonElement</code>: يمثل العنصر <code><a href="https://wiki.hsoub.com/HTML/button" rel="external">&lt;button&gt;</a></code>
	</li>
	<li>
		<code>HTMLFieldSetElement</code>: يمثل العنصر <code><a href="https://wiki.hsoub.com/HTML/fieldset" rel="external">&lt;fieldset&gt;</a></code>
	</li>
	<li>
		<code>HTMLInputElement</code>: يمثل العنصر <code><a href="https://wiki.hsoub.com/HTML/input" rel="external">&lt;Input&gt;</a></code>.
	</li>
	<li>
		<code>HTMLOutputElement</code>: يمثل العنصر <code><a href="https://wiki.hsoub.com/HTML/output" rel="external">&lt;output&gt;</a></code>.
	</li>
	<li>
		<code>HTMLSelectElement</code>: يمثل العنصر <a href="https://wiki.hsoub.com/HTML/select" rel="external">&lt;select&gt;</a>.
	</li>
	<li>
		<code>HTMLTextAreaElement</code>: يمثل العنصر <a href="https://wiki.hsoub.com/HTML/textarea" rel="external">&lt;textarea&gt;</a>.
	</li>
</ul>

<p>
	وتضم الواجهة البرمجية الخاصيات التالية:
</p>

<ul>
	<li>
		<code>validationMessage</code>: تعيد رسالة مخصصة تشرح حدود صلاحية قيمة عنصر التحكم في حال لم تكن صالحة. لكن إن لم يكن العنصر قابلًا لتحديد الصلاحية (أي قيمة الخاصية <code>willValidate</code> هي <code>false</code>) أو كانت قيمة العنصر صالحة، ستعيد الخاصية قيمة نية فارغة.
	</li>
	<li>
		<code>validity</code>: تعيد كائن <code>validityState</code> يضم عدة خاصيات تصف حالة صلاحية العنصر. إليك أبرز هذه الخاصيات:
	</li>
	<li>
		<code>patternMismatch</code>: تعيد القيمة <code>true</code> إن لم تطابق القيمة النمط الذي تحدده قيمة الخاصية <code>pattern</code>، و <code>false</code> إن لم يوجد تطابق. وفي حال أعادت القيمة <code>true</code> يمكن استهداف العنصر عبر الصنف <code>invalid:</code>.
	</li>
	<li>
		<code>tooLong</code>: تعيد الخاصية القيمة <code>true</code> إن كانت القيمة المدخلة أطول من القيمة التي تحددها الخاصية <code>maxlength</code> و <code>false</code> إن كانت أقصر منها أو تساويها. وفي حال أعادت القيمة <code>true</code> يمكن استهداف العنصر عبر الصنف <code>invalid:</code>.
	</li>
	<li>
		<code>tooShort</code>: تعيد الخاصية القيمة <code>true</code> إن كانت القيمة المدخلة أقصر من القيمة التي تحددها الخاصية <code>minlength</code> و <code>false</code> إن كانت أقصر منها أو تساويها. وفي حال أعادت القيمة <code>true</code> يمكن استهداف العنصر عبر الصنف <code>invalid:</code>.
	</li>
	<li>
		<code>rangeOverFlow</code>: تعيد الخاصية القيمة <code>true</code> إن كانت القيمة المدخلة أكبر من القيمة التي تحددها الخاصية <code>max</code> و <code>false</code> إن كانت أقل منها أو تساويها. وفي حال أعادت القيمة <code>true</code> يمكن استهداف العنصر عبر الصنف <code>invalid:</code> و <code>out-of-range:</code>.
	</li>
	<li>
		<code>rangeUnderFlow</code>: تعيد الخاصية القيمة <code>true</code> إن كانت القيمة المدخلة أكبر من القيمة التي تحددها الخاصية <code>min</code> و <code>false</code> إن كانت أكبر منها أو تساويها. وفي حال أعادت القيمة <code>true</code> يمكن استهداف العنصر عبر الصنف <code>invalid:</code>.
	</li>
	<li>
		<code>typeMismatch</code>: تعيد الخاصية القيمة <code>true</code> إن لم تحقق قيمة الخاصية الصياغة المطلوبة (عندما يكون نوع عنصر اﻹدخال <code>email</code> أو <code>url</code>) و <code>false</code> إن كانت الصياغة صحيحة. وفي حال أعادت القيمة <code>true</code> يمكن استهداف العنصر عبر الصنف <code>invalid:</code>.
	</li>
	<li>
		<code>valid</code>: تعيد الخاصية القيمة <code>true</code> إن لم تحقق قيمة الخاصية جميع حدود الصلاحية المتعلقة بها، وتُعد حينها صالحة، و <code>false</code> إن لم تحقق أحد القيود المفروضة عليها. وفي حال أعادت القيمة <code>true</code> يمكن استهداف العنصر عبر الصنف <code>valid:</code> وإلا يمكن استهدافها بالصنف <code>invalid:</code>.
	</li>
	<li>
		<code>valueMissing</code>: تعيد الخاصية القيمة <code>true</code> إن ظهرت الخاصية <code>required</code> في العنصر وكان فارغًا وإلا أعادت <code>false</code>. وفي حال أعادت القيمة <code>true</code> يمكن استهداف العنصر عبر الصنف <code>invalid:</code>.
	</li>
	<li>
		<code>willValidate</code>: تعيد الخاصية القيمة <code>true</code> في حال وجب التحقق من صلاحية القيمة المُدخلة عند إرسال الاستمارة، وإلا أعادت القيمة <code>false</code>.
	</li>
</ul>

<p>
	كما تزودنا الواجهة البرمجية بتوابع تُطبق على العناصر السابقة وعلى عناصر الاستمارة:
</p>

<ul>
	<li>
		<p>
			<code>()checkValidity</code>: يعيد القيمة <code>true</code> إن لم تكن هناك أية مشاكل في التحقق من صلاحية قيمة، وإلا تعيد <code>false</code>. وإن كانت القيمة غير صالحة، سيقع <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%EF%BB%B7%D8%AD%D8%AF%D8%A7%D8%AB-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2306/" rel="">الحدث</a> <code>invaild</code> على العنصر.
		</p>
	</li>
	<li>
		<p>
			<code>reportValidity()</code>: يبلغ عن الحقول غير الصالحة باستخدام <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%EF%BB%B7%D8%AD%D8%AF%D8%A7%D8%AB-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2306/" rel="">اﻷحداث</a>. ولهذا التابع أهميته عندما يجتمع مع التابع <code>()preventDefault</code> ضمن معالج الحدث <code>onSubmit</code>.
		</p>
	</li>
	<li>
		<p>
			<code>(setCustomValidity(message</code>: يضيف رسالة خطأ مخصصة إلى العنصر. وعندما تخصص رسالة خطأ، سيُعد العنصر غير صالح، وستُعرض رسالة الخطأ. يسمح هذا التابع باستخدام جافا سكريبت لتهيئة حالة إخفاق عملية التحقق بشكل مختلف عن الطريقة المعيارية التي تتبعها HTML في تقييم القيمة. تُعرض هذه الرسالة للمستخدم عند اﻹبلاغ عن المشكلة.
		</p>

		<h4 id="-16">
			تنفيذ رسالة خطأ مخصصة
		</h4>

		<p>
			يعرض المتصفح رسالة خطأ في كل مرة يحاول فيها المستخدم إرسال قيمة غير صالحة وفقًا لمعايير تحقق HTML، وتختلف طريقة عرض هذه الرسالة على المتصفح. ولهذا اﻵلية سلبيتان أساسيتان:
		</p>
	</li>
	<li>
		<p>
			لا توجد طريقة معيارية متبعة لتغيير شكل وطبيعة الرسالة باستخدام CSS.
		</p>
	</li>
	<li>
		<p>
			تعتمد على اﻹعدادات المحلية للمتصفح، فقد تكون لغة الصفحة مختلفة عن اللغة الافتراضية للمتصفح كما في لقطة الشاشة التالية:
		</p>
	</li>
</ul>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="157547" href="https://academy.hsoub.com/uploads/monthly_2024_09/01_error-firefox-win7.png.151682e3fa27c6d4c1913632cd339619.png" rel=""><img alt="01 error firefox win7" class="ipsImage ipsImage_thumbnailed" data-fileid="157547" data-unique="5igiadr4b" src="https://academy.hsoub.com/uploads/monthly_2024_09/01_error-firefox-win7.png.151682e3fa27c6d4c1913632cd339619.png"> </a>
</p>

<p>
	وتخصيص هذا النوع من الرسائل هو الاستخدام اﻷكثر شيوعًا للواجهة البرمجية الخاصة بالتحقق من صلاحية المدخلات، لهذا سنعمل على مثال يصف آلية عملها.
</p>

<p>
	سنبدأ بملف HTML بسيط نضع ضمنه الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6431_34" style=""><span class="tag">&lt;form&gt;</span><span class="pln">
 </span><span class="tag">&lt;label</span><span class="pln"> </span><span class="atn">for</span><span class="pun">=</span><span class="atv">"mail"</span><span class="tag">&gt;</span><span class="pln">
    I would like you to provide me with an email address:
 </span><span class="tag">&lt;/label&gt;</span><span class="pln">
 </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"email"</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"mail"</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"mail"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
 </span><span class="tag">&lt;button&gt;</span><span class="pln">Submit</span><span class="tag">&lt;/button&gt;</span><span class="pln">
</span><span class="tag">&lt;/form&gt;</span></pre>

<p>
	أضف شيفرة جافا سكريبت التالية إلى الصفحة ضمن العنصر <code>&lt;script&gt;</code>
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6431_36" style=""><span class="kwd">const</span><span class="pln"> email </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">"mail"</span><span class="pun">);</span><span class="pln">

email</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"input"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">event</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">email</span><span class="pun">.</span><span class="pln">validity</span><span class="pun">.</span><span class="pln">typeMismatch</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    email</span><span class="pun">.</span><span class="pln">setCustomValidity</span><span class="pun">(</span><span class="str">"I am expecting an email address!"</span><span class="pun">);</span><span class="pln">
 </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    email</span><span class="pun">.</span><span class="pln">setCustomValidity</span><span class="pun">(</span><span class="str">""</span><span class="pun">);</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	تخزن شيفرة جافا سكريبت السابقة مرجعًا إلى عنصر إدخال بريد إلكتروني، ثم تضيف مترصد أحداث ينفذ بعض الشيفرة في كل مرة تتغير فيها القيمة المدخلة في العنصر.
</p>

<p>
	تتحقق الشيفرة ضمن معالج الحدث إن أعادت الخاصية <code>validity.typeMismatch</code> لعنصر اﻹدخال القيمة <code>true</code> والتي تعني أن القيمة المدخلة غير صالحة، ولا تتطابق مع النمط المخصص لعنوان البريد اﻹلكتروني. عندها، نستدعي التابع <code>()setCustomValidity</code> ليعرض رسالة الخطأ المخصصة. يُصيّر هذا التابع عنصر اﻹدخال بحيث تُعرض رسالة الخطأ المخصصة، ويُخفق تسليم الاستمارة.
</p>

<p>
	بينما إن أعادت الخاصية القيمة <code>false</code>، نستدعي حينها التابع <code>()setCustomValidity</code> لكن مع رسالة فارغة، ويصيَّر عنصر اﻹدخال لينجح تسليم أو إرسال الاستمارة.
</p>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" frameborder="no" height="250" loading="lazy" scrolling="no" src="https://codepen.io/HsoubAcademy/embed/poXYpGw?default-tab=" style="width: 100%;" title="custom-error-message">See the Pen custom-error-message by Hsoub Academy (@HsoubAcademy) on CodePen.</iframe>
</p>

<h4 id="-12">
	مثال أكثر تفصيلًا
</h4>

<p>
	لنحاول اﻵن الاستفادة من الواجهة البرمجية لإنجاز مثال أكثر تعقيدًا للتحقق من صلاحية المدخلات.
</p>

<p>
	إليك شيفرة HTML:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6431_38" style=""><span class="tag">&lt;form</span><span class="pln"> </span><span class="atn">novalidate</span><span class="tag">&gt;</span><span class="pln">
 </span><span class="tag">&lt;p&gt;</span><span class="pln">
    </span><span class="tag">&lt;label</span><span class="pln"> </span><span class="atn">for</span><span class="pun">=</span><span class="atv">"mail"</span><span class="tag">&gt;</span><span class="pln">
     </span><span class="tag">&lt;span&gt;</span><span class="pln">Please enter an email address:</span><span class="tag">&lt;/span&gt;</span><span class="pln">
     </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"email"</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"mail"</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"mail"</span><span class="pln"> </span><span class="atn">required</span><span class="pln"> </span><span class="atn">minlength</span><span class="pun">=</span><span class="atv">"8"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
     </span><span class="tag">&lt;span</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"error"</span><span class="pln"> </span><span class="atn">aria-live</span><span class="pun">=</span><span class="atv">"polite"</span><span class="tag">&gt;&lt;/span&gt;</span><span class="pln">
    </span><span class="tag">&lt;/label&gt;</span><span class="pln">
 </span><span class="tag">&lt;/p&gt;</span><span class="pln">
 </span><span class="tag">&lt;button&gt;</span><span class="pln">Submit</span><span class="tag">&lt;/button&gt;</span><span class="pln">
</span><span class="tag">&lt;/form&gt;</span></pre>

<p>
	تستخدم الاستمارة البسيطة السابقة الخاصية <code>novalidate</code> ﻹلغاء التقييم التلقائي الذي ينفذه المتصفح. وهذا ما يسمح لشيفرتنا تولي مسؤولية التحقق. لن يمنع هذا اﻹلغاء بالطبع تقييم صلاحية المدخلات باستخدام الواجهة البرمجية ولا يمنع أيضًا استهداف العناصر عبر اﻷصناف الزائفة المناسبة. ويعني ذلك بكل بساطة أن المتصفح سيتنحى جانبًا ويكون عليك التحقق من صحة المدخلات وتنسيق الاستمارة بنفسك قبل تسليمها.
</p>

<p>
	إن عنصر اﻹدخال الذي نتحقق من صلاحية مدخلاته هو عنصر إدخال بريد إلكتروني <code>email</code> وقد استخدمت ضمنه الخاصية <code>required</code> حتى يكون مطلوبًا وقررنا أن يكون أدنى عدد مقبول من المحارف هو 8 من خلال الخاصية <code>minlength</code>. وهكذا سنتحقق من تحقيق المدخلات القيود السابقة من خلال شيفرتنا ونعرض رسالة خطأ في حال وجوده.
</p>

<p>
	ننوي عرض رسالة الخطأ ضمن عنصر <code>&lt;span&gt;</code>، واستخدمنا ضمنه الخاصية <code>aria-live</code> كي نتأكد من عرض رسالة الخطأ للجميع بمن فيهم مستخدمي قارئات الشاشة.
</p>

<p>
	<strong>ملاحظة</strong>: إن الفكرة المفتاحية هنا هو أن الخاصية <code>novalidate</code> في الاستمارة هي من تمنع عرض رسالة الخطأ الافتراضية وتسمح لنا بعرض الرسالة المخصصة ضمن شجرة DOM بالطريقة التي نشاء.
</p>

<p>
	إليك اآن بعض قواعد التنسيق البسيطة لتحسين مظهر الاستمارة وإظهار إشارات مناسبة عندما تكون البيانات المدخلة غير صالحة:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6431_40" style=""><span class="pln">body </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">font</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1em</span><span class="pln"> sans-serif</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">200px</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> auto</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

p </span><span class="pun">*</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">display</span><span class="pun">:</span><span class="pln"> block</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

input</span><span class="pun">[</span><span class="pln">type</span><span class="pun">=</span><span class="str">"email"</span><span class="pun">]</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">appearance</span><span class="pun">:</span><span class="pln"> none</span><span class="pun">;</span><span class="pln">

 </span><span class="kwd">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100%</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">border</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1px</span><span class="pln"> solid </span><span class="lit">#333</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

 </span><span class="kwd">font-family</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">inherit</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">font-size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">90%</span><span class="pun">;</span><span class="pln">

 </span><span class="kwd">box-sizing</span><span class="pun">:</span><span class="pln"> border-box</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">/* This is our style for the invalid fields */</span><span class="pln">
</span><span class="kwd">input</span><span class="pun">:</span><span class="pln">invalid </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">border-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#900</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">background-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#fdd</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">input</span><span class="pun">:</span><span class="kwd">focus</span><span class="pun">:</span><span class="pln">invalid </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">outline</span><span class="pun">:</span><span class="pln"> none</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">/* This is the style of our error messages */</span><span class="pln">
</span><span class="pun">.</span><span class="pln">error </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100%</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

 </span><span class="kwd">font-size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">80%</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">color</span><span class="pun">:</span><span class="pln"> white</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">background-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#900</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">border-radius</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">5px</span><span class="pln"> </span><span class="lit">5px</span><span class="pun">;</span><span class="pln">

 </span><span class="kwd">box-sizing</span><span class="pun">:</span><span class="pln"> border-box</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">error</span><span class="pun">.</span><span class="pln">active </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0.3em</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إليك شيفرة جافا سكريبت:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6431_42" style=""><span class="kwd">const</span><span class="pln"> form </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"form"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> email </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">"mail"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> emailError </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"#mail + span.error"</span><span class="pun">);</span><span class="pln">

email</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"input"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">event</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="com">// نتحقق في كل مرة نكتب فيها شيئًا من صلاحية القيمة المدخلة</span><span class="pln">
 d</span><span class="pun">.</span><span class="pln">

 </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">email</span><span class="pun">.</span><span class="pln">validity</span><span class="pun">.</span><span class="pln">valid</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// إن كانت هناك رسائل خطأ والقيم المدخلة صحيحة</span><span class="pln">
    </span><span class="com">// أزل رسالة الخطأ</span><span class="pln">
    emailError</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="str">""</span><span class="pun">;</span><span class="pln"> </span><span class="com">// Reset the content of the message</span><span class="pln">
    emailError</span><span class="pun">.</span><span class="pln">className </span><span class="pun">=</span><span class="pln"> </span><span class="str">"error"</span><span class="pun">;</span><span class="pln"> </span><span class="com">// Reset the visual state of the message</span><span class="pln">
 </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// أظهر الخطأ في حال وجوده</span><span class="pln">
    showError</span><span class="pun">();</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

form</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"submit"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">event</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="com">// نسمح بإرسال الاستمارة إذا كانت القيمة المدخلة صالحة</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">email</span><span class="pun">.</span><span class="pln">validity</span><span class="pun">.</span><span class="pln">valid</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// اعرض رسالة الخطأ التالية</span><span class="pln">
    showError</span><span class="pun">();</span><span class="pln">
    </span><span class="com">// نمنع إرسال الاستمارة بإلغاء الحدث</span><span class="pln">
    event</span><span class="pun">.</span><span class="pln">preventDefault</span><span class="pun">();</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> showError</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">email</span><span class="pun">.</span><span class="pln">validity</span><span class="pun">.</span><span class="pln">valueMissing</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// إن كان الحقل فارغًا</span><span class="pln">
    </span><span class="com">// اعرض رسالة الخطأ التالية</span><span class="pln">
    emailError</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="str">"You need to enter an email address."</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">email</span><span class="pun">.</span><span class="pln">validity</span><span class="pun">.</span><span class="pln">typeMismatch</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// إن لم يضم الحقل عنوان بريد إلكتروني</span><span class="pln">
    </span><span class="com">// اعرض رسالة الخطأ التالية</span><span class="pln">
    emailError</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Entered value needs to be an email address."</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">email</span><span class="pun">.</span><span class="pln">validity</span><span class="pun">.</span><span class="pln">tooShort</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// If the data is too short,</span><span class="pln">
    </span><span class="com">// اعرض رسالة الخطأ التالية</span><span class="pln">
    emailError</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="typ">Email</span><span class="pln"> should be at least $</span><span class="pun">{</span><span class="pln">email</span><span class="pun">.</span><span class="pln">minLength</span><span class="pun">}</span><span class="pln"> characters</span><span class="pun">;</span><span class="pln"> you entered $</span><span class="pun">{</span><span class="pln">email</span><span class="pun">.</span><span class="pln">value</span><span class="pun">.</span><span class="pln">length</span><span class="pun">}.`;</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">

 </span><span class="com">// اضبط التنسيق بالشكل المناسب</span><span class="pln">
 emailError</span><span class="pun">.</span><span class="pln">className </span><span class="pun">=</span><span class="pln"> </span><span class="str">"error active"</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تشرح التعليقات المضمنة في الشيفرة ما نفعله جيدًا، لكن إن أردنا اﻹيجاز:
</p>

<ul>
	<li>
		في كل مرة نغير فيها القيمة المدخلة نتحقق إن كانت البيانات صالحة، فإن كانت كذلك، نزيل أية رسالة خطأ تُعرض، وننفذ الدالة <code>()showError</code>خلاف ذلك، لعرض رسالة الخطأ المناسبة.
	</li>
	<li>
		نتحقق مجددًا من صلاحية البيانات في كل مرة نحاول فيها تسليم النموذج، ونسمح بإرسال النموذج فقط في حال كانت القيم صالحة وإلا ننفذ الدالة <code>()showError</code> لعرض رسالة الخطأ المناسبة، ونمنع إرسال الاستمارة باستخدام التابع <code>()preventDefault</code>.
	</li>
	<li>
		تستخدم الدالة <code>()showError</code> خاصيات مختلفة للكائن <code>valid</code> لتحديد ماهية الخطأ ومن ثم عرض رسالة الخطأ المناسبة.
	</li>
</ul>

<p>
	إليك نتيجة الشيفرة السابقة:
</p>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" frameborder="no" height="250" loading="lazy" scrolling="no" src="https://codepen.io/HsoubAcademy/embed/LYKaeBX?default-tab=" style="width: 100%;" title="detailed-custom-validation">See the Pen detailed-custom-validation by Hsoub Academy (@HsoubAcademy) on CodePen.</iframe>
</p>

<p>
	وهكذا نرى أن واجهة التحقق من صلاحية المدخلات هي أداة فعالة جدًا للتعامل مع مسائل تقييم المدخلات، وتسمح لك بمجال تحكم أوسع بكثير عما يمكن أن تقدمه HTML و CSS.
</p>

<h3 id="-13">
	تقييم النماذج دون استخدام الواجهة البرمجية المضمّنة
</h3>

<p>
	لن تتمكن من استخدام الواجهة البرمجية المضمنّة في بعض الحالات مثل حالة عناصر التحكم المخصصة، لكن باﻹمكان استخدام شيفرة جافا سكريبت للتحقق من بيانات الاستمارة، وعليك في هذه الحالة كتابة الشيفرة المناسبة بنفسك. وقبل أن تبدأ عليك طرح اﻷسئلة التالية على نفسك:
</p>

<ul>
	<li>
		<strong>ما نوع التحقق الذي عليّ تنفيذه؟</strong> عليك أن تحدد كيف تتحقق من صحة البيانات. هل ستستخدم العمليات على السلاسل النصية أو التحول بين اﻷنواع أو التعابير النمطية أو مزيج من كل ذلك، فاﻷمر يعود إليك.
	</li>
	<li>
		<strong>ما الذي علي فعله إن لم تنجح عملية التحقق؟</strong> هذه المسألة متعلقة تمامًا بواجهة المستخدم. إذ عليك أن تقرر سلوك الاستمارة: هل ترسل البيانات؟ هل يجب تظليل الحقول غير الصالحة؟ هل سأعرض رسالة خطأ؟
	</li>
	<li>
		<strong>كيف أساعد المستخدم على تصويب البيانات غير الصالحة؟</strong> من المهم جدًا تقديم معلومات تساعد المستخدم على ملء الاستمارة قدر اﻹمكان حتى لا يُصاب باﻹحباط. فعليك مثلًا تقديم اقتراحات مسبقة حتى يتوقع المستخدم ما سيفعله إضافة إلى تزويده برسائل خطأ واضحة.
	</li>
</ul>

<h4 id="-14">
	مثال دون استخدام واجهة التحقق البرمجية
</h4>

<p>
	إليك نسخة مبسطة من المثال السابق لتوضيح فكرة التحقق من صلاحية المدخلات دون استخدام الواجهة البرمجية المضمّنة.
</p>

<p>
	تبقى شيفرة HTML نفسها، لكننا أزلنا فقط الخاصية <code>novalidate</code>:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6431_44" style=""><span class="tag">&lt;form&gt;</span><span class="pln">
 </span><span class="tag">&lt;p&gt;</span><span class="pln">
    </span><span class="tag">&lt;label</span><span class="pln"> </span><span class="atn">for</span><span class="pun">=</span><span class="atv">"mail"</span><span class="tag">&gt;</span><span class="pln">
     </span><span class="tag">&lt;span&gt;</span><span class="pln">Please enter an email address:</span><span class="tag">&lt;/span&gt;</span><span class="pln">
     </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text"</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"mail"</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"mail"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
     </span><span class="tag">&lt;span</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"error"</span><span class="pln"> </span><span class="atn">aria-live</span><span class="pun">=</span><span class="atv">"polite"</span><span class="tag">&gt;&lt;/span&gt;</span><span class="pln">
    </span><span class="tag">&lt;/label&gt;</span><span class="pln">
 </span><span class="tag">&lt;/p&gt;</span><span class="pln">
 </span><span class="tag">&lt;button&gt;</span><span class="pln">Submit</span><span class="tag">&lt;/button&gt;</span><span class="pln">
</span><span class="tag">&lt;/form&gt;</span></pre>

<p>
	لم تتغير شيفرة CSS كثيرًا، لكننا حولنا الصنف الزائف <code>invalid:</code> إلى صنف حقيقي لتفادي استخدام <a href="https://academy.hsoub.com/programming/css/%D9%85%D8%AD%D8%AF%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D8%AE%D8%A7%D8%B5%D9%8A%D8%A9-attribute-%D9%81%D9%8A-css-r2085/" rel="">محددات الخاصيات attribute selector</a>:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6431_46" style=""><span class="pln">body </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">font</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1em</span><span class="pln"> sans-serif</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">200px</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> auto</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

form </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">max-width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">200px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

p </span><span class="pun">*</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">display</span><span class="pun">:</span><span class="pln"> block</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

input</span><span class="pun">#</span><span class="pln">mail </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">appearance</span><span class="pun">:</span><span class="pln"> none</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100%</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">border</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1px</span><span class="pln"> solid </span><span class="lit">#333</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

 </span><span class="kwd">font-family</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">inherit</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">font-size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">90%</span><span class="pun">;</span><span class="pln">

 </span><span class="kwd">box-sizing</span><span class="pun">:</span><span class="pln"> border-box</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">/* This is our style for the invalid fields */</span><span class="pln">
input</span><span class="pun">.</span><span class="pln">invalid </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">border-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#900</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">background-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#fdd</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">input</span><span class="pun">:</span><span class="kwd">focus</span><span class="pun">:</span><span class="pln">invalid </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">outline</span><span class="pun">:</span><span class="pln"> none</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">/* This is the style of our error messages */</span><span class="pln">
</span><span class="pun">.</span><span class="pln">error </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100%</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

 </span><span class="kwd">font-size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">80%</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">color</span><span class="pun">:</span><span class="pln"> white</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">background-color</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#900</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">border-radius</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="lit">5px</span><span class="pln"> </span><span class="lit">5px</span><span class="pun">;</span><span class="pln">
 </span><span class="kwd">box-sizing</span><span class="pun">:</span><span class="pln"> border-box</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">.</span><span class="pln">error</span><span class="pun">.</span><span class="pln">active </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0.3em</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أما التغيير الجذري فهو في شيفرة جافا سكريبت، إذ ستحمل العبء اﻷكبر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6431_48" style=""><span class="kwd">const</span><span class="pln"> form </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"form"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> email </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">"mail"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> error </span><span class="pun">=</span><span class="pln"> email</span><span class="pun">.</span><span class="pln">nextElementSibling</span><span class="pun">;</span><span class="pln">

</span><span class="com">// HTML وفق مواصفات</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> emailRegExp </span><span class="pun">=</span><span class="pln">
 </span><span class="str">/^[a-zA-Z0-9.!#$%&amp;'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/</span><span class="pun">;</span><span class="pln">

</span><span class="com">// يمكن اﻵن بناء حدود للتقييم</span><span class="pln">
</span><span class="com">// لأننا لم نعد نعتمد على اﻷصناف الزائفة</span><span class="pln">
window</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"load"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="com">// نتحقق أن عنصر اﻹدخال فارغ (تذكر العنصر غير مطلوب)</span><span class="pln">
 </span><span class="com">// إن لم يكن كذلك نتحقق أن المحتوى هو عنوان بريد إلكتروني صالح</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> isValid </span><span class="pun">=</span><span class="pln"> email</span><span class="pun">.</span><span class="pln">value</span><span class="pun">.</span><span class="pln">length </span><span class="pun">===</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> emailRegExp</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="pln">email</span><span class="pun">.</span><span class="pln">value</span><span class="pun">);</span><span class="pln">
 email</span><span class="pun">.</span><span class="pln">className </span><span class="pun">=</span><span class="pln"> isValid </span><span class="pun">?</span><span class="pln"> </span><span class="str">"valid"</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="str">"invalid"</span><span class="pun">;</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="com">// تعرف الشيفرة التالية ما يحدث عندما تتغير القيمة المدخلة</span><span class="pln">
email</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"input"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="kwd">const</span><span class="pln"> isValid </span><span class="pun">=</span><span class="pln"> email</span><span class="pun">.</span><span class="pln">value</span><span class="pun">.</span><span class="pln">length </span><span class="pun">===</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> emailRegExp</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="pln">email</span><span class="pun">.</span><span class="pln">value</span><span class="pun">);</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">isValid</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    email</span><span class="pun">.</span><span class="pln">className </span><span class="pun">=</span><span class="pln"> </span><span class="str">"valid"</span><span class="pun">;</span><span class="pln">
    error</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="str">""</span><span class="pun">;</span><span class="pln">
    error</span><span class="pun">.</span><span class="pln">className </span><span class="pun">=</span><span class="pln"> </span><span class="str">"error"</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    email</span><span class="pun">.</span><span class="pln">className </span><span class="pun">=</span><span class="pln"> </span><span class="str">"invalid"</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="com">// تعرف الشيفرة التالية ما يحدث عندما يحاول المستخدم إرسال الاستمارة</span><span class="pln">
form</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"submit"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">event</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 event</span><span class="pun">.</span><span class="pln">preventDefault</span><span class="pun">();</span><span class="pln">

 </span><span class="kwd">const</span><span class="pln"> isValid </span><span class="pun">=</span><span class="pln"> email</span><span class="pun">.</span><span class="pln">value</span><span class="pun">.</span><span class="pln">length </span><span class="pun">===</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> emailRegExp</span><span class="pun">.</span><span class="pln">test</span><span class="pun">(</span><span class="pln">email</span><span class="pun">.</span><span class="pln">value</span><span class="pun">);</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">isValid</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    email</span><span class="pun">.</span><span class="pln">className </span><span class="pun">=</span><span class="pln"> </span><span class="str">"invalid"</span><span class="pun">;</span><span class="pln">
    error</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="str">"I expect an email, darling!"</span><span class="pun">;</span><span class="pln">
    error</span><span class="pun">.</span><span class="pln">className </span><span class="pun">=</span><span class="pln"> </span><span class="str">"error active"</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    email</span><span class="pun">.</span><span class="pln">className </span><span class="pun">=</span><span class="pln"> </span><span class="str">"valid"</span><span class="pun">;</span><span class="pln">
    error</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="str">""</span><span class="pun">;</span><span class="pln">
    error</span><span class="pun">.</span><span class="pln">className </span><span class="pun">=</span><span class="pln"> </span><span class="str">"error"</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	إليك النتيجة:
</p>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" frameborder="no" height="200" loading="lazy" scrolling="no" src="https://codepen.io/HsoubAcademy/embed/OJeqzZP?default-tab=" style="width: 100%;" title="An example that doesnt use the constraint validation API sample">See the Pen An example that doesnt use the constraint validation <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> sample by Hsoub Academy (@HsoubAcademy) on CodePen.</iframe>
</p>

<p>
	وكما نرى، اﻷمر ليس صعبًا للغاية، وتكمن الصعوبة في جعل الحل عامًا بما يكفي لتستخدمه عبر المنصات المختلفة وعلى أي استمارة قد تحاول إنشاءها. وتجدر اﻹشارة إلى وجود العديد من المكتبات الجاهزة لتنفيذ عمليات التحقق من المدخلات مثل <a href="https://rickharrison.github.io/validate.js/" rel="external nofollow">Validate.js</a>.
</p>

<h2 id="-15">
	الخلاصة
</h2>

<p>
	تحتاج عملية التحقق في طرف العميل أحيانًا إلى شيفرة جافا سكريبت إن أردت تخصيص التنسيق ورسائل الخطأ، لكن من المهم جدًا التفكير بحرص شديد عندما يتعلق اﻷمر بالمستخدم. وتذكر أن تساعده في تصويب البيانات التي يُدخلها واحرص على أن:
</p>

<ul>
	<li>
		تعرض رسائل خطأ صريحة.
	</li>
	<li>
		تقدم صيغة بسيطة إدخال القيم وأن تتساهل في ذلك.
	</li>
	<li>
		تشير إلى موقع الخطأ تمامًا وخاصة عندما يتكون الاستمارة كبيرة.
	</li>
</ul>

<p>
	وعندما تتأكد من صلاحية جميع القيم في الاستمارة يمكنك حينها تسليمها إلى الخادم.
</p>

<p>
	ترجمة -وبتصرف- للمقال: <a href="https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation" rel="external nofollow">Client-side form validation</a>
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/css/%D8%A7%D9%84%D8%AA%D9%86%D8%B3%D9%8A%D9%82-%D8%A7%D9%84%D9%85%D8%AA%D9%82%D8%AF%D9%85-%D9%84%D8%A7%D8%B3%D8%AA%D9%85%D8%A7%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-r2404/" rel="">التنسيق المتقدم لاستمارات الويب</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/devops/servers/%D9%86%D8%B8%D8%B1%D8%A9-%D8%B9%D9%84%D9%89-%D8%AA%D9%81%D8%A7%D8%B9%D9%84%D8%A7%D8%AA-%D8%A7%D9%84%D8%AE%D8%A7%D8%AF%D9%85-%D9%85%D8%B9-%D8%A7%D9%84%D8%B9%D9%85%D9%8A%D9%84-%D9%81%D9%8A-%D9%85%D9%88%D9%82%D8%B9-%D9%88%D9%8A%D8%A8-%D8%AF%D9%8A%D9%86%D8%A7%D9%85%D9%8A%D9%83%D9%8A-r782/" rel="">نظرة على تفاعلات الخادم مع العميل في موقع ويب ديناميكي</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/html/%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D8%B9%D9%86%D8%A7%D8%B5%D8%B1-%D8%A7%EF%BB%B9%D8%AF%D8%AE%D8%A7%D9%84-%D9%81%D9%8A-html5-r2392/" rel="">أنواع عناصر اﻹدخال في HTML5</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A2%D9%84%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%A7%D8%AA%D8%B5%D8%A7%D9%84-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D9%85%D8%B1-%D9%85%D8%B9-%D8%A7%D9%84%D8%AE%D8%A7%D8%AF%D9%85-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1301/" rel="">آليات الاتصال المستمر مع الخادم في جافاسكربت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/java/%D8%A7%D9%84%D9%85%D8%AF%D8%AE%D9%84%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D9%85%D8%AE%D8%B1%D8%AC%D8%A7%D8%AA-%D8%A7%D9%84%D9%86%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1018/" rel="">المدخلات والمخرجات النصية في جافا</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2406</guid><pubDate>Wed, 25 Sep 2024 15:07:02 +0000</pubDate></item><item><title>&#x62A;&#x639;&#x631;&#x641; &#x639;&#x644;&#x649; &#x62A;&#x648;&#x627;&#x628;&#x639; &#x627;&#x644;&#x637;&#x631;&#x641;&#x64A;&#x629; Console &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627; &#x633;&#x643;&#x631;&#x64A;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D8%B1%D9%81-%D8%B9%D9%84%D9%89-%D8%AA%D9%88%D8%A7%D8%A8%D8%B9-%D8%A7%D9%84%D8%B7%D8%B1%D9%81%D9%8A%D8%A9-console-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2361/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_07/-----JavaScript.png.a12a36876a4d3f2f2265388ce15941b0.png" /></p>
<p>
	سنتعرف في مقال اليوم على توابع الواجهة البرمجية للطرفية التي توفرها لغة البرمجة جافا سكريبت JavaScript Console <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> فهي طرفية قوية توفر للمطور مجموعة من التوابع القادرة على اكتشاف جميع رسائل الخطأ الغامضة و إظهارها له بوضوح مما يسهل عليه فهم سببها وتصحيحها.
</p>

<p>
	تُستخدم الطرفية console لتسجيل بعض المخرجات مثل نتائج العمليات الحسابية، والقيم المعادة من الواجهة البرمجية <a href="https://academy.hsoub.com/programming/general/%D8%B4%D8%B1%D8%AD-%D9%81%D9%84%D8%B3%D9%81%D8%A9-restful-%D8%AA%D8%B9%D9%84%D9%85-%D9%83%D9%8A%D9%81-%D8%AA%D8%A8%D9%86%D9%8A-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-rest-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-r635/" rel="">REST <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a> ونتائج العمليات التي تنفذ على النصوص أو إعادة النظر في جزء معين من البرنامج والكثير من الأمور الأخرى، كما يمكنك استخدام الطرفية console لعرض أو إخراج أي نوع من البيانات ترغب بها. فعندما تقوم بتشغيل JavaScript في متصفحك تعرض سجلات الطرفية console logs في طرفية المطور الخاصة <a href="https://academy.hsoub.com/apps/operating-systems/linux/%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D8%A5%D9%86%D8%AA%D8%B1%D9%86%D8%AA-%D8%AE%D9%81%D9%8A%D9%81%D8%A9-%D9%88%D9%85%D9%81%D8%AA%D9%88%D8%AD%D8%A9-%D8%A7%D9%84%D9%85%D8%B5%D8%AF%D8%B1-%D9%84%D9%86%D8%B8%D8%A7%D9%85-%D9%84%D9%8A%D9%86%D9%83%D8%B3-r789/" rel="">بمتصفح الإنترنت</a>.
</p>

<p>
	سنتعرف في هذا المقال على المهام التي تستطيع الطرفية القيام بها كما سنتعرف على بعض التوابع غير المشهورة والتي ربما تكون مفيدة لك كمطور JavaScript سواًء كنت مطورًا مبتدئًا أو محترفًا أو كنت تريد فقط معرفة الغرض من هذه الطرفية فقط وسنبدأ بالتوابع المشهورة ثم ننتقل إلى التوابع الأقل شهرة مع الأمثلة العملية على كل منها.
</p>

<h2 id="loggingtotheconsole">
	تسجيل الدخول إلى الطرفية Logging to the console
</h2>

<p>
	إذا كنت على دراية بالتابع <code>()console.log</code> فيمكنك الانتقال إلى الفقرة التالية التي تشرح مستويات التسجيل لأننا سنغطي في هذه الفقرة الأساسيات والتي تتضمن معرفة ما هي الطرفية؟ وكيف تستخدم؟.
</p>

<p>
	يستخدم معظم المطورين التابع <code>()console.log</code> لإرسال معلومات عامة حول الكود الخاص بهم إلى الطرفية console والتي يمكن العثور عليها في <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%A7%D9%84%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D9%81%D9%8A-%D9%85%D8%AA%D8%B5%D9%81%D8%AD-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-devtools-r277/" rel="">أدوات التطوير الخاصة بمتصفح الويب DevTools</a>
</p>

<p>
	ملاحظة عامة: يمكنك قص جميع السفرات البرمجية التي سترد في الفقرات التالية ولصقها في طرفية متصفحك وتجريب تنفيذها بنفسك، كل ما عليك هو فتح أدوات المطور في متصفحك والنقر على علامة تبويب Console لفتح الطرفية ولصق الكود وتشغيله.
</p>

<p>
	التابع <code>()log</code> هو التابع الأساسي لكائن الطرفية console، ويمكنك أن تعطي أي قيمة لهذا التابع وسيسجل أو يعرض النتيجة في الطرفية كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6914_6" style=""><span class="kwd">const</span><span class="pln"> hello </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Hi there, welcome to MDN Web Docs!"</span><span class="pun">;</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">hello</span><span class="pun">);</span><span class="pln">
</span><span class="com">// Hi there, welcome to MDN Web Docs!</span></pre>

<p>
	سيفيدك هذا الأمر كمطور في معرفة فيما إذا الكود الذي تكتبه يعمل بشكل صحيح أم لا من خلال عرض الخرج الناتج من الكود وجعله مرئيًا بالنسبة لك، إليك على سبيل المثال ما يمكن القيام به لاختبار إن كان تابع تنسيق التاريخ والوقت <code>DateTimeFormat</code> يعمل بشكل صحيح:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6914_8" style=""><span class="kwd">const</span><span class="pln"> currentDate </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">();</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> formattedDate </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Intl</span><span class="pun">.</span><span class="typ">DateTimeFormat</span><span class="pun">(</span><span class="str">"en-US"</span><span class="pun">).</span><span class="pln">format</span><span class="pun">(</span><span class="pln">currentDate</span><span class="pun">);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Current date:"</span><span class="pun">,</span><span class="pln"> formattedDate</span><span class="pun">);</span><span class="pln">
</span><span class="com">// Current date: 7/12/2024</span></pre>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="PNG" data-fileid="152846" href="https://academy.hsoub.com/uploads/monthly_2024_07/DateTimeFormat-in-consol.PNG.f4f7d65fe1019119a5a0aab039ea9491.PNG" rel=""><img alt="datetimeformat in consol" class="ipsImage ipsImage_thumbnailed" data-fileid="152846" data-unique="a280dd3uk" src="https://academy.hsoub.com/uploads/monthly_2024_07/DateTimeFormat-in-consol.thumb.PNG.f408afe9f29873544b72d0beb2005003.PNG"> </a>
</p>

<p>
	يمكنك أيضًا تزويد التابع <code>()log</code> بعدة قيم أوعناصر لتطبيق بعض التنسيقات الجميلة على الخرج كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6914_10" style=""><span class="kwd">const</span><span class="pln"> currentDate </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">();</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> formattedDate </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Intl</span><span class="pun">.</span><span class="typ">DateTimeFormat</span><span class="pun">(</span><span class="str">"en-US"</span><span class="pun">).</span><span class="pln">format</span><span class="pun">(</span><span class="pln">currentDate</span><span class="pun">);</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Today's date is %s"</span><span class="pun">,</span><span class="pln"> formattedDate</span><span class="pun">);</span><span class="pln">
</span><span class="com">// Today's date is 7/12/2024</span></pre>

<p>
	من الخصائص الرائعة للطرفية أيضًا هو إمكانية استخدامها بشكل تفاعلي كما في حال خاصية الإكمال التلقائي المدعومة من أغلب متصفحات الإنترنت والتي تسمح لك بالتنقل بين الاقتراحات المقدمة، على سبيل المثال في حال لم تكن تعرف ماذا تكتب في التابع <code>log</code> اكتب فقط <code>.console.log(window</code> وستظهر لك العديد من الخيارات لإكمال ما كتبت مثل التابع <code>alert </code>على سبيل المثال.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6914_12" style=""><span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">window</span><span class="pun">.</span><span class="pln">navigator</span><span class="pun">.</span><span class="pln">oscpu</span><span class="pun">);</span><span class="pln">
</span><span class="com">// Intel Mac OS X 10.15</span></pre>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="152847" href="https://academy.hsoub.com/uploads/monthly_2024_07/-.png.f08dad955266d165bc1564338d816d54.png" rel=""><img alt="الاكمال التقائي" class="ipsImage ipsImage_thumbnailed" data-fileid="152847" data-unique="lhzlyanlu" src="https://academy.hsoub.com/uploads/monthly_2024_07/-.png.f08dad955266d165bc1564338d816d54.png"> </a>
</p>

<p>
	ملاحظة: لا تنس التخلص من بقايا استدعاءات التابع <code>()console.log</code> الزائدة وغير الضرورية التي أضفتها في الكود البرمجي الخاص بك خلال مرحلة تطويره قبل نشره للعموم كي تتجنب طباعة معلومات حساسة أو غير ضرورية للمستخدمين في بيئة الإنتاج النهائية.
</p>

<h2 id="infowarnerror">
	عرض سجلات أخرى في الطرفية باستخدام التوابع <code>info, warn, error</code>
</h2>

<p>
	يمكنك أن تضيف المزيد السجلات المفيدة للطرفية باستخدام توابع أخرى مثل التابع <code>()conlose.info</code> والتابع <code>()conlose.warn</code> والتابع <code>()conlose.error</code>.
</p>

<p>
	إن تأثير استخدام هذه التوابع سيكون مشابهًا لتأثير استخدام التابع <code>()console.log</code> لكن الفرق سيكون في تنسيق الخرج (الذي يعتمد بدوره على نوع المتصفح)، وسيمكنك من خلالها تصفية الخرج الناتج، وهذا مفيد جدًا إذا كنت تريد رؤية سجلات الأخطاء فقط كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6914_14" style=""><span class="kwd">const</span><span class="pln"> browser </span><span class="pun">=</span><span class="pln"> window</span><span class="pun">.</span><span class="pln">navigator</span><span class="pun">.</span><span class="pln">userAgent</span><span class="pun">;</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">info</span><span class="pun">(</span><span class="pln">browser</span><span class="pun">);</span><span class="pln">
</span><span class="com">// Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:121.0) Gecko/20100101 Firefox/121.0</span></pre>

<p>
	يجب عليك استخدام التابعين <code>warn</code> و <code>error</code> إذا كنت تتوقع حدوث أخطاء من المستخدمين عند تشغيل الكود
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6914_16" style=""><span class="pln">console</span><span class="pun">.</span><span class="pln">warn</span><span class="pun">(</span><span class="str">"Unknown device - there may be compatibility issues."</span><span class="pun">);</span><span class="pln">
</span><span class="com">// <span class="ipsEmoji">⚠️</span> Unknown device - there may be compatibility issues.</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="str">"Unsupported device. See &lt;docs url&gt; for more details."</span><span class="pun">);</span><span class="pln">
</span><span class="com">// <span class="ipsEmoji">🛑</span> Unsupported device. See &lt;docs url&gt; for more details.</span><span class="pln">
</span><span class="com">//    &lt;stack trace&gt;</span></pre>

<p>
	ملاحظة: تعرض بعض المتصفحات تتبعًا لرسائل الخطأ ولكن يمكنك باستخدام الطرفية console استدعاء هذه الميزة بسهولة وذلك عن طريق استخدام التابع <code>()console.trace</code> والذي سيتم شرحه مفصلًا في فقرة لاحقة.
</p>

<h2 id="consoletable">
	عرض جداول في الطرفية باستخدام التابع <code>()console.table</code>
</h2>

<p>
	يعد هذا التابع أحد التوابع المفيدة جدًا للمطورين، وهو يستخدم غالبًا عند الحاجة لنسخ مخرجات الكود ومن ثم لصقها في أحد محررات الأكواد editors، والقيام بعد ذلك ببعض عمليات التنسيق عليها لجعلها قابلة للقراءة.
</p>

<p>
	كما يمكنك باستخدام الطرفية console القيام بشيء رائع وهو إنشاء الجداول مباشرة عن طريق التابع <code>()console.table</code> وهو أمر منطقي لأننا نقوم بتشغيل الكود عادة ضمن المتصفح وهو قادر -أي المتصفح- على التعامل مع إخراج الجداول بشكل جيد جدًأ
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6914_18" style=""><span class="kwd">const</span><span class="pln"> dogs </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
  </span><span class="pun">{</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Yoshi"</span><span class="pun">,</span><span class="pln"> color</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Black"</span><span class="pun">,</span><span class="pln"> personality</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Calm"</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">{</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Melanie"</span><span class="pun">,</span><span class="pln"> color</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Brown"</span><span class="pun">,</span><span class="pln"> personality</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Hyperactive"</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">{</span><span class="pln"> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Peppers"</span><span class="pun">,</span><span class="pln"> color</span><span class="pun">:</span><span class="pln"> </span><span class="str">"white"</span><span class="pun">,</span><span class="pln"> personality</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Unpredictable"</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
</span><span class="pun">];</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">table</span><span class="pun">(</span><span class="pln">dogs</span><span class="pun">);</span></pre>

<p>
	سيظهر هذا الكود على المتصفح على على شكل جدول منسق بشكل جميل كما يلي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="152845" href="https://academy.hsoub.com/uploads/monthly_2024_07/console-table.png.51988c6957ab5d22ecc112231ca5aa65.png" rel=""><img alt="console table" class="ipsImage ipsImage_thumbnailed" data-fileid="152845" data-unique="aji57lob6" src="https://academy.hsoub.com/uploads/monthly_2024_07/console-table.thumb.png.45bd0d0ef547b4ce05fff2e95503fe17.png"> </a>
</p>

<h2 id="consolecount">
	التعداد باستخدام التابع <code>()console.count</code>
</h2>

<p>
	يمكنك إضافة عداد إلى سجلاتك عن طريق التابع <code>()console.count</code> وذلك لمعرفة عدد مرات حدوث أمر معين كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6914_20" style=""><span class="kwd">function</span><span class="pln"> postBoostClicked</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// My post has been boosted, do something here</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">count</span><span class="pun">(</span><span class="str">"Boost count"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

postBoostClicked</span><span class="pun">();</span><span class="pln">
</span><span class="com">// Boost count: 1</span><span class="pln">
postBoostClicked</span><span class="pun">();</span><span class="pln">
</span><span class="com">// Boost count: 2</span></pre>

<h2 id="consoletimeconsoletimer">
	إضافة المؤقتات باستخدام التابع <code>()console.time</code> والتابع <code>()console.timer</code>
</h2>

<p>
	يعد التابعان <code>()console.time</code> و <code>()console.timeEnd</code> من التوابع المفيدة في الحالات التي تتطلب تشغيل وإيقاف تشغيل كود معين ضمن برنامجك، حيث تستخدم هذه التوابع لقياس المدة التي يستغرقها الكود مثل الوقت الذي يستغرقه تنفيذ تابع ما.
</p>

<p>
	في المثال التالي يستغرق التابع <code>()myFunction</code> وقت 200 ميلي ثانية للتنفيذ:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6914_22" style=""><span class="pln">console</span><span class="pun">.</span><span class="pln">time</span><span class="pun">(</span><span class="str">"timerName"</span><span class="pun">);</span><span class="pln">
</span><span class="com">// call myFunction()</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">timeEnd</span><span class="pun">(</span><span class="str">"timerName"</span><span class="pun">);</span><span class="pln">
</span><span class="com">// timerName: 200ms - timer ended</span></pre>

<p>
	يمكنك إضافة بعض التفاصيل مع الوقت، فمثلًا يمكنك إضافة ملاحظات من خلال استخدام التابع <code>()console.timeLog</code> ، ويعج هذا الأمر مفيد جدًا إذا كان برنامجك يحتوي على مراحل منفصلة لتنفيذ المهمة المطلوبة كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6914_24" style=""><span class="pln">console</span><span class="pun">.</span><span class="pln">time</span><span class="pun">(</span><span class="str">"MyTimer"</span><span class="pun">);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">timeLog</span><span class="pun">(</span><span class="str">"MyTimer"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Starting application up…"</span><span class="pun">);</span><span class="pln">
</span><span class="com">// MyTimer: 0ms Starting application up…</span><span class="pln">
</span><span class="com">// call myFunction(), for example</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">timeLog</span><span class="pun">(</span><span class="str">"MyTimer"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"UI is setup, making API calls now"</span><span class="pun">);</span><span class="pln">
</span><span class="com">// MyTimer: 200ms UI is setup, making API calls now</span><span class="pln">
</span><span class="com">// call otherFunction(), for example</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">timeEnd</span><span class="pun">(</span><span class="str">"MyTimer"</span><span class="pun">);</span><span class="pln">
</span><span class="com">// MyTimer: 300ms - timer ended</span></pre>

<h2 id="consolegroup">
	تجميع السجلات باستخدام التابع <code>()console.group</code>
</h2>

<p>
	تفيد عملية تجميع السجلات باستخدام التابعين <code>()console.group</code> و <code>()console.groupCollapsed</code> في تنظيم الخرج في حال إنشاء سجلات كثيرة، خاصة إذا كان الكود يمر عبر عدة مراحل، مثل مراحل الإعداد أو مهام المعالجة فتجميع السجلات مناسب جدًا في هذه الحالة.
</p>

<p>
	يمكنك كذلك طي المجموعات، مما يعني أنه بإمكانك توسيع وطي المجموعة ضمن الطرفية console إذا كنت ترغب في عرض أو إخفاء السجلات خاصة إذا كان هناك الكثير من المعلومات التي يجب عليك التدقيق بها.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6914_26" style=""><span class="pln">console</span><span class="pun">.</span><span class="pln">group</span><span class="pun">(</span><span class="str">"Grouped Logs"</span><span class="pun">);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Log 1"</span><span class="pun">);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Log 2"</span><span class="pun">);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">groupEnd</span><span class="pun">();</span><span class="pln">
</span><span class="com">// Grouped Logs</span><span class="pln">
</span><span class="com">//     Log 1</span><span class="pln">
</span><span class="com">//     Log 2</span><span class="pln">

console</span><span class="pun">.</span><span class="pln">groupCollapsed</span><span class="pun">(</span><span class="str">"Collapsed Group"</span><span class="pun">);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Log 3"</span><span class="pun">);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Log 4"</span><span class="pun">);</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">groupEnd</span><span class="pun">();</span><span class="pln">
</span><span class="com">// &gt; Collapsed Group</span></pre>

<h2 id="consoletrace">
	التعقب باستخدام التابع <code>()console.trace</code>
</h2>

<p>
	يوجد التابع <code>()trace</code> في معظم لغات البرمجة وهو أحد الخيارات المهمة للمبرمج لمعرفة مراحل تنفيذ الكود ضمن البرنامج، وهو يستخدم في عملية تصحيح الأخطاء ومعرفة مكان تنفيذ الكود.
</p>

<p>
	تعتبر عملية التعقب tracing مفيدة جدًا عندما يكون الكود طويلًا ومعقدًا نوعًا ما إذ يصعب في هذه الحالة تعقب الكود كاملًا بشكل منطقي.
</p>

<p>
	يوضح المثال التالي طريقة إضافة تعقب إلى الدالة <code>()example</code> لمعرفة فيما إذا تم استدعاؤها من التابع <code>()one</code> أو من التابع <code>()two</code>. كما يوجد لدينا هنا شرط منطقي تتغير حالته بين <code>true</code> و <code>false</code> بالاعتماد على الزمن، لذلك من المستحيل معرفة أي من التابعين <code>()one</code> أو<code>()two</code> سيقوم باستدعاء الدالة <code>()example</code>
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6914_28" style=""><span class="com">// Is the "currentSeconds" value odd or even?</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> currentSeconds </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">().</span><span class="pln">getSeconds</span><span class="pun">();</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> condition </span><span class="pun">=</span><span class="pln"> currentSeconds </span><span class="pun">%</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">===</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> one</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  example</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> two</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  example</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> randomChoice</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">condition</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// OK, I'm lost!</span><span class="pln">
    one</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    two</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> example</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// Where is this function called?</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">trace</span><span class="pun">(</span><span class="str">"Trace from example() function"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

randomChoice</span><span class="pun">();</span><span class="pln">
</span><span class="com">//  console.trace() Trace from example function</span><span class="pln">
</span><span class="com">//     example debugger eval code:23</span><span class="pln">
</span><span class="com">//     one debugger eval code:6</span><span class="pln">
</span><span class="com">//     randomChoice debugger eval code:16</span></pre>

<p>
	نستنتج من هذا التعقب ما يلي:
</p>

<ol>
	<li>
		ينفذ التابع <code>trace</code> ضمن الدالة <code>()example</code> في السطر 23
	</li>
	<li>
		تستدعى الدالة <code>()example</code> من قبل الدالة <code>()one</code> في السطر 6
	</li>
	<li>
		ينتهي التعقب عندما يتم استدعاء <code>()randomChoice</code> في السطر 16 قد يكون استخدام التابع <code>()console.trace</code> مفيدًا جدًا في حال كون الشيفرات البرمجية طويلة ومعقدة وغير مفهومة وذلك من أجل اكتشاف مصدر الأخطاء بسهولة أكبر.
	</li>
</ol>

<h2 id="consoleclear">
	التنظيف باستخدام التابع <code>()console.clear</code>
</h2>

<p>
	يمسح التابع <code>()console.clear</code> كافة التعليمات السابقة التي كتبتها ضمن الطرفية، وهو مفيد في حالة استخدام عدد كبير من الأوامر ضمن الطرفية والحاجة لتنظيفها وجعلها خالية من أي أمر سابق
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_6914_30" style=""><span class="com">// Too much information!</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">clear</span><span class="pun">();</span><span class="pln">
</span><span class="com">// Console was cleared.</span></pre>

<p>
	وبهذا الأمر البسيط والمفيد نكون قد وصلنا لختام مقال اليوم.
</p>

<h2 id="">
	الخلاصة
</h2>

<p>
	تناولنا في هذا المقال عدة طرق لاستخدام الطرفية في لغة البرمجة جافا سكريبت JavaScript والتي يمكن أن يكون بعضها مألوفًا ومعروفًا بالنسبة لك كمطور، وبعضها الآخر جديدًا عليك سواء كنت في مستوى مبتدئ أو متقدم في <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D9%84%D9%85-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D9%85%D9%86-%D8%A7%D9%84%D8%B5%D9%81%D8%B1-%D8%AD%D8%AA%D9%89-%D8%A7%D9%84%D8%A7%D8%AD%D8%AA%D8%B1%D8%A7%D9%81-r2046/" rel="">تعلم لغة جافا سكريبت</a> والتعامل معها.<br>
	نتمنى لكم الاستفادة من هذا المقال، وفي حال كان لديكم أي تساؤل حول ما ورد فيه، يمكن تركه في قسم التعليقات أسفل المقال أو كتابته في <a href="https://academy.hsoub.com/questions/" rel="">قسم الأسئلة والأجوبة</a> في الأكاديمية.
</p>

<p>
	ترجمة وبتصرف للمقال <a href="https://developer.mozilla.org/en-US/blog/learn-javascript-console-methods/" rel="external nofollow">Developer essentials: JavaScript console methods</a> لكاتبه Brian Smith
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%83%D9%8A%D9%81-%D8%AA%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B7%D9%88%D9%90%D9%91%D8%B1-%D9%81%D9%8A-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D8%AD%D8%AF%D9%8A%D8%AB%D8%A9-r541/" rel="">كيف تستخدم أدوات المطوِّر في المتصفحات الحديثة</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/workflow/%D8%A3%D8%AF%D9%88%D8%A7%D8%AA-%D9%85%D8%B7%D9%88%D8%B1%D9%8A-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D9%85%D8%AF%D9%85%D8%AC%D8%A9-%D9%81%D9%8A-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-r1439/" rel="">أدوات مطوري الويب المدمجة في المتصفحات</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D9%85%D8%B4%D8%A7%D9%83%D9%84-%D8%A7%D9%84%D8%B4%D8%A7%D8%A6%D8%B9%D8%A9-%D9%84%D9%84%D8%AA%D9%88%D8%A7%D9%81%D9%82-%D9%85%D8%B9-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D9%81%D9%8A-%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1972/" rel="">معالجة المشاكل الشائعة للتوافق مع المتصفحات في شيفرة جافاسكربت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%87%D9%8A%D9%83%D9%84-%D8%A7%D9%84%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D8%AC-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1232/" rel="">هيكل البرنامج في جافاسكريبت</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2361</guid><pubDate>Fri, 12 Jul 2024 15:09:01 +0000</pubDate></item><item><title>&#x627;&#x644;&#x648;&#x627;&#x62C;&#x647;&#x627;&#x62A; &#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x64A;&#x629; &#x648;&#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x634;&#x62C;&#x631;&#x629; DOM &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627; &#x633;&#x643;&#x631;&#x64A;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%B4%D8%AC%D8%B1%D8%A9-dom-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2360/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_07/----.png.692dca672f732477213650b6349835b1.png" /></p>
<p>
	من اﻷمور التي ستحتاجها غالبًا عند كتابة شيفرة برمجية لصفحات وتطبيقات الويب هي التعامل مع مستندات الويب بطريقة أو بأخرى. وعادة ما يجري ذلك من خلال شجرة DOM وهي واجهة برمجية للتحكم بملف HTML وتنسيق المعلومات التي تستخدم الكائن <code>Document</code> بكثرة. سنفرد هذا المقال للحديث عن استخدام DOM بالتفصيل، إضافة إلى بعض الواجهات التي يمكنها تغيير بيئة العمل بطرائق مفيدة.
</p>

<p>
	ننصحك قبل المتابعة في قراءة هذه المقالات أن:
</p>

<ul>
	<li>
		<p>
			تكون ملمًا بلغتي <a href="HTTPS://wiki.hsoub.com/HTML" rel="external">HTML</a> و <a href="HTTPS://wiki.hsoub.com/CSS" rel="external">CSS</a>.
		</p>
	</li>
	<li>
		<p>
			تكون ملمًا بلغة <a href="HTTPS://wiki.hsoub.com/JavaScript" rel="external">جافا سكريبت</a>.
		</p>
	</li>
	<li>
		<p>
			تطلع على سلاسل المقالات السابقة التي ناقشت <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D8%B1%D9%91%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D9%85%D9%86-%D9%85%D9%86%D8%B8%D9%88%D8%B1-%D8%B9%D8%A7%D9%85-r2266/" rel="">أساسيات جافا سكريبت</a> و<a href="https://academy.hsoub.com/programming/javascript/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2305/" rel="">الكائنات في جافا سكريبت</a>.
		</p>
	</li>
</ul>

<h2 id="">
	اﻷجزاء المهمة في متصفح الويب
</h2>

<p>
	تُعد المتصفحات برمجيات معقدة تضم الكثير من اﻷجزاء التي لا يمكن لمطور الويب التعامل معها أو التحكم بها باستخدام جافا سكريبت. وقد تعتقد أن هذه المحدودية أمر سيء، لكن الأسباب الكامنة وراء إقفال بعض أجزاء المتصفحات وجيهة بالفعل ويتعلق معظمها باﻷمان. تخيّل مثلًا أن تتمكن صفحة ويب ما من الوصول إلى كلمات السر التي تخزنها في المتصفح أو غيرها من المعلومات الحساسة ومن ثم تستخدم هذه البيانات في الدخول إلى صفحات أخرى!
</p>

<p>
	وعلى الرغم من تلك المحدوديات، تمنح واجهات الويب البرمجية إمكانية الوصول إلى الكثير من الوظائف التي تمكنك من تنفيذ أشياء مفيدة جدًا في صفحات الويب، وهنالك بالفعل بعض النقاط الواضحة التي تراها باستمرار وتستخدمها في الشيفرة. تأمل مثلًا المخطط التالي الذي يمثل الأجزاء الرئيسية من المتصفح التي تشارك مباشرة في عرض صفحة الويب:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="152840" href="https://academy.hsoub.com/uploads/monthly_2024_07/01_document_window_navigator.png.a9d7a744a4dd42c8db64cc6300033836.png" rel=""><img alt="01 document window navigator" class="ipsImage ipsImage_thumbnailed" data-fileid="152840" data-unique="x5x53tcf0" src="https://academy.hsoub.com/uploads/monthly_2024_07/01_document_window_navigator.png.a9d7a744a4dd42c8db64cc6300033836.png"> </a>
</p>

<ul>
	<li>
		<strong>النافذة window</strong>: وهي الجزء الذي تُحمّل ضمنه صفحة الويب ويُمثّل في جافا سكريبت بالكائن <code>Window</code>. ونتمكن باستخدام التوابع التي يقدمها هذا الكائن من تنفيذ أشياء عديدة مثل الحصول على حجم النافذة (باستخدام خاصيات مثل <code>Window.innerWidth</code> و <code>Window.innerHeight</code>) أو التعامل مع المستند الذي يُحمّل ضمنها وتخزين بيانات متعلقة به في طرف العميل (مثل استخدام قاعدة بيانات محلية أو غيرها من آليات التخزين) أو ربط معالج أحداث بالنافذة الحالية وغيرها الكثير.
	</li>
	<li>
		<strong>المستكشف navigator</strong>: ويمثّل حالة وهوية المتصفح (العميل الذي يستخدم المتصفح) عندما يتصفح الويب. يُمثَّل المستكشف في جافا سكريبت عن طريق الكائن <code>Navigator</code> الذي يُستخدم في الحصول على معلومات متعلقة بالمستخدم مثل اللغة المفضلة والتقاط بث كاميرا الويب الخاصة به وغيرها.
	</li>
	<li>
		<strong>المستند document</strong>: تمثله شجرة DOM في المتصفح وهو في الواقع صفحة الويب التي تُحمّل ضمن النافذة. ويُمثَّل في جافا سكريبت من خلال الكائن <code>Document</code>. ويُستخدم هذا الكائن في التعامل مع عناصر HTML وأصناف CSS في المستند واستخلاص المعلومات منها أو تعديلها مثل الحصول على مرجع لأحد عناصر شجرة DOM وتغيير محتواه النصي وتنسيقه، كما يمكّنك من إنشاء عناصر جديدة وإضافتها إلى العنصر الحالي كعناصر أبناء، وكذلك حذفهم جميعًا.
	</li>
</ul>

<p>
	ونركز في مقالنا بشكل أساسي على التعامل مع المستند، مع بعض اﻹضافات اﻷخرى.
</p>

<h2 id="dom-1">
	شجرة DOM (نموذج كائن المستند)
</h2>

<p>
	يُمثّل المستند الذي يُحمَّل ضمن كل نافذة فرعية من المتصفح على شكل شجرة من العناصر تُعرف بنموذج كائن المستند Document Object Model أو شجرة DOM اختصارًا. وتسهّل هذه الشجرة التي يُنشئها المتصفح الوصول إلى عناصر HTML برمجيًا. كما يستخدم المتصفح هذه الشجرة لتطبيق التنسيقات وغيرها من المعلومات على العنصر الصحيح عند تصيير الصفحة. وستتمكن أنت كمطوّر من التعامل مع شجرة DOM من خلال جافا سكريبت بعد أن تُصيّر الصفحة.
</p>

<p>
	نعرض تاليًا <a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/document-manipulation/dom-example.html" rel="external nofollow">مثالًا بسيطًا</a> لتوضيح اﻷمر (يمكنك تجربته <a href="https://mdn.github.io/learning-area/javascript/apis/document-manipulation/dom-example.html" rel="external nofollow">مباشرة</a> أيضًا). لهذا جرّب أن تفتح الملف في متصفحك وسترى صفحة بسيطة جدًا تضم العنصر <code>&lt;section&gt;</code> وضمنه صورة وفقرة نصية تضم رابطًا تشعبيًا.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8223_8" style=""><span class="dec">&lt;!doctype html&gt;</span><span class="pln">
</span><span class="tag">&lt;html</span><span class="pln"> </span><span class="atn">lang</span><span class="pun">=</span><span class="atv">"en-US"</span><span class="tag">&gt;</span><span class="pln">
 </span><span class="tag">&lt;head&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"utf-8"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
    </span><span class="tag">&lt;title&gt;</span><span class="pln">Simple DOM example</span><span class="tag">&lt;/title&gt;</span><span class="pln">
 </span><span class="tag">&lt;/head&gt;</span><span class="pln">
 </span><span class="tag">&lt;body&gt;</span><span class="pln">
    </span><span class="tag">&lt;section&gt;</span><span class="pln">
     </span><span class="tag">&lt;img</span><span class="pln">
      </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"dinosaur.png"</span><span class="pln">
      </span><span class="atn">alt</span><span class="pun">=</span><span class="atv">"A red Tyrannosaurus Rex: A two legged dinosaur standing upright like a human,  with small arms, and a large head with lots of sharp teeth."</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
     </span><span class="tag">&lt;p&gt;</span><span class="pln">
      Here we will add a link to the
      </span><span class="tag">&lt;a</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"https://www.mozilla.org/"</span><span class="tag">&gt;</span><span class="pln">Mozilla homepage</span><span class="tag">&lt;/a&gt;</span><span class="pln">
     </span><span class="tag">&lt;/p&gt;</span><span class="pln">
    </span><span class="tag">&lt;/section&gt;</span><span class="pln">
 </span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	تبدو شجرة DOM كالتالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="152841" href="https://academy.hsoub.com/uploads/monthly_2024_07/02_dom_screenshot.png.dfd077a8ec2e4ac76f23410d31d1a5b2.png" rel=""><img alt="02 dom screenshot" class="ipsImage ipsImage_thumbnailed" data-fileid="152841" data-unique="3gzz8fjls" src="https://academy.hsoub.com/uploads/monthly_2024_07/02_dom_screenshot.png.dfd077a8ec2e4ac76f23410d31d1a5b2.png"> </a>
</p>

<p>
	يُدعى كل مدخل في الشجرة عقدة node وبإمكانك ملاحظة أن بعض العقد في المخطط السابق تمثل عناصر مثل <code>HTML</code> و <code>HEAD</code> و <code>META</code>، كما يمثل غيرها نصوصًا (معرّفة بالوسم <code>text#</code>). وهنالك أيضًا أنواع أخرى من العقد لكن ما ذكرناه هي العقد الرئيسية التي تواجهها.
</p>

<p>
	يُشار إلى العقد أيضًا بموقعها ضمن الشجرة نسبةً إلى عقد أخرى:
</p>

<ul>
	<li>
		<strong>عقدة جذرية root node</strong>: وهي أعلى عقدة في الشجرة وهي دائمًا العقدة <code>HTML</code> في ملفات HTML (وتختلف من لغة تأشير إلى أخرى).
	</li>
	<li>
		<strong>عقدة ابن child node</strong>: وهي عقدة تقع مباشرة ضمن عقدة أخرى، مثل العنصر <code>IMG</code> داخل العنصر <code>SECTION</code> في المثال السابق.
	</li>
	<li>
		<strong>عقدة سليلة Descendant node</strong>: وهي عقد تقع في أي مكان داخل عقدة أخرى، مثل العنصر <code>IMG</code> داخل العنصر <code>SECTION</code> في المثال السابق فهي أيضًا عقدة سليلة. وهي ليست عقدة ابن للعنصر <code>BODY</code> لأنها تبعد مستويين عنه لكنها عقدة سليلة له.
	</li>
	<li>
		<strong>عقدة أم Parent node</strong>: وهي عقدة تحتوي على عقد أخرى ضمنها، مثل العقدة <code>BODY</code> التي تمثل عقدة أم للعنصر <code>SECTION</code>.
	</li>
	<li>
		<strong>عقد شقيقة Sibling nodes</strong>: وهي عقد تقع في نفس المستوى من شجرة DOM مثل العنصرين <code>IMG</code> و <code>P</code>.
	</li>
</ul>

<p>
	ومن اﻷفضل طبعًا أن تعتاد على هذه المصطلحات قبل العمل مع DOM، إذ تُستخدم في العديد من مصطلحات الشيفرة التي تصادفها، ومن المحتمل أن تكون صادفتها فعلًا إن درست صفحات التنسيق CSS (مثل المحدد السليل والمحدد الابن).
</p>

<h2 id="dom-2">
	تطبيق عملي: أساسيات العمل مع شجرة DOM
</h2>

<p>
	لنبدأ العمل مع شجرة DOM من خلال التطبيق العملي التالي:
</p>

<ol>
	<li>
		أنشئ نسخة محلية من <a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/document-manipulation/dom-example.html" rel="external nofollow">صفحة التطبيق</a> و<a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/document-manipulation/dinosaur.png" rel="external nofollow">الصورة</a> التي ترافقها.
	</li>
	<li>
		أضف العنصر <code>&lt;script&gt;&lt;/script&gt;</code> إلى ملف الشيفرة بعد الوسم <code>&lt;body/&gt;</code> مباشرة.
	</li>
	<li>
		ولكي تتعامل مع عنصر ضمن شجرة DOM عليك تخزين مرجع إليه ضمن متغير، لهذا أضف الشيفرة التالية ضمن العنصر <code>&lt;script&gt;</code>:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8223_10" style=""><span class="kwd">const</span><span class="pln"> link </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"a"</span><span class="pun">);</span></pre>

<p>
	وهكذا سيكون بإمكانك اﻵن التعامل مع العنصر الذي تخزن مرجعًا إليه باستخدام الخاصيات والتوابع المتاحة لهذا العنصر (تُعرَّف هذه الخاصيات والتوابع ضمن واجهات خاصة نذكر منها <code>HTMLAnchorElement</code> التي تتعامل مع الرابط التشعبي <code>&lt;a&gt;</code> وكذلك الواجهة اﻷم <code>HTMLElement</code> والواجهة <code>Node</code> التي تمثل جميع عقد الشجرة).
</p>

<p>
	سنبدأ العمل بتغيير النص ضمن الرابط التشعبي بتحديث قيم الخاصية <code>Node.textContent</code>، لهذا أضف هذا السطر تحت السطر السابق:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8223_12" style=""><span class="pln">link</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Mozilla Developer Network"</span><span class="pun">;</span></pre>

<p>
	ولا بد أيضًا من تغيير عنوان URL الذي يشير إليه الرابط التشعبي كي لا ينقل المستخدم إلى وجهة خاطئة عند النقر عليه، لهذا نضيف السطر التالي تحت اﻷسطر السابقة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8223_14" style=""><span class="pln">link</span><span class="pun">.</span><span class="pln">href </span><span class="pun">=</span><span class="pln"> </span><span class="str">"https://developer.mozilla.org"</span><span class="pun">;</span></pre>

<p>
	وتجدر اﻹشارة إلى وجود عدة طرق لاختيار العنصر الذي نريد التعامل معه في جافا سكريبت، ويُعد التابع <code>()Document.querySelector</code> مقاربة حديثة ننصح بها لأنها تسمح لك باختيار العنصر باستخدام محددات تنسيق CSS. إذ يبحث التابع السابق عند استدعائه في شيفرتنا عن أول عنصر <code>&lt;a&gt;</code> في المستند، وإن أردت البحث عن المزيد العناصر كي تتعامل معها دفعة واحدة، تستطيع استخدام التابع <code>()Document.querySelectorAll</code> الذي يبحث عن كل عنصر في الصفحة له نفس <a href="https://academy.hsoub.com/programming/css/%D9%85%D8%AD%D8%AF%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D9%86%D9%88%D8%B9-%D9%88%D8%A7%D9%84%D8%A3%D8%B5%D9%86%D8%A7%D9%81-%D9%88%D8%A7%D9%84%D9%85%D8%B9%D8%B1%D9%91%D9%81%D8%A7%D8%AA-%D9%81%D9%8A-css-r2084/" rel="">مُحدد CSS</a> الذي نستهدفه ومن ثم يخزّن مراجعًا إلى هذه العناصر ضمن كائن شبيه بالمصفوفة يُدعى قائمة عقد <code>NodeList</code>.
</p>

<p>
	وتصادف أيضًا طرقًا أخرى للحصول على مراجع للعناصر ضمن الشجرة منها:
</p>

<ul>
	<li>
		<code>()Document.getElementByID</code>: يبحث هذا التابع عن عنصر ذو قيمة محددة للسمة <code>id</code>. فلو أردنا الوصول إلى العنصر <code>&lt;p id="myId"&gt;My paragraph&lt;/p&gt;</code>، نمرر قيمة هذه السمة إلى التابع كالتالي:
	</li>
</ul>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8223_16" style=""><span class="kwd">const</span><span class="pln"> elementRef </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">'myId'</span><span class="pun">);</span></pre>

<ul>
	<li>
		<code>()Document.getElementsByTagName</code>: تعيد كائنًا يشبه المصفوفة يضم كل العناصر من النوع نفسه مثل عناصر الفقرات النصية <code>&lt;p&gt;</code> أو الروابط التشعبية <code>&lt;a&gt;</code> وغيرها. يُمرر نوع العنصر إلى التابع كما في المثال التالي:
	</li>
</ul>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8223_18" style=""><span class="kwd">const</span><span class="pln"> elementRefArray </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">getElementsByTagName</span><span class="pun">(</span><span class="str">'p'</span><span class="pun">);</span></pre>

<p>
	يعمل هذان التابعان بشكل أفضل مع المتصفحات القديمة، لكنهما أقل ملائمة مقارنة بالتابع <code>()Document.querySelector</code>.
</p>

<h3 id="dom-3">
	إنشاء عقد جديدة ووضعها ضمن شجرة DOM
</h3>

<p>
	رأيت مما سبق ما يمكن إنجازه، لكننا سنتقدم الآن خطوة للأمام لإلقاء نظرة على كيفية إنشاء عناصر جديدة.
</p>

<ol>
	<li>
		بالعودة إلى الشيفرة السابقة سنحاول إنشاء مرجع إلى العنصر <code>&lt;section&gt;</code>، لهذا ضع الشيفرة التالية في نهاية السكريبت (وهذا ما سنفعله مع اﻷسطر التي نضيفها تاليًا):
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8223_20" style=""><span class="kwd">const</span><span class="pln"> sect </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"section"</span><span class="pun">);</span></pre>

<ol start="2">
	<li>
		لننشئ الآن فقرة نصية جديدة باستخدام <code>()DocumentCreateElement</code> ونضع فيها بعض العبارات وفق نفس اﻷسلوب الذي اتبعناه سابقًا:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8223_22" style=""><span class="kwd">const</span><span class="pln"> para </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">"p"</span><span class="pun">);</span><span class="pln">
para</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="str">"We hope you enjoyed the ride."</span><span class="pun">;</span></pre>

<ol start="3">
	<li>
		نُلحق الفقرة النصية الجديدة بنهاية العنصر <code>&lt;section&gt;</code> من خلال التابع <code>()Node.appendChild</code>:
	</li>
</ol>

<pre class="ipsCode">sect.appendChild(para);
</pre>

<ol start="4">
	<li>
		لنُضف أخيرًا عقدة نصية text node إلى الفقرة النصية التي تضم الرابط التشعبي، لهذا ننشئ أولًا العقدة النصية باستخدام التابع <code>()Document.createTextNode</code>:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8223_24" style=""><span class="kwd">const</span><span class="pln"> text </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createTextNode</span><span class="pun">(</span><span class="pln">
 </span><span class="str">" — the premier source for web development knowledge."</span><span class="pun">,</span><span class="pln">
</span><span class="pun">);</span></pre>

<p>
	ثم ننشئ مرجعًا إلى الفقرة النصية ونلحق بها العقدة النصية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8223_26" style=""><span class="kwd">const</span><span class="pln"> linkPara </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"p"</span><span class="pun">);</span><span class="pln">
linkPara</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">text</span><span class="pun">);</span></pre>

<p>
	هذا كل ما تحتاجه غالبًا ﻹضافة عقد إلى شجرة DOM وستستخدم التوابع السابقة كثيرًا عند بناء واجهة ديناميكية.
</p>

<h3 id="-1">
	نقل وإزالة عناصر
</h3>

<p>
	تحتاج أحيانًا إلى نقل عقدة أو حذفها وهذا أمر ممكن، فإن أردت نقل الفقرة النصية التي تضم الرابط التشعبي إلى نهاية العنصر <code>&lt;section&gt;</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8223_28" style=""><span class="pln">sect</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">linkPara</span><span class="pun">);</span></pre>

<p>
	ولن تحدث بالطبع عملية نسخ للفقرة النصية إلى المكان الجديد لأن المتغير <code>linkPara</code> هو مرجع إلى النسخة الوحيدة فقط من هذه الفقرة. لكن إن أردت إنشاء نسخة عنها وإضافتها إلى المكان الذي تريد، فهذا أمر ممكن أيضًا من خلال التابع <code>()Node.cloneNode</code>.
</p>

<p>
	أما إزالة العقدة فهو أمر سهل وخاصة عندما يكون لديك مرجع إلى العقدة التي تريد إزالتها ومرجع إلى العقدة اﻷم لها، وعندها نستدعي التابع <code>Node.removeChild</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8223_30" style=""><span class="pln">sect</span><span class="pun">.</span><span class="pln">removeChild</span><span class="pun">(</span><span class="pln">linkPara</span><span class="pun">);</span></pre>

<p>
	باﻹمكان أيضًا إزالة عقدة بناء على مرجعها فقط، وهذا أمر شائع باستخدام التابع <code>()Element.remove</code>:
</p>

<pre class="ipsCode">linkPara.remove();
</pre>

<p>
	لا تدعم المتصفحات الأقدم هذا التابع، إذ لا تمتلك أي طريقة لتطلب من عقدة حذف نفسها، لهذا عليك اﻹلتفاف على الموضوع كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8223_32" style=""><span class="pln">linkPara</span><span class="pun">.</span><span class="pln">parentNode</span><span class="pun">.</span><span class="pln">removeChild</span><span class="pun">(</span><span class="pln">linkPara</span><span class="pun">);</span></pre>

<p>
	أضف السطر السابق إلى شيفرتك.
</p>

<h3 id="-2">
	العمل مع تنسيقات العناصر
</h3>

<p>
	بإمكاننا تعديل تنسيق CSS لعناصر الصفحة من خلال أكواد جافا سكريبت بطرق كثيرة. وكبداية بإمكانك الحصول على قائمة بكل ملفات التنسيق المرتبطة بالصفحة من خلال التابع <code>Document.stylesheets</code> الذي يعيد كائنًا مشابهًا للمصفوفات يضم عناصر من النوع <code>CSSStyleSheet</code>. وعندها ستتمكن من إضافة أو إزالة التنسيقات كما تشاء. ولن نتوسع في هذا اﻷسلوب لأنه قديم نوعًا ما ويصعب التعامل مع التنسيقات باستخدامه، وهنالك بالطبع طرق أسهل.
</p>

<p>
	تقتضي الطريقة اﻷولى إضافة تنسيقات سطرية inline style مباشرة ضمن العناصر التي تريد تعديل تنسيقها ديناميكيًا. يُنفَّذ الأمر من خلال الخاصية <code>HTMLElemet.style</code> التي تضم معلومات عن التنسيق السطري لكل عنصر في الصفحة. وعندما تغيير قيمة هذه الخاصية سيتغير تنسيق العنصر مباشرة.
</p>

<p>
	جرّب تغيير تنسيقات الفقرة النصية في شيفرتنا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8223_34" style=""><span class="pln">para</span><span class="pun">.</span><span class="pln">style</span><span class="pun">.</span><span class="pln">color </span><span class="pun">=</span><span class="pln"> </span><span class="str">"white"</span><span class="pun">;</span><span class="pln">
para</span><span class="pun">.</span><span class="pln">style</span><span class="pun">.</span><span class="pln">backgroundColor </span><span class="pun">=</span><span class="pln"> </span><span class="str">"black"</span><span class="pun">;</span><span class="pln">
para</span><span class="pun">.</span><span class="pln">style</span><span class="pun">.</span><span class="pln">padding </span><span class="pun">=</span><span class="pln"> </span><span class="str">"10px"</span><span class="pun">;</span><span class="pln">
para</span><span class="pun">.</span><span class="pln">style</span><span class="pun">.</span><span class="pln">width </span><span class="pun">=</span><span class="pln"> </span><span class="str">"250px"</span><span class="pun">;</span><span class="pln">
para</span><span class="pun">.</span><span class="pln">style</span><span class="pun">.</span><span class="pln">textAlign </span><span class="pun">=</span><span class="pln"> </span><span class="str">"center"</span><span class="pun">;</span></pre>

<p>
	ثم أعد تحميل الصفحة لتشاهد التنسيقات الجديدة وقد طُبّقت على الفقرة النصية. ولو حاولت تفحّص الفقرة النصية من خلال المتصفح ستجد أن تنسيقًا سطريًا قد أضيف إلى شيفرتها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8223_36" style=""><span class="pun">&lt;</span><span class="pln">p style</span><span class="pun">=</span><span class="str">"color: white; background-color: black; padding: 10px; width: 250px; text-align: center;"</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="typ">We</span><span class="pln"> hope you enjoyed the ride</span><span class="pun">.</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">p</span><span class="pun">&gt;</span></pre>

<p>
	<strong>ملاحظة</strong>: لاحظ كيف كُتبت نسخ جافا سكريبت من تنسيقات CSS بأسلوب سنام الجمل camelCase (مثلًا <code>backgroundColor</code>) مقارنة مع أسلوب CSS اﻷصلي وهو أسلوب الكباب lower-kebab-case (مثلًا <code>background-color</code>). احرص ألا تختلط عليك اﻷمور.
</p>

<p>
	وهنالك أيضًا طريقة شائعة أخرى للتعامل مع تنسيق العناصر دينياميكيًا، سنلقي عليها نظرة اﻵن:
</p>

<ol>
	<li>
		احذف اﻷسطر الخمسة اﻷخيرة التي أضفتها.
	</li>
	<li>
		أضف ما يلي ضمن ترويسة الصفحة <code>&lt;head&gt;</code>:
	</li>
</ol>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_8223_38" style=""><span class="pun">&lt;</span><span class="pln">style</span><span class="pun">&gt;</span><span class="pln">
 </span><span class="pun">.</span><span class="pln">highlight </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">color</span><span class="pun">:</span><span class="pln"> white</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">background-color</span><span class="pun">:</span><span class="pln"> black</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">padding</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10px</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">250px</span><span class="pun">;</span><span class="pln">
    </span><span class="kwd">text-align</span><span class="pun">:</span><span class="pln"> center</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">style</span><span class="pun">&gt;</span></pre>

<p>
	نستخدم التابع <code>()Element.setAttribute</code> للتعامل مع عناصر HTML عمومًا، ويأخذ هذا التابع وسيطان أولهما الخاصية التي تريد ضبطها والثاني قيمة هذه الخاصية. وفي حالتنا ستكون الخاصية هي صنف الفقرة النصية <code>class</code> وقيمتها هي محدد CSS الذي نريد إسناده إلى الفقرة النصية لتنسيقها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8223_40" style=""><span class="pln">para</span><span class="pun">.</span><span class="pln">setAttribute</span><span class="pun">(</span><span class="str">"class"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"highlight"</span><span class="pun">);</span></pre>

<p>
	حدّث صفحتك ولن ترى أية تغيير في شيفرة HTML للفقرة النصية لأن التنسيق قد طّبق عليها بمنحها صنف تنسيق وليس بإضافة تنسيق سطري إليها.
</p>

<p>
	إن اعتماد أسلوب معين هي حرية شخصية، ولكل اﻷساليب إيجابياتها وسلبياتها. فاﻷسلوب الأول بسيط ولا يتطلب إعدادات خاصة وهو جيد في بعض الحالات، بينما يعزز اﻷسلوب الثاني مبدأ فصل شيفرة التنسيق CSS عن شيفرة الصفحة HTML والذي يعُدّ ممارسة جيدة). وقد تميل مع تقدمك في مسيرة بناء تطبيقات أضخم وأعقد إلى استخدام اﻷسلوب الثاني أكثر، لكن في النهاية اﻷمر كما ذكرنا يعود إليك.
</p>

<p>
	ربما لم تلمس حتى اللحظة فائدة استخدام جافا سكريبت من إنشاء عناصر ساكنة، ومن الممكن كتابتها في شيفرة HTML مباشرة دون الحاجة إلى جافا سكريبت. فاستخدام جافا سكريبت أعقد وإنشاء المحتوى باستخدامها ينطوي على مشاكل أخرى تتعلق مثلًا بقدرة محركات البحث على قراءتها. لهذا سننقل في الفقرة القادمة إلى تطبيق آخر يُظهر استخدامًا عمليًا أكثر.
</p>

<p>
	<strong>ملاحظة</strong>: يمكنك إلقاء نظرة على <a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/document-manipulation/dom-example-manipulated.html" rel="external nofollow">التمرين التطبيقي المنتهي</a>) (أو تجربته <a href="https://mdn.github.io/learning-area/javascript/apis/document-manipulation/dom-example-manipulated.html" rel="external nofollow">مباشرة</a> أيضًا).
</p>

<h2 id="-3">
	تطبيق عملي: قائمة تسوّق ديناميكية
</h2>

<p>
	نريد في هذا التطبيق إنشاء قائمة تسوّق بسيطة تسمح بإضافة عناصر بشكل ديناميكي إلى القائمة باستخدام نموذج إدخال مكوّن من مربع نصي وزر. وينبغي عند إضافة مُدخل إلى المربع النصي والنقر على الزر:
</p>

<ul>
	<li>
		أن يُعرض العنصر الجديد ضمن القائمة.
	</li>
	<li>
		أن يجاور كل عنصر زر لتتمكن من حذف هذا العنصر عند النقر على الزر.
	</li>
	<li>
		أن يُفرّغ مربع اﻹدخال ويكتسب تركيز الدخل بعد إضافة عنصر إلى القائمة استعدادًا ﻹدخال عنصر آخر.
	</li>
</ul>

<p>
	قد يبدو الشكل النهائي للتطبيق كالتالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="152842" href="https://academy.hsoub.com/uploads/monthly_2024_07/03_shopping_ist.png.bb84460241e7a66eecb368d6367f2e83.png" rel=""><img alt="03 shopping ist" class="ipsImage ipsImage_thumbnailed" data-fileid="152842" data-unique="w0srgl43u" src="https://academy.hsoub.com/uploads/monthly_2024_07/03_shopping_ist.png.bb84460241e7a66eecb368d6367f2e83.png"> </a>
</p>

<p>
	وﻹكمال التمرين، اتبع الخطوات التالية وتأكد أن سلوك القائمة سيكون مطابقًا لما أشرنا إليه:
</p>

<ol>
	<li>
		نزّل نسخة عن <a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/document-manipulation/shopping-list.html" rel="external nofollow">ملف HTML الخاص بالتطبيق</a>، وضعها في مكان مناسب. وسترى أن الملف يضم تنسيقًا بسيطًا وعنصر <code>&lt;div&gt;</code> وعنوان وعنصر إدخال <code>&lt;input&gt;</code> وزر وقائمة فارغة وعنصر <code>&lt;script&gt;</code> تضع ضمنه كل الشيفرة التي تحتاجها.
	</li>
	<li>
		أنشئ ثلاثة متغيرات لتحمل مراجع إلى القائمة <code>&lt;ul&gt;</code> والزرين <code>&lt;button&gt;</code>.
	</li>
	<li>
		أنشئ دالة تعمل عند النقر على الزر.
	</li>
	<li>
		خزّن ضمن الدالة القيمة الحالية لعنصر اﻹدخال ضمن متغير.
	</li>
	<li>
		فرّغ عنصر اﻹدخال من المحتوى بإسناد القيمة <code>''</code> له.
	</li>
	<li>
		أنشئ ثلاثة عناصر جديدة هي عنصر قائمة <code>&lt;li&gt;</code> وعنصر <code>&lt;span&gt;</code> وعنصر <code>&lt;button&gt;</code>.
	</li>
	<li>
		ألحق الزر والعنصر <code>&lt;span&gt;</code> كابنين لعنصر القائمة.
	</li>
	<li>
		اجعل المحتوى النصي للعنصر <code>&lt;span&gt;</code> مطابقًا لقيمة عنصر اﻹدخال واجعل العبارة 'Delete' المحتوى النصي للزر.
	</li>
	<li>
		ألحق عنصر القائمة بالقائمة كعنصر ابن.
	</li>
	<li>
		اربط معالج حدث بزر الحذف كي يُحذف عنصر القائمة بأكمله (<code>&lt;li&gt;...&lt;/li&gt;</code>) عند النقر على الزر.
	</li>
	<li>
		استخدم أخيرًا التابع <code>()focus</code> لنقل تركيز الدخل إلى عنصر اﻹدخال كي يكون جاهزًا ﻹدخال العنصر التالي في القائمة.
	</li>
</ol>

<p>
	<strong>ملاحظة</strong>: يمكنك إلقاء نظرة على <a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/document-manipulation/shopping-list-finished.html" rel="external nofollow">التمرين التطبيقي المنتهي</a> (أو تجربته <a href="https://mdn.github.io/learning-area/javascript/apis/document-manipulation/shopping-list-finished.html" rel="external nofollow">مباشرة</a> أيضًا).
</p>

<h2 id="-4">
	الخلاصة
</h2>

<p>
	وصلنا اﻵن إلى نهاية مقالنا عن استخدام DOM وتعديلها من خلال جافا سكريبت، ومن المفترض في هذه المرحلة أن تكون قد أدركت اﻷجزاء المهمة من متصفح ويب فيما يتعلق بالتحكم بالمستندات وغيرها من النواحي التي تؤثر على تجربة مستخدم الويب. ومن المهم اﻷكثر أن تستوعب ماهية شجرة DOM وكيفية التعامل معها لتقديم ميزات مفيدة لتطبيقك.
</p>

<p>
	ترجمة -وبتصرف- للمقال: <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Manipulating_documents" rel="external nofollow">Manipulating documents</a>
</p>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-web-apis-r2355/" rel="">مدخل إلى واجهات الويب البرمجية Web APIs</a><span style="display: none;"> </span>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D8%A9-%D8%A7%D9%84%D8%B4%D8%AC%D8%B1%D9%8A%D8%A9-%D9%84%D9%80-dom-r646/" rel="">تعرف على البنية الشجرية لـ DOM</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-dom-r644/" rel="">مدخل إلى DOM</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/html/%D9%85%D9%83%D9%88%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D8%B4%D8%AC%D8%B1%D8%A9-dom-%D8%A7%D9%84%D8%AE%D9%81%D9%8A%D8%A9-r1353/" rel="">مكونات الويب: التعامل مع شجرة DOM الخفية</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/html/%D9%85%D9%83%D9%88%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%B9%D9%86%D8%A7%D8%B5%D8%B1-html-%D8%A7%D9%84%D9%85%D8%AE%D8%B5%D8%B5%D8%A9-%D9%88%D9%82%D9%88%D8%A7%D9%84%D8%A8%D9%87%D8%A7-r1352/" rel="">مكونات الويب: عناصر HTML المخصصة وقوالبها</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2360</guid><pubDate>Wed, 10 Jul 2024 15:08:02 +0000</pubDate></item><item><title>&#x645;&#x62F;&#x62E;&#x644; &#x625;&#x644;&#x649; &#x648;&#x627;&#x62C;&#x647;&#x627;&#x62A; &#x627;&#x644;&#x648;&#x64A;&#x628; &#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x64A;&#x629; Web APIs</title><link>https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-web-apis-r2355/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_06/Introduction-to-web-APIs.png.210e17f55abf55fe5d90a3fd8d7c4fbc.png" /></p>
<p>
	نلقي نظرة في هذا المقال على الواجهات البرمجية في جافا سكريبت، ونشرح ماهيتها وكيفية عملها، وآلية استخدامها في الشيفرة و أساليب هيكلتها. كما نلقي نظرة على اﻷصناف اﻷساسية للواجهات البرمجية، وإمكانيات استخدامها.
</p>

<p>
	ننصحك قبل المتابعة في قراءة هذه المقالات أن:
</p>

<ul>
	<li>
		<p>
			تكون ملمًا بلغتي <a href="HTTPS://wiki.hsoub.com/HTML" rel="external">HTML</a> و <a href="HTTPS://wiki.hsoub.com/CSS" rel="external">CSS</a>.
		</p>
	</li>
	<li>
		<p>
			تكون ملمًا بلغة <a href="HTTPS://wiki.hsoub.com/JavaScript" rel="external">جافا سكريبت</a>.
		</p>
	</li>
	<li>
		<p>
			تطلع على سلاسل المقالات السابقة التي ناقشت <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%B9%D8%B1%D9%91%D9%81-%D8%B9%D9%84%D9%89-%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D9%84%D8%BA%D8%A9-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D9%85%D9%86-%D9%85%D9%86%D8%B8%D9%88%D8%B1-%D8%B9%D8%A7%D9%85-r2266/" rel="">أساسيات جافا سكريبت</a> و<a href="https://academy.hsoub.com/programming/javascript/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2305/" rel="">الكائنات في جافا سكريبت</a>.
		</p>
	</li>
</ul>

<h2 id="">
	ما هي الواجهات البرمجية؟
</h2>

<p>
	واجهات التطبيقات البرمجية أو <a href="https://academy.hsoub.com/programming/general/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-r1314/" rel="">الواجهات البرمجية <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a> هي بُنىً برمجية تقدمها لغات البرمجة لتتيح للمطورين إنشاء مهام معقدة بسهولة أكبر. إذ تساهم في تحييدهم عن الشيفرات المعقدة وتقدم صياغة قواعدية أوضح وأسهل.
</p>

<p>
	فكّر كمثال عن الواجهة البرمجية بالتغذية الكهربائية في منزلك، فإن أردت تشغيل جهاز كهربائي ستضع قابس الجهاز في مأخذ الكهرباء المخصص وسيعمل الجهاز، ولن تضطر بالطبع إلى إجراء التوصيلات الكهربائية إلى مصدر التغذية مباشرة، على الرغم من إمكانية ذلك، لكن إن لم تكن كهربائيًا مختصًا فالأمر خطر وصعب.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="152026" href="https://academy.hsoub.com/uploads/monthly_2024_06/01_plug_socket.png.0884878604d06e6b69e830f35af325c6.png" rel=""><img alt="01 plug socket" class="ipsImage ipsImage_thumbnailed" data-fileid="152026" data-unique="ucytgqv82" src="https://academy.hsoub.com/uploads/monthly_2024_06/01_plug_socket.png.0884878604d06e6b69e830f35af325c6.png"> </a>
</p>

<p>
	وبالمثل، لو أردت برمجة رسوميات ثلاثية اﻷبعاد، من اﻷسهل حينها استخدام واجهة برمجية مكتوبة بلغة عالية المستوى مثل جافا سكريبت أو بايثون بدلًا من كتبتها باستخدام لغات أكثر تعقيدًا تتحكم مباشرة بوحدة المعالجة الرسومية GPU أو غيرها من الوظائف الرسومية مثل C و ++C.
</p>

<h2 id="-1">
	واجهات جافا سكريبت البرمجية في طرف العميل
</h2>

<p>
	تقدم لغة جافا سكريبت في طرف العميل تحديدًا العديد من الواجهات البرمجية المهمة. ولا تُعد هذه الواجهات جزءًا من اللغة بحد ذاتها وإنما بُنيت على أساس جافا سكريبت لزيادة إمكانات شيفرتها، وتصنف هذه الواجهات عمومًا ضمن فئتين رئيسيتين:
</p>

<ul>
	<li>
		<strong>واجهات المتصفح البرمجية Browser <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></strong>: وهي برمجيات مضمّنة في المتصفح وقادرة على عرض البيانات الموجودة في المتصفح و البيئة الحاسوبية التي تستضيفه، وتنفيذ عمليات مركّبة على تلك البيانات. وكمثال على هذه الواجهات نذكر <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API" rel="external nofollow">Web Audio <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a> التي تقدم هيكلية لجافا سكريبت تساعد في التعامل مع الصوتيات في المتصفح، علمًا أن المتصفح يتعامل مع الموارد الصوتية عمليًا من خلال شيفرة معقدة مكتوبة بلغات أخرى مثل (++C و Rust)، مع ذلك ما تقدمه تلك الواجهة هو تفادي تعقيد تلك العمليات.
	</li>
	<li>
		<strong>واجهات يقدمها طرف آخر Third-party APIs</strong>: لا تُضمّن هذه الواجهات ضمن المتصفح افتراضيًا، وعليك الحصول على شيفرتها والمعلومات المتعلقة بها من الويب. فالواجهة <a href="https://developers.google.com/maps/documentation/javascript" rel="external nofollow">Google Maps <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a> على سبيل المثال تمنحك القدرة على عرض خرائط تفاعلية على موقعك، وتقدم هيكلية مخصصة تساعدك في استعلام خدمة خرائط جوجل Google Maps للحصول على معلومات محددة.
	</li>
</ul>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="152027" href="https://academy.hsoub.com/uploads/monthly_2024_06/02_browser_third_party_APIs.png.a8cca5df42b29c4688e36450bcde5fb0.png" rel=""><img alt="02 browser third party apis" class="ipsImage ipsImage_thumbnailed" data-fileid="152027" data-unique="9zcxir7ba" src="https://academy.hsoub.com/uploads/monthly_2024_06/02_browser_third_party_APIs.png.a8cca5df42b29c4688e36450bcde5fb0.png"> </a>
</p>

<h3 id="-2">
	العلاقة بين جافا سكريبت و الواجهات البرمجية و أدوات جافا سكريبت
</h3>

<p>
	تحدثنا في الفقرة السابقة عن طبيعة واجهات جافا سكريبت البرمجية في طرف العميل وعلاقتها بلغة جافا سكريبت، لهذا دعونا نسترجع ما شرحناها لتوضيح الأمر أكثر واﻹشارة إلى أدوات جافا سكريبت الملائمة:
</p>

<ul>
	<li>
		<strong>لغة جافا سكريبت</strong>: وهي <a href="https://academy.hsoub.com/programming/general/%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9/#%D9%84%D8%BA%D8%A7%D8%AA-%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D8%B3%D9%83%D8%B1%D8%A8%D8%AA" rel="">لغة سكريبت</a> عالية المستوى مضمنة ضمن المتصفحات، وتسمح لك بتنفيذ مهام مختلفة في صفحات وتطبيقات الويب. وتجدر اﻹشارة أن جافا سكريبت متاحة أيضًا في بيئات أخرى غير بيئة المتصفحات مثل <a href="https://wiki.hsoub.com/Node.js" rel="external">بيئة Node.js</a>.
	</li>
	<li>
		<strong>واجهات المتصفح البرمجية</strong>: وهي هيكليات مضمنة في المتصفح أساسها جافا سكريبت وتسمح بتنفيذ وظائف مختلفة بسهولة أكبر.
	</li>
	<li>
		<strong>واجهات برمجية يقدمها طرف آخر</strong>: وهي هيكليات مبنية على منصات مختلفة (مثل فيسبوك) تتيح لك استخدام بعض وظائف هذه المنصة في موقعك الخاص.
	</li>
	<li>
		<strong>مكتبات جافا سكريبت</strong>: وهي عادة ملف جافا سكريبت أو أكثر تتضمن دوال مخصصة يمكنك ربطها بصفحات الويب الخاصة بك لتسريع تنفيذ أو استخدام وظائف معينة. من اﻷمثلة عليها المكتبات jQuery و Mootools و React.
	</li>
	<li>
		<strong>إطارات عمل جافا سكريبت</strong>: وهي تقنية أكثر تقدمًا من المكتبات مثل Angular و Ember وتتكون عادة من حزم HTML و CSS وجافا سكريبت وتقنيات أخرى تُثبّت لتساعدك في كتابة تطبيقات ويب كاملة من الصفر. أما الاختلاف الجوهري بين المكتبات وإطارات العمل فهو "التحكم المعكوس Inversion of Control". إذ يتحكم المطور باستدعاء دوال من مكتبة، لكن إطار العمل هو من يستدعي شيفرة المطور.
	</li>
</ul>

<h2 id="-3">
	ما الذي يمكن أن تقدّمه الواجهات البرمجية؟
</h2>

<p>
	ستجد كمًا هائلًا من الواجهات البرمجية المتاحة التي تسمح لك بتنفيذ مهام ووظائف متنوعة في شيفرتك، لهذا نلقي نظرة في الفقرات التالية على أهم هذه الواجهات ووظائفها.
</p>

<h3 id="-4">
	واجهات المتصفح البرمجية اﻷكثر شيوعًا
</h3>

<p>
	من أكثر فئات واجهات المتصفح البرمجية شيوعًا والتي سنشرحها في هذه المقالات نجد:
</p>

<h4 id="-5">
	الواجهات البرمجية التي تتعامل مع المستندات
</h4>

<p>
	تُحمّل هذه الواجهات ضمن المتصفح ومن أكثرها وضوحًا الواجهة DOM التي تتيح لك العمل مع HTML و CSS مثل إنشاء العناصر وإزالتها وتنسيقها ديناميكيًا وتطبيق التغييرات على صفحة الويب وغيرها. فكلما رأيت نافذ منبثقة من الصفحة أو لاحظت تغيرًا في المحتوى المعروض فهو من فعل واجهة DOM.
</p>

<h4 id="-6">
	الواجهات التي تحضر البيانات من الخادم
</h4>

<p>
	وتستخدم هذه الواجهات مثلًا في تحديث محتوى جزء من الصفحة فقط، ولهذا اﻷمر الذي يبدو بسيطًا أثرًا هائلًا على أداء وسلوك المواقع. فلو أردت أن تحدث فقط قائمة البضائع في متجر أو عرض قائمة بالقصص الجديدة المتوفرة وتمكنت من تنفيذ اﻷمر مباشرة دون الحاجة إلى إعادة تحميل الصفحة، سيبدو حينها الموقع أو التطبيق أكثر تجاوبًا. ومن الواجهات الرئيسية المستخدمة في تحقيق ذلك نجد <code>Fetch</code> وكذلك <code>XMLHttpRequest</code> التي تستخدمها الشيفرات اﻷقدم. وقد تمر بالمصطلح AJAX الذي يصف تمامًا هذه التقنية القديمة.
</p>

<h4 id="-7">
	الواجهات البرمجية الخاصة بالرسوميات
</h4>

<p>
	وهي واجهات مدعومة جيدًا في معظم المتصفحات الحديثة وأكثرها شهرة Canvas و WebGL. إذ تسمح هذه الواجهات بتغيير بيانات كل بكسل من بكسلات عنصر HTML المخصص <code>&lt;canvas&gt;</code> لتكوين مشاهد ثنائية وثلاثية اﻷبعاد. وتتمكن أيضًا من رسم أشكال ضمن هذا العنصر مثل المربعات والدوائر أو عرض صورة وتطبيق مرشحات عليها كأن تحولها إلى اللون الرمادي وذلك باستخدام الواجهة البرمجية Canvas. وتتيح لك الواجهة WebGL إنشاء مشاهد ثلاثية أبعاد مركّبة مع إضاءة وخامة texture وغيرها. وتُستخدم هذه الواجهات عادة مع واجهات أخرى لتنفيذ حلقات رسومية للتحريك مثل <a href="https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame" rel="external nofollow"><code>()window.requestAnimationFrame</code></a> وغيرها كي تحدّث باستمرار المشهد كما في الرسوم المتحركة واﻷلعاب.
</p>

<h4 id="-8">
	الواجهات البرمجية الخاصة بالصوت والفيديو
</h4>

<p>
	تسمح لك هذه الواجهات بتنفيذ الكثير من المهام المتعلقة بتشغيل الوسائط المتعدد وإنشاء واجهات تحكم مخصصة للتعامل مع ملفات الصوت والفيديو وعرض معلومات نصية عن المقاطع والمسارات إضافة إلى عناوينها وكلماتها. كما تساعدك في التقاط مقاطع فيديو باستخدام كاميرا الويب والتعديل عليها بمساعدة الواجهة البرمجية Canvas أو عرضها على حاسوب شخص آخر أو إضافة تأثيرات إلى المسارات الصوتية. نذكر من هذه الواجهات <code>HTMLMediaElement</code> و Web Audio <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> و WebRTC
</p>

<h4 id="-9">
	الواجهات البرمجية التي تتعامل مع الأجهزة
</h4>

<p>
	تمكنك هذه الواجهات من التفاعل مع العتاد الصلب لجهازك مثل الوصول إلى شريحة GPS لتحديد موقع المستخدم من خلال الواجهة Geolocation <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>.
</p>

<h4 id="-10">
	الواجهات البرمجية التي تتعامل مع تخزين البيانات في طرف العميل
</h4>

<p>
	تمكنك هذه الواجهات من تخزين البيانات على جهاز العميل، وبالتالي ستكون قادرًا على إنشاء تطبيق يحتفظ بحالته أثناء تحميل الصفحات المختلفة وحتى العمل دون الإتصال باﻹنترنت. وستجد العديد من الخيارات المتاحة مثل التخزين البسيط المبني على مبدأ اسم/قيمة من خلال الواجهة Web Storage <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> أو تخزين البيانات ضمن قواعد بيانات أعقد مثل الواجهة IndexedDB <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>.
</p>

<h3 id="-11">
	الواجهات البرمجية التي تطورها أطراف أخرى
</h3>

<p>
	تتنوع هذه الواجهات وتغطي استخدامات متنوعة، لكننا سنذكر منها الأكثر شعبية، فقد تضطر إلى استخدامها عاجلًا أم آجلًا.
</p>

<ul>
	<li>
		<strong>واجهات الخرائط</strong>: مثل <a href="https://developer.mapquest.com/" rel="external nofollow">Mapquest</a> و <a href="https://developers.google.com/maps/" rel="external nofollow">Google Maps <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a>، وتسمح لك بتنفيذ الكثير من اﻷشياء التي تتعلق بالخرائط ضمن موقعك.
	</li>
	<li>
		<strong>مجموعة واجهات فيسبوك <a href="https://developers.facebook.com/docs/" rel="external nofollow">Facebook suite of APIs</a></strong>: تمكنك هذه الواجهات من استخدام اﻷجزاء المختلفة لمنظومة فيسبوك لدعم تطبيقك مثل تقديم آلية لتسجيل الدخول اعتمادًا على حساب فيسبوك واستخدام بوابات الدفع اﻹلكتروني في تطبيقك وإدارة حملات دعائية وغيرها.
	</li>
	<li>
		<strong>واجهات تلغرام <a href="https://core.telegram.org/api" rel="external nofollow">Telegram APIs</a></strong>: وتتيح لك إدراج محتوى أقنية تلغرام في موقعك، وتقديم دعم للبرامج اﻵلية bots.
	</li>
	<li>
		<strong>واجهة يوتيوب <a href="https://developers.google.com/youtube/" rel="external nofollow">YouTube <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a></strong>: وتتيح لك إدراج مقاطع فيديو من يوتيوب في موقعك أو البحث ضمن يوتيوب أو إنشاء قوائم تشغيل وغيرها.
	</li>
	<li>
		<strong>الواجهة <a href="https://www.twilio.com/docs" rel="external nofollow">Twilio <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a></strong>: وتقدم إطار عمل لبناء وظائف تتعلق بمكالمات الصوت والفيديو في تطبيقك، وإرسال رسائل SMS و MMS من التطبيق وغيرها.
	</li>
	<li>
		<strong>الواجهة <a href="https://disqus.com/api/docs/" rel="external nofollow">Disqus <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a></strong>: تزودك بمنصةّ للتعليقات يمكن إضافتها إلى موقعك اﻹلكتروني.
	</li>
	<li>
		<strong>الواجهة <a href="https://docs.joinmastodon.org/api/" rel="external nofollow">Mastodon <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a></strong>: تساعدك في التعامل مع ميزات شبكة Mastodon للتواصل الاجتماعي برمجيًا.
	</li>
	<li>
		<strong>الواجهة <a href="https://ifttt.com/developers" rel="external nofollow">IFTTT <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a></strong>: وتساعدك على دمج عدة واجهات برمجية ضمن منصة واحدة.
	</li>
</ul>

<h2 id="-12">
	كيف تعمل الواجهات البرمجية؟
</h2>

<p>
	توجد بعض الاختلافات في طريقة عمل واجهات جافا سكريبت البرمجية، إلا أنها تشترك بالعديد من الميزات وسمات العمل. وسنشرح أوجه التشابه في الفقرات التالية.
</p>

<h3 id="-13">
	الاعتماد على الكائنات
</h3>

<p>
	تتواصل شيفرتك مع الواجهات البرمجية باستخدام كائن أو أكثر من كائنات جافا سكريبت. وتعمل هذه الكائنات كحاويات لتخزين البيانات التي تستخدمها الواجهة (من خلال خاصيات الكائن properties) وكوسيلة لإيصال الوظائف التي تقدمها الواجهة البرمجية (من خلال توابع الكائن methods).
</p>

<p>
	<strong>ملاحظة</strong>: إن لم تكن على دراية بطريقة عمل الكائنات، ننصحك بالعودة إلى مقال "<a href="https://academy.hsoub.com/programming/javascript/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%B9%D9%85%D9%84-%D9%85%D8%B9-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2305/" rel="">أساسيات العمل مع الكائنات في جافا سكريبت</a>".
</p>

<p>
	لو عدنا اﻵن إلى واجهة الويب الخاصة بالصوتيات Web Audio <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> وهي واجهات معقدة نوعًا ما، ستجد أنها تضم عددًا من الكائنات أكثرها وضوحًا:
</p>

<ul>
	<li>
		<code>AudioContext</code>: ويقدم وسيلة للتعامل مع تشغيل الصوتيات ضمن المتصفح ويتضمن عدة توابع وخاصيات تساعدك في ذلك.
	</li>
	<li>
		<code>MediaElementAudioSourceNode</code>: ويمثّل العنصر <code>&lt;audio&gt;</code> الذي يضم المقطع الصوتي المطلوب تشغيله ضمن الكائن <code>AudioContext</code>.
	</li>
	<li>
		<code>AudioDestinationNode</code>: ويمثّل الطرفية التي تصدر الصوت فعليًا في جهازك، وعادة ما تكون مكبّر الصوت أو سماعات الرأس.
	</li>
</ul>

<p>
	لكن كيف تتفاعل هذه الكائنات مع بعضها؟ ألق نظرة على مثال <a href="https://github.com/mdn/learning-area/blob/main/javascript/apis/introduction/web-audio/index.html" rel="external nofollow">تشغيل الصوتيات في المتصفح</a> (بإمكانك تجربة التطبيق <a href="https://mdn.github.io/learning-area/javascript/apis/introduction/web-audio/" rel="external nofollow">مباشرة</a> أيضًا) وستجد شيفرة HTML التالية:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9068_9" style=""><span class="tag">&lt;audio</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"outfoxing.mp3"</span><span class="tag">&gt;&lt;/audio&gt;</span><span class="pln">

</span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"paused"</span><span class="tag">&gt;</span><span class="pln">Play</span><span class="tag">&lt;/button&gt;</span><span class="pln">
</span><span class="tag">&lt;br</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
</span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"range"</span><span class="pln"> </span><span class="atn">min</span><span class="pun">=</span><span class="atv">"0"</span><span class="pln"> </span><span class="atn">max</span><span class="pun">=</span><span class="atv">"1"</span><span class="pln"> </span><span class="atn">step</span><span class="pun">=</span><span class="atv">"0.01"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"1"</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"volume"</span><span class="pln"> </span><span class="tag">/&gt;</span></pre>

<p>
	أول ما فعلناه هو استخدام العنصر <code>&lt;audio&gt;</code> الذي يرتبط بمقطع صوتي MP3، ولم نضف أية أدوات تحكم افتراضية بالصوت مضمّنة في المتصفح. كما أضفنا بعد ذلك العنصر <code>&lt;button&gt;</code> الذي نستخدمه لتشغيل وإيقاف المقطع الصوتي، وعنصر إدخال <code>&lt;input&gt;</code> من النوع "مجال <code>"type="range</code>" الذي نستخدمه للتحكم بمستوى الصوت أثناء التشغيل.
</p>

<p>
	ونبدأ شيفرة جافا سكريبت بإنشاء نسخة عن الكائن <code>AudioContext</code> للتعامل مع الملف الصوتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9068_11" style=""><span class="kwd">const</span><span class="pln"> </span><span class="typ">AudioContext</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> window</span><span class="pun">.</span><span class="typ">AudioContext</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> window</span><span class="pun">.</span><span class="pln">webkitAudioContext</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> audioCtx </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">AudioContext</span><span class="pun">();</span></pre>

<p>
	ننشئ بعد ذلك ثوابت لتخزين مراجع إلى العناصر <code>&lt;audio&gt;</code> و <code>&lt;button&gt;</code> و <code>&lt;input&gt;</code> ومن ثم نستخدم التابع <code>()AudioContext.createMediaElementSource</code> ﻹنشاء الكائن <code>MediaElementAudioSourceNode</code> الذي يمثّل بدوره المصدر الذي ينبع منه الصوت وهو العنصر <code>&lt;audio&gt;</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9068_13" style=""><span class="kwd">const</span><span class="pln"> audioElement </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"audio"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> playBtn </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"button"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> volumeSlider </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">".volume"</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> audioSource </span><span class="pun">=</span><span class="pln"> audioCtx</span><span class="pun">.</span><span class="pln">createMediaElementSource</span><span class="pun">(</span><span class="pln">audioElement</span><span class="pun">);</span></pre>

<p>
	نستخدم تاليًا معالجي أحداث أحدهما للتنقل بين وضعي التشغيل واﻹيقاف عند النقر على الزر واﻵخر ﻹعادة الملف الصوتي إلى بدايته عند انتهاء تشغيله.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9068_15" style=""><span class="com">// تشغيل وايقاف الملف الصوتي</span><span class="pln">
playBtn</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"click"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="com">// check if context is in suspended state (autoplay policy)</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">audioCtx</span><span class="pun">.</span><span class="pln">state </span><span class="pun">===</span><span class="pln"> </span><span class="str">"suspended"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    audioCtx</span><span class="pun">.</span><span class="pln">resume</span><span class="pun">();</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">

 </span><span class="com">// إن توقف تشغيل الملف أعد تشغيله</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">playBtn</span><span class="pun">.</span><span class="pln">getAttribute</span><span class="pun">(</span><span class="str">"class"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">===</span><span class="pln"> </span><span class="str">"paused"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    audioElement</span><span class="pun">.</span><span class="pln">play</span><span class="pun">();</span><span class="pln">
    playBtn</span><span class="pun">.</span><span class="pln">setAttribute</span><span class="pun">(</span><span class="str">"class"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"playing"</span><span class="pun">);</span><span class="pln">
    playBtn</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Pause"</span><span class="pun">;</span><span class="pln">
    </span><span class="com">// إن كان الملف في وضع التشغيل أوقفه</span><span class="pln">
 </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">playBtn</span><span class="pun">.</span><span class="pln">getAttribute</span><span class="pun">(</span><span class="str">"class"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">===</span><span class="pln"> </span><span class="str">"playing"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    audioElement</span><span class="pun">.</span><span class="pln">pause</span><span class="pun">();</span><span class="pln">
    playBtn</span><span class="pun">.</span><span class="pln">setAttribute</span><span class="pun">(</span><span class="str">"class"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"paused"</span><span class="pun">);</span><span class="pln">
    playBtn</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Play"</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="com">// عند إنتهاء اﻷغنية</span><span class="pln">
audioElement</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"ended"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 playBtn</span><span class="pun">.</span><span class="pln">setAttribute</span><span class="pun">(</span><span class="str">"class"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"paused"</span><span class="pun">);</span><span class="pln">
 playBtn</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Play"</span><span class="pun">;</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	<strong>ملاحظة</strong>: قد يلاحظ البعض أن التابعين <code>()play</code> و <code>()pause</code> المستخدمان في تشغيل وايقاف الملف الصوتي ليسا جزءًا من الواجهة Web Audio <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>، بل جزءًا من الواجهة البرمجية <code>HTMLMediaElement</code> التي تختلف قليلًا عنها.
</p>

<p>
	ننشئ بعد ذلك الكائن <code>GainNode</code> باستخدام التابع <code>()AudioContext.createGain</code> وذلك لضبط مستوى الصوت، ثم ننشئ معالج حدث آخر يغيّر قيمة مستوى الصوت كلما تغير موقع الزالقة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9068_17" style=""><span class="com">// مستوى الصوت</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> gainNode </span><span class="pun">=</span><span class="pln"> audioCtx</span><span class="pun">.</span><span class="pln">createGain</span><span class="pun">();</span><span class="pln">

volumeSlider</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"input"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 gainNode</span><span class="pun">.</span><span class="pln">gain</span><span class="pun">.</span><span class="pln">value </span><span class="pun">=</span><span class="pln"> volumeSlider</span><span class="pun">.</span><span class="pln">value</span><span class="pun">;</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	آخر ما نفعله ﻹنجاز مثالنا هو ربط العقد المختلفة معًا عن طريق التابع <code>()AudioNode.connect</code> الموجود في كل عقدة
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9068_19" style=""><span class="pln">audioSource</span><span class="pun">.</span><span class="pln">connect</span><span class="pun">(</span><span class="pln">gainNode</span><span class="pun">).</span><span class="pln">connect</span><span class="pun">(</span><span class="pln">audioCtx</span><span class="pun">.</span><span class="pln">destination</span><span class="pun">);</span></pre>

<p>
	نبدأ بمصدر الصوت ومن ثم نربط به عقدة التحكم بمستوى الصوت والتي تتصل بدورها إلى العقدة التي تشغّل الصوت فعليًا في جهازك (تمثّل الخاصية <code>AudioContext.destination</code> الوجهة الافتراضية <code>AudioDestinationNode</code> المتاحة على حاسوبك لتشغيل الصوت مثل المكبرات).
</p>

<h3 id="-14">
	لجميع الواجهات مداخل مميزة
</h3>

<p>
	عليك أن تعرف تمامًا مداخل entry points الواجهات البرمجية قبل أن تتعامل معها، وهذا اﻷمر بسيط نوعًا ما في الواجهة Web Audio <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>، فمدخلها هو الكائن <code>AudioContext</code> الذي تحتاجه للتعامل مع أي ملف صوتي.
</p>

<p>
	وللواجهة البرمجية DOM مدخل بسيط أيضًا، إذ تتواجد معظم ميزاتها ضمن الكائن <code>Document</code>، أو ضمن نسخة عن كان HTML الذي نريد التعامل معه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9068_21" style=""><span class="kwd">const</span><span class="pln"> em </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">"em"</span><span class="pun">);</span><span class="pln"> </span><span class="com">//إنشاء عنصر جديد</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> para </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"p"</span><span class="pun">);</span><span class="pln"> </span><span class="com">//موجود &lt;p&gt; مرجع إلى عنصر</span><span class="pln">
em</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Hello there!"</span><span class="pun">;</span><span class="pln"> </span><span class="com">//em إسناد محتوى إلى المتغير</span><span class="pln">
para</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">em</span><span class="pun">);</span><span class="pln"> </span><span class="com">//para ضمن المتغير em وضع</span></pre>

<p>
	وتعتمد الواجهة أيضًا على كائن سياق context object للتعامل مع المقادير المختلفة. وعلى الرغم من أن السياق في هذه الحالة رسومي وليس صوتي، إلا أن كائن السياق الخاص سيُنشأ من خلال مرجع إلى العنصر <code>&lt;canvas&gt;</code> الذي تريد الرسم ضمنه ومن ثم تستدعي التابع <code>()HTMLCanvasElement.getContext</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9068_23" style=""><span class="kwd">const</span><span class="pln"> canvas </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"canvas"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> ctx </span><span class="pun">=</span><span class="pln"> canvas</span><span class="pun">.</span><span class="pln">getContext</span><span class="pun">(</span><span class="str">"2d"</span><span class="pun">);</span></pre>

<p>
	وكل ما تحتاجه بعد ذلك للعمل مع canvas هو استدعاء الخاصيات والتوابع لكائن السياق الرسومي (وهو في هذه الحالة نسخة عن <code>CanvasRenderingContext2D</code><span class="ipsEmoji">?</span>
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9068_25" style=""><span class="typ">Ball</span><span class="pun">.</span><span class="pln">prototype</span><span class="pun">.</span><span class="pln">draw </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 ctx</span><span class="pun">.</span><span class="pln">beginPath</span><span class="pun">();</span><span class="pln">
 ctx</span><span class="pun">.</span><span class="pln">fillStyle </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">color</span><span class="pun">;</span><span class="pln">
 ctx</span><span class="pun">.</span><span class="pln">arc</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">y</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">size</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">PI</span><span class="pun">);</span><span class="pln">
 ctx</span><span class="pun">.</span><span class="pln">fill</span><span class="pun">();</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	<strong>ملاحظة</strong>: بإمكانك رؤية هذه الشيفرة وهي تعمل ضمن <a href="https://academy.hsoub.com/programming/javascript/%D8%A8%D9%86%D8%A7%D8%A1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D8%A7%D9%84%D9%83%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B1%D8%AA%D8%AF%D8%A9-%D8%A7%D9%84%D9%85%D9%84%D9%88%D9%86%D8%A9-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%83%D8%A7%D8%A6%D9%86%D8%A7%D8%AA-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D8%A7%D9%84%D8%AC%D8%B2%D8%A1-%D8%A7%D9%84%D8%A3%D9%88%D9%84-r2323/" rel="">تطبيق الكرات القافزة التجريبي</a> (وبإمكانك تجربته <a href="https://mdn.github.io/learning-area/javascript/apis/introduction/bouncing-balls.html" rel="external nofollow">مباشرة</a> أيضًا)
</p>

<h3 id="-15">
	تستخدم اﻷحداث للتعامل مع تغيرات الحالة
</h3>

<p>
	ناقشنا في مقال<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%EF%BB%B7%D8%AD%D8%AF%D8%A7%D8%AB-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2306/" rel="">مدخل إلى اﻷحداث في جافا سكريبت</a> موضوع اﻷحداث والتعامل معها وكيفية استخدامها في الشيفرة. فإن لم تكن على دراية بها، ننصحك بالعودة إلى هذا المقال والعمل عليه قبل المتابعة.
</p>

<p>
	لا تتضمن بعض واجهات الويب البرمجية أحداثًا ويضم بعضها اﻵخر القليل منها، ولقد رأينا عمل عدد من معالجات اﻷحداث في مثالنا عن الواجهة Web Audio APi:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9068_30" style=""><span class="com">// play/pause audio</span><span class="pln">
playBtn</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"click"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 </span><span class="com">// check if context is in suspended state (autoplay policy)</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">audioCtx</span><span class="pun">.</span><span class="pln">state </span><span class="pun">===</span><span class="pln"> </span><span class="str">"suspended"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    audioCtx</span><span class="pun">.</span><span class="pln">resume</span><span class="pun">();</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">

 </span><span class="com">// if track is stopped, play it</span><span class="pln">
 </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">playBtn</span><span class="pun">.</span><span class="pln">getAttribute</span><span class="pun">(</span><span class="str">"class"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">===</span><span class="pln"> </span><span class="str">"paused"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    audioElement</span><span class="pun">.</span><span class="pln">play</span><span class="pun">();</span><span class="pln">
    playBtn</span><span class="pun">.</span><span class="pln">setAttribute</span><span class="pun">(</span><span class="str">"class"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"playing"</span><span class="pun">);</span><span class="pln">
    playBtn</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Pause"</span><span class="pun">;</span><span class="pln">
    </span><span class="com">// if track is playing, stop it</span><span class="pln">
 </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">playBtn</span><span class="pun">.</span><span class="pln">getAttribute</span><span class="pun">(</span><span class="str">"class"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">===</span><span class="pln"> </span><span class="str">"playing"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    audioElement</span><span class="pun">.</span><span class="pln">pause</span><span class="pun">();</span><span class="pln">
    playBtn</span><span class="pun">.</span><span class="pln">setAttribute</span><span class="pun">(</span><span class="str">"class"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"paused"</span><span class="pun">);</span><span class="pln">
    playBtn</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Play"</span><span class="pun">;</span><span class="pln">
 </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="com">// if track ends</span><span class="pln">
audioElement</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"ended"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 playBtn</span><span class="pun">.</span><span class="pln">setAttribute</span><span class="pun">(</span><span class="str">"class"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"paused"</span><span class="pun">);</span><span class="pln">
 playBtn</span><span class="pun">.</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Play"</span><span class="pun">;</span><span class="pln">
</span><span class="pun">});</span></pre>

<h3 id="-16">
	تمتلك آليات إضافية لمسائل اﻷمان عند الحاجة
</h3>

<p>
	لواجهات الويب البرمجية الاعتبارات اﻷمنية نفسها التي نواجهها في جافا سكريبت وفي غيرها من تقنيات الويب (مثل سياسة اﻷصل المشترك same-origin policy) لكنها تتمتع في بعض اﻷحيان بميزات أمنية إضافة. فلن تعمل على سبيل المثال بعض واجهات الويب البرمجية الحديثة سوى في الصفحات التي تُخدَّم وفق بروتوكول HTTPS وذلك لإمكانية نقلها بيانات حساسة (من اﻷمثلة عليها عمال الخدمة service workers والواجهة <code>Push</code>).
</p>

<p>
	وإضافة إلى ذلك، تطلب بعض الواجهات الحصول على بعض اﻷدونات من المستخدم عندما يستدعيها من خلال الشيفرة مثل الواجهة Notifications <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> التي تطلب من المستخدم إذنًا لعرض النوافذ المنبثقة pop-ups:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="152028" href="https://academy.hsoub.com/uploads/monthly_2024_06/03_notification-permission.png.6ec06e85fe269be25f454a6d31b4a0a2.png" rel=""><img alt="03 notification permission" class="ipsImage ipsImage_thumbnailed" data-fileid="152028" data-unique="nbfn8szem" src="https://academy.hsoub.com/uploads/monthly_2024_06/03_notification-permission.png.6ec06e85fe269be25f454a6d31b4a0a2.png"> </a>
</p>

<p>
	وتخضع الواجهتين Web Audio و <code>HTMLMediaElement</code> إلى آلية أمنية تُدعى سياسة التشغيل التلقائي autoplay policy، وتعني مبدئيًا أنك لن تستطيع تشغيل الملف الصوتي تلقائيًا عند تحميل الصفحة، وعليك أن تدفع المستخدم إلى تشغيل المقطع بالنقر على زر مثلًا. والسبب في ذلك أن تشغيل المقطع الصوتي تلقائيًا أمر مزعج ولا ينبغي فرض اﻷمر على المستخدم.
</p>

<p>
	<strong>ملاحظة</strong>: تبعًا لصرامة المتصفح الذي تستخدمه، فقد لا تسمح سياسات أمنية كالتي ذكرناها تشغيل المثال السابق محليًا (على حاسوبك) إن حمّلت ملف المثال وحاولت تشغيله بدلًا من تشغيله مباشرة على الخادم.
</p>

<h2 id="-17">
	الخلاصة
</h2>

<p>
	من المفترض في نهاية هذا المقال أن تمتلك فكرة لا بأس بها عن الواجهات البرمجية وفوائدها عندما تستخدمها في جافا سكريبت. وأن تكون قد أصبحت جاهزًا نوعًا ما لاستخدام الواجهات التي شرحناها لتنفيذ مهام مختلفة، وهذا ما سنراه في مقالات لاحقة.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Introduction" rel="external nofollow">Introduction to web APIs</a>
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%AD%D8%B1%D9%8A%D9%83-%D8%B3%D9%84%D8%B3%D9%84%D8%A9-%D9%85%D9%86-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D8%A7%D8%AA-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%88%D8%B9%D9%88%D8%AF-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2350/" rel="">تحريك سلسلة رسوم متحركة باستخدام الوعود في جافا سكريبت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/general/%D9%85%D8%A7-%D9%87%D9%8A-%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%84%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-api%D8%9F-r1512/" rel="">ما هي الواجهة البرمجية للتطبيقات <abbr title="Application Programming Interface | واجهة برمجية">API</abbr>؟</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/apps/web/%D8%AF%D9%84%D9%8A%D9%84-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-chatgpt-api-%D9%84%D8%AA%D8%AD%D8%B3%D9%8A%D9%86-%D8%AE%D8%AF%D9%85%D8%A7%D8%AA%D9%83-%D8%B9%D8%A8%D8%B1-%D8%A7%D9%84%D8%A5%D9%86%D8%AA%D8%B1%D9%86%D8%AA-r909/" rel="">دليل استخدام ChatGPT <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> لتحسين خدماتك عبر الإنترنت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/nodejs/express/%D8%AF%D9%84%D9%8A%D9%84%D9%83-%D9%84%D8%B1%D8%A8%D8%B7-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-openai-api-%D9%85%D8%B9-nodejs-r2233/" rel="">دليلك لربط واجهة OpenAI <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> مع Node.js</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/php/laravel/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-api-%D9%81%D9%8A-laravel-5-r232/" rel="">إنشاء واجهة برمجية <abbr title="Application Programming Interface | واجهة برمجية">API</abbr> في Laravel</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2355</guid><pubDate>Wed, 03 Jul 2024 15:01:01 +0000</pubDate></item><item><title>&#x62A;&#x62D;&#x631;&#x64A;&#x643; &#x633;&#x644;&#x633;&#x644;&#x629; &#x645;&#x646; &#x627;&#x644;&#x631;&#x633;&#x648;&#x645;&#x627;&#x62A; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x627;&#x644;&#x648;&#x639;&#x648;&#x62F; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627; &#x633;&#x643;&#x631;&#x64A;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%AA%D8%AD%D8%B1%D9%8A%D9%83-%D8%B3%D9%84%D8%B3%D9%84%D8%A9-%D9%85%D9%86-%D8%A7%D9%84%D8%B1%D8%B3%D9%88%D9%85%D8%A7%D8%AA-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%88%D8%B9%D9%88%D8%AF-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2350/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_06/--.png.21c1a13269ec9d03f53e345a813e5ced.png" /></p>
<p>
	سنتعلم في هذا المقال كيفية استخدام الوعود (Promises) في جافا سكريبت لتحريك مجموعة من الصورأو الرسومات بترتيب محدد باستخدام الواجهة البرمجية Web Animations <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr>. حيث سنطبق بشكل عملي كل كود برمجي يعرض صورًا ثابتة ثم نعدله شيفرة معينة لتحقيق حركة تسلسلية لهذه الصور وفق تتابع محدد بحيث تتحرك الصورة الأولى، وعند انتهائها تتحرك الصورة الثانية، وعند انتهائها تتحرك الصورة الثالثة.
</p>

<h2 id="-1">
	ملفات التطبيق
</h2>

<p>
	لدينا في مجلد التطبيق الملفات الأساسية التالية:
</p>

<ul>
	<li>
		index.html
	</li>
	<li>
		style.css
	</li>
	<li>
		main.js
	</li>
	<li>
		style.css
	</li>
	<li>
		وملف الصورة التي سنحركها alice.svg
	</li>
</ul>

<p>
	لنوضح دلالة كل ملف من هذه الملفات
</p>

<h3 id="indexhtml">
	الملف index.html
</h3>

<p>
	بداية نكتب الكود التالي في مستند HTML لإنشاء صفحة ويب تحتوي على ثلاث صور متماثلة مرتبة قطريًا، والتي سيتم تحريكها باستخدام جافا سكريبت
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3793_6" style=""><span class="dec">&lt;!DOCTYPE html&gt;</span><span class="pln">
</span><span class="tag">&lt;html</span><span class="pln"> </span><span class="atn">lang</span><span class="pun">=</span><span class="atv">"en-US"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;head&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"UTF-8"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"viewport"</span><span class="pln"> </span><span class="atn">content</span><span class="pun">=</span><span class="atv">"width=device-width"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;title&gt;</span><span class="pln">Sequencing animations</span><span class="tag">&lt;/title&gt;</span><span class="pln">
    </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text/javascript"</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"main.js"</span><span class="pln"> </span><span class="atn">defer</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
    </span><span class="tag">&lt;link</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"style.css"</span><span class="atn">rel</span><span class="pun">=</span><span class="atv">"stylesheet"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;/head&gt;</span><span class="pln">

  </span><span class="tag">&lt;body&gt;</span><span class="pln">

    </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"alice-container"</span><span class="tag">&gt;</span><span class="pln">
      </span><span class="tag">&lt;img</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"alice1"</span><span class="pln">  </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"alice.svg"</span><span class="pln"> </span><span class="atn">role</span><span class="pun">=</span><span class="atv">"img"</span><span class="pln"> </span><span class="atn">alt</span><span class="pun">=</span><span class="atv">"silhouette of crouching long haired character in dress and short boots"</span><span class="tag">&gt;</span><span class="pln">
      </span><span class="tag">&lt;img</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"alice2"</span><span class="pln">  </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"alice.svg"</span><span class="pln"> </span><span class="atn">role</span><span class="pun">=</span><span class="atv">"img"</span><span class="pln"> </span><span class="atn">alt</span><span class="pun">=</span><span class="atv">"silhouette of crouching long haired character in dress and short boots"</span><span class="tag">&gt;</span><span class="pln">
      </span><span class="tag">&lt;img</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"alice3"</span><span class="pln">  </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"alice.svg"</span><span class="pln"> </span><span class="atn">role</span><span class="pun">=</span><span class="atv">"img"</span><span class="pln"> </span><span class="atn">alt</span><span class="pun">=</span><span class="atv">"silhouette of crouching long haired character in dress and short boots"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;/div&gt;</span><span class="pln">

  </span><span class="tag">&lt;/body&gt;</span><span class="pln">

</span><span class="tag">&lt;/html&gt;</span></pre>

<h3 id="stylecss">
	الملف style.css
</h3>

<p>
	سنكتب في هذا الملف التنسيقات اللازمة لتنسيق الصور الثلاثة لعرضها في حاوية في وسط الصفحة باستخدام تخطيط الشبكة <a href="https://academy.hsoub.com/programming/css/%D8%AA%D8%AE%D8%B7%D9%8A%D8%B7-%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%AA%D8%AE%D8%B7%D9%8A%D8%B7-%D8%A7%D9%84%D8%B4%D8%A8%D9%83%D8%A9-grid-%D9%81%D9%8A-css-r2282/" rel="">Grid Layout</a>. ونعين مناطق محددة لكل صورة ضمن الشبكة لضمان ترتيبها بشكل قطري كما يلي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_3793_8" style=""><span class="pln">body </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">background</span><span class="pun">:</span><span class="pln"> </span><span class="lit">#6c373f</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">display</span><span class="pun">:</span><span class="pln"> flex</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">justify-content</span><span class="pun">:</span><span class="pln"> center</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">#</span><span class="pln">alice-container </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">90vh</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">display</span><span class="pun">:</span><span class="pln"> grid</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">place-items</span><span class="pun">:</span><span class="pln"> center</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">grid-template-areas</span><span class="pun">:</span><span class="pln">
    </span><span class="str">"a1 . ."</span><span class="pln">
    </span><span class="str">". a2 ."</span><span class="pln">
    </span><span class="str">". . a3"</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">#</span><span class="pln">alice1 </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">grid-area</span><span class="pun">:</span><span class="pln"> a1</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="pun">#</span><span class="pln">alice2 </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">grid-area</span><span class="pun">:</span><span class="pln"> a2</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="pun">#</span><span class="pln">alice3 </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">grid-area</span><span class="pun">:</span><span class="pln"> a3</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3 id="mainjs">
	الملف main.js
</h3>

<p>
	سنكتب في هذا الملف كود لاختيار الصور الثلاثة من الصفحة باستخدام معرفاتها الفريدة وتخزينها في متغيرات لتسهيل استخدامها لاحقًا في تحريكها. ونعرف ثابت <code>aliceTumbling</code> يحدد كيفية الحركة من خلال التحولات transforms التي ستقع للصورة وثابت <code>aliceTiming</code> يحدد تفاصيل توقيت الحركة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3793_12" style=""><span class="kwd">const</span><span class="pln"> aliceTumbling </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
  </span><span class="pun">{</span><span class="pln"> transform</span><span class="pun">:</span><span class="pln"> </span><span class="str">'rotate(0) scale(1)'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">{</span><span class="pln"> transform</span><span class="pun">:</span><span class="pln"> </span><span class="str">'rotate(360deg) scale(0)'</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="pun">];</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> aliceTiming </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  duration</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2000</span><span class="pun">,</span><span class="pln">
  iterations</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
  fill</span><span class="pun">:</span><span class="pln"> </span><span class="str">'forwards'</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> alice1 </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"#alice1"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> alice2 </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"#alice2"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> alice3 </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"#alice3"</span><span class="pun">);</span></pre>

<p>
	لو فتحت اﻵن الملف "index.html" ضمن متصفحك سترى ثلاث صور مرتبة قطريًا على النحو التالي:
</p>

<p style="text-align: center;">
	<img alt="01_sequencing-animations.png" class="ipsImage ipsImage_thumbnailed" data-fileid="151519" data-ratio="100.00" data-unique="ug5ch9ucu" width="434" src="https://academy.hsoub.com/uploads/monthly_2024_06/01_sequencing-animations.png.7a4fe856474aba299b1b04074b2184a2.png">
</p>

<p>
	انتبه إلى أن الملف الوحيد الذي ستعدله في الخطوات التالية من أجل تحريك الصور هو الملف "main.js".
</p>

<h2 id="-2">
	كتابة كود التحريك في جافا سكريبت
</h2>

<p>
	نريد تحديث صفحة الويب السابقة لتحريك الصور الثلاث واحدة تلو اﻷخرى باستخدام فكرة الوعود <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%88%D8%B9%D9%88%D8%AF-promises-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2341/" rel="">Promises</a> في جافا سكريبت، فعندما تنتهي حركة الصورة اﻷولى، نحرّك الصورة الثانية بعدها مباشرة، ومن ثم نحرك الصورة الثالثة.
</p>

<p>
	لتحقيق ذلك سنعدل شيفرة التحريك في الملف "main.js" لتدوير الصور وتقليص حجمها حتى تختفي، وانتبه إلى أن عملية التحريك تجري مرة واحدة لذا عليك إعادة تحميل الصفحة لمشاهدة الحركة مجددًا
</p>

<p>
	اتبع الخطوات المشروحة في اﻷقسام التالية.
</p>

<h3 id="-3">
	تحريك الصورة الأولى
</h3>

<p>
	نستخدم الواجهة البرمجية <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API" rel="external nofollow">Web Animations <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr></a> لتحريك الصورة، وبالتحديد التابع <code>()element.animate</code>، لهذا، حدّثنا الملف "main.js" باستدعاء التابع <code>()alice1.animate</code> كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3793_14" style=""><span class="kwd">const</span><span class="pln"> aliceTumbling </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
 </span><span class="pun">{</span><span class="pln"> transform</span><span class="pun">:</span><span class="pln"> </span><span class="str">"rotate(0) scale(1)"</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
 </span><span class="pun">{</span><span class="pln"> transform</span><span class="pun">:</span><span class="pln"> </span><span class="str">"rotate(360deg) scale(0)"</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
</span><span class="pun">];</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> aliceTiming </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
 duration</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2000</span><span class="pun">,</span><span class="pln">
 iterations</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
 fill</span><span class="pun">:</span><span class="pln"> </span><span class="str">"forwards"</span><span class="pun">,</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> alice1 </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"#alice1"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> alice2 </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"#alice2"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> alice3 </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"#alice3"</span><span class="pun">);</span><span class="pln">

alice1</span><span class="pun">.</span><span class="pln">animate</span><span class="pun">(</span><span class="pln">aliceTumbling</span><span class="pun">,</span><span class="pln"> aliceTiming</span><span class="pun">);</span></pre>

<p>
	أعد تحميل الصفحة، وسترى كيف تدور الصورة اﻷولى وتتقلص.
</p>

<h3 id="-4">
	تحريك جميع الصور
</h3>

<p>
	نريد الآن تحريك الصورة <code>alice2</code> عند اكتمال حركة الصورة <code>alice1</code> ثم تحريك <code>alice3</code> عند اكتمال <code>alice2</code>. ويعيد التابع <code>()animate</code> الكائن <code>Animation</code> الذي يمتلك الخاصية <code>finished</code> وهذه الاخيرة هي وعد <code>Promise</code> يُنجز عند انتهاء تحريك الصورة. لهذا بإمكانك استخدام هذا الوعد لتحديد متى تحرّك الصورة التالية.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3793_16" style=""><span class="kwd">const</span><span class="pln"> aliceTumbling </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
  </span><span class="pun">{</span><span class="pln"> transform</span><span class="pun">:</span><span class="pln"> </span><span class="str">'rotate(0) scale(1)'</span><span class="pln"> </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">{</span><span class="pln"> transform</span><span class="pun">:</span><span class="pln"> </span><span class="str">'rotate(360deg) scale(0)'</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="pun">];</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> aliceTiming </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  duration</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2000</span><span class="pun">,</span><span class="pln">
  iterations</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln">
  fill</span><span class="pun">:</span><span class="pln"> </span><span class="str">'forwards'</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> alice1 </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"#alice1"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> alice2 </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"#alice2"</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> alice3 </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"#alice3"</span><span class="pun">);</span><span class="pln">

alice1</span><span class="pun">.</span><span class="pln">animate</span><span class="pun">(</span><span class="pln">aliceTumbling</span><span class="pun">,</span><span class="pln"> aliceTiming</span><span class="pun">).</span><span class="pln">finished
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> alice2</span><span class="pun">.</span><span class="pln">animate</span><span class="pun">(</span><span class="pln">aliceTumbling</span><span class="pun">,</span><span class="pln"> aliceTiming</span><span class="pun">).</span><span class="pln">finished</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">then</span><span class="pun">(()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> alice3</span><span class="pun">.</span><span class="pln">animate</span><span class="pun">(</span><span class="pln">aliceTumbling</span><span class="pun">,</span><span class="pln"> aliceTiming</span><span class="pun">).</span><span class="pln">finished</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">.</span><span class="kwd">catch</span><span class="pun">(</span><span class="pln">error </span><span class="pun">=&gt;</span><span class="pln"> console</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(`</span><span class="typ">Error</span><span class="pln"> animating </span><span class="typ">Alices</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">error</span><span class="pun">}`));</span></pre>

<p>
	عند تنفيذ الكود أعلاه ستحصل على الخرج التالي(اضغط على زر Rerun في الأسفل لمشاهدة الحركة من جديد):
</p>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" frameborder="no" height="300" loading="lazy" scrolling="no" src="https://codepen.io/HsoubAcademy/embed/jOoLmOx?default-tab=result" style="width: 100%;" title="sequencing-animations">See the Pen sequencing-animations by Hsoub Academy (@HsoubAcademy) on CodePen.</iframe>
</p>

<h2 id="-5">
	تنفيذ التسلسل باستخدام الوعود بطرق مختلفة
</h2>

<p>
	نطلب منك أن تجرّب عدة طرق مختلفة في تنفيذ اﻷمر باستخدام الوعود:
</p>

<ol>
	<li>
		نفّذ الأمر باستخدام نسخة "جحيم الاستدعاءات Callback Hell" التي تحدثنا عنها في <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A7%D9%84%D9%88%D8%B9%D9%88%D8%AF-promises-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2341/" rel="">مقال سابق</a>.
	</li>
	<li>
		نفّذ اﻷمر من جديد باستخدام سلسلة الوعود، وانتبه إلى وجود عدة طرق لكتابة الشيفرة نظرًا للأشكال المختلفة التي يمكنك من خلالها كتابة دالة سهمية. لهذا جرّب بعض اﻷشكال واستنتج الشكل اﻷسهل واﻷوضح قراءة.
	</li>
	<li>
		نفّذ اﻷمر باستخدام التعليمتين <code>await</code> و <code>async</code>.
	</li>
</ol>

<p>
	وتذكّر أن <code>()element.animate</code> لا تعيد وعدًا <code>Promise</code>، بل كائن <code>Animation</code> له الخاصية <code>finished</code> وهي بحد ذاتها وعد.
</p>

<h2 id="-6">
	الخلاصة
</h2>

<p>
	استعرضنا في هذا المقال طريقة تحريك مجموعة من الصور في جافا سكريبت من خلال تطبيق عملي بسيط لكود برمجي يعرض صورًا ثابتة ثم عدلناه لتحقيق حركة تسلسلية لهذه الصور بحيث تتحرك الصورة الأولى، وعند انتهائها تتحرك الصورة الثانية، وعند انتهائها تتحرك الصورة الثالثة باستخدام باستخدام الوعود لتحقيق التسلسل والواجهة البرمجية Web Animations <abbr title="Application Programming Interface | واجهة برمجية"><abbr title="Application Programming Interface | واجهة برمجية">API</abbr></abbr> لتحقيق الحركة، وبإمكانك إلقاء نظرة على التمرين <a href="https://mdn.github.io/learning-area/javascript/asynchronous/sequencing-animations/finished/" rel="external nofollow">بشكله النهائي</a> وبهذا الأسلوب، يمكنك تطبيق الوعود في مشاريعك لتحريك أي رسومات بترتيب محدد وإضافة ديناميكية وتفاعلية أكبر لصفحات الويب الخاصة بك.
</p>

<p>
	ترجمة -وبتصرف- للمقال: <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Sequencing_animations" rel="external nofollow">Sequencing animations</a>
</p>

<h2>
	اقرأ أيضًا
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B9%D9%85%D9%91%D8%A7%D9%84-workers-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2349/" rel="">مدخل إلى عمّال Workers جافا سكريبت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%B1%D8%AF%D9%88%D8%AF-%D8%A7%D9%84%D9%86%D8%AF%D8%A7%D8%A1-callbacks-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r914/" rel="">مقدمة إلى ردود النداء callbacks في جافاسكربت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-promise-%D9%81%D9%8A-javascript-r740/" rel="">الواجهة البرمجية Promise في JavaScript</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A5%D9%86%D8%B4%D8%A7%D8%A1-%D8%A7%D9%84%D8%AD%D8%B1%D9%83%D8%A7%D8%AA-%D8%B9%D8%A8%D8%B1-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1351/" rel="">إنشاء الحركات عبر جافاسكربت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%81%D9%87%D9%85-%D8%A7%D9%84%D8%A3%D8%AD%D8%AF%D8%A7%D8%AB-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r690/" rel="">فهم الأحداث في جافاسكربت</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2350</guid><pubDate>Sun, 30 Jun 2024 15:01:05 +0000</pubDate></item><item><title>&#x645;&#x62F;&#x62E;&#x644; &#x625;&#x644;&#x649; &#x639;&#x645;&#x651;&#x627;&#x644; Workers &#x62C;&#x627;&#x641;&#x627; &#x633;&#x643;&#x631;&#x64A;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%B9%D9%85%D9%91%D8%A7%D9%84-workers-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r2349/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2024_06/----.png.53b39205e245256f174cfa40913b6da3.png" /></p>
<p>
	نتحدث في مقالنا عن عمّال جافا سكريبت workers وهي تقنية تساعدك في تنفيذ المهام ضمن خيوط معالجة threads منفصلة. ولقد أشرنا في مقالات سابقة إلى ما يحدث عندما تنفّذ عملية متزامنة طويلة في برنامجك، فقد يصبح البرنامج غير متجاوب بالكامل. ويعود السبب أساسًا إلى أن البرنامج يعمل على خيط معالجة واحد single-threaded.
</p>

<p>
	يُعرّف <a href="https://academy.hsoub.com/programming/java/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D8%AE%D9%8A%D9%88%D8%B7-threads-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-r1483/" rel="">خيط المعالجة</a> بأنه سلسلة من التعليمات المتلاحقة التي يتبعها البرنامج. فإن كان البرنامج وحيد الخيط سيُنفَّذ تعليمة تلو اﻷخرى، وسينتظر البرنامج انتهاء أي عملية متزامنة طويلة التنفيذ حتى يتابع ولا يمكنه أثناء ذلك تنفيذ أي شيء.
</p>

<p>
	لهذا السبب ظهرت تقنية العمال workers لتمنحك القدرة على إنجاز بعض المهام ضمن خيوط معالجة مختلفة، إذ تبدأ حينها مهمة معينة ثم تنتقل (وهي لا تزال قيد المعالجة) لتعمل على أخرى (كأن تعالج مدخلات المستخدم).
</p>

<p>
	لكن ما يجب الانتباه إليه هو قدرة العمال على الوصول إلى البيانات المشتركة وتغييرها بشكل مستقل وغير متوقع أحيانًا وهذا ما يسبب بعض الثغرات التي يصعب إيجادها. لهذا ولكي نتفادى هذا النوع من المشاكل وخاصة في تطبيقات الويب، لا ينبغي لشيفرة العمال وللشيفرة اﻷساسية الوصول إلى متغيرات اﻷخرى، وقد يشتركا ببعض البيانات في حالات خاصة جدًا. تُنفذ الشيفرة اﻷساسية وشيفرة العمال في عالمين منفصلين تمامًا، وتتواصلان مع بعضهما من خلال تبادل رسائل. ويعني ذلك تحديدًا عدم قدرة العمال على الوصول إلى شجرة DOM (مثل النوافذ أو المستند أو عناصر صفحة الويب).
</p>

<p>
	وهناك ثلاث أنواع مختلفة من العمال:
</p>

<ul>
	<li>
		عمال مختصون Dedicated workers.
	</li>
	<li>
		عمال مشتركون Shared workers.
	</li>
	<li>
		عمال خدمة Service workers.
	</li>
</ul>

<p>
	سنتعرف على النوع الأول من خلال مثال تطبيقي، ثم نناقش النوعين اﻵخرين باختصار.
</p>

<h2 id="">
	استخدام عمال ويب
</h2>

<p>
	هل تتذكر تلك الصفحة التي توّلد أعدادًا أولية في مقال سابق؟ سنعود إليها ونستخدم عاملًا لتنفيذ الحسابات حتى تبقى الصفحة متجاوبة مع ما يفعله المستخدم.
</p>

<h3 id="-1">
	مولّد اﻷعداد اﻷولية المتزامن
</h3>

<p>
	لنلق نظرة في البداية إلى شيفرة جافا سكريبت الموافقة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1549_19" style=""><span class="kwd">function</span><span class="pln"> generatePrimes</span><span class="pun">(</span><span class="pln">quota</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">function</span><span class="pln"> isPrime</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">let</span><span class="pln"> c </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln"> c </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">sqrt</span><span class="pun">(</span><span class="pln">n</span><span class="pun">);</span><span class="pln"> </span><span class="pun">++</span><span class="pln">c</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">n </span><span class="pun">%</span><span class="pln"> c </span><span class="pun">===</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> primes </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> maximum </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1000000</span><span class="pun">;</span><span class="pln">

  </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">primes</span><span class="pun">.</span><span class="pln">length </span><span class="pun">&lt;</span><span class="pln"> quota</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> candidate </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">floor</span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">(</span><span class="pln">maximum </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">));</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">isPrime</span><span class="pun">(</span><span class="pln">candidate</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      primes</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">candidate</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">return</span><span class="pln"> primes</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"#generate"</span><span class="pun">).</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"click"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> quota </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"#quota"</span><span class="pun">).</span><span class="pln">value</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> primes </span><span class="pun">=</span><span class="pln"> generatePrimes</span><span class="pun">(</span><span class="pln">quota</span><span class="pun">);</span><span class="pln">
  document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"#output"</span><span class="pun">).</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln">
    </span><span class="pun">`</span><span class="typ">Finished</span><span class="pln"> generating $</span><span class="pun">{</span><span class="pln">quota</span><span class="pun">}</span><span class="pln"> primes</span><span class="pun">!`;</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"#reload"</span><span class="pun">).</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"click"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"#user-input"</span><span class="pun">).</span><span class="pln">value </span><span class="pun">=</span><span class="pln">
    </span><span class="str">'Try typing in here immediately after pressing "Generate primes"'</span><span class="pun">;</span><span class="pln">
  document</span><span class="pun">.</span><span class="pln">location</span><span class="pun">.</span><span class="pln">reload</span><span class="pun">();</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	ستتوقف استجابة البرنامج يعد أن نستدعي الدالة <code>()generatePrimes</code>.
</p>

<h3 id="webworker">
	مولّد أرقام أولية باستخدام عامل ويب Web worker
</h3>

<p>
	عليك أولًا قبل أن تشرع العمل معنا تنزيل نسخة من <a href="https://github.com/mdn/learning-area/blob/main/javascript/asynchronous/workers/start." rel="external nofollow">ملفات البرنامج</a>، وهي أربعة ملفات:
</p>

<ul>
	<li>
		index.html
	</li>
	<li>
		style.css
	</li>
	<li>
		main.js
	</li>
	<li>
		generate.js
	</li>
</ul>

<p>
	ستجد أن الملفين "index.html" و "style.css" مكتملان. إليك أولًا ملف HTML:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1549_17" style=""><span class="dec">&lt;!doctype html&gt;</span><span class="pln">
</span><span class="tag">&lt;html</span><span class="pln"> </span><span class="atn">lang</span><span class="pun">=</span><span class="atv">"en-US"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;head&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"utf-8"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"viewport"</span><span class="pln"> </span><span class="atn">content</span><span class="pun">=</span><span class="atv">"width=device-width"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
    </span><span class="tag">&lt;title&gt;</span><span class="pln">Prime numbers</span><span class="tag">&lt;/title&gt;</span><span class="pln">
    </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"main.js"</span><span class="pln"> </span><span class="atn">defer</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
    </span><span class="tag">&lt;link</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"style.css"</span><span class="pln"> </span><span class="atn">rel</span><span class="pun">=</span><span class="atv">"stylesheet"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;/head&gt;</span><span class="pln">

  </span><span class="tag">&lt;body&gt;</span><span class="pln">
    </span><span class="tag">&lt;label</span><span class="pln"> </span><span class="atn">for</span><span class="pun">=</span><span class="atv">"quota"</span><span class="tag">&gt;</span><span class="pln">Number of primes:</span><span class="tag">&lt;/label&gt;</span><span class="pln">
    </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text"</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"quota"</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"quota"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"1000000"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">

    </span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"generate"</span><span class="tag">&gt;</span><span class="pln">Generate primes</span><span class="tag">&lt;/button&gt;</span><span class="pln">
    </span><span class="tag">&lt;button</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"reload"</span><span class="tag">&gt;</span><span class="pln">Reload</span><span class="tag">&lt;/button&gt;</span><span class="pln">

    </span><span class="tag">&lt;textarea</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"user-input"</span><span class="pln"> </span><span class="atn">rows</span><span class="pun">=</span><span class="atv">"5"</span><span class="pln"> </span><span class="atn">cols</span><span class="pun">=</span><span class="atv">"62"</span><span class="tag">&gt;</span><span class="pln">
Try typing in here immediately after pressing "Generate primes"
    </span><span class="tag">&lt;/textarea&gt;</span><span class="pln">

    </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"output"</span><span class="tag">&gt;&lt;/div&gt;</span><span class="pln">
  </span><span class="tag">&lt;/body&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	وهذا محتوى ملف CSS:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_1549_21" style=""><span class="pln">textarea </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">display</span><span class="pun">:</span><span class="pln"> block</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">margin</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1rem</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	كما ستجد الملفين "main.js" و "generate.js" فارغين وسنضع الشيفرة الرئيسية في الملف "main.js " وشيفرة العامل ضمن الملف "generate.js".
</p>

<p>
	إذا ما نلاحظه أولًا كيفية فصل شيفرة العامل عن الشيفرة الرئيسية، كما سترى أننا أضفنا فقط الشيفرة اﻷساسية إلى صفحة الويب "index.html" ضمن العنصر <code>&lt;script&gt;</code>.
</p>

<p>
	انسخ اﻵن الشيفرة التالية إلى الملف "main.js":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1549_13" style=""><span class="com">//"generate.js" أنشئ عاملًا جديدًا، ونسند إليه الشيفرة الموجودة في الملف</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> worker </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Worker</span><span class="pun">(</span><span class="str">"./generate.js"</span><span class="pun">);</span><span class="pln">
</span><span class="com">//أرسل رسالة إلى العامل "Generate primes" عندما ينقر المستخدم</span><span class="pln">
</span><span class="com">//"quota" وتضم أيضًا القيمة "generate" اﻷمر الموجود في الرسالة هو</span><span class="pln">
</span><span class="com">//وهي عدد اﻷرقام اﻷولية التي نولّدها</span><span class="pln">

document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"#generate"</span><span class="pun">).</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"click"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> quota </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"#quota"</span><span class="pun">).</span><span class="pln">value</span><span class="pun">;</span><span class="pln">
  worker</span><span class="pun">.</span><span class="pln">postMessage</span><span class="pun">({</span><span class="pln">
    command</span><span class="pun">:</span><span class="pln"> </span><span class="str">"generate"</span><span class="pun">,</span><span class="pln">
    quota</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span><span class="pln">
</span><span class="com">//عندما يعيد العامل ارسال إلى خيط المعالجة الرئيسي</span><span class="pln">
</span><span class="com">//حدّث صندوق الخرج برسال إلى المستخدم تتضمن عدد اﻷعداد الأولية التي ولدناها</span><span class="pln">
</span><span class="com">// والمأخوذة من بيانات الرسالة اﻷصلية</span><span class="pln">

worker</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"message"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">message</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"#output"</span><span class="pun">).</span><span class="pln">textContent </span><span class="pun">=</span><span class="pln">
    </span><span class="pun">`</span><span class="typ">Finished</span><span class="pln"> generating $</span><span class="pun">{</span><span class="pln">message</span><span class="pun">.</span><span class="pln">data</span><span class="pun">}</span><span class="pln"> primes</span><span class="pun">!`;</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"#reload"</span><span class="pun">).</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"click"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  document</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">"#user-input"</span><span class="pun">).</span><span class="pln">value </span><span class="pun">=</span><span class="pln">
    </span><span class="str">'Try typing in here immediately after pressing "Generate primes"'</span><span class="pun">;</span><span class="pln">
  document</span><span class="pun">.</span><span class="pln">location</span><span class="pun">.</span><span class="pln">reload</span><span class="pun">();</span><span class="pln">
</span><span class="pun">});</span></pre>

<ul>
	<li>
		<p>
			ننشئ بداية عاملًا باستخدام البانية <code>()Worker</code>، ونمرر إليها عنوان URL يشير إلى سكريبت العامل.تُنفَّذ شيفرة العامل بمجرد إنشاءه.
		</p>
	</li>
	<li>
		<p>
			وكما هو الحال في النسخة المتزامنة من التطبيق نضيف معالجًا للحدث <code>click</code> خاصًا بالزر "Generate primes". لكن وبدلًا من استدعاء الدالة <code>()generatePrimes</code>، نرسل رسالة إلى العامل باستخدام التابع <code>()worker.postMessage</code> الذي يأخذ وسيطًا واحدًا. لهذا نمرر له كائن JSON يضم خاصيتين:
		</p>
	</li>
	<li>
		<p>
			<code>command</code>: وتضم قيمة نصية تخبر العامل ما عليه فعله (في حال كان باستطاعته تنفيذ أكثر من شيء).
		</p>
	</li>
	<li>
		<p>
			<code>quota</code>: عدد اﻷعداد اﻷولية التي يولّدها.
		</p>
	</li>
	<li>
		<p>
			نضيف تاليًا معالج الحدث <code>message</code> إلى العامل، لكي يبلغنا العامل من انتهاء عمله ويعيد أية نتائج نريدها. يأخذ معالج الحدث بياناته من الخاصية <code>data</code> العائدة للرسالة ويطبعها ضمن عنصر الخرج (البيانات هنا هي نفسها قيمة الخاصية <code>quota</code>، لذا لا حاجة لها عمليًا ووضعناها لعرض مبدأ العمل فقط).
		</p>
	</li>
	<li>
		<p>
			أضفنا اخيرة شيفرة معالج حدث النقر <code>click</code> للزر "Reload"، وهي مشابهة تمامًا لشيفرة النسخة المتزامنة.
		</p>
	</li>
</ul>

<p>
	انقل اﻵن الشيفرة التالية إلى الملف "generate.js":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1549_11" style=""><span class="com">// Listen for messages from the main thread.</span><span class="pln">
</span><span class="com">// If the message command is "generate", call `generatePrimes()`</span><span class="pln">
addEventListener</span><span class="pun">(</span><span class="str">"message"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">(</span><span class="pln">message</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">message</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">command </span><span class="pun">===</span><span class="pln"> </span><span class="str">"generate"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    generatePrimes</span><span class="pun">(</span><span class="pln">message</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">quota</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="com">// Generate primes (very inefficiently)</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> generatePrimes</span><span class="pun">(</span><span class="pln">quota</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">function</span><span class="pln"> isPrime</span><span class="pun">(</span><span class="pln">n</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">let</span><span class="pln"> c </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln"> c </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">sqrt</span><span class="pun">(</span><span class="pln">n</span><span class="pun">);</span><span class="pln"> </span><span class="pun">++</span><span class="pln">c</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">n </span><span class="pun">%</span><span class="pln"> c </span><span class="pun">===</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="kwd">const</span><span class="pln"> primes </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">
  </span><span class="kwd">const</span><span class="pln"> maximum </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1000000</span><span class="pun">;</span><span class="pln">

  </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">primes</span><span class="pun">.</span><span class="pln">length </span><span class="pun">&lt;</span><span class="pln"> quota</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> candidate </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">floor</span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">random</span><span class="pun">()</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">(</span><span class="pln">maximum </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">));</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">isPrime</span><span class="pun">(</span><span class="pln">candidate</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      primes</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">candidate</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="com">// When we have finished, send a message to the main thread,</span><span class="pln">
  </span><span class="com">// including the number of primes we generated.</span><span class="pln">
  postMessage</span><span class="pun">(</span><span class="pln">primes</span><span class="pun">.</span><span class="pln">length</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	وتذكّر أن هذه الشيفرة ستُنفَّذ بمجرد إنشاء عامل جديد.
</p>

<p>
	يترصّد العامل بداية الرسائل التي ترسلها الشيفرة الرئيسية من خلال الدالة <code>()addEventListener</code> وهي دالة عامة في العامل. وتضم الخاصية <code>data</code> الموجودة ضمن معالج حدث الرسالة <code>message</code> نسخة من الوسيط الذي تمرره الشيفرة الرئيسية. فإذا مررت الشيفرة الرئيسية اﻷمر <code>generate</code>، نستدعي حينها الدالة <code>()generatePrimes</code> ونمرر لها القيمة <code>quota</code> من الحدث <code>message</code>.
</p>

<p>
	تشبه الدالة <code>()generatePrimes</code> مقابلتها في النسخة المتزامنة من التمرين ما عدا أننا نرسل رسالة إلى السكريبت الرئيسي عند الانتهاء بدلًا من إعادة قيمة. ونستخدم في هذه الحالة التابع <code>()postMessage</code> والذي يشبه الدالة <code>()addEventListener</code> بأنه عام في شيفرة العامل أيضًا. وكما رأينا، يستمع السكريبت الرئيسي إلى الرسالة ويُحدّث شجرة DOM عند استقبال الرسالة.
</p>

<p>
	<strong>ملاحظة</strong>: لتشغيل هذا الموقع، عليك تشغيل خادم محلي على حاسوبك،إذ لا يُسمح بتحميل شيفرة العامل من الوجهة.
</p>

<p>
	وإن صادفتك أي مشاكل في إنشاء نسختك من التمرين` بإمكانك الاطلاع على <a href="https://github.com/mdn/learning-area/blob/main/javascript/asynchronous/workers/finished" rel="external nofollow">النسخة المكتملة</a>) منه على جت-هب أو <a href="https://mdn.github.io/learning-area/javascript/asynchronous/workers/finished" rel="external nofollow">تجربته مباشرة</a>.
</p>

<h2 id="workers-1">
	أنواع أخرى من العمال workers
</h2>

<p>
	يُدعى العامل الذي أنشأناه في المثال السابق بالعامل المخصص dedicated worker. ويعني ذلك أنه استخدم من قبل سكريبت واحد. وهنالك نوعين آخرين هما:
</p>

<ul>
	<li>
		العمال المشتركون shared workers: ويمكن مشاركتهم بين أكثر من سكريبت أثناء تنفيذها في نوافذ مختلفة للمتصفح.
	</li>
	<li>
		عمّال الخدمة service workers: ويعملون كخوادم وكيلة proxy servers أو لتخزين الموارد مؤقتًا كي تعمل صفحة ويب عندما لا يكون المتصفح متصلا بالشبكة، فهي مكوّن أساسي من مكوّنات تطبيقات الويب المتقدمة <a href="https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps" rel="external nofollow">Progressive Web Apps</a>
	</li>
</ul>

<h2 id="-2">
	الخلاصة
</h2>

<p>
	تعرّفنا في هذا المقال على عمال ويب web workers، وهي تقنية تمكّن تطبيق ويب من نقل المهام إلى خيط معالجة آخر. ورأينا أن خيط المعالجة اﻷساسي وخيط العمال لا يتشاركان المتغيرات، بل يتواصلان من خلال إرسال الرسائل التي يتلقاها الطرف اﻵخر على شكل أحداث للكائن <code>message</code>.
</p>

<p>
	يمكن أن تقدم هذه التقنية طريقة فعالة ﻹبقاء التطبيق اﻷساسي متجاوبًا، على الرغم من عدم قدرة العمال على الوصول إلى كل الواجهات البرمجية التي يصلها التطبيق اﻷساسي وخصوصًا عناصر شجرة DOM.
</p>

<p>
	ترجمة -وبتصرف- للمقال: <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Introducing_workers" rel="external nofollow">Introducing workers</a>
</p>

<h2>
	اقرأ أيضًا:
</h2>

<ul>
	<li>
		المقال السابق: <a href="https://academy.hsoub.com/programming/javascript/%D8%A5%D9%86%D8%AC%D8%A7%D8%B2-%D9%88%D8%A7%D8%AC%D9%87%D8%A9-%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D8%AA%D8%B9%D8%AA%D9%85%D8%AF-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%B9%D9%88%D8%AF-r2348/" rel="">إنجاز واجهة برمجية في جافا سكريبت تعتمد على الوعود</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%A9-%D8%A7%D9%84%D8%A3%D8%AD%D8%AF%D8%A7%D8%AB-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1354/" rel="">معالجة الأحداث في جافا سكريبت</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AC%D8%A7%D9%81%D8%A7-%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-%D8%BA%D9%8A%D8%B1-%D8%A7%D9%84%D9%85%D8%AA%D8%B2%D8%A7%D9%85%D9%86%D8%A9-r2337/" rel="">مدخل إلى جافا سكريبت غير المتزامنة</a>
	</li>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D9%85%D8%B2%D8%AE%D8%B1%D9%81%D8%A7%D8%AA-decorators-%D9%88%D8%A7%D9%84%D8%AA%D9%85%D8%B1%D9%8A%D8%B1-forwarding-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r878/" rel="">المزخرفات decorators والتمرير forwarding في جافاسكربت</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">2349</guid><pubDate>Mon, 24 Jun 2024 15:02:32 +0000</pubDate></item></channel></rss>
