<?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/page/7/?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>&#x636;&#x628;&#x637; &#x627;&#x644;&#x62A;&#x62D;&#x62F;&#x64A;&#x62F; &#x62F;&#x627;&#x62E;&#x644; &#x635;&#x641;&#x62D;&#x627;&#x62A; HTML &#x648;&#x645;&#x646;&#x639;&#x647; &#x639;&#x628;&#x631; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%B6%D8%A8%D8%B7-%D8%A7%D9%84%D8%AA%D8%AD%D8%AF%D9%8A%D8%AF-%D8%AF%D8%A7%D8%AE%D9%84-%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-html-%D9%88%D9%85%D9%86%D8%B9%D9%87-%D8%B9%D8%A8%D8%B1-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1240/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_05/Selection-and-Range.png.38ae460a41cc337d3933a978b7b1b8f2.png" /></p>

<p>
	سنتناول في هذا المقال التحديد داخل المستند، وكذلك التحديد داخل حقول الاستمارات، مثل <code>&lt;input&gt;</code>.
</p>

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

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

<h2>
	المدى
</h2>

<p>
	يُعدّ <a data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" href="https://dom.spec.whatwg.org/#ranges" rel="external nofollow">المدى</a> هو المفهوم اﻷساسيّ في موضوع التحديد، وهو عبارة عن زوج من "النقط الحدّية": بداية المدى ونهاية المدى.
</p>

<p>
	يُنشأ كائن <code>Range</code> دون وسائط:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3295_7" style="">
<span class="pln">let range </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Range</span><span class="pun">();</span></pre>

<p>
	يمكننا بعدها ضبط حدود التحديد باستخدام <code>range.setStart(node, offset)‎</code> و<code>range.setEnd(node, offset)‎</code>.
</p>

<p>
	يمكن للوسيط الأوّل <code>node</code> أن يكون إمّا عقدة نصيّة أو عقدة عنصريّة، ويكون معنى الوسيط الثاني معتمدا على ذلك:
</p>

<ul>
<li>
		إذا كان <code>node</code> عقدة نصيّة، فلابدّ أن يكون <code>offset</code> هو الموضع داخل النصّ.
	</li>
	<li>
		إذا كان <code>node</code> عقدة عنصريّة، فلابدّ أن يكون <code>offset</code> هو رقم الابن.
	</li>
</ul>
<p>
	على سبيل المثال، لننشئ مدى في هذا المقطع
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3295_11" style="">
<span class="tag">&lt;p</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"p"</span><span class="tag">&gt;</span><span class="pln">Example: </span><span class="tag">&lt;i&gt;</span><span class="pln">italic</span><span class="tag">&lt;/i&gt;</span><span class="pln"> and </span><span class="tag">&lt;b&gt;</span><span class="pln">bold</span><span class="tag">&lt;/b&gt;&lt;/p&gt;</span></pre>

<p>
	هذه هي بنية DOM الخاصّة به:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="67989" data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" href="https://academy.hsoub.com/uploads/monthly_2021_05/dom1.png.85560e38eb7cb730820b4cd61448d6b9.png" rel=""><img alt="dom1.png" class="ipsImage ipsImage_thumbnailed" data-fileid="67989" data-unique="qgo63fayp" src="https://academy.hsoub.com/uploads/monthly_2021_05/dom1.png.85560e38eb7cb730820b4cd61448d6b9.png"></a>
</p>

<p>
	لننشئ مدى من أجل <code>‎"Example: &lt;i&gt;italic&lt;/i&gt;"‎</code>. كما يمكن أن نلاحظ، تتشكّل هذه الجملة من الابنين الأوّل والثاني لـ <code>&lt;p&gt;</code>:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="67990" data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" href="https://academy.hsoub.com/uploads/monthly_2021_05/range-example-p-0-1.png.f59a8f8d11b9037c554d00a7f690f04c.png" rel=""><img alt="range-example-p-0-1.png" class="ipsImage ipsImage_thumbnailed" data-fileid="67990" data-unique="ln2kn5hy0" src="https://academy.hsoub.com/uploads/monthly_2021_05/range-example-p-0-1.png.f59a8f8d11b9037c554d00a7f690f04c.png"></a>
</p>

<ul>
<li>
		لنقطة البداية العقدة الأب <code>&lt;p&gt;</code>، والانحراف offset هو <code>0</code>.
	</li>
	<li>
		لنقطة النهاية العقدة الأب <code>&lt;p&gt;</code> أيضا، لكن لها الانحراف <code>2</code> (تضبط المدى إلى غاية <code>offset</code>، لكن دونه).
	</li>
</ul>
<p>
	لو أجرينا الشيفرة أدناه، يمكن أن نرى بأنّ النصّ يُحدّد.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3295_13" style="">
<span class="tag">&lt;p</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"p"</span><span class="tag">&gt;</span><span class="pln">Example: </span><span class="tag">&lt;i&gt;</span><span class="pln">italic</span><span class="tag">&lt;/i&gt;</span><span class="pln"> and </span><span class="tag">&lt;b&gt;</span><span class="pln">bold</span><span class="tag">&lt;/b&gt;&lt;/p&gt;</span><span class="pln">

</span><span class="tag">&lt;script&gt;</span><span class="pln">
  let range </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Range</span><span class="pun">();</span><span class="pln">

  range</span><span class="pun">.</span><span class="pln">setStart</span><span class="pun">(</span><span class="pln">p</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln">
  range</span><span class="pun">.</span><span class="pln">setEnd</span><span class="pun">(</span><span class="pln">p</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">);</span><span class="pln">

  </span><span class="com">// للمدى محتوياته على شكل نص، دون الوسوم‏ toString تعيد</span><span class="pln">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">range</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Example: italic</span><span class="pln">

  </span><span class="com">// لنطبّق هذا المدى على تحديد المستند (سيُوضَّح هذا لاحقا)‏</span><span class="pln">
  document</span><span class="pun">.</span><span class="pln">getSelection</span><span class="pun">().</span><span class="pln">addRange</span><span class="pun">(</span><span class="pln">range</span><span class="pun">);</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<p>
	هذه منصّة اختبار أكثر مرونة حيث يمكنك تجربة المزيد من التحديدات المختلفة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3295_15" style="">
<span class="pun">&lt;</span><span class="pln">p id</span><span class="pun">=</span><span class="str">"p"</span><span class="pun">&gt;</span><span class="typ">Example</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">i</span><span class="pun">&gt;</span><span class="pln">italic</span><span class="pun">&lt;</span><span class="str">/i&gt; and &lt;b&gt;bold&lt;/</span><span class="pln">b</span><span class="pun">&gt;&lt;/</span><span class="pln">p</span><span class="pun">&gt;</span><span class="pln">

</span><span class="typ">From</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">input id</span><span class="pun">=</span><span class="str">"start"</span><span class="pln"> type</span><span class="pun">=</span><span class="str">"number"</span><span class="pln"> value</span><span class="pun">=</span><span class="lit">1</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">–</span><span class="pln"> </span><span class="typ">To</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">input id</span><span class="pun">=</span><span class="str">"end"</span><span class="pln"> type</span><span class="pun">=</span><span class="str">"number"</span><span class="pln"> value</span><span class="pun">=</span><span class="lit">4</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">button id</span><span class="pun">=</span><span class="str">"button"</span><span class="pun">&gt;</span><span class="typ">Click</span><span class="pln"> to select</span><span class="pun">&lt;/</span><span class="pln">button</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
  button</span><span class="pun">.</span><span class="pln">onclick </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">
    let range </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Range</span><span class="pun">();</span><span class="pln">

    range</span><span class="pun">.</span><span class="pln">setStart</span><span class="pun">(</span><span class="pln">p</span><span class="pun">,</span><span class="pln"> start</span><span class="pun">.</span><span class="pln">value</span><span class="pun">);</span><span class="pln">
    range</span><span class="pun">.</span><span class="pln">setEnd</span><span class="pun">(</span><span class="pln">p</span><span class="pun">,</span><span class="pln"> end</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">
    document</span><span class="pun">.</span><span class="pln">getSelection</span><span class="pun">().</span><span class="pln">removeAllRanges</span><span class="pun">();</span><span class="pln">
    document</span><span class="pun">.</span><span class="pln">getSelection</span><span class="pun">().</span><span class="pln">addRange</span><span class="pun">(</span><span class="pln">range</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">script</span><span class="pun">&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" frameborder="no" height="228" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/WNpZErv?height=228&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-selection-range -ex1">See the Pen JS-p2-selection-range -ex1 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	على سبيل المثال، يعطي التحديد من 1 إلى 4 المدى التالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="67991" data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" href="https://academy.hsoub.com/uploads/monthly_2021_05/range-example-p-1-3.png.091c342c07f2fcd0a2b43ab554f7e794.png" rel=""><img alt="range-example-p-1-3.png" class="ipsImage ipsImage_thumbnailed" data-fileid="67991" data-unique="ulnpx8hsk" src="https://academy.hsoub.com/uploads/monthly_2021_05/range-example-p-1-3.png.091c342c07f2fcd0a2b43ab554f7e794.png"></a>
</p>

<p>
	لا يجب علينا استخدام العقدة نفسها في <code>setStart</code> و<code>setEnd</code> فقد يمتدّ المدى عبر العديد من العقد غير المترابطة. ما يهمّ فقط هو أن تكون النهاية موجودة بعد البداية.
</p>

<h3>
	تحديد أجزاء من العقد النصية
</h3>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="67993" data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" href="https://academy.hsoub.com/uploads/monthly_2021_05/range-example-p-2-b-3.png.8df1ba4ee4374c0ebeca96c91fd95262.png" rel=""><img alt="range-example-p-2-b-3.png" class="ipsImage ipsImage_thumbnailed" data-fileid="67993" data-unique="a5r6j9sah" src="https://academy.hsoub.com/uploads/monthly_2021_05/range-example-p-2-b-3.png.8df1ba4ee4374c0ebeca96c91fd95262.png"></a>
</p>

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

<p>
	نحتاج إلى أن ننشئ مدى بحيث:
</p>

<ul>
<li>
		يبدأ من الموضع 2 في الابن اﻷوّل لـ <code>&lt;p&gt;</code> (آخذًا جميع الحروف ما عدا الحرفين اﻷوّلين لـ "Ex<b>ample:</b> ").
	</li>
	<li>
		ينتهي عند الموضع 3 في الابن اﻷوّل لـ <code>&lt;b&gt;</code> (آخذا الحروف الثلاثة اﻷولى لكلمة "<b>bol</b>d"، لا أكثر):
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3295_19" style="">
<span class="tag">&lt;p</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"p"</span><span class="tag">&gt;</span><span class="pln">Example: </span><span class="tag">&lt;i&gt;</span><span class="pln">italic</span><span class="tag">&lt;/i&gt;</span><span class="pln"> and </span><span class="tag">&lt;b&gt;</span><span class="pln">bold</span><span class="tag">&lt;/b&gt;&lt;/p&gt;</span><span class="pln">

</span><span class="tag">&lt;script&gt;</span><span class="pln">
  let range </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Range</span><span class="pun">();</span><span class="pln">

  range</span><span class="pun">.</span><span class="pln">setStart</span><span class="pun">(</span><span class="pln">p</span><span class="pun">.</span><span class="pln">firstChild</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">);</span><span class="pln">
  range</span><span class="pun">.</span><span class="pln">setEnd</span><span class="pun">(</span><span class="pln">p</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">'b'</span><span class="pun">).</span><span class="pln">firstChild</span><span class="pun">,</span><span class="pln"> </span><span class="lit">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="pln">range</span><span class="pun">);</span><span class="pln"> </span><span class="com">// ample: italic and bol</span><span class="pln">

  </span><span class="com">// استخدم هذا المدى للتحديد (سيُوضَّح هذا لاحقا)‏</span><span class="pln">
  window</span><span class="pun">.</span><span class="pln">getSelection</span><span class="pun">().</span><span class="pln">addRange</span><span class="pun">(</span><span class="pln">range</span><span class="pun">);</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" frameborder="no" height="168" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/GRWMvNa?height=168&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-selection-range -ex2">See the Pen JS-p2-selection-range -ex2 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	لكائن المدى الخاصّيّات التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="67992" data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" href="https://academy.hsoub.com/uploads/monthly_2021_05/range-example-p-2-b-3-range.png.e78e05932616e0b26190984df7b578d2.png" rel=""><img alt="range-example-p-2-b-3-range.png" class="ipsImage ipsImage_thumbnailed" data-fileid="67992" data-unique="2canvq7ik" src="https://academy.hsoub.com/uploads/monthly_2021_05/range-example-p-2-b-3-range.png.e78e05932616e0b26190984df7b578d2.png"></a>
</p>

<ul>
<li>
		<code>startContainer</code> و<code>startOffset</code> -- العقدة والانحراف عن البداية.

		<ul>
<li>
				في المثال أعلاه: العقدة النصّيّة اﻷولى داخل <code>&lt;p&gt;</code> و<code>2</code>.
			</li>
		</ul>
</li>
	<li>
		<code>endContainer</code> و<code>endOffset</code> -- العقدة والانحراف عن النهاية.
		<ul>
<li>
				في المثال أعلاه: العقدة النصّيّة اﻷولى داخل <code>&lt;b&gt;</code> و<code>3</code>.
			</li>
		</ul>
</li>
	<li>
		<code>collapsed</code> -- قيمة بوليانية، تكون <code>true</code> عندما يبدأ المدى وينتهي في النقطة نفسها (وبذلك لا يوجد محتوى داخل المدى)،
		<ul>
<li>
				في المثال أعلاه: <code>false</code>
			</li>
		</ul>
</li>
	<li>
		<code>commonAncestorContainer</code> -- أقرب سلف مشترك بين جميع العقد داخل المدى،
		<ul>
<li>
				في المثال أعلاه: <code>&lt;p&gt;</code>
			</li>
		</ul>
</li>
</ul>
<h2>
	توابع المدى
</h2>

<p>
	هناك العديد من التوابع الملائمة للتحكّم في اﻷمداء سنعرضها كلها، ونبدأ بتلك التي تضبط بداية المدى:
</p>

<ul>
<li>
		<code>setStart(node, offset)‎</code> - يضع البداية عند موضع الانحراف <code>offset</code> في العقدة <code>node</code>
	</li>
	<li>
		<code>setStartBefore(node)‎</code> - يضع البداية قبل <code>node</code> مباشرة
	</li>
	<li>
		<code>setStartAfter(node)‎</code> - يضع البداية بعد <code>node</code> مباشرة
	</li>
</ul>
<p>
	ضبط نهاية المدى (توابع مماثلة):
</p>

<ul>
<li>
		<code>setEnd(node, offset)‎</code> - يضع النهاية عند موضع الانحراف <code>offset</code> في العقدة <code>node</code>
	</li>
	<li>
		<code>setEndBefore(node)‎</code> - يضع النهاية قبل <code>node</code> مباشرة
	</li>
	<li>
		<code>setEndAfter(node)‎</code> - يضع النهاية بعد <code>node</code> مباشرة
	</li>
</ul>
<p>
	كما سبق بيانه، يمكن أن تكون <code>node</code> عقدة نصّيّة أو عنصريّة، بالنسبة للعقد العنصريّة تتخطّى <code>offset</code> ذلك العدد من الحروف، بينما للعقد العنصريّة تتخطّى ذلك العدد من العقد اﻷبناء.
</p>

<p>
	توابع أخرى:
</p>

<ul>
<li>
		<code>selectNode(node)‎</code> - يضبط المدى بحيث تُحدَّد كامل العقدة <code>node</code>
	</li>
	<li>
		<code>selectNodeContents(node)‎</code> - يضبط المدى بحيث تُحدَّد جميع محتويات العقدة <code>node</code>
	</li>
	<li>
		<code>collapse(toStart)‎</code> إذا كانت <code>toStart=true</code> - يضع end=start، وإلّا يضع start=end، فينطوي بذلك المدى.
	</li>
	<li>
		<code>cloneRange()‎</code> - ينشئ مدى جديدا بنفس البداية/النهاية
	</li>
</ul>
<p>
	للتحكّم في المحتوى الموجود ضمن المدى:
</p>

<ul>
<li>
		<code>deleteContents()‎</code> - يزيل محتوى المدى من المستند
	</li>
	<li>
		<code>extractContents()‎</code> - يزيل محتوى المدى من المستند ويعيده على شكل قطعة مستند DocumentFragment
	</li>
	<li>
		<code>cloneContents()‎</code> - يستنسخ محتوى المستند ويعيده على شكل قطعة مستند ‎DocumentFragment
	</li>
	<li>
		<code>insertNode(node)‎</code> - يدرج العقدة <code>node</code> في المستند عند بداية المدى
	</li>
	<li>
		<code>surroundContents(node)‎</code> - يحيط العقدة بمحتوى المدى. لكي يعمل هذا، يجب أن يحتوي المدى على وسوم فتح وإغلاق لجميع العناصر التي في داخله: لا وجود ﻷمداء جزئيّة مثل <code>‎&lt;i&gt;abc</code>.
	</li>
</ul>
<p>
	بهذه التوابع يمكننا فعل أيّ شيء بالعقد المُحدّدة.
</p>

<p>
	هذه منصّة الاختبار لرؤيتها كيف تعمل:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3295_21" style="">
<span class="pln">Click buttons to run methods on the selection, "resetExample" to reset it.

</span><span class="tag">&lt;p</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"p"</span><span class="tag">&gt;</span><span class="pln">Example: </span><span class="tag">&lt;i&gt;</span><span class="pln">italic</span><span class="tag">&lt;/i&gt;</span><span class="pln"> and </span><span class="tag">&lt;b&gt;</span><span class="pln">bold</span><span class="tag">&lt;/b&gt;&lt;/p&gt;</span><span class="pln">

</span><span class="tag">&lt;p</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"result"</span><span class="tag">&gt;&lt;/p&gt;</span><span class="pln">
</span><span class="tag">&lt;script&gt;</span><span class="pln">
  let range </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Range</span><span class="pun">();</span><span class="pln">

  </span><span class="com">// جميع التوابع المعروضة ممثّلة هنا</span><span class="pln">
  let methods </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    deleteContents</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      range</span><span class="pun">.</span><span class="pln">deleteContents</span><span class="pun">()</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    extractContents</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      let content </span><span class="pun">=</span><span class="pln"> range</span><span class="pun">.</span><span class="pln">extractContents</span><span class="pun">();</span><span class="pln">
      result</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">
      result</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="str">"extracted: "</span><span class="pun">,</span><span class="pln"> content</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    cloneContents</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      let content </span><span class="pun">=</span><span class="pln"> range</span><span class="pun">.</span><span class="pln">cloneContents</span><span class="pun">();</span><span class="pln">
      result</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">
      result</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="str">"cloned: "</span><span class="pun">,</span><span class="pln"> content</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    insertNode</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      let newNode </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">'u'</span><span class="pun">);</span><span class="pln">
      newNode</span><span class="pun">.</span><span class="pln">innerHTML </span><span class="pun">=</span><span class="pln"> </span><span class="str">"NEW NODE"</span><span class="pun">;</span><span class="pln">
      range</span><span class="pun">.</span><span class="pln">insertNode</span><span class="pun">(</span><span class="pln">newNode</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    surroundContents</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      let newNode </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">'u'</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">
        range</span><span class="pun">.</span><span class="pln">surroundContents</span><span class="pun">(</span><span class="pln">newNode</span><span class="pun">);</span><span class="pln">
      </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</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"> </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    resetExample</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">innerHTML </span><span class="pun">=</span><span class="pln"> </span><span class="pun">`</span><span class="typ">Example</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">i</span><span class="pun">&gt;</span><span class="pln">italic</span><span class="pun">&lt;</span><span class="str">/i&gt; and &lt;b&gt;bold&lt;/</span><span class="pln">b</span><span class="pun">&gt;`;</span><span class="pln">
      result</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">

      range</span><span class="pun">.</span><span class="pln">setStart</span><span class="pun">(</span><span class="pln">p</span><span class="pun">.</span><span class="pln">firstChild</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">);</span><span class="pln">
      range</span><span class="pun">.</span><span class="pln">setEnd</span><span class="pun">(</span><span class="pln">p</span><span class="pun">.</span><span class="pln">querySelector</span><span class="pun">(</span><span class="str">'b'</span><span class="pun">).</span><span class="pln">firstChild</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">);</span><span class="pln">

      window</span><span class="pun">.</span><span class="pln">getSelection</span><span class="pun">().</span><span class="pln">removeAllRanges</span><span class="pun">();</span><span class="pln">  
      window</span><span class="pun">.</span><span class="pln">getSelection</span><span class="pun">().</span><span class="pln">addRange</span><span class="pun">(</span><span class="pln">range</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">for</span><span class="pun">(</span><span class="pln">let method in methods</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">write</span><span class="pun">(`&lt;</span><span class="pln">div</span><span class="pun">&gt;&lt;</span><span class="pln">button onclick</span><span class="pun">=</span><span class="str">"methods.${method}()"</span><span class="pun">&gt;</span><span class="pln">$</span><span class="pun">{</span><span class="pln">method</span><span class="pun">}&lt;</span><span class="str">/button&gt;&lt;/</span><span class="pln">div</span><span class="pun">&gt;`);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  methods</span><span class="pun">.</span><span class="pln">resetExample</span><span class="pun">();</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" frameborder="no" height="297" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/xxqXLgW?height=297&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-selection-range -ex3">See the Pen JS-p2-selection-range -ex3 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	توجد أيضا توابع للمقارنة بين اﻷمداء. لكن يندر استخدامها. إن احتجت لها، يُرجى الرجوع إلى <a data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" href="https://dom.spec.whatwg.org/#interface-range" rel="external nofollow">المواصفة</a> أو <a data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" href="https://developer.mozilla.org/en-US/docs/Web/api/Range" rel="external nofollow">دليل MDN</a>.
</p>

<h2>
	التحديد
</h2>

<p>
	<code>Range</code> هو كائن عامّ لإدارة أمداء التحديد. يمكننا إنشاء مثل هذه الكائنات، وتمريرها إلى هنا وهناك، لكنّها لا تُحدِّد ظاهريّا أيّ شيء بمفردها.
</p>

<p>
	يُمثَّل تحديد المستند بواسطة الكائن <code>Selection</code>، الذي يمكن الحصول عليه من <code>window.getSelection()‎</code> أو <code>document.getSelection()‎</code>. قد يتضمّن التحديد صفرا فأكثر من اﻷمداء. على الأقلّ، كما تنصّ على ذلك <a data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" href="https://www.w3.org/TR/selection-api/" rel="external nofollow">مواصفة الواجهة البرمجيّة للتحديد</a>. لكن عمليّا، فايرفوكس فقط هو الذي يمكّن من تحديد أمداء متعدّدة في المستند باستخدام المفاتيح <code>Ctrl+click</code>‏ (<code>Cmd+click</code> بالنسبة لـ Mac).
</p>

<p>
	هذه لقطة شاشة فيها تحديد بثلاثة أمداء، تمّ عملها في فايرفوكس:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="67996" data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" href="https://academy.hsoub.com/uploads/monthly_2021_05/selection-firefox.png.3464982a51f9dae60f42f8b822b354c5.png" rel=""><img alt="selection-firefox.png" class="ipsImage ipsImage_thumbnailed" data-fileid="67996" data-unique="3grxfjkpa" src="https://academy.hsoub.com/uploads/monthly_2021_05/selection-firefox.png.3464982a51f9dae60f42f8b822b354c5.png"></a>
</p>

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

<h2>
	خاصيات التحديد
</h2>

<p>
	بشكل مشابه للمدى، يملك التحديد بداية، تُسمّى "المرساة (anchor)"، ونهاية تُسمّى "التركيز (focus)".
</p>

<p>
	خاصّيّات التحديد الرئيسيّة هي:
</p>

<ul>
<li>
		<code>anchorNode</code> -- العقدة التي يبدأ فيها التحديد
	</li>
	<li>
		<code>anchorOffset</code> -- الانحراف الذي يبدأ منه التحديد في <code>anchorNode</code>
	</li>
	<li>
		<code>focusNode</code> -- العقدة التي ينتهي فيها التحديد
	</li>
	<li>
		<code>focusOffset</code> -- الانحراف الذي ينتهي عنده التحديد في <code>focusNode</code>
	</li>
	<li>
		<code>isCollapsed</code> --‏ <code>true</code> إذا لم يكن التحديد يحدّد أيّ شيء (مدى فارغ)، أو لم يكن موجودا أصلا
	</li>
	<li>
		<code>rangeCount</code> -- عدد اﻷمداء في التحديد، <code>1</code> على اﻷكثر في جميع المتصفّحات باستثناء فايرفوكس
	</li>
</ul>
<hr>
<p>
	<strong>ملاحظة: قد تكون نهاية التحديد في المستند قبل البداية</strong>
</p>

<p>
	هناك عدة طرق لتحديد المحتوى، حسب وكيل المستخدم (user agent): الفأرة والمفاتيح المختصرة والضغط على الهاتف، إلى غير ذلك. يمكّن بعضها، مثل الفأرة، من عمل نفس التحديد في الاتجاهين: "من اليمين إلى اليسار" و"من اليسار إلى اليمين".
</p>

<p>
	إذا كانت البداية (المرساة) تأتي في المستند قبل النهاية (التركيز)، فيقال أنّ اتجاه هذا التحديد "إلى اﻷمام".
</p>

<p>
	إذا بدأ المستخدم مثلا التحديد بالفأرة وذهب من "Example" إلى "italic":
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="67995" data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" href="https://academy.hsoub.com/uploads/monthly_2021_05/selection-direction-forward.png.aed69869aad1943d2386f8c11c978db1.png" rel=""><img alt="selection-direction-forward.png" class="ipsImage ipsImage_thumbnailed" data-fileid="67995" data-unique="usvo5tbft" src="https://academy.hsoub.com/uploads/monthly_2021_05/selection-direction-forward.png.aed69869aad1943d2386f8c11c978db1.png"></a>
</p>

<p>
	على خلاف ذلك، إذا ذهب من نهاية "italic" إلى "Example"، فإنّ التحديد متّجه "إلى الخلف"، ويكون التركيز فيه قبل المرساة:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="67994" data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" href="https://academy.hsoub.com/uploads/monthly_2021_05/selection-direction-backward.png.594d19a1578a5d5f93e212d056c5774a.png" rel=""><img alt="selection-direction-backward.png" class="ipsImage ipsImage_thumbnailed" data-fileid="67994" data-unique="yc7l50h3z" src="https://academy.hsoub.com/uploads/monthly_2021_05/selection-direction-backward.png.594d19a1578a5d5f93e212d056c5774a.png"></a>
</p>

<p>
	هذا مختلف عن كائنات <code>Range</code> التي تتجه دائما إلى اﻷمام: لا يمكن أن تكون بداية المدى قبل نهايته.
</p>

<hr>
<h2>
	أحداث التحديد
</h2>

<p>
	هناك أحداث تمكّننا من متابعة التحديد:
</p>

<ul>
<li>
		<code>elem.onselectstart</code> -- عندما يبدأ التحديد في <code>elem</code>، كأن يبدأ المستخدم مثلا بتحريك الفأرة والزر مضغوط.

		<ul>
<li>
				يؤدي منع الفعل الافتراضيّ إلى عدم ابتداء التحديد.
			</li>
		</ul>
</li>
	<li>
		<code>document.onselectionchange</code> -- كلّما تغيّر التحديد.
		<ul>
<li>
				يُرجى التنبّه: يمكن أن يُسند هذا المعالج إلى <code>document</code> فقط.
			</li>
		</ul>
</li>
</ul>
<h3>
	مثال لتتبع التحديد
</h3>

<p>
	إليك مثالا صغيرا يظهر حدود التحديد ديناميكيّا حال تغيّرها:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3295_26" style="">
<span class="tag">&lt;p</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"p"</span><span class="tag">&gt;</span><span class="pln">Select me: </span><span class="tag">&lt;i&gt;</span><span class="pln">italic</span><span class="tag">&lt;/i&gt;</span><span class="pln"> and </span><span class="tag">&lt;b&gt;</span><span class="pln">bold</span><span class="tag">&lt;/b&gt;&lt;/p&gt;</span><span class="pln">

From </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"from"</span><span class="pln"> </span><span class="atn">disabled</span><span class="tag">&gt;</span><span class="pln"> – To </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"to"</span><span class="pln"> </span><span class="atn">disabled</span><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;script&gt;</span><span class="pln">
  document</span><span class="pun">.</span><span class="pln">onselectionchange </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let </span><span class="pun">{</span><span class="pln">anchorNode</span><span class="pun">,</span><span class="pln"> anchorOffset</span><span class="pun">,</span><span class="pln"> focusNode</span><span class="pun">,</span><span class="pln"> focusOffset</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">getSelection</span><span class="pun">();</span><span class="pln">

    from</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><span class="pln">anchorNode </span><span class="pun">&amp;&amp;</span><span class="pln"> anchorNode</span><span class="pun">.</span><span class="pln">data</span><span class="pun">}:</span><span class="pln">$</span><span class="pun">{</span><span class="pln">anchorOffset</span><span class="pun">}`;</span><span class="pln">
    to</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><span class="pln">focusNode </span><span class="pun">&amp;&amp;</span><span class="pln"> focusNode</span><span class="pun">.</span><span class="pln">data</span><span class="pun">}:</span><span class="pln">$</span><span class="pun">{</span><span class="pln">focusOffset</span><span class="pun">}`;</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" frameborder="no" height="193" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/dyvVzvQ?height=193&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-selection-range -ex4">See the Pen JS-p2-selection-range -ex4 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<h3>
	مثال للحصول على التحديد
</h3>

<p>
	للحصول على كامل التحديد:
</p>

<ul>
<li>
		على شكل نص: استدعي فقط <code>document.getSelection().toString()‎</code>.
	</li>
	<li>
		على شكل عقد DOM: احصل على الأمداء المنشأة واستدعي توابع <code>cloneContents()‎</code> الخاصّة بها (المدى الأوّل فقط إذا لم نكن ندعم التحديد المتعدّد لفايرفوكس).
	</li>
</ul>
<p>
	هذا مثال للحصول على التحديد سواء على شكل نصّ أو عقد DOM:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3295_28" style="">
<span class="tag">&lt;p</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"p"</span><span class="tag">&gt;</span><span class="pln">Select me: </span><span class="tag">&lt;i&gt;</span><span class="pln">italic</span><span class="tag">&lt;/i&gt;</span><span class="pln"> and </span><span class="tag">&lt;b&gt;</span><span class="pln">bold</span><span class="tag">&lt;/b&gt;&lt;/p&gt;</span><span class="pln">

Cloned: </span><span class="tag">&lt;span</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"cloned"</span><span class="tag">&gt;&lt;/span&gt;</span><span class="pln">
</span><span class="tag">&lt;br&gt;</span><span class="pln">
As text: </span><span class="tag">&lt;span</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"astext"</span><span class="tag">&gt;&lt;/span&gt;</span><span class="pln">

</span><span class="tag">&lt;script&gt;</span><span class="pln">
  document</span><span class="pun">.</span><span class="pln">onselectionchange </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let selection </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">getSelection</span><span class="pun">();</span><span class="pln">

    cloned</span><span class="pun">.</span><span class="pln">innerHTML </span><span class="pun">=</span><span class="pln"> astext</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">

    </span><span class="com">// من اﻷمداء (ندعم التحديد المتعدّد هنا)‏ DOM استنسخ عقد</span><span class="pln">
    </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let 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"> selection</span><span class="pun">.</span><span class="pln">rangeCount</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      cloned</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">selection</span><span class="pun">.</span><span class="pln">getRangeAt</span><span class="pun">(</span><span class="pln">i</span><span class="pun">).</span><span class="pln">cloneContents</span><span class="pun">());</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="com">// على شكل نصّ</span><span class="pln">
    astext</span><span class="pun">.</span><span class="pln">innerHTML </span><span class="pun">+=</span><span class="pln"> selection</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" frameborder="no" height="220" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/wvJrqda?height=220&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-selection-range -ex5">See the Pen JS-p2-selection-range -ex5 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<h2>
	توابع التحديد
</h2>

<p>
	توابع التحديد لإضافة/إزالة اﻷمداء:
</p>

<ul>
<li>
		<code>getRangeAt(i)‎</code> -- يحصل على المدى رقم i، ابتداءًا من <code>0</code>. في جميع المتصفّحات ما عدا فايرفوكس، لا يُستعمل سوى <code>0</code>.
	</li>
	<li>
		<code>addRange(range)‎</code> -- يضيف المدى <code>range</code> إلى التحديد. تتجاهل جميع المتصفّحات ما عدا فايرفوكس هذا الاستدعاء إذا كان للتحديد مدى مرفقًا به
	</li>
	<li>
		<code>removeRange(range)‎</code> -- يزيل المدى <code>range</code> من التحديد.
	</li>
	<li>
		<code>removeAllRanges()‎</code> -- يزيل جميع الأمداء.
	</li>
	<li>
		<code>empty()‎</code> -- اسم بديل لـ <code>removeAllRanges</code>.
	</li>
</ul>
<p>
	زيادة على ذلك، هناك توابع ملائمة للتحكّم في المدى الخاصّ بالتحديد مباشرة، دون <code>Range</code>:
</p>

<ul>
<li>
		<code>collapse(node, offset)‎</code> - استبدل المدى المُحدَّد بواحد جديد يبدأ وينتهي عند العقدة المعطاة، <code>node</code>، وفي الموضع <code>offset</code>.
	</li>
	<li>
		<code>setPosition(node, offset)‎</code> -- اسم بديل لـ <code>collapse</code>.
	</li>
	<li>
		<code>collapseToStart()‎</code> -- يطوي (يستبدل بمدى فارغ) إلى بداية التحديد،
	</li>
	<li>
		<code>collapseToEnd()‎</code> -- يطوي إلى نهاية التحديد،
	</li>
	<li>
		<code>extend(node, offset)‎</code> -- ينقل تركيز التحديد إلى العقدة المعطاة <code>node</code>، والموضع <code>offset</code>،
	</li>
	<li>
		<code>setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset)‎</code> -- يستبدل مدى التحديد بالبداية المعطاة <code>anchorNode/anchorOffset</code> والنهاية <code>focusNode/focusOffset</code>. يُحدّد جميع المحتوى الذي بينها.
	</li>
	<li>
		<code>selectAllChildren(node)‎</code> -- يحدّد جميع أبناء العقدة <code>node</code>.
	</li>
	<li>
		<code>deleteFromDocument()‎</code> -- يزيل المحتوى المُحدَّد من المستند.
	</li>
	<li>
		<code>containsNode(node, allowPartialContainment = false)‎</code> -- يفحص ما إذا كان التحديد يحتوي على العقدة <code>node</code> (جزئيّا إذا كان الوسيط الثاني <code>true</code>).
	</li>
</ul>
<p>
	وبذلك، يمكننا في كثير من المهامّ استدعاء توابع <code>Selection</code>، ولا حاجة للوصول إلى كائن <code>Range</code> المنشأ.
</p>

<p>
	على سبيل المثال، تحديد كامل محتويات الفقرة <code>&lt;p&gt;</code>:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3295_30" style="">
<span class="tag">&lt;p</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"p"</span><span class="tag">&gt;</span><span class="pln">Select me: </span><span class="tag">&lt;i&gt;</span><span class="pln">italic</span><span class="tag">&lt;/i&gt;</span><span class="pln"> and </span><span class="tag">&lt;b&gt;</span><span class="pln">bold</span><span class="tag">&lt;/b&gt;&lt;/p&gt;</span><span class="pln">

</span><span class="tag">&lt;script&gt;</span><span class="pln">
  </span><span class="com">// إلى آخر ابن &lt;p&gt; يحدّد من الابن رقم 0 لـ</span><span class="pln">
  document</span><span class="pun">.</span><span class="pln">getSelection</span><span class="pun">().</span><span class="pln">setBaseAndExtent</span><span class="pun">(</span><span class="pln">p</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> p</span><span class="pun">,</span><span class="pln"> p</span><span class="pun">.</span><span class="pln">childNodes</span><span class="pun">.</span><span class="pln">length</span><span class="pun">);</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" frameborder="no" height="141" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/GRWMvmb?height=141&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-selection-range -ex6">See the Pen JS-p2-selection-range -ex6 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	نفس الشيء باستخدام اﻷمداء:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3295_32" style="">
<span class="tag">&lt;p</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"p"</span><span class="tag">&gt;</span><span class="pln">Select me: </span><span class="tag">&lt;i&gt;</span><span class="pln">italic</span><span class="tag">&lt;/i&gt;</span><span class="pln"> and </span><span class="tag">&lt;b&gt;</span><span class="pln">bold</span><span class="tag">&lt;/b&gt;&lt;/p&gt;</span><span class="pln">

</span><span class="tag">&lt;script&gt;</span><span class="pln">
  let range </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Range</span><span class="pun">();</span><span class="pln">
  range</span><span class="pun">.</span><span class="pln">selectNodeContents</span><span class="pun">(</span><span class="pln">p</span><span class="pun">);</span><span class="pln"> </span><span class="com">// or selectNode(p) to select the &lt;p&gt; tag too</span><span class="pln">

  document</span><span class="pun">.</span><span class="pln">getSelection</span><span class="pun">().</span><span class="pln">removeAllRanges</span><span class="pun">();</span><span class="pln"> </span><span class="com">// clear existing selection if any</span><span class="pln">
  document</span><span class="pun">.</span><span class="pln">getSelection</span><span class="pun">().</span><span class="pln">addRange</span><span class="pun">(</span><span class="pln">range</span><span class="pun">);</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" frameborder="no" height="141" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/jOBGLwM?height=141&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-selection-range -ex7">See the Pen JS-p2-selection-range -ex7 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<hr>
<p>
	<strong>ملاحظة: للقيام بالتحديد، أزل التحديد الموجود أوّلا</strong>
</p>

<p>
	إذا كان التحديد موجودًا، أفرغه أوّلا باستخدام <code>removeAllRanges()‎</code>. ثمّ أضف اﻷمداء. وإلّا، ستتجاهل جميع المتصفّحات ما عدا فايرفوكس الأمداء الجديدة.
</p>

<p>
	يُستثنى من ذلك بعض توابع التحديد، التي تستبدل التحديد الموجود، مثل <code>setBaseAndExtent</code>.
</p>

<hr>
<h2>
	التحديد في عناصر التحكم بالاستمارات
</h2>

<p>
	توفّر عناصر الاستمارات، مثل <code>input</code> و<code>textarea</code>‏ <a data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" href="https://html.spec.whatwg.org/#textFieldSelection" rel="external nofollow">واجهات برمجيّة خاصّة بالتحديد</a>، دون الكائنات <code>Selection</code> أو <code>Range</code>. بما أنّ قيمة المُدخل هي نصّ صرف، وليس HTML، فلا حاجة هناك لهذه الكائنات، كلّ شيء أبسط بكثير.
</p>

<p>
	الخاصّيّات:
</p>

<ul>
<li>
		<code>input.selectionStart</code> -- موضع بداية التحديد (قابل للكتابة)
	</li>
	<li>
		<code>input.selectionEnd</code> -- موضع نهاية التحديد (قابل للكتابة)
	</li>
	<li>
		<code>input.selectionDirection</code> -- اتجاه التحديد، واحد من: "forward" (اﻷمام) أو "backward" (الخلف) أو "none" (إذا كان التحديد بواسطة النقر المزدوج بالفأرة مثلا)
	</li>
</ul>
<p>
	اﻷحداث:
</p>

<ul>
<li>
		<code>input.onselect</code> -- يقع عندما يُحدّد شيء ما
	</li>
</ul>
<p>
	التوابع:
</p>

<ul>
<li>
		<code>input.select()‎</code> -- يحدّد كلّ شيء في عنصر التحكّم بالنصّ (قد يكون <code>textarea</code> بدل <code>input</code>)
	</li>
	<li>
		<code>input.setSelectionRange(start, end, [direction])‎</code> -- يغيّر التحديد ليمتدّ من الموضع <code>start</code> إلى <code>end</code>، في الاتجاه المُعطى (اختياريّ)
	</li>
	<li>
		<code>input.setRangeText(replacement, [start], [end], [selectionMode])‎</code> -- يستبدل المدى من النصّ بنصّ جديد
	</li>
</ul>
<p>
	تضبط الوسائطُ الاختياريّة <code>start</code> و<code>end</code>، إذا أُعطيت، بداية المدى ونهايته، وإلّا فيُعتمد تحديد المستخدم.
</p>

<p>
	يوضّح الوسيط اﻷخير، <code>selectionMode</code>، كيفيّة ضبط التحديد بعد استبدال النصّ. القيم الممكنة هي:
</p>

<ul>
<li>
		<code>"select"</code> -- يُحدَّد النصّ المُدرج الجديد.
	</li>
	<li>
		<code>"start"</code> -- ينطوي مدى التحديد قبل النصّ المُدرج مباشرة (يكون المؤشّر قبله مباشرة).
	</li>
	<li>
		<code>"end"</code> -- ينطوي مدى التحديد بعد النصّ المُدرج مباشرة (يكون المؤشّر بعده مباشرة).
	</li>
	<li>
		<code>"preserve"</code> -- يحاول المحافظة على التحديد. هذه هي القيمة الافتراضيّة.
	</li>
</ul>
<p>
	لنرى الآن هذه التوابع تعمل.
</p>

<h3>
	مثال: تتبع التحديد
</h3>

<p>
	على سبيل المثال، تستخدم هذه الشيفرة حدث <code>onselect</code> لتتبّع التحديد:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3295_34" style="">
<span class="tag">&lt;textarea</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"area"</span><span class="pln"> </span><span class="atn">style</span><span class="pun">=</span><span class="atv">"</span><span class="pln">width</span><span class="pun">:</span><span class="lit">80</span><span class="pun">%;</span><span class="pln">height</span><span class="pun">:</span><span class="lit">60px</span><span class="atv">"</span><span class="tag">&gt;</span><span class="pln">
Selecting in this text updates values below.
</span><span class="tag">&lt;/textarea&gt;</span><span class="pln">
</span><span class="tag">&lt;br&gt;</span><span class="pln">
From </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"from"</span><span class="pln"> </span><span class="atn">disabled</span><span class="tag">&gt;</span><span class="pln"> – To </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"to"</span><span class="pln"> </span><span class="atn">disabled</span><span class="tag">&gt;</span><span class="pln">

</span><span class="tag">&lt;script&gt;</span><span class="pln">
  area</span><span class="pun">.</span><span class="pln">onselect </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    from</span><span class="pun">.</span><span class="pln">value </span><span class="pun">=</span><span class="pln"> area</span><span class="pun">.</span><span class="pln">selectionStart</span><span class="pun">;</span><span class="pln">
    to</span><span class="pun">.</span><span class="pln">value </span><span class="pun">=</span><span class="pln"> area</span><span class="pun">.</span><span class="pln">selectionEnd</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" frameborder="no" height="217" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/bGqorrV?height=217&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-selection-range -ex8">See the Pen JS-p2-selection-range -ex8 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	يُرجى التنبّه:
</p>

<ul>
<li>
		يقع <code>onselect</code> عندما يُحدَّد شيء ما، لكن ليس عندما يُزال التحديد.
	</li>
	<li>
		يجب ألّا يقع <code>document.onselectionchange</code> من أجل التحديدات التي بداخل عناصر التحكّم في الاستمارة، وفقا <a data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" href="https://w3c.github.io/selection-api/#dfn-selectionchange" rel="external nofollow">للمواصفة</a>، إذ ليست له علاقة بـتحديد وأمداء المستند <code>document</code>. تقوم بعض المتصفّحات بتوليده، لكن ينبغي ألّا نعتمد عليه.
	</li>
</ul>
<h3>
	مثال: تحريك المؤشر
</h3>

<p>
	يمكننا تغيير <code>selectionStart</code> و<code>selectionEnd</code>، التي تضبط التحديد.
</p>

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

<p>
	على سبيل المثال:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3295_36" style="">
<span class="tag">&lt;textarea</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"area"</span><span class="pln"> </span><span class="atn">style</span><span class="pun">=</span><span class="atv">"</span><span class="pln">width</span><span class="pun">:</span><span class="lit">80</span><span class="pun">%;</span><span class="pln">height</span><span class="pun">:</span><span class="lit">60px</span><span class="atv">"</span><span class="tag">&gt;</span><span class="pln">
Focus on me, the cursor will be at position 10.
</span><span class="tag">&lt;/textarea&gt;</span><span class="pln">

</span><span class="tag">&lt;script&gt;</span><span class="pln">
  area</span><span class="pun">.</span><span class="pln">onfocus </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">// بتأخير منعدم للتنفيذ بعد انتهاء فعل “التركيز” من المتصفّح setTimeout</span><span class="pln">
    setTimeout</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">
      area</span><span class="pun">.</span><span class="pln">selectionStart </span><span class="pun">=</span><span class="pln"> area</span><span class="pun">.</span><span class="pln">selectionEnd </span><span class="pun">=</span><span class="pln"> </span><span class="lit">10</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="tag">&lt;/script&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" frameborder="no" height="183" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/eYvGEEG?height=183&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-selection-range -ex9">See the Pen JS-p2-selection-range -ex9 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<h3>
	مثال: تعديل التحديد
</h3>

<p>
	لتعديل محتوى التحديد، يمكننا استخدام التابع <code>input.setRangeText()‎</code>. بالطبع، يمكننا قراءة <code>selectionStart/End</code> و، مع معرفتنا بالتحديد، تغيير السلسلة النصّيّة الجزئيّة الموافقة لـ <code>value</code>، لكنّ <code>setRangeText</code> أقوى و أكثر ملائمة في الكثير من اﻷحيان.
</p>

<p>
	هو تابع معقّد بعض الشيء. يقوم في صورته البسيطة ذات وسيط وحيد باستبدال المدى المُحدَّد من طرف المستخدم ويزيل التحديد.
</p>

<p>
	على سبيل المثال، سُيحاط تحديد المستخدم هنا بـ <code>*...*</code>:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3295_38" style="">
<span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"input"</span><span class="pln"> </span><span class="atn">style</span><span class="pun">=</span><span class="atv">"</span><span class="pln">width</span><span class="pun">:</span><span class="lit">200px</span><span class="atv">"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"Select here and click the button"</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">"button"</span><span class="tag">&gt;</span><span class="pln">Wrap selection in stars *...*</span><span class="tag">&lt;/button&gt;</span><span class="pln">

</span><span class="tag">&lt;script&gt;</span><span class="pln">
button</span><span class="pun">.</span><span class="pln">onclick </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">input</span><span class="pun">.</span><span class="pln">selectionStart </span><span class="pun">==</span><span class="pln"> input</span><span class="pun">.</span><span class="pln">selectionEnd</span><span class="pun">)</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="com">// nothing is selected</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  let selected </span><span class="pun">=</span><span class="pln"> input</span><span class="pun">.</span><span class="pln">value</span><span class="pun">.</span><span class="pln">slice</span><span class="pun">(</span><span class="pln">input</span><span class="pun">.</span><span class="pln">selectionStart</span><span class="pun">,</span><span class="pln"> input</span><span class="pun">.</span><span class="pln">selectionEnd</span><span class="pun">);</span><span class="pln">
  input</span><span class="pun">.</span><span class="pln">setRangeText</span><span class="pun">(`*</span><span class="pln">$</span><span class="pun">{</span><span class="pln">selected</span><span class="pun">}*`);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" frameborder="no" height="165" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/jOBGLGV?height=165&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-selection-range -ex10">See the Pen JS-p2-selection-range -ex10 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	بالمزيد من الوسائط، يمكننا ضبط بداية المدى <code>start</code> ونهايته <code>end</code>.
</p>

<p>
	في هذا المثال نجد أنّ <code>"THIS"</code> في نصّ المُدخل، نستبدله ونبقي البديل مُحدّدا:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3295_40" style="">
<span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"input"</span><span class="pln"> </span><span class="atn">style</span><span class="pun">=</span><span class="atv">"</span><span class="pln">width</span><span class="pun">:</span><span class="lit">200px</span><span class="atv">"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"Replace THIS in text"</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">"button"</span><span class="tag">&gt;</span><span class="pln">Replace THIS</span><span class="tag">&lt;/button&gt;</span><span class="pln">

</span><span class="tag">&lt;script&gt;</span><span class="pln">
button</span><span class="pun">.</span><span class="pln">onclick </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">
  let pos </span><span class="pun">=</span><span class="pln"> input</span><span class="pun">.</span><span class="pln">value</span><span class="pun">.</span><span class="pln">indexOf</span><span class="pun">(</span><span class="str">"THIS"</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">pos </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">
    input</span><span class="pun">.</span><span class="pln">setRangeText</span><span class="pun">(</span><span class="str">"*THIS*"</span><span class="pun">,</span><span class="pln"> pos</span><span class="pun">,</span><span class="pln"> pos </span><span class="pun">+</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="str">"select"</span><span class="pun">);</span><span class="pln">
    input</span><span class="pun">.</span><span class="pln">focus</span><span class="pun">();</span><span class="pln"> </span><span class="com">// focus to make selection visible</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" frameborder="no" height="156" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/xxqXLXz?height=156&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-selection-range -ex11">See the Pen JS-p2-selection-range -ex11 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<h3>
	مثال: الإدراج عند المؤشر
</h3>

<p>
	إذا لم يكن هناك شيء مُحدّد، أو استخدمنا <code>start</code> و<code>end</code> متساويتان في <code>setRangeText</code>، فسيُدرج النصّ الجديد فقط، ولن يزال أيّ شيء، ويمكننا أيضا إدراج شيء ما "عند المؤشّر" باستخدام <code>setRangeText</code>.
</p>

<p>
	هذا زرّ يعمل على إدراج <code>"HELLO"</code> عند موضع المؤشّر ويضع المؤشّر بعده مباشرة. إذا لم يكن التحديد فارغا، فإنّه يُستبدل (يمكننا اكتشاف ذلك بالمقارنة <code>selectionStart!=selectionEnd</code> وعمل شيء آخر بدل ذلك):
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3295_42" style="">
<span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"input"</span><span class="pln"> </span><span class="atn">style</span><span class="pun">=</span><span class="atv">"</span><span class="pln">width</span><span class="pun">:</span><span class="lit">200px</span><span class="atv">"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"Text Text Text Text Text"</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">"button"</span><span class="tag">&gt;</span><span class="pln">Insert "HELLO" at cursor</span><span class="tag">&lt;/button&gt;</span><span class="pln">

</span><span class="tag">&lt;script&gt;</span><span class="pln">
  button</span><span class="pun">.</span><span class="pln">onclick </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">
    input</span><span class="pun">.</span><span class="pln">setRangeText</span><span class="pun">(</span><span class="str">"HELLO"</span><span class="pun">,</span><span class="pln"> input</span><span class="pun">.</span><span class="pln">selectionStart</span><span class="pun">,</span><span class="pln"> input</span><span class="pun">.</span><span class="pln">selectionEnd</span><span class="pun">,</span><span class="pln"> </span><span class="str">"end"</span><span class="pun">);</span><span class="pln">
    input</span><span class="pun">.</span><span class="pln">focus</span><span class="pun">();</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">    
</span><span class="tag">&lt;/script&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" frameborder="no" height="150" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/xxqXLPz?height=150&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-selection-range -ex12">See the Pen JS-p2-selection-range -ex12 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<h2>
	جعل الشيء غير قابل للتحديد
</h2>

<p>
	لجعل شيء ما غير قابل للتحديد، هناك ثلاث طرق:
</p>

<h3>
	الطريقة الأولى
</h3>

<p>
	باستخدام الخاصّيّة <code>user-select: none</code> في CSS
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3295_44" style="">
<span class="pln">    </span><span class="tag">&lt;style&gt;</span><span class="pln">
    </span><span class="com">#elem {</span><span class="pln">
      user</span><span class="pun">-</span><span class="kwd">select</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="tag">&lt;/style&gt;</span><span class="pln">
    </span><span class="tag">&lt;div&gt;</span><span class="pln">Selectable </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"elem"</span><span class="tag">&gt;</span><span class="pln">Unselectable</span><span class="tag">&lt;/div&gt;</span><span class="pln"> Selectable</span><span class="tag">&lt;/div&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" frameborder="no" height="168" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/XWMeaVZ?height=168&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-selection-range -ex13">See the Pen JS-p2-selection-range -ex13 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	لا يسمح هذا للتحديد بالبدء عند <code>elem</code> لكن قد يبدأ المستخدم التحديد من مكان آخر ويدخل فيه <code>elem</code>.
</p>

<p>
	وبذلك يصير <code>elem</code> جزءًا من <code>document.getSelection()‎</code>، ويكون التحديد قد وقع فعلًا، إلّا أنّ مُحتواه يُتجاهل عادة عند النسخ-اللصق.
</p>

<h3>
	الطريقة الثانية
</h3>

<p>
	بمنع الفعل الافتراضيّ عند اﻷحداث <code>onselectstart</code> أو <code>mousedown</code>:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3295_46" style="">
<span class="pln">    </span><span class="tag">&lt;div&gt;</span><span class="pln">Selectable </span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"elem"</span><span class="tag">&gt;</span><span class="pln">Unselectable</span><span class="tag">&lt;/div&gt;</span><span class="pln"> Selectable</span><span class="tag">&lt;/div&gt;</span><span class="pln">

    </span><span class="tag">&lt;script&gt;</span><span class="pln">
      elem</span><span class="pun">.</span><span class="pln">onselectstart </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">false</span><span class="pun">;</span><span class="pln">
    </span><span class="tag">&lt;/script&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" frameborder="no" height="168" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/eYvGEVe?height=168&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-selection-range -ex14">See the Pen JS-p2-selection-range -ex14 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	يمنع هذا بدأ التحديد من <code>elem</code>، لكن الزائر قد يبدأ من عنصر آخر، ثمّ يمدّه إلى <code>elem</code>.
</p>

<p>
	هذا ملائم عندما يكون هناك معالج حدث آخر لنفس الفعل الذي يُحدث التحديد (<code>mousedown</code> مثلا). فيمكننا إذًا تعطيل التحديد لتجنّب التعارض، مع السماح لمحتويات <code>elem</code> أن تُنسخ.
</p>

<h3>
	الطريقة الثالثة
</h3>

<p>
	يمكننا أيضا مسح التحديد بعد حصوله باستخدام <code>document.getSelection().empty()‎</code>. يندر استخدام هذا، إذ يتسبّب في ومضات غير محبّذة عند ظهور-اختفاء التحديد.
</p>

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

<p>
	تناولنا واجهتين برمجيّتين للتحديدات:
</p>

<ol>
<li>
		للصفحة: الكائنان <code>Selection</code> و<code>Range</code>.
	</li>
	<li>
		للمدخلات النصية <code>input</code> و<code>textarea</code>: توابع وخاصّيّات إضافيّة.
	</li>
</ol>
<p>
	الواجهة البرمجيّة الثانية بسيطة للغاية، إذ تعمل مع النصّ.
</p>

<p>
	قد تكون أكثر الوصفات استخداما هي:
</p>

<ol>
<li>
		الحصول على التحديد:
	</li>
</ol>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3295_48" style="">
<span class="pln">    let selection </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">getSelection</span><span class="pun">();</span><span class="pln">

    let cloned </span><span class="pun">=</span><span class="pln"> </span><span class="com">/* العنصر الذي تُستنسخ فيه العقد المُحدَّدة*/</span><span class="pun">;</span><span class="pln">

    </span><span class="com">// selection.getRangeAt(0) على Range ثمّ طبّق توابع</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="pln">let 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"> selection</span><span class="pun">.</span><span class="pln">rangeCount</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      cloned</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">selection</span><span class="pun">.</span><span class="pln">getRangeAt</span><span class="pun">(</span><span class="pln">i</span><span class="pun">).</span><span class="pln">cloneContents</span><span class="pun">());</span><span class="pln">
    </span><span class="pun">}</span></pre>

<ol start="2">
<li>
		ضبط التحديد
	</li>
</ol>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3295_50" style="">
<span class="pln">    let selection </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">getSelection</span><span class="pun">();</span><span class="pln">

    </span><span class="com">// :مباشرة</span><span class="pln">
    selection</span><span class="pun">.</span><span class="pln">setBaseAndExtent</span><span class="pun">(...</span><span class="pln">from</span><span class="pun">...</span><span class="pln">to</span><span class="pun">...);</span><span class="pln">

    </span><span class="com">// أو يمكننا إنشاء مدى و:‏</span><span class="pln">
    selection</span><span class="pun">.</span><span class="pln">removeAllRanges</span><span class="pun">();</span><span class="pln">
    selection</span><span class="pun">.</span><span class="pln">addRange</span><span class="pun">(</span><span class="pln">range</span><span class="pun">);</span></pre>

<p>
	وفي النهاية، بالنسبة للمؤشّر. يكون موضع المؤشّر في العناصر القابلة للتحرير، مثل <code>&lt;textarea&gt;</code>، دائما عند بداية التحديد أو نهايته. يمكننا استخدام ذلك للحصول على موضع المؤشّر أو لتحريك المؤشّر بضبط <code>elem.selectionStart</code> و<code>elem.selectionEnd</code>.
</p>

<h2>
	المراجع
</h2>

<ul>
<li>
		<a data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" href="https://dom.spec.whatwg.org/#ranges" rel="external nofollow">مواصفة DOM: المدى</a>
	</li>
	<li>
		<a data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" href="https://www.w3.org/TR/selection-api/#dom-globaleventhandlers-onselectstart" rel="external nofollow">الواجهة البرمجيّة للتحديد</a>
	</li>
	<li>
		<a data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#textFieldSelection" rel="external nofollow">مواصفة HTML: الواجهات البرمجيّة لتحديدات عناصر التحكّم في النصّ</a>
	</li>
</ul>
<p>
	ترجمة -وبتصرف- للمقال <a data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" href="https://javascript.info/selection-range" rel="external nofollow">Selection and Range</a> من سلسلة <a data-ss1622214531="1" data-ss1622214923="1" data-ss1622215033="1" data-ss1623482990="1" href="https://javascript.info/ui" rel="external nofollow">Browser: Document, Events, Interfaces</a> لصاحبها Ilya Kantor
</p>
]]></description><guid isPermaLink="false">1240</guid><pubDate>Sat, 12 Jun 2021 09:14:00 +0000</pubDate></item><item><title>&#x645;&#x631;&#x627;&#x642;&#x628; &#x627;&#x644;&#x62A;&#x62D;&#x648;&#x644; MutationObserver &#x639;&#x628;&#x631; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A; &#x644;&#x645;&#x631;&#x627;&#x642;&#x628;&#x629; &#x634;&#x62C;&#x631;&#x629; DOM</title><link>https://academy.hsoub.com/programming/javascript/%D9%85%D8%B1%D8%A7%D9%82%D8%A8-%D8%A7%D9%84%D8%AA%D8%AD%D9%88%D9%84-mutationobserver-%D8%B9%D8%A8%D8%B1-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-%D9%84%D9%85%D8%B1%D8%A7%D9%82%D8%A8%D8%A9-%D8%B4%D8%AC%D8%B1%D8%A9-dom-r1239/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_05/Mutation-observer.png.d8094fb4e499e3d1c7b4d5f2c3dafbf1.png" /></p>

<p>
	الكائن <code>MutationObserver</code> هو كائن مُضمّن built-in object يعمل على مراقبة عنصر من DOM ويطلق ردّ نداء عندما يلاحظ تغيّرا ما. سنلقي في البداية نظرة على صيغة استعماله، ونستكشف بعدها حالة استخدام واقعيّة، لرؤية متى قد يكون مفيدا.
</p>

<h2>
	الصيغة
</h2>

<p>
	يعد <code>MutationObserver</code> سهل الاستخدام. أوّلا، ننشئ مراقبا مع دالّة ردّ نداء:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9087_6" style="">
<span class="pln">let observer </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">MutationObserver</span><span class="pun">(</span><span class="pln">callback</span><span class="pun">);</span></pre>

<p>
	ثمّ نربطه بعقدة في DOM:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9087_8" style="">
<span class="pln">observer</span><span class="pun">.</span><span class="pln">observe</span><span class="pun">(</span><span class="pln">node</span><span class="pun">,</span><span class="pln"> config</span><span class="pun">);</span></pre>

<p>
	<code>config</code> هو كائنٌ بخيارات بوليانية تمثّل "نوع التغيرات التي يُستجاب لها":
</p>

<ul>
<li>
		<code>childList</code> -- التغيرات في الأبناء المباشرين للعقدة <code>node</code>،
	</li>
	<li>
		<code>subtree</code> -- في جميع العناصر السليلة للعقدة <code>node</code>،
	</li>
	<li>
		<code>attributes</code> -- سمات العقدة <code>node</code>،
	</li>
	<li>
		<code>attributeFilter</code> -- مصفوفة بأسماء السمات، لمراقبة المحدّدة منها فقط،
	</li>
	<li>
		<code>characterData</code> -- ما إذا تُراقَب <code>node.data</code> (المحتوى النصّي)،
	</li>
</ul>
<p>
	بعض الخيارات الأخرى:
</p>

<ul>
<li>
		<code>attributeOldValue</code> -- إذا كانت <code>true</code>، تُمرّر كلا القيمتان القديمة والجديدة للسمة إلى دالة ردّ النداء (انظر أسفله) وإلّا فالجديدة فقط (تحتاج الخيار <code>attributes</code>)،
	</li>
	<li>
		<code>characterDataOldValue</code> -- إذا كانت <code>true</code>، تُمرّر كلا القيمتان القديمة والجديدة لـ <code>node.data</code> إلى دالّة ردّ النداء (انظر أسفله) وإلّا فالجديدة فقط (تحتاج الخيار <code>characterData</code>).
	</li>
</ul>
<p>
	وبذلك بعد أيّ تغيّر، تُنفّذ <code>callback</code>: تُمرَّر التغيّرات كوسيط أوّل على شكل قائمة من كائنات سجلّات التحوّل <a data-ss1622214051="1" data-ss1622214265="1" data-ss1622214295="1" data-ss1623482993="1" href="https://dom.spec.whatwg.org/#mutationrecord" rel="external nofollow">MutationRecord</a>، والمراقب نفسه كوسيط ثاني.
</p>

<p>
	لكائنات <a data-ss1622214051="1" data-ss1622214265="1" data-ss1622214295="1" data-ss1623482993="1" href="https://dom.spec.whatwg.org/#mutationrecord" rel="external nofollow">MutationRecord</a> الخاصيّات التالية:
</p>

<ul>
<li>
		<code>type</code> -- نوع التحوّل، واحد من:

		<ul>
<li>
				<code>"attributes"</code>: تغيّرت السمة
			</li>
			<li>
				<code>"characterData"</code>: تغيّرت البيانات، تُستخدم مع العقد النصّيّة
			</li>
			<li>
				<code>"childList"</code>: أضيفت/أزيلت عناصر أبناء
			</li>
		</ul>
</li>
	<li>
		<code>target</code> -- أين وقع التغيّر: يكون عنصرا بالنسبة لـ <code>"attributes"</code>، أوعقدة نصيّة بالنسبة لـ <code>"characterData"</code>، أو عنصرا بالنسبة لتحوّل <code>"childList"</code>
	</li>
	<li>
		<code>addedNodes/removedNodes</code> -- العقد التي أضيفت/أزيلت
	</li>
	<li>
		<code>previousSibling/nextSibling</code> -- الأخ السابق واللاحق للعقد التي أضيفت/أزيلت
	</li>
	<li>
		<code>attributeName/attributeNamespace</code> -- اسم/مساحة اسم namespace بالنسبة لـ XML للسمة التي تغيّرت
	</li>
	<li>
		<code>oldValue</code> -- القيمة السابقة، فقط للتغيّرات في السمات والعقد النصّيّة، إذا كان الخيار الموافق مضبوطا <code>attributeOldValue </code>/ <code>characterDataOldValue</code>
	</li>
</ul>
<p>
	على سبيل المثال، هذا الـ <code>&lt;div&gt;</code> له السمة <code>contentEditable</code>. تمكّننا هذه السمة من التركيز عليه و تعديله.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9087_10" style="">
<span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">contentEditable</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"elem"</span><span class="tag">&gt;</span><span class="pln">Click and </span><span class="tag">&lt;b&gt;</span><span class="pln">edit</span><span class="tag">&lt;/b&gt;</span><span class="pln">, please</span><span class="tag">&lt;/div&gt;</span><span class="pln">

</span><span class="tag">&lt;script&gt;</span><span class="pln">
let observer </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">MutationObserver</span><span class="pun">(</span><span class="pln">mutationRecords </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">mutationRecords</span><span class="pun">);</span><span class="pln"> </span><span class="com">// console.log(التغييرات)</span><span class="pln">
</span><span class="pun">});</span><span class="pln">

</span><span class="com">// راقب كلّ شيء ما عدا السمات</span><span class="pln">
observer</span><span class="pun">.</span><span class="pln">observe</span><span class="pun">(</span><span class="pln">elem</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  childList</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln"> </span><span class="com">// راقب اﻷبناء المباشرين</span><span class="pln">
  subtree</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln"> </span><span class="com">// وكذلك العناصر السليلة في اﻷسفل</span><span class="pln">
  characterDataOldValue</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="com">// مرّر البيانات القديمة إلى ردّ النداء</span><span class="pln">
</span><span class="pun">});</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<p>
	إذا أجرينا هذا المثال في متصفّح، ثمّ ركّزنا على ذلك الـ <code>&lt;div&gt;</code> وغيّرنا النصّ الذي بداخل <code>&lt;b&gt;edit&lt;/b&gt;</code>، ستُظهر <code>console.log</code> تحوّلا واحدًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9087_18" style="">
<span class="pln">mutationRecords </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[{</span><span class="pln">
  type</span><span class="pun">:</span><span class="pln"> </span><span class="str">"characterData"</span><span class="pun">,</span><span class="pln">
  oldValue</span><span class="pun">:</span><span class="pln"> </span><span class="str">"edit"</span><span class="pun">,</span><span class="pln">
  target</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">text node</span><span class="pun">&gt;,</span><span class="pln">
  </span><span class="com">// الخاصّيّات اﻷخرى فارغة</span><span class="pln">
</span><span class="pun">}];</span></pre>

<p>
	إذا أجرينا تغييرات أكثر تعقيدا، مثل إزالة <code>&lt;b&gt;edit&lt;/b&gt;</code>، فإنّ حدث التحوّل قد يحتوي على عدّة سجلّات تحوّل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9087_16" style="">
<span class="pln">mutationRecords </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[{</span><span class="pln">
  type</span><span class="pun">:</span><span class="pln"> </span><span class="str">"childList"</span><span class="pun">,</span><span class="pln">
  target</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">div</span><span class="pun">#</span><span class="pln">elem</span><span class="pun">&gt;,</span><span class="pln">
  removedNodes</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[&lt;</span><span class="pln">b</span><span class="pun">&gt;],</span><span class="pln">
  nextSibling</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">text node</span><span class="pun">&gt;,</span><span class="pln">
  previousSibling</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">text node</span><span class="pun">&gt;</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><span class="pln">
  type</span><span class="pun">:</span><span class="pln"> </span><span class="str">"characterData"</span><span class="pln">
  target</span><span class="pun">:</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln">text node</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="com">// تعتمد تفاصيل التحوّل على كيفيّة تعامل المتصفّح مع مثل هذا الحذف...</span><span class="pln">
  </span><span class="com">// في عقدة واحدة ", please" و "edit " قد يجمع بين العقدتين المتجاورتين</span><span class="pln">
  </span><span class="com">// أو قد يبقيهما عقدتين نصّيّتين منفصلتين</span><span class="pln">
</span><span class="pun">}];</span></pre>

<p>
	وبذلك، تمكّن <code>MutationObserver</code> من الاستجابة لأيّة تغيّرات ضمن الشجرة الفرعيّة.
</p>

<h2>
	الاستخدام في الإدماج
</h2>

<p>
	فيما قد يفيدنا شيء كهذا؟
</p>

<p>
	تصوّر حالة تحتاج فيها إلى إضافة سكربت طرف ثالث يحتوي على وظيفة مفيدة، لكنّه أيضا يقوم بشيء غير مرغوب كإظهار الإعلانات <code>&lt;div class="ads"&gt;Unwanted ads&lt;/div&gt;</code> مثلا، وبالطبع، لن توفّر سكربتات الطرف الثالث تلك آليّات لإزالتها.
</p>

<p>
	باستخدام <code>MutationObserver</code>، يمكننا اكتشاف ظهور العنصر غير المرغوب في DOM وإزالته.
</p>

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

<h2>
	الاستخدام في الهندسة
</h2>

<p>
	هناك أيضا حالات يكون فيها <code>MutationObserver</code> جيّدا من المنظور الهندسيّ.
</p>

<p>
	لنفترض أنّنا بصدد إنشاء موقع عن البرمجة. بالطبع، قد تحتوي المقالات و الموادّ الأخرى على قصاصات من الشيفرات المصدريّة.
</p>

<p>
	تبدو هذه القصاصات من HTML هكذا:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9087_20" style="">
<span class="pln">...
</span><span class="tag">&lt;pre</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"language-javascript"</span><span class="tag">&gt;&lt;code&gt;</span><span class="pln">
  // هذه هي الشيفرة
  let hello = "world";
</span><span class="tag">&lt;/code&gt;&lt;/pre&gt;</span><span class="pln">
...</span></pre>

<p>
	لمقروئيّة أفضل، وفي الوقت نفسه لتجميلها، سنستخدم في موقعنا (موقع <a data-ss1623482993="1" href="http://javascript.info" rel="external nofollow">javascript.info</a>) مكتبة لتلوين أو إبراز صيغة جافاسكربت، مثل <a data-ss1622214051="1" data-ss1622214265="1" data-ss1622214295="1" data-ss1623482993="1" href="https://prismjs.com/" rel="external nofollow">Prism.js</a>. لإبراز الصيغة في القصاصات أعلاه بواسطة Prism، يُستدعى <code>Prism.highlightElem(pre)‎</code>، الذي يفحص محتويات عناصر <code>pre</code> تلك ويضيف لها وسوما خاصّة وتنسيقات بغرض الإبراز الملوّن للصيغة، مثلما ترى في اﻷمثلة التي في هذه الصفحة.
</p>

<p>
	متى ينبغي أن نجري عمليّة الإبراز تلك؟ حسنا، يمكننا القيام بها عند الحدث <code>DOMContentLoaded</code>، أو بوضع السكربت في أسفل الصفحة. حالما يكون DOM الذي لدينا جاهزا، يمكننا البحث عن العناصر <code>pre[class*="language"]‎</code> واستدعاء <code>Prism.highlightElem</code> عليها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9087_22" style="">
<span class="com">// أبرز جميع قصاصات الشيفرات على الصفحة</span><span class="pln">
document</span><span class="pun">.</span><span class="pln">querySelectorAll</span><span class="pun">(</span><span class="str">'pre[class*="language"]'</span><span class="pun">).</span><span class="pln">forEach</span><span class="pun">(</span><span class="typ">Prism</span><span class="pun">.</span><span class="pln">highlightElem</span><span class="pun">);</span></pre>

<p>
	إلى الآن كلّ شيء بسيط، صحيح؟ نعثر على قصاصات الشيفرات في HTML ونبرزها.
</p>

<p>
	لنواصل الآن. لنفترض أنّنا سنجلب موادّ من الخادم ديناميكيّا. سندرس توابع لهذا الغرض <a data-ss1622214051="1" data-ss1622214265="1" data-ss1622214295="1" data-ss1623482993="1" href="fetch#" rel="">لاحقا في هذا الدليل</a>. ما يهمّ حاليّا هو أن نجلب مقال HTML من خادم الويب وعرضه حسب الطلب:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9087_24" style="">
<span class="pln">let article </span><span class="pun">=</span><span class="pln"> </span><span class="com">/* جلب المحتوى الجديد من الخادم */</span><span class="pln">
articleElem</span><span class="pun">.</span><span class="pln">innerHTML </span><span class="pun">=</span><span class="pln"> article</span><span class="pun">;</span></pre>

<p>
	قد يحتوي مقال HTML الجديد <code>article</code> على قصاصات شيفرات. نحتاج إلى استدعاء <code>Prism.highlightElem</code> عليها، وإلّا فلن يتمّ إبرازها.
</p>

<p>
	<strong>أين ومتى يُستدعى <code>Prism.highlightElem</code> للمقالات المحمّلة ديناميكيّا؟</strong>
</p>

<p>
	يمكننا إلحاق ذلك الاستدعاء بالشيفرة التي تحمّل المقال، هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9087_26" style="">
<span class="pln">let article </span><span class="pun">=</span><span class="pln"> </span><span class="com">/* جلب المحتوى الجديد من الخادم */</span><span class="pln">
articleElem</span><span class="pun">.</span><span class="pln">innerHTML </span><span class="pun">=</span><span class="pln"> article</span><span class="pun">;</span><span class="pln">

let snippets </span><span class="pun">=</span><span class="pln"> articleElem</span><span class="pun">.</span><span class="pln">querySelectorAll</span><span class="pun">(</span><span class="str">'pre[class*="language-"]'</span><span class="pun">);</span><span class="pln">
snippets</span><span class="pun">.</span><span class="pln">forEach</span><span class="pun">(</span><span class="typ">Prism</span><span class="pun">.</span><span class="pln">highlightElem</span><span class="pun">);</span></pre>

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

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

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

<h3>
	عرض الإبراز الديناميكي
</h3>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9087_28" style="">
<span class="pln">let observer </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">MutationObserver</span><span class="pun">(</span><span class="pln">mutations </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  </span><span class="kwd">for</span><span class="pun">(</span><span class="pln">let mutation of mutations</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="pun">(</span><span class="pln">let node of mutation</span><span class="pun">.</span><span class="pln">addedNodes</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">node instanceof </span><span class="typ">HTMLElement</span><span class="pun">))</span><span class="pln"> </span><span class="kwd">continue</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">node</span><span class="pun">.</span><span class="pln">matches</span><span class="pun">(</span><span class="str">'pre[class*="language-"]'</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Prism</span><span class="pun">.</span><span class="pln">highlightElement</span><span class="pun">(</span><span class="pln">node</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="pun">(</span><span class="pln">let elem of node</span><span class="pun">.</span><span class="pln">querySelectorAll</span><span class="pun">(</span><span class="str">'pre[class*="language-"]'</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="typ">Prism</span><span class="pun">.</span><span class="pln">highlightElement</span><span class="pun">(</span><span class="pln">elem</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">

let demoElem </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">'highlight-demo'</span><span class="pun">);</span><span class="pln">

observer</span><span class="pun">.</span><span class="pln">observe</span><span class="pun">(</span><span class="pln">demoElem</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">childList</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln"> subtree</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">});</span></pre>

<p>
	في اﻷسفل عنصر HTML وجافاسكربت لملئه ديناميكيّا باستخدام <code>innerHTML</code>.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="67987" data-ss1622214051="1" data-ss1622214265="1" data-ss1622214295="1" data-ss1623482993="1" href="https://academy.hsoub.com/uploads/monthly_2021_05/example1.png.3532be9be6ede14d8d4a46eda467b84c.png" rel=""><img alt="example1.png" class="ipsImage ipsImage_thumbnailed" data-fileid="67987" data-unique="hyv5j5wgz" src="https://academy.hsoub.com/uploads/monthly_2021_05/example1.png.3532be9be6ede14d8d4a46eda467b84c.png"></a>
</p>

<p>
	بإجراء الشيفرة السابقة (التي في اﻷعلى) يُراقب العنصر، ثمّ عند الشيفرة التي في اﻷسفل. تكتشف <code>MutationObserver</code> القصاصة وتبرزها، هكذا:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="67988" data-ss1622214051="1" data-ss1622214265="1" data-ss1622214295="1" data-ss1623482993="1" href="https://academy.hsoub.com/uploads/monthly_2021_05/example2.png.1acffa22cf37ef4fb23a0fa628e3f96d.png" rel=""><img alt="example2.png" class="ipsImage ipsImage_thumbnailed" data-fileid="67988" data-unique="yn9lzsq86" src="https://academy.hsoub.com/uploads/monthly_2021_05/example2.png.1acffa22cf37ef4fb23a0fa628e3f96d.png"></a>
</p>

<p>
	تملأ الشيفرة التالية <code>innerHTML</code> الخاصّ بالعنصر، ما يؤدّي بـ <code>MutationObserver</code> إلى الاستجابة وإبراز محتوياته:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_9087_30" style="">
<span class="pln">let demoElem = document.getElementById('highlight-demo');

// إدراج المحتوى الذي فيه قصاصات الشيفرات ديناميكيّا
demoElem.innerHTML = `A code snippet is below:
  </span><span class="tag">&lt;pre</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"language-javascript"</span><span class="tag">&gt;&lt;code&gt;</span><span class="pln"> let hello = "world!"; </span><span class="tag">&lt;/code&gt;&lt;/pre&gt;</span><span class="pln">
  </span><span class="tag">&lt;div&gt;</span><span class="pln">Another one:</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;pre</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"language-css"</span><span class="tag">&gt;&lt;code&gt;</span><span class="pln">.class { margin: 5px; } </span><span class="tag">&lt;/code&gt;&lt;/pre&gt;</span><span class="pln">
  </span><span class="tag">&lt;/div&gt;</span><span class="pln">
`;</span></pre>

<p>
	لدينا الآن <code>MutationObserver</code> يمكنه تتبّع جميع عمليّات الإبراز في العناصر المراقبة أو في كامل <code>document</code>. يمكننا إضافة/إزالة قصاصات الشيفرات في HTML دون التفكير في ذلك.
</p>

<h2>
	توابع إضافية
</h2>

<p>
	هناك تابع لإيقاف مراقبة العقدة:
</p>

<ul>
<li>
		<code>observer.disconnect()‎</code> -- يوقف المراقبة.
	</li>
</ul>
<p>
	عندما نوقف المراقبة، قد يكون من الممكن أنّ بعض التغيّرات لم تُعالج بعد من طرف المراقب. في تلك الحالات، نستخدم
</p>

<ul>
<li>
		<code>observer.takeRecords()‎</code> -- يحصل على قائمة سجّلات التحوّلات غير المعالجة - تلك التي حصلت، لكنّ ردود النداء لم تعالجها.
	</li>
</ul>
<p>
	ويمكن أن تُستخدم هذه التوابع معا، هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_9087_32" style="">
<span class="com">// احصل على قائمة التحوّلات التي لم تُعالج</span><span class="pln">
</span><span class="com">// يجب أن تُستدعى قبل قطع الاتصال</span><span class="pln">
</span><span class="com">// إذا كنت تهتمّ للتحوّلات الحديثة التي قد لا تكون عولجت</span><span class="pln">
let mutationRecords </span><span class="pun">=</span><span class="pln"> observer</span><span class="pun">.</span><span class="pln">takeRecords</span><span class="pun">();</span><span class="pln">

</span><span class="com">// أوقف تتبّع التغيّرات</span><span class="pln">
observer</span><span class="pun">.</span><span class="pln">disconnect</span><span class="pun">();</span><span class="pln">
</span><span class="pun">...</span></pre>

<hr>
<p>
	<strong>ملاحظة: تُحذف السجلّات التي يعيدها <code>observer.takeRecords()‎</code> من رتل المعالجة</strong>
</p>

<p>
	لن يُستدعى ردّ النداء للسجلّات المعادة من طرف <code>observer.takeRecords()‎</code>.
</p>

<hr>
<hr>
<p>
	<strong>ملاحظة: التفاعل مع جمع المهملات (garbage collection)</strong>
</p>

<p>
	تستخدم المراقبات داخليّا مراجع ضعيفة (weak references) للعقد. بمعنى، إذا حُذفت عقدة ما من DOM، وصارت غير قابلة ل لوصول unreachable، فيمكن عندئذ <a data-ss1623482993="1" href="https://academy.hsoub.com/programming/javascript/%D9%83%D9%86%D8%B3-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D9%87%D9%85%D9%84%D8%A9-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r797/" rel="">جمعها مع المهملات</a>.
</p>

<p>
	مجرّد كون عقدة ما من DOM تحت المراقبة لا يمنع جمعها مع المهملات.
</p>

<hr>
<h2>
	الملخص
</h2>

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

<p>
	يستطيع <code>MutationObserver</code> تتبّع أيّ تغيير. تُستخدم خيارات ضبط "مالذي يُراقب" لغرض التحسينات optimizations، لا لإنفاق الموارد على استدعاءات ردّ نداء لا حاجة لها.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a data-ss1622214051="1" data-ss1622214265="1" data-ss1622214295="1" data-ss1623482993="1" href="https://javascript.info/mutation-observer" rel="external nofollow">Mutation observer</a> من سلسلة <a data-ss1622214051="1" data-ss1622214265="1" data-ss1622214295="1" data-ss1623482993="1" href="https://javascript.info/ui" rel="external nofollow">Browser: Document, Events, Interfaces</a> لصاحبها Ilya Kantor.
</p>

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

<ul>
<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%AA%D8%AD%D9%85%D9%8A%D9%84-%D8%A7%D9%84%D9%85%D9%88%D8%A7%D8%B1%D8%AF-%D8%A7%D9%84%D8%AE%D8%A7%D8%B1%D8%AC%D9%8A%D8%A9-%D9%81%D9%8A-%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%88%D8%AA%D8%AA%D8%A8%D8%B9-%D8%AD%D8%A7%D9%84%D8%AA%D9%87%D8%A7-%D8%B9%D8%A8%D8%B1-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1238/" rel="">تحميل الموارد الخارجية في صفحات الويب وتتبع حالتها عبر جافاسكربت</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/javascript/%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%A7%D9%84%D8%A7%D8%B3%D8%AA%D9%85%D8%A7%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%A5%D9%84%D9%83%D8%AA%D8%B1%D9%88%D9%86%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AA%D9%86%D9%82%D9%84-%D8%A8%D9%8A%D9%86%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-r1194/" rel="">التركيز على عناصر الاستمارات الإلكترونية والتنقل بينها في جافاسكربت</a>.
	</li>
</ul>
]]></description><guid isPermaLink="false">1239</guid><pubDate>Sat, 12 Jun 2021 11:03:00 +0000</pubDate></item><item><title>&#x62A;&#x62D;&#x645;&#x64A;&#x644; &#x627;&#x644;&#x645;&#x648;&#x627;&#x631;&#x62F; &#x627;&#x644;&#x62E;&#x627;&#x631;&#x62C;&#x64A;&#x629; &#x641;&#x64A; &#x635;&#x641;&#x62D;&#x627;&#x62A; &#x627;&#x644;&#x648;&#x64A;&#x628; &#x648;&#x62A;&#x62A;&#x628;&#x639; &#x62D;&#x627;&#x644;&#x62A;&#x647;&#x627; &#x639;&#x628;&#x631; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%AA%D8%AD%D9%85%D9%8A%D9%84-%D8%A7%D9%84%D9%85%D9%88%D8%A7%D8%B1%D8%AF-%D8%A7%D9%84%D8%AE%D8%A7%D8%B1%D8%AC%D9%8A%D8%A9-%D9%81%D9%8A-%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%88%D8%AA%D8%AA%D8%A8%D8%B9-%D8%AD%D8%A7%D9%84%D8%AA%D9%87%D8%A7-%D8%B9%D8%A8%D8%B1-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1238/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_05/Resource-loading--onload-and-onerror.png.4d1265ade94390385c00696fb7cdec82.png" /></p>

<p>
	يمكّننا المتصفّح من تتبّع تحميل الموارد الخارجيّة، مثل السكربتات والعناصر iframes والصور وما إلى ذلك، وهناك حدثان لهذا الغرض:
</p>

<ul>
<li>
		<code>onload</code> -- نجاح التحميل
	</li>
	<li>
		<code>onerror</code> -- حصل خطأ ما
	</li>
</ul>
<h2>
	تحميل سكربت
</h2>

<p>
	لنفترض أنّنا نودّ تحميل سكربت طرف ثالث واستدعاء دالّة موجودة فيه. يمكننا تحميله ديناميكيّا هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_33_7" style="">
<span class="pln">let script </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">'script'</span><span class="pun">);</span><span class="pln">
script</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> </span><span class="str">"my.js"</span><span class="pun">;</span><span class="pln">

document</span><span class="pun">.</span><span class="pln">head</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">script</span><span class="pun">);</span></pre>

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

<hr>
<p>
	<strong>ملاحظة:</strong>
</p>

<p>
	بالنسبة لسكربتاتنا الخاصّة يمكننا استخدام <a data-ss1622210353="1" data-ss1622210657="1" data-ss1623482996="1" href="https://academy.hsoub.com/programming/javascript/%D9%85%D9%82%D8%AF%D9%85%D8%A9-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-modules-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r926/" rel="">وحدات جافاسكربت</a> لذلك، لكنّها ليست شائعة التبنّي من مكتبات الطرف الثالث.
</p>

<hr>
<h3>
	script.onload
</h3>

<p>
	المساعد الرئيسيّ هو الحدث <code>load</code> ويقع عند الانتهاء من تحميل السكربت وتنفيذه. على سبيل المثال:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_33_9" style="">
<span class="pln">let script </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">'script'</span><span class="pun">);</span><span class="pln">

</span><span class="com">// يمكن أن يحمّل أيّ سكربت، من أيّ نطاق</span><span class="pln">
script</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> </span><span class="str">"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js"</span><span class="pln">
document</span><span class="pun">.</span><span class="pln">head</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">script</span><span class="pun">);</span><span class="pln">

script</span><span class="pun">.</span><span class="pln">onload </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// "_" ينشئ السكربت المتغيّر</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln"> _</span><span class="pun">.</span><span class="pln">VERSION </span><span class="pun">);</span><span class="pln"> </span><span class="com">// shows library version</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622210353="1" data-ss1622210657="1" data-ss1623482996="1" frameborder="no" height="282" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/wvJrdQy?height=282&amp;theme-id=light&amp;default-tab=js" style="width: 100%;" title="JS-p2-onload-onerror -ex1">See the Pen JS-p2-onload-onerror -ex1 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	بداخل <code>onload</code>، يمكننا إذًا استخدام متغيّرات السكربت وتنفيذ الدوالّ وما إلى ذلك. لكن ماذا لو فشل التحميل؟ على سبيل المثال، إذا كان لا وجود لهذا السكربت (الخطأ 404) أو الخادم معطّل (غير متاح).
</p>

<h3>
	script.onerror
</h3>

<p>
	يمكن تتبّع الأخطاء التي تحصل خلال تحميل السكربت في حدث <code>error</code>.
</p>

<p>
	على سبيل المثال، لنقم بطلب سكربت غير موجود:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_33_11" style="">
<span class="pln">let script </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">'script'</span><span class="pun">);</span><span class="pln">
script</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> </span><span class="str">"https://example.com/404.js"</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">head</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">script</span><span class="pun">);</span><span class="pln">

script</span><span class="pun">.</span><span class="pln">onerror </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="str">"Error loading "</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">src</span><span class="pun">);</span><span class="pln"> </span><span class="com">// https://example.com/404.js خطأ في تحميل</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622210353="1" data-ss1622210657="1" data-ss1623482996="1" frameborder="no" height="241" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/JjWrNwY?height=241&amp;theme-id=light&amp;default-tab=js" style="width: 100%;" title="JS-p2-onload-onerror -ex2">See the Pen JS-p2-onload-onerror -ex2 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	يُرجى التنبّه إلى أنّه لا يمكننا الحصول على تفاصيل خطأ HTTP هنا. لا نعلم إن كان خطأ 400 أو 500 أو شيئا آخر. نعلم فقط أنّ التحميل قد فشل.
</p>

<hr>
<p>
	<strong>تنبيه: لا تتبّع الأحداثُ <code>onload</code>/<code>onerror</code> سوى التحميل نفسه.</strong>
</p>

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

<hr>
<h2>
	الموارد الأخرى
</h2>

<p>
	تعمل الأحداث <code>load</code> و<code>error</code> مع الموارد الأخرى كذلك، وتعمل أساسا مع أيّ مورد له <code>src</code> خارجيّ. مثلا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_33_13" style="">
<span class="pln">let 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">
img</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> </span><span class="str">"https://js.cx/clipart/train.gif"</span><span class="pun">;</span><span class="pln"> </span><span class="com">// (*)</span><span class="pln">

img</span><span class="pun">.</span><span class="pln">onload </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(`</span><span class="typ">Image</span><span class="pln"> loaded</span><span class="pun">,</span><span class="pln"> size $</span><span class="pun">{</span><span class="pln">img</span><span class="pun">.</span><span class="pln">width</span><span class="pun">}</span><span class="pln">x$</span><span class="pun">{</span><span class="pln">img</span><span class="pun">.</span><span class="pln">height</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">onerror </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="str">"Error occurred while loading image"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622210353="1" data-ss1622210657="1" data-ss1623482996="1" frameborder="no" height="265" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/gOmGWZG?height=265&amp;theme-id=light&amp;default-tab=js" style="width: 100%;" title="JS-p2-onload-onerror -ex3">See the Pen JS-p2-onload-onerror -ex3 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	لكنّ هناك بعض الملاحظات تعود لأسباب تاريخيّة:
</p>

<ul>
<li>
		تبدأ معظم الموارد في التحميل عندما تُضاف إلى المستند لكنّ <code>&lt;img&gt;</code> تُعدّ استثناءً، حيث تبدأ في التحميل عندما تحصل على الرابط أو المصدر src <code>(*)</code>.
	</li>
	<li>
		بالنسبة لـ <code>&lt;iframe&gt;</code>، يقع الحدث <code>iframe.onload</code> عندما ينتهي تحميل iframe، سواء بنجاح التحميل أو في حالة خطأ.
	</li>
</ul>
<h2>
	سياسة تعدد المصادر Crossorigin policy
</h2>

<p>
	هناك قاعدة تقول: لا يمكن لسكربتات موقع ما الوصول إلى محتويات موقع آخر. فمثلا، لا يستطيع سكربت موجود في <code><a data-ss1622210353="1" data-ss1622210657="1" data-ss1623482996="1" href="https://facebook.com" ipsnoembed="true" rel="external nofollow">https://facebook.com</a></code> قراءة صندوق بريد المستخدم في <code><a data-ss1622210353="1" data-ss1622210657="1" data-ss1623482996="1" href="https://gmail.com" ipsnoembed="true" rel="external nofollow">https://gmail.com</a></code>.
</p>

<p>
	أو بشكل أدقّ، لا يمكن لأحد المصادر (الثلاثيّة domain/port/protocol البروتوكول/المنفذ/النطاق) الوصول إلى محتوى مصدر آخر. فحتى لو كان لدينا نطاق فرعيّ أو اختلف المنفذ فقط، فستُعدّ هذه مصادر مختلفة ولا يكون لديها مدخل إلى بعضها البعض.
</p>

<p>
	تشمل هذه القاعدة أيضا الموارد التي هي من نطاقات أخرى.
</p>

<p>
	إذا كنا نستخدم سكربتا من نطاق آخر، وكان فيه خطأ ما، فلا يمكننا الحصول على تفاصيل ذلك الخطأ. على سبيل المثال، لنأخذ السكربت (الفاسد) <code>error.js</code> المكوّن من مجرّد استدعاء لدالّة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_33_15" style="">
<span class="com">// ? error.js</span><span class="pln">
noSuchFunction</span><span class="pun">();</span></pre>

<p>
	ثمّ لنحمّله من نفس الموقع الموجود فيه:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_33_17" style="">
<span class="tag">&lt;script&gt;</span><span class="pln">
window</span><span class="pun">.</span><span class="pln">onerror </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">message</span><span class="pun">,</span><span class="pln"> url</span><span class="pun">,</span><span class="pln"> line</span><span class="pun">,</span><span class="pln"> col</span><span class="pun">,</span><span class="pln"> errorObj</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</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">\n$</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">line</span><span class="pun">}:</span><span class="pln">$</span><span class="pun">{</span><span class="pln">col</span><span class="pun">}`);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="tag">&lt;/script&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">"/article/onload-onerror/crossorigin/error.js"</span><span class="tag">&gt;&lt;/script&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622210353="1" data-ss1622210657="1" data-ss1623482996="1" frameborder="no" height="265" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/qBrPmgb?height=265&amp;theme-id=light&amp;default-tab=html,result" style="width: 100%;" title="JS-p2-onload-onerror -ex4">See the Pen JS-p2-onload-onerror -ex4 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	يمكننا رؤية تقرير جيّد للخطأ، هكذا:
</p>

<pre class="ipsCode">
Uncaught ReferenceError: noSuchFunction is not defined
https://javascript.info/article/onload-onerror/crossorigin/error.js, 1:1
</pre>

<p>
	لنحمّل الآن نفس السكربت من نطاق آخر:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_33_20" style="">
<span class="tag">&lt;script&gt;</span><span class="pln">
window</span><span class="pun">.</span><span class="pln">onerror </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">message</span><span class="pun">,</span><span class="pln"> url</span><span class="pun">,</span><span class="pln"> line</span><span class="pun">,</span><span class="pln"> col</span><span class="pun">,</span><span class="pln"> errorObj</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</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">\n$</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">line</span><span class="pun">}:</span><span class="pln">$</span><span class="pun">{</span><span class="pln">col</span><span class="pun">}`);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="tag">&lt;/script&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">"https://cors.javascript.info/article/onload-onerror/crossorigin/error.js"</span><span class="tag">&gt;&lt;/script&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622210353="1" data-ss1622210657="1" data-ss1623482996="1" frameborder="no" height="243" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/qBrPmvb?height=243&amp;theme-id=light&amp;default-tab=html" style="width: 100%;" title="JS-p2-onload-onerror -ex5">See the Pen JS-p2-onload-onerror -ex5 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	التقرير الآن مختلف:
</p>

<pre class="ipsCode">
Script error.
, 0:0
</pre>

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

<p>
	لماذا قد نحتاج إلى تفاصيل الخطأ؟
</p>

<p>
	توجد هناك العديد من الخدمات (يمكن أن نبني واحدة لنا) التي تعمل على الانصات للأخطاء العموميّة بواسطة <code>window.onerror</code>، وحفظها وتوفير واجهة للوصول إليها وتحليلها. هذا جيّد، إذ يمكننا رؤية الأخطاء الحقيقيّة، التي يتسبّب فيها مستخدمونا. لكن إذا كان السكربت من مصدر آخر، فلا تكون هناك الكثير من المعلومات حول الأخطاء فيها، كما شاهدنا للتوّ.
</p>

<p>
	تُفرض نفس <a data-ss1622210353="1" data-ss1622210657="1" data-ss1623482996="1" href="https://ar.wikipedia.org/wiki/%D8%B3%D9%8A%D8%A7%D8%B3%D8%A9_%D8%A7%D9%84%D9%85%D8%B5%D8%AF%D8%B1_%D8%A7%D9%84%D8%A3%D9%88%D8%AD%D8%AF" rel="external nofollow">سياسة تعدّد المصادر</a> CORS على أنواع الموارد الأخرى كذلك. لإتاحة الوصول متعدّد المصادر، يجب أن يكون للوسم <code>&lt;script&gt;</code> السمة <code>crossorigin</code>، ويجب كذلك أن يقدّم الخادم ترويسات Headers خاصّة. هناك ثلاثة مستويات للوصول متعدّد المصادر:
</p>

<ol>
<li>
		<strong>لا وجود للسمة <code>crossorigin</code></strong> -- الوصول ممنوع.
	</li>
	<li>
		<strong><code>crossorigin="anonymous"‎</code></strong> -- الوصول متاح إذا أجاب الخادم بالترويسة <code>Access-Control-Allow-Origin</code> مع <code>*</code> أو مع مصدرنا. لا يرسل المتصفّح معلومات الترخيص authorization وملفّات الارتباط cookies إلى الخادم البعيد.
	</li>
	<li>
		<strong><code>crossorigin="use-credentials"‎</code></strong> -- الوصول متاح إذا أجاب الخادم بالترويسة <code>Access-Control-Allow-Origin</code> مع مصدرنا و <code>Access-Control-Allow-Credentials: true</code>. يرسل المتصفّح معلومات الترخيص وملفّات الارتباط إلى الخادم البعيد.
	</li>
</ol>
<p>
	في حالتنا هذه، لم يكن لدينا أيّ وسم crossorigin. لذا فإنّ الوصول متعدّد المصادر قد مُنع. لنضفه الآن.
</p>

<p>
	يمكننا الاختيار بين <code>"anonymous"</code> (لا إرسال لملفات الارتباط، يُحتاج إلى ترويسة واحدة من جانب الخادم) و<code>"use-credentials"</code> (يرسل ملفّات الارتباط أيضا، يُحتاج إلى ترويستين من جانب الخادم).
</p>

<p>
	إذا لم نكن نهتمّ بملفّات الارتباط، فإنّ <code>"anonymous"</code> هي الخيار:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_33_22" style="">
<span class="tag">&lt;script&gt;</span><span class="pln">
window</span><span class="pun">.</span><span class="pln">onerror </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">message</span><span class="pun">,</span><span class="pln"> url</span><span class="pun">,</span><span class="pln"> line</span><span class="pun">,</span><span class="pln"> col</span><span class="pun">,</span><span class="pln"> errorObj</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</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">\n$</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">line</span><span class="pun">}:</span><span class="pln">$</span><span class="pun">{</span><span class="pln">col</span><span class="pun">}`);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span><span class="pln">
</span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">crossorigin</span><span class="pun">=</span><span class="atv">"anonymous"</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"https://cors.javascript.info/article/onload-onerror/crossorigin/error.js"</span><span class="tag">&gt;&lt;/script&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622210353="1" data-ss1622210657="1" data-ss1623482996="1" frameborder="no" height="262" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/PopJmvE?height=262&amp;theme-id=light&amp;default-tab=html" style="width: 100%;" title="JS-p2-onload-onerror -ex6">See the Pen JS-p2-onload-onerror -ex6 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	حاليّا، على افتراض أنّ الخادم يوفّر ترويسة <code>Access-Control-Allow-Origin</code>، كلّ شيء على ما يرام، لدينا تقرير الخطأ الكامل.
</p>

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

<p>
	توفّر الصور <code>&lt;img&gt;</code> والتنسيقات الخارجيّة والسكربتات والموارد الأخرى الأحداث <code>load</code> و<code>error</code> لتتبّع تحميلها:
</p>

<ul>
<li>
		يقع <code>load</code> عند نجاح التحميل.
	</li>
	<li>
		يقع <code>error</code> عند فشل التحميل.
	</li>
</ul>
<p>
	الاستثناء الوحيد هو <code>&lt;iframe&gt;</code>: لأسباب تاريخيّة يحدث <code>load</code> على الدوام، عند أيّ انتهاء للتحميل، حتى لو لم توجد الصفحة.
</p>

<p>
	يعمل الحدث <code>readystatechange</code> أيضا مع الموارد، لكن يندر استخدامه، لأنّ الأحداث <code>load/error</code> أبسط.
</p>

<h2>
	التمارين
</h2>

<h3>
	حمل الصور بواسطة ردود النداء callbacks
</h3>

<p>
	الأهميّة: 4
</p>

<p>
	عادة، تُحمّل الصور عند إنشائها. فعندما نضيف <code>&lt;img&gt;</code> إلى الصفحة، لا يرى المستخدم الصورة فورا. يحتاج المتصفّح إلى تحميلها أوّلا.
</p>

<p>
	لإظهار الصورة فورا، يمكننا إنشاؤها "مسبقا"، هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_33_24" style="">
<span class="pln">let 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">
img</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> </span><span class="str">'my.jpg'</span><span class="pun">;</span></pre>

<p>
	يبدأ المتصفّح تحميل الصورة ويحفظها في الذاكرة المؤقّتة. لاحقا، عندما تحضر نفس الصورة في المستند (مهما كانت الطريقة)، فإنّها تظهر فورا.
</p>

<p>
	أنشئ الدالّة <code>preloadImages(sources, callback)‎</code> التي تحمّل جميع الصور التي في المصفوفة <code>sources</code>، وعندما تجهز، تنفّذ <code>callback</code>.
</p>

<p>
	على سبيل المثال، سيظهر هذا التنبيه <code>alert</code> بعدما تُحمّل الصور:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_33_26" style="">
<span class="kwd">function</span><span class="pln"> loaded</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  alert</span><span class="pun">(</span><span class="str">"Images loaded"</span><span class="pun">)</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

preloadImages</span><span class="pun">([</span><span class="str">"1.jpg"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"2.jpg"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"3.jpg"</span><span class="pun">],</span><span class="pln"> loaded</span><span class="pun">);</span></pre>

<p>
	حتى في حالة خطأ، يجب أن تفترض الدالّة أنّ الصورة "محمّلة". بعبارة أخرى، تُنفّذ <code>callback</code> سواء عند نجاح تحميل الصور أو فشله.
</p>

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

<p>
	ستجد في المستند المصدريّ الشيفرة وروابط لاختبار الصور، للتحقّق من أنّها قد حُمّلت. يجب أن يكون المُخرج هو <code>300</code>.
</p>

<p>
	<a data-ss1622210353="1" data-ss1622210657="1" data-ss1623482996="1" href="https://plnkr.co/edit/dZ22M5URaGd0Dx0Q?p=preview" rel="external nofollow">أنجز الحلّ في البيئة التجريبية</a>
</p>

<h3>
	الحل
</h3>

<p>
	الخوارزميّة:
</p>

<ol>
<li>
		اجعل <code>img</code> لكلّ مصدر.
	</li>
	<li>
		أضف <code>onload/onerror</code> لكلّ صورة.
	</li>
	<li>
		زد في العدّاد كلّما اشتغل <code>onload</code> أو<code>onerror</code>.
	</li>
	<li>
		عندما تصير قيمة العدّاد تساوي عدد المصادر، نكون قد انتهينا: <code>callback()‎</code>.
	</li>
</ol>
<p>
	<a data-ss1622210353="1" data-ss1622210657="1" data-ss1623482996="1" href="https://plnkr.co/edit/D31flNmxdXwV2g8G?p=preview" rel="external nofollow">افتح الحلّ في البيئة التجريبية</a>
</p>

<p>
	ترجمة -وبتصرف- للمقال <a data-ss1622210353="1" data-ss1622210657="1" data-ss1623482996="1" href="https://javascript.info/onload-onerror" rel="external nofollow">Resource loading: onload and onerror</a> من سلسلة <a data-ss1622210353="1" data-ss1622210657="1" data-ss1623482996="1" href="https://javascript.info/ui" rel="external nofollow">Browser: Document, Events, Interfaces</a> لصاحبها Ilya Kantor
</p>
]]></description><guid isPermaLink="false">1238</guid><pubDate>Tue, 08 Jun 2021 11:02:00 +0000</pubDate></item><item><title>&#x634;&#x631;&#x62D; &#x627;&#x644;&#x644;&#x627;&#x62A;&#x632;&#x627;&#x645;&#x646; async &#x648;&#x639;&#x62F;&#x645; &#x627;&#x644;&#x627;&#x646;&#x62A;&#x638;&#x627;&#x631; defer &#x623;&#x62B;&#x646;&#x627;&#x621; &#x62A;&#x62D;&#x645;&#x64A;&#x644; &#x633;&#x643;&#x631;&#x628;&#x62A;&#x627;&#x62A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A; &#x641;&#x64A; &#x635;&#x641;&#x62D;&#x627;&#x62A; HTML</title><link>https://academy.hsoub.com/programming/javascript/%D8%B4%D8%B1%D8%AD-%D8%A7%D9%84%D9%84%D8%A7%D8%AA%D8%B2%D8%A7%D9%85%D9%86-async-%D9%88%D8%B9%D8%AF%D9%85-%D8%A7%D9%84%D8%A7%D9%86%D8%AA%D8%B8%D8%A7%D8%B1-defer-%D8%A3%D8%AB%D9%86%D8%A7%D8%A1-%D8%AA%D8%AD%D9%85%D9%8A%D9%84-%D8%B3%D9%83%D8%B1%D8%A8%D8%AA%D8%A7%D8%AA-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-%D9%81%D9%8A-%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-html-r1237/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_05/Scripts--async-defer.png.0f9199ceeb433f353f3355e9732e2fec.png" /></p>

<p>
	عندما يُحمّل المتصفّح ملف HTML ويجد وسم <code>&lt;script&gt;...&lt;/script&gt;</code>، فلا يمكنه مواصلة بناء DOM. يجب أن ينفّذ السكربت حالا. نفس الشيء بالنسبة للسكربتات الخارجيّة ‎<code>&lt;script src="..."&gt;..&lt;/script&gt;</code>‎: يجب أن ينتظر المتصفّح تحميل السكربت، ثم ينفّذ السكربت المحمّل، وعندها فقط يمكنه معالجة بقيّة الصفحة.
</p>

<p>
	يرافق ذلك مشكلتان مهمّتان:
</p>

<ol>
<li>
		لا يمكن أن تطّلع السكربتات على عناصر DOM التي تحتها، ولا يمكنها إذًا إضافة معالجات وما إلى ذلك.
	</li>
	<li>
		إذا كان هناك سكربت كبير في أعلى الصفحة، فإنّه "يحبس الصفحة". لا يمكن للمستخدمين رؤية محتوى الصفحة حتى يُحمّل السكربت ويُنفّذ:
	</li>
</ol>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8294_23" style="">
<span class="tag">&lt;p&gt;</span><span class="pln">...content before script...</span><span class="tag">&lt;/p&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">"https://javascript.info/article/script-async-defer/long.js?speed=1"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">

</span><span class="com">&lt;!-- هذا غير مرئيّ إلى أن يُحمّل السكربت --&gt;</span><span class="pln">
</span><span class="tag">&lt;p&gt;</span><span class="pln">...content after script...</span><span class="tag">&lt;/p&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622209276="1" data-ss1622209986="1" data-ss1622210079="1" frameborder="no" height="187" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/GRWMmOr?height=187&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-script-async-defer -ex1">See the Pen JS-p2-script-async-defer -ex1 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

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

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8294_25" style="">
<span class="tag">&lt;body&gt;</span><span class="pln">
  ...all content is above the script...

  </span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"https://javascript.info/article/script-async-defer/long.js?speed=1"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
</span><span class="tag">&lt;/body&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622209276="1" data-ss1622209986="1" data-ss1622210079="1" frameborder="no" height="265" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/zYZEwpQ?height=265&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-script-async-defer -ex2">See the Pen JS-p2-script-async-defer -ex2 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

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

<p>
	لا تُلاحظ هذه الأمور لمن يملكون اتصالات سريعة جدا، لكن لا يزال الكثير من الناس حول العالم يملكون اتصالات بطيئة بالانترنت ويستخدمون اتصالات انترنت محمولة أبعد ما تكون عن المثاليّة.
</p>

<p>
	لحسن الحظّ، هناك سمتان للعنصر <code>&lt;script&gt;</code> يمكنها أن تحلّ لنا هذه المشكلة: <code>defer</code> و<code>async</code>.
</p>

<h2>
	السمة defer
</h2>

<p>
	تطلب السمة <code>defer</code> من المتصفّح أن لا ينتظر السكربت. بل يواصل المتصفّح معالجة HTML وبناء DOM. يُحمّل السكربت في "الخلفيّة"، ثمّ يشتغل عندما يتمّ بناء DOM كاملا.
</p>

<p>
	إليك نفس المثال الذي في الأعلى لكن باستخدام <code>defer</code>:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8294_27" style="">
<span class="tag">&lt;p&gt;</span><span class="pln">...content before script...</span><span class="tag">&lt;/p&gt;</span><span class="pln">

</span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">defer</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"https://javascript.info/article/script-async-defer/long.js?speed=1"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">

</span><span class="com">&lt;!-- يكون مرئيّا على الفور --&gt;</span><span class="pln">
</span><span class="tag">&lt;p&gt;</span><span class="pln">...content after script...</span><span class="tag">&lt;/p&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622209276="1" data-ss1622209986="1" data-ss1622210079="1" frameborder="no" height="190" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/mdWBmXR?height=190&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-script-async-defer -ex3">See the Pen JS-p2-script-async-defer -ex3 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	بعبارة أخرى:
</p>

<ul>
<li>
		لا تحبس السكربتات التي لها <code>defer</code> الصفحة أبدا.
	</li>
	<li>
		دائما ما تُنفّذ السكربتات التي لها <code>defer</code> عندما يكون DOM جاهزا (لكن قبل الحدث <code>DOMContentLoaded</code>).
	</li>
</ul>
<p>
	يوضّح هذا المثال النقطة الثانية:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8294_29" style="">
<span class="tag">&lt;p&gt;</span><span class="pln">...content before scripts...</span><span class="tag">&lt;/p&gt;</span><span class="pln">

</span><span class="tag">&lt;script&gt;</span><span class="pln">
  document</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">'DOMContentLoaded'</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"> alert</span><span class="pun">(</span><span class="str">"DOM ready after defer!"</span><span class="pun">));</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span><span class="pln">

</span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">defer</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"https://javascript.info/article/script-async-defer/long.js?speed=1"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">

</span><span class="tag">&lt;p&gt;</span><span class="pln">...content after scripts...</span><span class="tag">&lt;/p&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622209276="1" data-ss1622209986="1" data-ss1622210079="1" frameborder="no" height="187" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/LYWzydV?height=187&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-script-async-defer -ex4">See the Pen JS-p2-script-async-defer -ex4 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<ol>
<li>
		يظهر محتوى الصفحة مباشرة.
	</li>
	<li>
		ينتظر معالج الحدث <code>DOMContentLoaded</code> السكربت المُرجأ (deferred). لا يشتغل حتى يُحمّل السكربت ويُنفّذ.
	</li>
</ol>
<p>
	<strong>تحافظ السكربتات المُرجأة على الترتيب بينها، تماما كما هو الحال مع السكربتات العاديّة</strong>
</p>

<p>
	لنفترض أنّ لدينا سكربتين مُرجأين: الطويل <code>long.js</code>وثمّ القصير <code>small.js</code>:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8294_31" style="">
<span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">defer</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"https://javascript.info/article/script-async-defer/long.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
</span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">defer</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"https://javascript.info/article/script-async-defer/small.js"</span><span class="tag">&gt;&lt;/script&gt;</span></pre>

<p>
	يبحث المتصفّح في الصفحة على السكربتات ويُحمّلها معًا، لتحسين الأداء. ففي المثال أعلاه يُحمّل السكربتان بشكل متوازي. من المُرجّح أن ينتهي <code>small.js</code> أوّلا.
</p>

<p>
	لكنّ السمة <code>defer</code>، بالإضافة إلى طلبها من المتصفّح "عدم الحبس"، تضمن المحافظة على الترتيب بين السكربتات. فحتى لو حُمّل <code>small.js</code> أوّلا، فإنّ عليه أن ينتظر ويُنفّذ بعد تنفيذ <code>long.js</code>.
</p>

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

<hr>
<p>
	<strong>ملاحظة: السمة <code>defer</code> هي للسكربتات الخارجيّة فقط</strong>
</p>

<p>
	تُتجاهل السمة <code>defer</code> إذا لم يكن الوسم <code>&lt;script&gt;</code> له <code>src</code>.
</p>

<hr>
<h2>
	async
</h2>

<p>
	السمة <code>async</code> هي مثل <code>defer</code> نوعا ما. هي أيضا تجعل السكربت غير حابس. لكنّ بينهما فروقات مهمّة في السلوك.
</p>

<p>
	تعني السمة <code>async</code> أن السكربت مستقلّ تماما:
</p>

<ul>
<li>
		لا يحبس المتصفّح عند السكربتات التي لها <code>async</code> (مثل <code>defer</code>).
	</li>
	<li>
		لا تنتظر السكربتات الأخرى السكربتات التي لها <code>async</code>، ولا تنتظر السكربتات التي لها <code>async</code> السكربتات الأخرى.
	</li>
	<li>
		لا ينتظر<code>DOMContentLoaded</code> والسكربتات غير المتزامنة بعضها البعض:
		<ul>
<li>
				قد يقع <code>DOMContentLoaded</code> سواء قبل السكربت غير المتزامن (عندما ينتهي تحميل السكربت غير المتزامن قبل تمام الصفحة)
			</li>
			<li>
				…أو بعد السكربت غير المتزامن (إذا كان السكربت غير المتزامن قصيرا أو كان في ذاكرة التخزين المؤقّت HTTP).
			</li>
		</ul>
</li>
</ul>
<p>
	بعبارة أخرى، تُحمّل السكربتات التي لها <code>async</code> في الخلفيّة وتُنفّذ عندما تجهز. لا ينتظرها DOM ولا السكربتات الأخرى، ولا تنتظر هي بدورها أيّ شيء. سكربت مستقلّ تماما يُنفّذ عندما يجهز. من أبسط ما يكون، صحيح؟
</p>

<p>
	إليك مثالا مشابها للذي رأينا مع <code>defer</code>: سكربتان <code>long.js</code> و<code>small.js</code>، لكن الآن لها <code>async</code> بدل <code>defer</code>.
</p>

<p>
	لا ينتظر أحدهما الآخر. أيّا كان الذي يُحمّل أوّلا (<code>small.js</code> على الأرجح) فإنّه يُنفّذ أوّلا:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8294_33" style="">
<span class="tag">&lt;p&gt;</span><span class="pln">...content before scripts...</span><span class="tag">&lt;/p&gt;</span><span class="pln">

</span><span class="tag">&lt;script&gt;</span><span class="pln">
  document</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">'DOMContentLoaded'</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"> alert</span><span class="pun">(</span><span class="str">"DOM ready!"</span><span class="pun">));</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span><span class="pln">

</span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">async</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"https://javascript.info/article/script-async-defer/long.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">
</span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">async</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"https://javascript.info/article/script-async-defer/small.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln">

</span><span class="tag">&lt;p&gt;</span><span class="pln">...content after scripts...</span><span class="tag">&lt;/p&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622209276="1" data-ss1622209986="1" data-ss1622210079="1" frameborder="no" height="179" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/bGqoWjL?height=179&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-script-async-defer -ex5">See the Pen JS-p2-script-async-defer -ex5 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<ul>
<li>
		يظهر محتوى الصفحة فورا: لا يحبسه <code>async</code>.
	</li>
	<li>
		قد يقع <code>DOMContentLoaded</code> إمّا قبل <code>async</code> أو بعد، لا ضمانات هنا.
	</li>
	<li>
		يأتي السكربت الأقصر <code>small.js</code> ثانيا، لكنّه على الأرجح يُحمّل قبل <code>long.js</code>، وبالتالي فإنّ <code>small.js</code> يُنفّذ أوّلا. رغم ذلك، من الممكن أن يُحمّل <code>long.js</code> أوّلا، إذا كان في ذاكرة التخزين المؤقّتة، فينُفّذ بذلك أوّلا. بعبارة أخرى، تُنفّذ السكربتات غير المتزامنة حسب ترتيب "أوّليّة التحميل".
	</li>
</ul>
<p>
	تفيد السكربتات غير المتزامنة كثيرا عندما ندمج سكربت طرف ثالث مستقلّ في الصفحة، كالعدّادات والإعلانات وما إلى ذلك، إذ لا تعتمد على سكربتاتنا، وليس على سكربتاتنا أن تنتظرها.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_8294_35" style="">
<span class="com">&lt;!-- هكذا عادة Google Analytics تضاف--&gt;</span><span class="pln">
</span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">async</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"https://google-analytics.com/analytics.js"</span><span class="tag">&gt;&lt;/script&gt;</span></pre>

<h2>
	السكربتات الديناميكيّة
</h2>

<p>
	هناك طريقة مهمّة أخرى لإضافة سكربت إلى الصفحة.
</p>

<p>
	يمكننا إنشاء سكربت وإلحاقه بالمستند ديناميكيّا باستخدام جافاسكربت:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8294_37" style="">
<span class="pln">let script </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">'script'</span><span class="pun">);</span><span class="pln">
script</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> </span><span class="str">"/article/script-async-defer/long.js"</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">append</span><span class="pun">(</span><span class="pln">script</span><span class="pun">);</span><span class="pln"> </span><span class="com">// (*)</span></pre>

<p>
	يبدأ تحميل السكربت بعد إلحاقه بالمستند مباشرة <code>(*)</code>.
</p>

<p>
	تتصرّف السكربتات الديناميكيّة افتراضيًّا وكأنّها "غير متزامنة"، يعني ذلك:
</p>

<ul>
<li>
		لا تنتظر أيّ شيء، ولاشيء ينتظرها.
	</li>
	<li>
		السكربت الذي يُحمّل أوّلا يُنفّذ أوّلا (حسب ترتيب "أوّليّة التحميل").
	</li>
</ul>
<p>
	يمكن تغيير ذلك إذا وضعنا <code>script.async=false</code> صراحة. عندها تُنفّذ السكربتات حسب ترتيبها في المستند، تماما مثل <code>defer</code>.
</p>

<p>
	في هذا المثال، تضيف الدالّة <code>loadScript(src)‎</code> سكربتا وتضبط أيضا <code>async</code> على <code>false</code>.
</p>

<p>
	وبذلك يُنفّذ <code>long.js</code> أوّلا على الدوام (إذ قد أُضيف أوّلا):
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8294_39" style="">
<span class="kwd">function</span><span class="pln"> loadScript</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">
  let script </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">'script'</span><span class="pun">);</span><span class="pln">
  script</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> src</span><span class="pun">;</span><span class="pln">
  script</span><span class="pun">.</span><span class="pln">async </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">body</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">script</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="com">// async=false أوّلا لأنّ long.js يُنفّذ</span><span class="pln">
loadScript</span><span class="pun">(</span><span class="str">"/article/script-async-defer/long.js"</span><span class="pun">);</span><span class="pln">
loadScript</span><span class="pun">(</span><span class="str">"/article/script-async-defer/small.js"</span><span class="pun">);</span></pre>

<p>
	بدون <code>script.async=false</code>، تُنفّذ السكربتات حسب الترتيب الافتراضيّ "أوّليّة التحميل" (<code>small.js</code> أوّلا على الأرجح).
</p>

<p>
	ثانيا، كما هو الأمر مع <code>defer</code>، يصير الترتيب مهمّا عندما نريد تحميل مكتبة ما ومن ثمّ سكربت يعتمد عليها.
</p>

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

<p>
	لكلّ من <code>async</code> و<code>defer</code> أمر مشترك: لا يحبس تحميلُ هذه السكربتات تصييرَ (rendering) الصفحة. وبذلك يمكن للمستخدم الاطلاع على الصفحة وقراءة محتواها فورا.
</p>

<p>
	لكنّ هناك أيضا فروقات جوهريّة بينها:
</p>

<table>
<thead><tr>
<th>
				 
			</th>
			<th>
				الترتيب
			</th>
			<th>
				‏ <code>DOMContentLoaded</code>
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				<code>async</code>‏
			</td>
			<td>
				الترتيب وفق أوليّة التحميل. لا يهمّ ترتيبها في المستند -- الذي يُحمّل أوّلا يُنفّذ أوّلا
			</td>
			<td>
				لا علاقة له. قد يُحمّل السكربت ويُنفّذ قبل أن يتمّ تحميل كامل المستند. يحصل هذا إذا كانت السكربتات صغيرة أو مخزّنة في الذاكرة المؤقّتة، والمستند طويل كفاية.
			</td>
		</tr>
<tr>
<td>
				<code>defer</code>‏
			</td>
			<td>
				الترتيب وفق المستند (حسب موضعها في المستند).
			</td>
			<td>
				تُنفّذ السكربتات بعد الانتهاء من تحميل المستند وتفسيره (تنتظر عند الحاجة لذلك)، مباشرة قبل <code>DOMContentLoaded</code>.
			</td>
		</tr>
</tbody>
</table>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<p>
	عمليّا، تُستخدم <code>defer</code> في السكربتات التي تحتاج DOM كاملا و/أو التي يكون ترتيب التنفيذ فيما بينها مهمّا.
</p>

<p>
	وتُستخدم <code>async</code> في السكربتات المستقلّة، مثل العدّادات والإعلانات. ويكون ترتيب تنفيذها غير مهمّ.
</p>

<hr>
<p>
	<strong>تنبيه: يجب أن تكون الصفحة قابلة للاستخدام دون السكربتات</strong>
</p>

<p>
	يُرجى التنبه: إذا استخدمت <code>defer</code> أو <code>async</code>، فسيرى المستخدم الصفحة قبل تحميل السكربت.
</p>

<p>
	في هذه الحالة، قد لا تكون بعض المكوّنات الرسوميّة تهيّئت بعد.
</p>

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

<hr>
<p>
	ترجمة -وبتصرف- للمقال <a data-ss1622209276="1" data-ss1622209986="1" data-ss1622210079="1" href="https://javascript.info/script-async-defer" rel="external nofollow">Scripts: async, defer</a> من سلسلة <a data-ss1622209276="1" data-ss1622209986="1" data-ss1622210079="1" href="https://javascript.info/ui" rel="external nofollow">Browser: Document, Events, Interfaces</a> لصاحبها Ilya Kantor
</p>
]]></description><guid isPermaLink="false">1237</guid><pubDate>Sat, 05 Jun 2021 11:01:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x623;&#x62D;&#x62F;&#x627;&#x62B; &#x627;&#x644;&#x645;&#x62A;&#x639;&#x644;&#x642;&#x629; &#x628;&#x62F;&#x648;&#x631;&#x629; &#x62D;&#x64A;&#x627;&#x629; &#x635;&#x641;&#x62D;&#x629; HTML &#x648;&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x627;&#x644;&#x62A;&#x62D;&#x643;&#x645; &#x628;&#x647;&#x627; &#x639;&#x628;&#x631; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%A3%D8%AD%D8%AF%D8%A7%D8%AB-%D8%A7%D9%84%D9%85%D8%AA%D8%B9%D9%84%D9%82%D8%A9-%D8%A8%D8%AF%D9%88%D8%B1%D8%A9-%D8%AD%D9%8A%D8%A7%D8%A9-%D8%B5%D9%81%D8%AD%D8%A9-html-%D9%88%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D8%AD%D9%83%D9%85-%D8%A8%D9%87%D8%A7-%D8%B9%D8%A8%D8%B1-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1236/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_05/Page-DOMContentLoaded-load-beforeunload-unload.png.022caf19e3fd8e6e3c8c73a17162bca4.png" /></p>

<p>
	لدورة حياة صفحة HTML ثلاثة أحداث مهمّة:
</p>

<ul>
<li>
		<code>DOMContentLoaded</code> -- حمّل المتصفّحُ بنية HTML الكاملة للصفحة، وبُنيت شجرة DOM، لكنّ الموارد الخارجيّة كالصور <code>&lt;img&gt;</code> وأوراق التنسيق stylesheets قد لا تكون حُمّلت بعد.
	</li>
	<li>
		<code>load</code> -- حمّل المتصفّح الـ HTML، وكذلك جميع الموارد الخارجيّة من صور وتنسيقات وغيرها.
	</li>
	<li>
		<code>beforeunload/unload</code> -- يغادر المستخدم الصفحة.
	</li>
</ul>
<p>
	يمكن لأيّ من هذه الأحداث أن يكون ذا فائدة:
</p>

<ul>
<li>
		الحدث <code>DOMContentLoaded</code> -- صارت شجرة DOM جاهزة، ويمكن للمعالج إذًا البحث فيه عن العقد، وتهيئة الواجهة.
	</li>
	<li>
		الحدث <code>load</code> -- قد حُمّلت الموارد الخارجيّة، وبذلك تكون التنسيقات قد طُبّقت، ومقاسات الصور عُلمت، وما إلى ذلك.
	</li>
	<li>
		الحدث <code>beforeunload</code> -- يهمّ المُستخدم بالمغادرة. يمكننا عندها أن نتحقّق من أنّه قد حفظ التغييرات وأن نسأله إن كان حقّا يودّ المغادرة.
	</li>
	<li>
		الحدث <code>unload</code> -- المُستخدم على وشك المغادرة، لكنّنا لازلنا نستطيع إجراء بعض العمليّات كإرسال الإحصائيّات مثلا.
	</li>
</ul>
<p>
	لنطّلع على تفاصيل هذه الأحداث.
</p>

<h2>
	الحدث DOMContentLoaded
</h2>

<p>
	يقع الحدث <code>DOMContentLoaded</code> على الكائن <code>document</code>، ولابدّ أن نستعمل <code>addEventListener</code> لالتقاطه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7973_7" style="">
<span class="pln">document</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"DOMContentLoaded"</span><span class="pun">,</span><span class="pln"> ready</span><span class="pun">);</span><span class="pln">
</span><span class="com">// "document.onDOMContentLoaded = ..." ليس</span></pre>

<p>
	على سبيل المثال:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7973_9" style="">
<span class="pun">&lt;</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="kwd">function</span><span class="pln"> ready</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    alert</span><span class="pun">(</span><span class="str">'DOM is ready'</span><span class="pun">);</span><span class="pln">

    </span><span class="com">// 0x0 لم تُحمّل الصورة بعد (إلّا إن كانت قد خُبّئت في ذاكرة التخزين المؤقّت)، لذا فمقاسها هو</span><span class="pln">
    alert</span><span class="pun">(`</span><span class="typ">Image</span><span class="pln"> size</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">offsetWidth</span><span class="pun">}</span><span class="pln">x$</span><span class="pun">{</span><span class="pln">img</span><span class="pun">.</span><span class="pln">offsetHeight</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">addEventListener</span><span class="pun">(</span><span class="str">"DOMContentLoaded"</span><span class="pun">,</span><span class="pln"> ready</span><span class="pun">);</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">

</span><span class="pun">&lt;</span><span class="pln">img id</span><span class="pun">=</span><span class="str">"img"</span><span class="pln"> src</span><span class="pun">=</span><span class="str">"https://en.js.cx/clipart/train.gif?speed=1&amp;cache=0"</span><span class="pun">&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622208355="1" data-ss1622208816="1" data-ss1622213329="1" frameborder="no" height="265" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/PopJmpp?height=265&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-loading -ex1">See the Pen JS-p2-loading -ex1 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	في هذا المثال، يشتغل معالج <code>DOMContentLoaded</code> عندما يُحمَّل المستند، ويمكنه عندها رؤية جميع العناصر، بما في ذلك <code>&lt;img&gt;</code> الذي في الأسفل. لكنّه لا ينتظر الصورة حتى تُحمّل، ولذلك يُظهر <code>alert</code> المقاسات صفرا.
</p>

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

<h3>
	DOMContentLoaded والسكربتات
</h3>

<p>
	عندما يعالج المتصفّح مستند HTML ويلاقي وسم <code>&lt;script&gt;</code>، فيجب عليه تنفيذه قبل أن يتابع بناء DOM. هذا من باب الاحتياط، إذ قد تودّ السكربتات تغيير DOM، أو حتى إجراء <code>document.write</code> عليه، فيجب إذًا على <code>DOMContentLoaded</code> أن ينتظر. لذلك لابدّ أن يكون حدوث DOMContentLoaded بعد هذه السكربتات:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7973_11" style="">
<span class="pun">&lt;</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
  document</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"DOMContentLoaded"</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">
    alert</span><span class="pun">(</span><span class="str">"DOM ready!"</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">script</span><span class="pun">&gt;</span><span class="pln">

</span><span class="pun">&lt;</span><span class="pln">script src</span><span class="pun">=</span><span class="str">"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js"</span><span class="pun">&gt;&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">

</span><span class="pun">&lt;</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
  alert</span><span class="pun">(</span><span class="str">"Library loaded, inline script executed"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622208355="1" data-ss1622208816="1" data-ss1622213329="1" frameborder="no" height="265" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/abJLWJr?height=265&amp;theme-id=light&amp;default-tab=html,result" style="width: 100%;" title="JS-p2-loading -ex2">See the Pen JS-p2-loading -ex2 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	في المثال أعلاه، نلاحظ "Library loaded…‎" أوّلا، ثمّ "DOM ready!‎" (جميع السكربتات قد نُفّذت).
</p>

<p>
	<strong>تنبيه: السكربتات التي لا تعيق DOMContentLoaded</strong>
</p>

<p>
	هناك استثناءان لهذه القاعدة:
</p>

<ol>
<li>
		السكربتات ذوات السمة <code>async</code>، التي سنتناولها <a data-ss1622213329="1" href="https://academy.hsoub.com/programming/javascript/%D8%B4%D8%B1%D8%AD-%D8%A7%D9%84%D9%84%D8%A7%D8%AA%D8%B2%D8%A7%D9%85%D9%86-async-%D9%88%D8%B9%D8%AF%D9%85-%D8%A7%D9%84%D8%A7%D9%86%D8%AA%D8%B8%D8%A7%D8%B1-defer-%D8%A3%D8%AB%D9%86%D8%A7%D8%A1-%D8%AA%D8%AD%D9%85%D9%8A%D9%84-%D8%B3%D9%83%D8%B1%D8%A8%D8%AA%D8%A7%D8%AA-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-%D9%81%D9%8A-%D8%B5%D9%81%D8%AD%D8%A7%D8%AA-html-r1237/" rel="">عن قريب</a>، لا تعيق <code>DOMContentLoaded</code>.
	</li>
	<li>
		السكربتات االتي تُولَّد ديناميكيّا بواسطة <code>document.createElement('script')‎</code> ثمّ تُضاف إلى صفحة الويب لا تعيق هذا الحدث أيضا.
	</li>
</ol>
<h3>
	DOMContentLoaded والتنسيقات
</h3>

<p>
	لا تؤثّر أوراق التنسيق في DOM، لذا فإنّ <code>DOMContentLoaded</code> لا ينتظرها. لكنّ هناك مزلقا. إذا كان لدينا سكربت موجود بعد التنسيق، فيجب على ذلك السكربت أن ينتظر حتى تُحمّل ورقة التنسيق:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7973_13" style="">
<span class="pun">&lt;</span><span class="pln">link type</span><span class="pun">=</span><span class="str">"text/css"</span><span class="pln"> rel</span><span class="pun">=</span><span class="str">"stylesheet"</span><span class="pln"> href</span><span class="pun">=</span><span class="str">"style.css"</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
  </span><span class="com">// لا يُنفّذ السكربت حتى تُحمّل ورقة التنسيق</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln">getComputedStyle</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">marginTop</span><span class="pun">);</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622208355="1" data-ss1622208816="1" data-ss1622213329="1" frameborder="no" height="256" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/poeWPPK?height=256&amp;theme-id=light&amp;default-tab=html,result" style="width: 100%;" title="JS-p2-loading -ex3">See the Pen JS-p2-loading -ex3 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

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

<p>
	بانتظار الحدث <code>DOMContentLoaded</code> للسكربتات، عليه انتظار التنسيقات التي قبلها كذلك.
</p>

<h3>
	تعبئة الاستمارت تلقائيا في المتصفح
</h3>

<p>
	تُعبّئ المتصفّحاتُ Firefox وChrome وOpera الاستمارات تلقائيّا عند حدوث <code>DOMContentLoaded</code>.
</p>

<p>
	على سبيل المثال، إذا كان في الصفحة استمارة لاسم المستخدم وكلمة المرور، وتذكّر المتصفّح القيم الخاصّة بها، فعند حدوث <code>DOMContentLoaded</code>، قد يحاول تعبئتها تلقائيّا (إذا أقرّ المستخدم ذلك).
</p>

<p>
	فإذا أُجِّل <code>DOMContentLoaded</code> بسبب سكربتات طويلة التحميل، فإنّ التعبئة التلقائيّة تنتظر أيضا. قد تكون لاحظت ذلك في بعض المواقع (إذا كنت تستخدم تعبئة المتصفّح التلقائيّة) -- لا تُعبّأ حقول اسم المستخدم وكلمة المرور فورا، بل إنّ هناك تأخيرا إلى حين تحميل الصفحة بالكامل. ذلك في الواقع هو الوقت الذي يأخذه <code>DOMContentLoaded</code> للحدوث.
</p>

<h2>
	الحدث window.onload
</h2>

<p>
	يقع الحدث <code>load</code> على كائن النافذة <code>window</code> عندما تُحمّل الصفحة كاملة، بما في ذلك التنسيقات والصور وغيرها من الموارد. هذا الحدث متاح من خلال الخاصّيّة <code>onload</code>.
</p>

<p>
	يظهر المثال أدناه مقاسات الصور الصحيحة، لأنّ <code>window.onload</code> ينتظر جميع الصور:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7973_15" style="">
<span class="pun">&lt;</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
  window</span><span class="pun">.</span><span class="pln">onload </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="com">// window.addEventListener('load', (event) =&gt; { :هذا مماثل لكتابة</span><span class="pln">
    alert</span><span class="pun">(</span><span class="str">'Page loaded'</span><span class="pun">);</span><span class="pln">

    </span><span class="com">// تُحمّل الصورة في هذه اﻷثناء</span><span class="pln">
    alert</span><span class="pun">(`</span><span class="typ">Image</span><span class="pln"> size</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">offsetWidth</span><span class="pun">}</span><span class="pln">x$</span><span class="pun">{</span><span class="pln">img</span><span class="pun">.</span><span class="pln">offsetHeight</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">script</span><span class="pun">&gt;</span><span class="pln">

</span><span class="pun">&lt;</span><span class="pln">img id</span><span class="pun">=</span><span class="str">"img"</span><span class="pln"> src</span><span class="pun">=</span><span class="str">"https://en.js.cx/clipart/train.gif?speed=1&amp;cache=0"</span><span class="pun">&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1622208355="1" data-ss1622208816="1" data-ss1622213329="1" frameborder="no" height="265" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/dyvVWRo?height=265&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-loading -ex4">See the Pen JS-p2-loading -ex4 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<h2>
	الحدث window.onunload
</h2>

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

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

<p>
	هناك التابع <code>navigator.sendBeacon(url, data)‎</code> المخصّص لهذا الغرض، وهو مبيّن في <a data-ss1622208355="1" data-ss1622208816="1" data-ss1622213329="1" href="https://w3c.github.io/beacon/" rel="external nofollow">المواصفة</a>. يعمل هذا التابع على إرسال المعطيات في الخلفيّة، فلا يتأخّر بذلك الانتقال إلى صفحة أخرى: يغادر المتصفّح الصفحة، لكنّه يبقى مؤدّيا لـ <code>sendBeacon</code>. هذه كيفيّة استخدامه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7973_17" style="">
<span class="pln">let analyticsData </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">

window</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"unload"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</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">"/analytics"</span><span class="pun">,</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">(</span><span class="pln">analyticsData</span><span class="pun">));</span><span class="pln">
</span><span class="pun">});</span></pre>

<ul>
<li>
		يُرسل الطلب على شكل POST.
	</li>
	<li>
		لا يمكننا إرسال السلاسل النصّيّة فقط، بل الاستمارات وصيغ أخرى كذلك، كما سنوضع ذلك في مقال لاحق، لكن يُرسل غالبا كائن محوّل إلى سلسلة نصّيّة stringified object.
	</li>
	<li>
		يُحدّ حجم المعطيات بـ 64kb.
	</li>
</ul>
<p>
	عند انتهاء الطلب <code>sendBeacon</code>، يكون المتصفّح قد غادر المستند، وبالتالي لا سبيل للحصول على إجابة من الخادم (التي تكون في العادة فارغة بالنسبة للتحليلات).
</p>

<p>
	هناك أيضا الراية <code>keepalive</code>، للقيام بطلبات مماثلة تتعلّق بما "بعد مغادرة الصفحة"، في التابع Fetch الخاصّ بالطلبات العامّة على الشبكة. يمكنك الاطلاع على المزيد من المعلومات حول ذلك في مقال <a data-ss1622213329="1" 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>.
</p>

<p>
	إذا أردنا إلغاء الانتقال إلى صفحة أخرى، لا يمكننا فعل ذلك هنا. لكن يمكننا استعمال حدث آخر -- <code>onbeforeunload</code>.
</p>

<h2>
	الحدث window.onbeforeunload
</h2>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7973_19" style="">
<span class="pln">window</span><span class="pun">.</span><span class="pln">onbeforeunload </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</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></pre>

<p>
	لأسباب تاريخيّة، تُعدّ إعادة سلسلة نصيّة غير فارغة أيضا بمثابة إلغاء الحدث. اعتادت المتصفّحات في السابق على إظهارها على شكل رسالة، لكن كما تنصّ [المواصفة الحديثة](https://html.spec.whatwg.org/#unloading-documents )، يجب ألا يفعلوا ذلك.
</p>

<p>
	هذا مثال:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7973_21" style="">
<span class="pln">window</span><span class="pun">.</span><span class="pln">onbeforeunload </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</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="str">"There are unsaved changes. Leave now?"</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span></pre>

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

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

<p>
	ماذا يحصل لو وضعنا معالجا للحدث <code>DOMContentLoaded</code> بعد تحميل المستند؟ من الطبيعيّ أن لا يشتغل أبدا.
</p>

<p>
	هناك حالات لا نكون فيها متأكّدين ما إذا كان المستند جاهزا أو لا. نريد لدالّتنا أن تُنفّذ عندما يكون DOM قد حُمّل، كان ذلك الآن أو لاحقا.
</p>

<p>
	تخبرنا الخاصّيّة <code>document.readyState</code> بحالة التحميل الحاليّة. وتأخذ ثلاث قيم ممكنة:
</p>

<ul>
<li>
		<code>"loading"</code> -- المستند قيد التحميل.
	</li>
	<li>
		<code>"interactive"</code> -- تمّت قراءة المستند بأكمله.
	</li>
	<li>
		<code>"complete"</code> -- تمّت قراءة المستند بأكمله وحُمّلت جميع الموارد (مثل الصور) أيضا.
	</li>
</ul>
<p>
	فيمكننا إذًا تفحّص <code>document.readyState</code> وتهيئة معالج أو تنفيذ الشيفرة مباشرة إن كان المستند جاهزا.
</p>

<p>
	هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7973_23" style="">
<span class="kwd">function</span><span class="pln"> work</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">readyState </span><span class="pun">==</span><span class="pln"> </span><span class="str">'loading'</span><span class="pun">)</span><span class="pln"> </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">'DOMContentLoaded'</span><span class="pun">,</span><span class="pln"> work</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">// جاهز DOM</span><span class="pln">
  work</span><span class="pun">();</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	الحدث <code>readystatechange</code> هو آليّة بديلة لتتبّع حالة تحميل المستند، وقد ظهر قديما. أمّا حاليّا، فيندر استخدامه. لنرى مجرى الأحداث كاملا من باب التمام:
</p>

<p>
	هذا مستند فيه <code>&lt;iframe&gt;</code> و<code>&lt;img&gt;</code> ومعالجات تقوم بتسجيل اﻷحداث:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7973_25" style="">
<span class="pun">&lt;</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
  log</span><span class="pun">(</span><span class="str">'initial readyState:'</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">readyState</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">'readystatechange'</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"> log</span><span class="pun">(</span><span class="str">'readyState:'</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">readyState</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">'DOMContentLoaded'</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"> log</span><span class="pun">(</span><span class="str">'DOMContentLoaded'</span><span class="pun">));</span><span class="pln">

  window</span><span class="pun">.</span><span class="pln">onload </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"> log</span><span class="pun">(</span><span class="str">'window onload'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">

</span><span class="pun">&lt;</span><span class="pln">iframe src</span><span class="pun">=</span><span class="str">"iframe.html"</span><span class="pln"> onload</span><span class="pun">=</span><span class="str">"log('iframe onload')"</span><span class="pun">&gt;&lt;/</span><span class="pln">iframe</span><span class="pun">&gt;</span><span class="pln">

</span><span class="pun">&lt;</span><span class="pln">img src</span><span class="pun">=</span><span class="str">"http://en.js.cx/clipart/train.gif"</span><span class="pln"> id</span><span class="pun">=</span><span class="str">"img"</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
  img</span><span class="pun">.</span><span class="pln">onload </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"> log</span><span class="pun">(</span><span class="str">'img onload'</span><span class="pun">);</span><span class="pln">
</span><span class="pun">&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span></pre>

<p>
	يمكن مشاهدة المثال يعمل في <a data-ss1622208355="1" data-ss1622208816="1" data-ss1622213329="1" href="https://plnkr.co/edit/L0Cijdek96r72cGe?p=preview" rel="external nofollow">البيئة التجريبيّة</a>
</p>

<p>
	النتيجة الاعتياديّة:
</p>

<ol>
<li>
		[1] initial readyState:loading
	</li>
	<li>
		[2] readyState:interactive
	</li>
	<li>
		[2] DOMContentLoaded
	</li>
	<li>
		[3] iframe onload
	</li>
	<li>
		[4] img onload
	</li>
	<li>
		[4] readyState:complete
	</li>
	<li>
		[4] window onload
	</li>
</ol>
<p>
	تشير اﻷرقام داخل اﻷقواس المربّعة إلى وقت الحدوث بالتقريب. تقع الأحداث المُعلّمة بنفس الأرقام في نفس الوقت تقريبا (+- بضع ms).
</p>

<ul>
<li>
		<code>document.readyState</code> يصير <code>interactive</code> مباشرة قبل <code>DOMContentLoaded</code>. يعني هذان نفس الشيء في الحقيقة.
	</li>
	<li>
		<code>document.readyState</code> يصير <code>complete</code> عندما تُحمّل جميع الموارد (<code>iframe</code> و <code>img</code>). يمكننا أن نرى هنا أنّه يحدث في نفس الوقت تقريبا مع <code>img.onload</code>‏ (<code>img</code> هو آخر الموارد) و <code>window.onload</code>. يعني الانتقال إلى الحالة <code>complete</code> نفس <code>window.onload</code>. الفرق الذي هناك هو أنّ <code>window.onload</code> يشتغل دائما بعد جميع معالجات <code>load</code> اﻷخرى.
	</li>
</ul>
<h2>
	الملخص
</h2>

<p>
	أحداث تحميل الصفحة:
</p>

<ul>
<li>
		يقع الحدث <code>DOMContentLoaded</code> على <code>document</code> عندما يكون DOM جاهزا. يمكننا تطبيق جافاسكربت على العناصر في هذه المرحلة.

		<ul>
<li>
				تعيق السكربتات مثل <code>&lt;script&gt;...&lt;/script&gt;</code> أو <code>&lt;script src="..."&gt;&lt;/script&gt;</code> الحدث DOMContentLoaded، إذ ينتظر المتصفّح تنفيذها.
			</li>
			<li>
				يمكن أن تواصل الصور والموارد اﻷخرى التحميل.
			</li>
		</ul>
</li>
	<li>
		يقع الحدث <code>load</code> على <code>window</code> عندما تكون الصفحة وجميع الموارد قد حُمّلت. يندر استخدامه، إذ لا حاجة للانتظار كلّ هذا الوقت في العادة.
	</li>
	<li>
		يقع الحدث <code>beforeunload</code> على <code>window</code> عندما يغادر المستخدم في النهاية، يمكننا فعل أشياء بسيطة فقط في المعالج، لا تستلزم تأخّرات ولا تسأل المستخدم. بسبب هذه القيود، يندر استخدامها. يمكننا إرسال طلب عبر الشبكة باستعمال <code>navigator.sendBeacon</code>.
	</li>
	<li>
		<code>document.readyState</code> هي حالة جاهزيّة المستند الحاليّة، يمكن تتبّع تغيّراتها بواسطة الحدث <code>readystatechange</code>:
		<ul>
<li>
				<code>loading</code> -- المستند قيد التحميل.
			</li>
			<li>
				<code>interactive</code> -- قد حُلّل المستند. يحدث في نفس الوقت تقريبا مع <code>DOMContentLoaded</code>، لكن قبله.
			</li>
			<li>
				<code>complete</code> -- قد حُمّل المستند والموارد. يحدث في نفس الوقت تقريبا مع <code>window.onload</code>، لكن قبله.
			</li>
		</ul>
</li>
</ul>
<p>
	ترجمة -وبتصرف- للمقال <a data-ss1622208355="1" data-ss1622208816="1" data-ss1622213329="1" href="https://javascript.info/onload-ondomcontentloaded" rel="external nofollow">Page: DOMContentLoaded, load, beforeunload, unload</a> من سلسلة <a data-ss1622208355="1" data-ss1622208816="1" data-ss1622213329="1" href="https://javascript.info/ui" rel="external nofollow">Browser: Document, Events, Interfaces</a> لصاحبها Ilya Kantor
</p>
]]></description><guid isPermaLink="false">1236</guid><pubDate>Tue, 01 Jun 2021 11:09:00 +0000</pubDate></item><item><title>&#x647;&#x64A;&#x627;&#x643;&#x644; &#x627;&#x644;&#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A;: &#x627;&#x644;&#x643;&#x627;&#x626;&#x646;&#x627;&#x62A; &#x648;&#x627;&#x644;&#x645;&#x635;&#x641;&#x648;&#x641;&#x627;&#x62A; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x64A;&#x628;&#x62A;</title><link>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/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_05/60adeb6402f52_---.png.ebce5eb93db4dec04b054b036295e983.png" /></p>
<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

	<div class="ipsQuote_contents ipsClearfix" data-gramm="false">
		<p>
			لقد سئلت مرتين من قبل أني لو أَدخلتُ إلى الآلة أرقامًا خاطئةً فهل سأحصل على إجابات صحيحة؟ وإني في الحقيقة لعاجز عن فهم هذا الخلط في الفِكر الذي يجعل العقل يثير مثل هذا السؤال. ـــ تشارلز بابج Charles Babbage، مقاطع من حياة فيلسوف (1864).
		</p>
	</div>
</blockquote>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="67614" href="https://academy.hsoub.com/uploads/monthly_2021_05/chapter_picture_4.jpg.b334ba991a2c5e9cbf1caa5625d30b4b.jpg" rel="" data-fileext="jpg"><img alt="chapter_picture_4.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="67614" data-unique="hkjddknib" src="https://academy.hsoub.com/uploads/monthly_2021_05/chapter_picture_4.jpg.b334ba991a2c5e9cbf1caa5625d30b4b.jpg"></a>
</p>

<p>
	تشكل الأعداد، والقيم البوليانية، والسلاسل، الذرات التي تُبنى منها هياكل البيانات في مجال <a href="https://academy.hsoub.com/programming/general/%D8%B9%D9%84%D9%88%D9%85-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D9%88%D8%A8/#%D8%A7%D9%84%D9%81%D8%B1%D9%82-%D8%A8%D9%8A%D9%86-%D8%B9%D9%84%D9%88%D9%85-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D8%A8-%D9%88%D9%87%D9%86%D8%AF%D8%B3%D8%A9-%D8%A7%D9%84%D8%AD%D8%A7%D8%B3%D8%A8" rel="">هندسة البرمجيات وعلوم الحاسوب</a>، وستحتاج عند عملك في البرمجة إلى أكثر من ذرة واحدة، إذ تسمح لنا الكائنات objects بتجميع القيم -بما في ذلك الكائنات الأخرى-، من أجل بناء هياكل أكثر تعقيدًا.
</p>

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

<h2>
	الإنسان المتحول إلى سنجاب
</h2>

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

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

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

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

<h2>
	مجموعات البيانات
</h2>

<p>
	إذا أردت العمل مع كميات كبيرة من البيانات الرقمية، فعليك أولًا إيجاد طريقة لتمثيلها في ذاكرة الحواسيب، فعلى سبيل المثال، إذا أردنا تمثيل تجميعة من الأرقام 2، و3، و5، و7، و11، فسنستطيع حل الأمر بأسلوب مبتكر باستخدام السلاسل النصية -إذ لا حد لطول السلسلة النصية، وعليه نستطيع وضع بيانات كثيرة فيها- واعتماد <code>‎"2 3 5 7 11"‎</code> على أنه التمثيل الخاص بنا، لكن هذا منظور غريب ومستهجن، إذ يجب استخراج الأعداد بطريقة ما، وإعادة تحويلها إلى أعداد من أجل الوصول إليها.
</p>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_7" style=""><span class="pln">let listOfNumbers </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="lit">11</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">listOfNumbers</span><span class="pun">[</span><span class="lit">2</span><span class="pun">]);</span><span class="pln">
</span><span class="com">// → 5</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">listOfNumbers</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]);</span><span class="pln">
</span><span class="com">// → 2</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">listOfNumbers</span><span class="pun">[</span><span class="lit">2</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">]);</span><span class="pln">
</span><span class="com">// → 3</span></pre>

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

<p>
	الفهرس الأول للمصفوفة هو الصفر وليس الواحد، لذا يُسترد العنصر الأول باستخدام <code>listOfNumbers[0]‎</code>، وإن كنت جديدًا على علوم الحاسوب، فسيمر وقت قبل اعتياد بدء العد على أساس الصفر، وهو تقليد قديم في التقنية وله منطق مبني عليه، لكن كما قلنا، ستأخذ وقتك لتتعود عليه؛ ولتريح نفسك، فكر في الفهرس على أنه عدد العناصر التي يجب تخطيها بدءًا من أول المصفوفة.
</p>

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

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

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_9" style=""><span class="kwd">null</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln">
</span><span class="com">// → TypeError: null has no properties</span></pre>

<p>
	الطريقتان الرئيسيتان للوصول إلى الخصائص في جافاسكربت، هما: النقطة <code>.</code>، والأقواس المربعة <code>[]</code>، حيث تصل كل من <code>value.x</code>، و<code>value[x]‎</code> مثلًا، إلى خاصية ما في <code>value</code>، لكن ليس إلى الخاصية نفسها بالضرورة، ويكمن الفرق في كيفية تفسير <code>x</code>، فحين نستخدم النقطة فإن الكلمة التي تليها هي الاسم الحرفي للخاصية؛ أما عند استخدام الأقواس المربعة فيُقيَّم التعبير الذي بين الأقواس للحصول على اسم الخاصية.
</p>

<p>
	في حين تجلب <code>value.x</code> خاصيةً اسمها x لـ <code>value</code>، فستحاول <code>value[x]‎</code> تقييم التعبير <code>x</code>، وتستخدم النتيجة -المحوَّلة إلى سلسلة نصية- على أساس اسم للخاصية، لذا فإن كنت على علم بأنّ الخاصية التي تريدها تحمل الاسم "color"، فتقول <code>value.color</code>؛ أما إن أردت استخراج الخاصية المسماة بالقيمة المحفوظة في الرابطة <code>i</code>، فتقول <code>value‎</code>.
</p>

<p>
	واعلم أنّ أسماء الخصائص ما هي إلا سلاسل نصية، فقد تكون أي سلسلة نصية، لكن صيغة النقطة لا تعمل إلا مع الأسماء التي تبدو مثل أسماء رابطات صالحة. فإذا أردت الوصول إلى خاصية اسمها "2" أو "John Doh"، فيجب عليك استخدام الأقواس المربعة: <code>value[2]‎</code>، أو <code>value ["John Doh"]‎</code>.
</p>

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

<p>
	تخبرنا خاصية <code>length</code> للمصفوفة كم عدد العناصر التي تحتوي عليها، واسم الخاصية ذاك هو اسم رابطة صالح، كما نعرِّف اسمه مسبقًا، لذلك نكتب <code>array.length</code> للعثور على طول المصفوفة، وذلك أسهل من كتابة <code>array["length"]‎</code>.
</p>

<h2>
	التوابع Methods
</h2>

<p>
	تحتوي قيم السلاسل النصية وقيم المصفوفات على عدد من الخصائص التي تحمل قيمًا للدالة، إضافةً إلى خاصية <code>length</code>، كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_11" style=""><span class="pln">let doh </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Doh"</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="kwd">typeof</span><span class="pln"> doh</span><span class="pun">.</span><span class="pln">toUpperCase</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → function</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">doh</span><span class="pun">.</span><span class="pln">toUpperCase</span><span class="pun">());</span><span class="pln">
</span><span class="com">// → DOH</span></pre>

<p>
	كل سلسلة لها خاصية <code>toUpperCase</code>، إذ تعيد عند استدعائها نسخةً من السلسلة التي تم فيها تحويل جميع الأحرف إلى أحرف كبيرة. وبالمثل، تسير خاصية <code>toLowerCase</code> في الاتجاه العكسي. ومن المثير أنّ الدالة لديها وصول لسلسلة <code>"Doh"</code> النصية، وهي القيمة التي استدعينا خاصيتها، رغم أن استدعاء <code>toUpperCase</code> لا يمرر أي وسائط، وسننظر في تفصيل كيفية حدوث ذلك في المقال السادس.
</p>

<p>
	تسمى الخصائص التي تحتوي على دوال توابعًا للقيم المنتمية إليها، فمثلًا، يُعَد <code>toUpperCase</code> تابعًا لسلسلة نصية، ويوضح المثال التالي تابعَيْن يمكنك استخدامهما للتعامل مع المصفوفات:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_13" style=""><span class="pln">let sequence </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="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">];</span><span class="pln">
sequence</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="lit">4</span><span class="pun">);</span><span class="pln">
sequence</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="lit">5</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">sequence</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → [1, 2, 3, 4, 5]</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">sequence</span><span class="pun">.</span><span class="pln">pop</span><span class="pun">());</span><span class="pln">
</span><span class="com">// → 5</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">sequence</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → [1, 2, 3, 4]</span></pre>

<p>
	يضيف تابع <code>push</code> قيمًا إلى نهاية مصفوفة ما؛ أما تابع <code>pop</code> فيفعل العكس تمامًا، حيث يحذف القيمة الأخيرة في المصفوفة ويعيدها. وهذه الأسماء السخيفة هي المصطلحات التقليدية للعمليات على المكدِّس stack، والمكدِّس في البرمجة هو أحد هياكل البيانات التي تسمح لك بدفع القيم إليها وإخراجها مرة أخرى بالترتيب المعاكس، بحيث يُبتدأ بإزالة العنصر الذي أضيف آخر مرة، وذلك استنادًا على منطق "آخرهم دخولًا أولهم خروجًا". ولعلك تذكر دالة مكدس الاستدعاءات من <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1233/" rel="">المقال السابق</a> الذي يشرح الفكرة نفسها.
</p>

<h2>
	الكائنات Objects
</h2>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_15" style=""><span class="pln">let day1 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  squirrel</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln">
  events</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">"work"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"touched tree"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"pizza"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"running"</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">day1</span><span class="pun">.</span><span class="pln">squirrel</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → false</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">day1</span><span class="pun">.</span><span class="pln">wolf</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → undefined</span><span class="pln">
day1</span><span class="pun">.</span><span class="pln">wolf </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">false</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">day1</span><span class="pun">.</span><span class="pln">wolf</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → false</span></pre>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_17" style=""><span class="pln">let descriptions </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  work</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Went to work"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"touched tree"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Touched a tree"</span><span class="pln">
</span><span class="pun">};</span></pre>

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

<p>
	كذلك سيعطيك قراءة خاصية غير موجودة القيمة <code>undefined</code>، ونستطيع استخدام عامل <code>=</code> لإسناد قيمة إلى تعبيرِ خاصية ليغير القيمة الموجودة أصلًا، أو ينشئ خاصيةً جديدةً للكائن إن لم تكن.
</p>

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

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_19" style=""><span class="pln">let anObject </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">left</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> right</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2</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">anObject</span><span class="pun">.</span><span class="pln">left</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 1</span><span class="pln">
</span><span class="kwd">delete</span><span class="pln"> anObject</span><span class="pun">.</span><span class="pln">left</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">anObject</span><span class="pun">.</span><span class="pln">left</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → undefined</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"left"</span><span class="pln"> in anObject</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → false</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"right"</span><span class="pln"> in anObject</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → true</span></pre>

<p>
	عند تطبيق العامل الثنائي <code>in</code>على سلسلة نصية وكائن، فسيخبرك إذا كان الكائن به خاصية باسم تلك السلسلة النصية، والفرق بين جعل الخاصية <code>undefined</code> وحذفها على الحقيقة، هو أنّ الكائن ما زال يحتفظ بالخاصية في الحالة الأولى مما يعني عدم حمله لقيمة ذات شأن؛ أما في الحالة الثانية فإن الخاصية لم تَعُدْ موجودة، وعليه فستعيد <code>in</code> القيمة <code>false</code>.
</p>

<p>
	تُستخدَم دالة <code>Object.keys</code> لمعرفة الخصائص التي يحتوي عليها الكائن، وذلك بإعطائها كائنًا، فتعيد مصفوفةً من السلاسل النصية التي تمثل أسماء خصائص الكائن. انظر كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_21" style=""><span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="typ">Object</span><span class="pun">.</span><span class="pln">keys</span><span class="pun">({</span><span class="pln">x</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> z</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2</span><span class="pun">}));</span><span class="pln">
</span><span class="com">// → ["x", "y", "z"]</span></pre>

<p>
	تُستخدَم دالة <code>Object.assign</code> لنسخ جميع الخصائص من كائن إلى آخر، انظر كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_23" style=""><span class="pln">let objectA </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">a</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2</span><span class="pun">};</span><span class="pln">
</span><span class="typ">Object</span><span class="pun">.</span><span class="pln">assign</span><span class="pun">(</span><span class="pln">objectA</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">b</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> c</span><span class="pun">:</span><span class="pln"> </span><span class="lit">4</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">objectA</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → {a: 1, b: 3, c: 4}</span></pre>

<p>
	وتكون المصفوفات حينئذ نوعًا من الكائنات المتخصصة في تخزين سلاسل من أشياء بعينها، وإذا قيَّمت <code>typeof[]‎</code>، فستُنتج "object"، وسترى هذه المصفوفات كأخطبوطات طويلة بمجساتها في صف أنيق له عناوين من الأعداد. انظر الآن إلى السجل journal الذي يحتفظ به سمير في صورة مصفوفة من الكائنات:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_25" style=""><span class="pln">let journal </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
  </span><span class="pun">{</span><span class="pln">events</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">"work"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"touched tree"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"pizza"</span><span class="pun">,</span><span class="pln">
            </span><span class="str">"running"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"television"</span><span class="pun">],</span><span class="pln">
   squirrel</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">events</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">"work"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"ice cream"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"cauliflower"</span><span class="pun">,</span><span class="pln">
            </span><span class="str">"lasagna"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"touched tree"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"brushed teeth"</span><span class="pun">],</span><span class="pln">
   squirrel</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">events</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">"weekend"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"cycling"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"break"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"peanuts"</span><span class="pun">,</span><span class="pln">
            </span><span class="str">"juice"</span><span class="pun">],</span><span class="pln">
   squirrel</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">},</span><span class="pln">
  </span><span class="com">/* and so on... */</span><span class="pln">
</span><span class="pun">];</span></pre>

<h2>
	قابلية التغير Mutability
</h2>

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

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

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_27" style=""><span class="pln">let object1 </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">10</span><span class="pun">};</span><span class="pln">
let object2 </span><span class="pun">=</span><span class="pln"> object1</span><span class="pun">;</span><span class="pln">
let object3 </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">10</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">object1 </span><span class="pun">==</span><span class="pln"> object2</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → true</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">object1 </span><span class="pun">==</span><span class="pln"> object3</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → false</span><span class="pln">

object1</span><span class="pun">.</span><span class="pln">value </span><span class="pun">=</span><span class="pln"> </span><span class="lit">15</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">object2</span><span class="pun">.</span><span class="pln">value</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 15</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">object3</span><span class="pun">.</span><span class="pln">value</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 10</span></pre>

<p>
	تلتقط رابطتي<code>object1</code>، و<code>object2</code> الكائن نفسه، لهذا ستتغير قيمة <code>object2</code> إذا تغير <code>object1</code>، فيقال أنّ لهما "الهوية" نفسها إن صح التعبير؛ أما الرابطة <code>object3</code>، فتشير إلى كائن آخر يحتوي على خصائص <code>object1</code> نفسها، لكنه منفصل ومستقل بذاته.
</p>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_30" style=""><span class="kwd">const</span><span class="pln"> score </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">visitors</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> home</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">};</span><span class="pln">
</span><span class="com">// This is okay</span><span class="pln">
score</span><span class="pun">.</span><span class="pln">visitors </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
</span><span class="com">// This isn't allowed</span><span class="pln">
score </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">visitors</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> home</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">};</span></pre>

<p>
	يوازن العامل <code>==</code> بين الكائنات من منظور هويتها، فلا يعطي <code>true</code> إلا إذا كان لكلا الكائنين القيمة نفسها تمامًا؛ أما عند موازنة كائنات مختلفة، فسيعطي <code>false</code> حتى ولو كان لهذه الكائنات الخصائص نفسها، وعليه فليس هناك عملية موازنة "عميقة" في جافاسكربت توازن بين الكائنات من خلال محتوياتها، لكن من الممكن كتابة ذلك بنفسك.
</p>

<h2>
	سجل المستذئب
</h2>

<p>
	نعود إلى سمير الذي يظن بأنّه يتحول إلى حيوان في الليل، إذ يبدأ مفسِّر جافاسكربت الخاص به، ويضبط البيئة التي يحتاجها من أجل سجله journal، انظر كما يأتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_32" style=""><span class="pln">let journal </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"> addEntry</span><span class="pun">(</span><span class="pln">events</span><span class="pun">,</span><span class="pln"> squirrel</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  journal</span><span class="pun">.</span><span class="pln">push</span><span class="pun">({</span><span class="pln">events</span><span class="pun">,</span><span class="pln"> squirrel</span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_34" style=""><span class="pln">addEntry</span><span class="pun">([</span><span class="str">"work"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"touched tree"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"pizza"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"running"</span><span class="pun">,</span><span class="pln">
          </span><span class="str">"television"</span><span class="pun">],</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">);</span><span class="pln">
addEntry</span><span class="pun">([</span><span class="str">"work"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"ice cream"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"cauliflower"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"lasagna"</span><span class="pun">,</span><span class="pln">
          </span><span class="str">"touched tree"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"brushed teeth"</span><span class="pun">],</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">);</span><span class="pln">
addEntry</span><span class="pun">([</span><span class="str">"weekend"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"cycling"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"break"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"peanuts"</span><span class="pun">,</span><span class="pln">
          </span><span class="str">"juice"</span><span class="pun">],</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">);</span></pre>

<p>
	وهو ينوي اتباع أسلوب إحصائي عند حصوله على نقاط بيانات كافية، وذلك لرؤية أيَّ تلك الأحداث هي التي تحث تحوله إلى حيوان ليلًا.
</p>

<p>
	يختلف المتغير في الإحصاء عن المتغير البرمجي، إذ يكون لدينا مجموعة من المقاييس، بحيث يقاس كل متغير بها جميعًا، وتُمثَّل <a href="https://www.verywellmind.com/what-is-correlation-2794986" rel="external nofollow" target="_blank">علاقة الترابط</a> Correlation بين المتغيرات بقيمة بين -1، و1، وعلاقة الترابط هي مقياس اعتمادية المتغير الإحصائي على متغير آخر. فإذا كانت قيمة علاقة الترابط هذه صفرًا، فهذا يعني أنّ المتغيرين غير مرتبطان ببعضهما؛ أما إذا كان 1، فهذا يعني أنّ المتغيرين متطابقان تمامًا. بحيث إذا كنت تعرف أحدهما، فأنت تعرف الأخر يقينًا؛ أما إذا كانت قيمة علاقة الترابط تلك -1، فهذا يعني أنهما متطابقان لكنهما متقابلان، بحيث إن كان الأول true، فالآخر false.
</p>

<p>
	نستخدم معامِل فاي <code>ϕ</code> لحساب مقياس علاقة الترابط بين متغيرين بوليانيين، وهي معادلة يكون دخلها جدول تردد يحتوي على عدد المرات التي لوحظت مجموعات المتغيرات فيها؛ ويصف الخرج علاقة الترابط بينها بحيث يكون عددًا بين -1، و1.
</p>

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

<p style="text-align: center;">
	<img alt="pizza-squirrel.png.2c8aeb23065872b29eef7624b403db7c.png" class="ipsImage ipsImage_thumbnailed" data-fileid="89910" data-unique="pm4vwt0hy" src="https://academy.hsoub.com/uploads/monthly_2022_01/pizza-squirrel.png.2c8aeb23065872b29eef7624b403db7c.png.d633f754c43df0a6c68f97fa9a352862.png">
</p>

<p>
	فإذا سمينا هذا الجدول بجدول n مثلًا، فإننا سنستطيع حساب ϕ باستخدام المعادلة التالية:
</p>

<p style="text-align: center;">
	<img alt="phi_eq.png" class="ipsImage ipsImage_thumbnailed" data-fileid="68003" data-unique="gwvx72oir" style="" src="https://academy.hsoub.com/uploads/monthly_2021_05/phi_eq.png.17190e26fad4b069625aea9bbc093fca.png">
</p>

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

<p>
	تشير الصيغة n<sub>01</sub> إلى عدد القياسات التي يكون فيها المتغير الأول <code>squirrel</code> "السنجاب" غير متحقق أو خطأ false، والمتغير الثاني <code>pizza</code> "البيتزا" متحقق أو صحيح true، ففي جدول البيتزا مثلًا، تكون قيمة n<sub>01</sub> هي 9.
</p>

<p>
	تشير القيمة n<sub>1•</sub>‎ إلى مجموع القياسات التي كان فيها المتغير الأول متحققًا -أي true-، وهي 5 في الجدول المثال. بالمثل، تشير n<sub>•0</sub>‎ إلى مجموع القياسات التي كان فيها المتغير الثاني يساوي false.
</p>

<p>
	لذا سيكون الجزء الموجود أعلى خط الفصل في جدول البيتزا 1×76−4×9 = 40، وسيكون الجزء السفلي هو الجذر التربيعي لـ 5×85×10×80، أو 340000√، ونخرج من هذا أن قيمة فاي هي 0.069 تقريبًا، وهي قيمة ضئيلة قطعًا، وعليه فلا يبدو أنّ البيتزا لها تأثير على تحول سمير.
</p>

<h2>
	حساب علاقة الترابط
</h2>

<p>
	نستطيع تمثيل جدول من صفين وعمودين في جافاسكربت، باستخدام مصفوفة من أربعة عناصر (<code>‎[76, 9, 4, 1]‎</code>)، أو مصفوفة تحتوي على مصفوفتين، بحيث تتكون كل واحدة منهما من عنصرين (<code>‎[[76, 9], [4, 1]]‎</code>)، أو كائن له أسماء خصائص، مثل: <code>"11"</code>، و"01"`.
</p>

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

<p>
	انظر الدالة التي تحسب قيمة معامل ϕ من مثل هذه المصفوفة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_36" style=""><span class="kwd">function</span><span class="pln"> phi</span><span class="pun">(</span><span class="pln">table</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">table</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"> table</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> table</span><span class="pun">[</span><span class="lit">2</span><span class="pun">]</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> table</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">
    </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">sqrt</span><span class="pun">((</span><span class="pln">table</span><span class="pun">[</span><span class="lit">2</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> table</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">
              </span><span class="pun">(</span><span class="pln">table</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> table</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">
              </span><span class="pun">(</span><span class="pln">table</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"> table</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">
              </span><span class="pun">(</span><span class="pln">table</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> table</span><span class="pun">[</span><span class="lit">2</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">phi</span><span class="pun">([</span><span class="lit">76</span><span class="pun">,</span><span class="pln"> </span><span class="lit">9</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">]));</span><span class="pln">
</span><span class="com">// → 0.068599434</span></pre>

<p>
	الشيفرة أعلاه هي ترجمة حرفية لمعادلة فاي الرياضية السابقة في جافاسكربت، وتكون فيها <code>Math.sqrt</code> هي دالة الجذر التربيعي التي يوفرها كائن <code>Math</code> في بيئة جافاسكربت القياسية، ويجب إضافة حقلين من الجدول لنحصل على حقول مثل n<sub>1•</sub>‎، ذلك أن مجموع الصفوف أو الأعمدة لا يُخزَّن في قاعدة بياناتنا مباشرةً وقد احتفظ سمير بسجله لمدة ثلاثة أشهر، وستجد النتائج لتلك الفترة متاحة في <a href="https://eloquentjavascript.net/code#4" rel="external nofollow" target="_blank">صندوق التجارب</a> لهذا المقال، حيث تُخزَّن في رابطة <code>JOURNAL</code> وهي <a href="https://eloquentjavascript.net/code/journal.js" rel="external nofollow" target="_blank">متاحة للتحميل</a>.
</p>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_38" style=""><span class="kwd">function</span><span class="pln"> tableFor</span><span class="pun">(</span><span class="pln">event</span><span class="pun">,</span><span class="pln"> journal</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let table </span><span class="pun">=</span><span class="pln"> </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"> </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">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let 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"> journal</span><span class="pun">.</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><span class="pln">
    let entry </span><span class="pun">=</span><span class="pln"> journal</span><span class="pun">[</span><span class="pln">i</span><span class="pun">],</span><span class="pln"> index </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">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">entry</span><span class="pun">.</span><span class="pln">events</span><span class="pun">.</span><span class="pln">includes</span><span class="pun">(</span><span class="pln">event</span><span class="pun">))</span><span class="pln"> index </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">entry</span><span class="pun">.</span><span class="pln">squirrel</span><span class="pun">)</span><span class="pln"> index </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
    table</span><span class="pun">[</span><span class="pln">index</span><span class="pun">]</span><span class="pln"> </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">
  </span><span class="kwd">return</span><span class="pln"> table</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">tableFor</span><span class="pun">(</span><span class="str">"pizza"</span><span class="pun">,</span><span class="pln"> JOURNAL</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → [76, 9, 4, 1]</span></pre>

<p>
	يتحقق تابع <code>includes</code> من وجود قيمة ما في المصفوفة، وتَستخدِم الدالة ذلك لتحديد وجود اسم الحدث الذي تريده في قائمة الأحداث في يوم ما.
</p>

<p>
	يبيّن متن الحلقة التكرارية في <code>tableFor</code> أيّ صندوق في الجدول يقع فيه إدخال السجل، من خلال النظر في احتواء الإدخال على حدث بعينه أم لا، والنظر هل وقع الحدث مع وقوع التحول السنجابي أم لا، ثم تزيد الحلقة التكرارية الصندوق الصحيح بمقدار 1 داخل الجدول.
</p>

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

<h2>
	حلقات المصفوفات التكرارية
</h2>

<p>
	لدينا حلقة تكرارية في دالة <code>tableFor</code>، وهي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_40" style=""><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let 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"> JOURNAL</span><span class="pun">.</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><span class="pln">
  let entry </span><span class="pun">=</span><span class="pln"> JOURNAL</span><span class="pun">[</span><span class="pln">i</span><span class="pun">];</span><span class="pln">
  </span><span class="com">// Do something with entry</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_42" style=""><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let entry of JOURNAL</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">$</span><span class="pun">{</span><span class="pln">entry</span><span class="pun">.</span><span class="pln">events</span><span class="pun">.</span><span class="pln">length</span><span class="pun">}</span><span class="pln"> events</span><span class="pun">.`);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	حين تُكتَب حلقة <code>for</code>مع الكلمة <code>of</code> بعد تعريفِ متغير، ستتكرر على عناصر القيمة المعطاة بعد <code>of</code>، ويصلح هذا في المصفوفات، والسلاسل النصية، وبعض هياكل البيانات الأخرى، كما سيأتي بيانه في المقال السادس.
</p>

<h2>
	التحليل النهائي
</h2>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_44" style=""><span class="kwd">function</span><span class="pln"> journalEvents</span><span class="pun">(</span><span class="pln">journal</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let events </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="pln">let entry of journal</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="pln">let event of entry</span><span class="pun">.</span><span class="pln">events</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">events</span><span class="pun">.</span><span class="pln">includes</span><span class="pun">(</span><span class="pln">event</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        events</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">event</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="kwd">return</span><span class="pln"> events</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">journalEvents</span><span class="pun">(</span><span class="pln">JOURNAL</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → ["carrot", "exercise", "weekend", "bread", …]</span></pre>

<p>
	تجمع الدالة <code>journalEvents</code> كل أنواع الأحداث من خلال المرور على الأحداث وإضافة الغير موجود منها إلى المصفوفة <code>events</code>، ونستطيع رؤية كل الالتزامات من خلال الحلقة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_46" style=""><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let event of journalEvents</span><span class="pun">(</span><span class="pln">JOURNAL</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">event </span><span class="pun">+</span><span class="pln"> </span><span class="str">":"</span><span class="pun">,</span><span class="pln"> phi</span><span class="pun">(</span><span class="pln">tableFor</span><span class="pun">(</span><span class="pln">event</span><span class="pun">,</span><span class="pln"> JOURNAL</span><span class="pun">)));</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">// → carrot:   0.0140970969</span><span class="pln">
</span><span class="com">// → exercise: 0.0685994341</span><span class="pln">
</span><span class="com">// → weekend:  0.1371988681</span><span class="pln">
</span><span class="com">// → bread:   -0.0757554019</span><span class="pln">
</span><span class="com">// → pudding: -0.0648203724</span><span class="pln">
</span><span class="com">// and so on...</span></pre>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_48" style=""><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let event of journalEvents</span><span class="pun">(</span><span class="pln">JOURNAL</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let correlation </span><span class="pun">=</span><span class="pln"> phi</span><span class="pun">(</span><span class="pln">tableFor</span><span class="pun">(</span><span class="pln">event</span><span class="pun">,</span><span class="pln"> JOURNAL</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">correlation </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0.1</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> correlation </span><span class="pun">&lt;</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="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"> </span><span class="str">":"</span><span class="pun">,</span><span class="pln"> correlation</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">// → weekend:        0.1371988681</span><span class="pln">
</span><span class="com">// → brushed teeth: -0.3805211953</span><span class="pln">
</span><span class="com">// → candy:          0.1296407447</span><span class="pln">
</span><span class="com">// → work:          -0.1371988681</span><span class="pln">
</span><span class="com">// → spaghetti:      0.2425356250</span><span class="pln">
</span><span class="com">// → reading:        0.1106828054</span><span class="pln">
</span><span class="com">// → peanuts:        0.5902679812</span></pre>

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

<p>
	دعنا نجرب الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_50" style=""><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let entry of JOURNAL</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">entry</span><span class="pun">.</span><span class="pln">events</span><span class="pun">.</span><span class="pln">includes</span><span class="pun">(</span><span class="str">"peanuts"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln">
     </span><span class="pun">!</span><span class="pln">entry</span><span class="pun">.</span><span class="pln">events</span><span class="pun">.</span><span class="pln">includes</span><span class="pun">(</span><span class="str">"brushed teeth"</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    entry</span><span class="pun">.</span><span class="pln">events</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="str">"peanut teeth"</span><span class="pun">);</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">log</span><span class="pun">(</span><span class="pln">phi</span><span class="pun">(</span><span class="pln">tableFor</span><span class="pun">(</span><span class="str">"peanut teeth"</span><span class="pun">,</span><span class="pln"> JOURNAL</span><span class="pun">)));</span><span class="pln">
</span><span class="com">// → 1</span></pre>

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

<h2>
	زيادة على المصفوفات
</h2>

<p>
	نريد أن نعرّفك على بعض المفاهيم الأخرى المتعلقة بالكائنات قبل إنهاء هذا المقال، فقد رأينا في بداية هذا المقال <code>push</code>، و<code>pop</code> لإضافة العناصر وحذفها من نهاية مصفوفة ما؛ أما التابعان الموافقان لإضافة وحذف العناصر من بداية المصفوفة، فهما: <code>unshift</code>، و<code>shift</code>، وذلك كما يأتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_52" style=""><span class="pln">let todoList </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"> remember</span><span class="pun">(</span><span class="pln">task</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  todoList</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">task</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"> getTask</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"> todoList</span><span class="pun">.</span><span class="pln">shift</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"> rememberUrgently</span><span class="pun">(</span><span class="pln">task</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  todoList</span><span class="pun">.</span><span class="pln">unshift</span><span class="pun">(</span><span class="pln">task</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ينظم البرنامج أعلاه مجموعةً من المهام المرتبة في طابور، حيث تضيف مهامًا إلى نهاية الطابور باستدعاء <code>remember("groceries")‎</code>، وتَستدعِي <code>getTask()‎</code> إذا أردت فعل شيء ما، وذلك لجلب -وحذف- العنصر الأمامي من الطابور، كما تضيف دالة <code>rememberUrgently</code> مهمةً إلى أول الطابور، وليس إلى آخره.
</p>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_54" style=""><span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">([</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">].</span><span class="pln">indexOf</span><span class="pun">(</span><span class="lit">2</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 1</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">([</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">].</span><span class="pln">lastIndexOf</span><span class="pun">(</span><span class="lit">2</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 3</span></pre>

<p>
	ويأخذ كلا التابعين <code>indexOf</code>، و<code>lastIndexOf</code> وسيطًا ثانيًا اختياريًا يوضح أين يجب أن يبدأ البحث.
</p>

<p>
	يُعَدّ التابع <code>slice</code> من التوابع الأساسية للمصفوفات، إذ يأخذ فهرس البداية والنهاية، ويعيد مصفوفةً تحوي العناصر المحصورة بين هذين الفهرسين، ويكون فهرس البداية موجودًا في هذه المصفوفة الناتجة، أما فهرس النهاية فلا. انظر المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_56" style=""><span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">([</span><span class="lit">0</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">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">].</span><span class="pln">slice</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → [2, 3]</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">([</span><span class="lit">0</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">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">].</span><span class="pln">slice</span><span class="pun">(</span><span class="lit">2</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → [2, 3, 4]</span></pre>

<p>
	إذا لم يُعط فهرس النهاية لتابع <code>slice</code>، فسيأخذ كل العناصر التي تلي فهرس البداية، وإن لم تذكر فهرس البداية، فسينسخ المصفوفة كلها.
</p>

<p>
	يُستخدَم تابع <code>concat</code> للصق المصفوفات معًا لإنشاء مصفوفة جديدة، وهو في هذا يماثل وظيفة عامل <code>+</code> في السلاسل النصية.
</p>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_58" style=""><span class="kwd">function</span><span class="pln"> remove</span><span class="pun">(</span><span class="pln">array</span><span class="pun">,</span><span class="pln"> index</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"> array</span><span class="pun">.</span><span class="pln">slice</span><span class="pun">(</span><span class="lit">0</span><span class="pun">,</span><span class="pln"> index</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">concat</span><span class="pun">(</span><span class="pln">array</span><span class="pun">.</span><span class="pln">slice</span><span class="pun">(</span><span class="pln">index </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">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">remove</span><span class="pun">([</span><span class="str">"a"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"b"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"c"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"d"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"e"</span><span class="pun">],</span><span class="pln"> </span><span class="lit">2</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → ["a", "b", "d", "e"]</span></pre>

<p>
	إذا مرّرنا وسيطًا ليس بمصفوفة إلى <code>concat</code>، فستضاف تلك القيمة إلى المصفوفة الجديدة كما لو كانت مصفوفة من عنصر واحد.
</p>

<h2>
	السلاسل النصية وخصائصها
</h2>

<p>
	نستطيع قراءة خصائص من قيم السلاسل النصية، مثل الخاصتين: <code>length</code>، و<code>toUpperCase</code>، لكن إذا حاولت إضافة خاصية جديدة، فلن تبقى، انظر المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_60" style=""><span class="pln">let kim </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Kim"</span><span class="pun">;</span><span class="pln">
kim</span><span class="pun">.</span><span class="pln">age </span><span class="pun">=</span><span class="pln"> </span><span class="lit">88</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">kim</span><span class="pun">.</span><span class="pln">age</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → undefined</span></pre>

<p>
	ذلك أن قيم السلاسل النصية، والأعداد، والقيم البوليانية، ليست بكائنات. وعليه فلن تمنعك اللغة من وضع خصائص جديدة على هذه القيم، فهي لا تخزن تلك الخصائص على الحقيقة، إذ لا يمكن تغيير تلك القيم كما ذكرنا من قبل؛ غير أنّ هذه الأنواع لها خصائصها المدمجة فيها، فكل قيمة سلسلة نصية لها عدد من التوابع، لعلّ <code>slice</code>، و<code>indexOf</code> أكثرها نفعًا واستخدامًا، واللذين يشبهان في وظائفهما التابعَين المذكورَين قبل قليل، انظر المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_62" style=""><span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"coconuts"</span><span class="pun">.</span><span class="pln">slice</span><span class="pun">(</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="lit">7</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → nut</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"coconut"</span><span class="pun">.</span><span class="pln">indexOf</span><span class="pun">(</span><span class="str">"u"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 5</span></pre>

<p>
	الفرق بينهما أنه يستطيع تابع <code>indexOf</code> في السلسلة النصية، البحث عن سلسلة تحتوي على أكثر من محرف واحد؛ بينما تابع المصفوفة الذي يحمل الاسم نفسه لا يبحث إلا عن عنصر واحد، أي كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_64" style=""><span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"one two three"</span><span class="pun">.</span><span class="pln">indexOf</span><span class="pun">(</span><span class="str">"ee"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 11</span></pre>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_66" style=""><span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"  okay \n "</span><span class="pun">.</span><span class="pln">trim</span><span class="pun">());</span><span class="pln">
</span><span class="com">// → okay</span></pre>

<p>
	الدالة <code>zeroPad</code> المستخدَمة في المقال السابق، موجودة هنا على أساس تابع أيضًا، ويسمى <code>padStart</code>، حيث يأخذ الطول المطلوب، ومحرف الحشو على أساس وسائط، كما في المثال التالي:
</p>

<pre class="ipsCode">console.log(String(6).padStart(3, "0"));
// → 006
</pre>

<p>
	تستطيع تقسيم سلسلة نصية عند كل ظهور لسلسلة أخرى باستخدام تابع <code>split</code>، ثم دمجها مرةً أخرى باستخدام تابع <code>join</code>، أي كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_69" style=""><span class="pln">let sentence </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Secretarybirds specialize in stomping"</span><span class="pun">;</span><span class="pln">
let words </span><span class="pun">=</span><span class="pln"> sentence</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">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">words</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → ["Secretarybirds", "specialize", "in", "stomping"]</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">words</span><span class="pun">.</span><span class="pln">join</span><span class="pun">(</span><span class="str">". "</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → Secretarybirds. specialize. in. stomping</span></pre>

<p>
	يمكن تكرار السلسلة النصية باستخدام تابع <code>repeat</code>، حيث ينشِئ سلسلةً نصيةً جديدةً تحتوي نسخًا متعددةً من السلسلة الأصلية، وملصقةً معًا. انظر المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_71" style=""><span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"LA"</span><span class="pun">.</span><span class="pln">repeat</span><span class="pun">(</span><span class="lit">3</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → LALALA</span></pre>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_73" style=""><span class="pln">let string </span><span class="pun">=</span><span class="pln"> </span><span class="str">"abc"</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">string</span><span class="pun">.</span><span class="pln">length</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 3</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">string</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]);</span><span class="pln">
</span><span class="com">// → b</span></pre>

<h2>
	معامل rest
</h2>

<p>
	من المفيد لدالة قبول أي عدد من الوسائط، فمثلًا، تحسب الدالة <code>Math.max</code> القيمة العظمى لكل الوسائط المعطاة؛ إذ يمكننا تحقيق ذلك بوضع ثلاث نقاط قبل آخر معامِل للدالة، كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_75" style=""><span class="kwd">function</span><span class="pln"> max</span><span class="pun">(...</span><span class="pln">numbers</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let result </span><span class="pun">=</span><span class="pln"> </span><span class="pun">-</span><span class="kwd">Infinity</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let number of numbers</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">number </span><span class="pun">&gt;</span><span class="pln"> result</span><span class="pun">)</span><span class="pln"> result </span><span class="pun">=</span><span class="pln"> number</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"> result</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">max</span><span class="pun">(</span><span class="lit">4</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">9</span><span class="pun">,</span><span class="pln"> </span><span class="pun">-</span><span class="lit">2</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 9</span></pre>

<p>
	وحين تُستدعى هذه الدالة فإن معامل rest يكون ملزَمًا بمصفوفة تحتوي كل الوسائط الأخرى، وإذا كان ثمة معامِلات أخرى قبله، فلا تكون قيمها جزءًا من المصفوفة؛ أما حين يكون هو المعامل الوحيد كما في حالة <code>max</code>، فستحمل المصفوفة كل الوسائط.
</p>

<p>
	تستطيع استخدام صيغة النقاط الثلاثة لاستدعاء دالة مع مصفوفة وسائط، كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_77" style=""><span class="pln">let numbers </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="lit">5</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">7</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">max</span><span class="pun">(...</span><span class="pln">numbers</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 7</span></pre>

<p>
	يوسع هذا المصفوفة إلى استدعاء الدالة ممررًا عناصرها على أساس وسائط منفصلة، ومن الممكن إضافة مصفوفة مثل هذه إلى جانب وسائط أخرى كما في <code>max(9, ...numbers, 2)‎</code>، كذلك تسمح صيغة الأقواس المربعة لمصفوفة، لعامل النقاط الثلاثة، بتوسيع مصفوفة أخرى داخل هذه المصفوفة الجديدة، كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_79" style=""><span class="pln">let words </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[</span><span class="str">"never"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"fully"</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">"will"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">...</span><span class="pln">words</span><span class="pun">,</span><span class="pln"> </span><span class="str">"understand"</span><span class="pun">]);</span><span class="pln">
</span><span class="com">// → ["will", "never", "fully", "understand"]</span></pre>

<h2>
	الكائن Math
</h2>

<p>
	كما رأينا سابقًا، فـ <code>Math</code> ما هو إلا حقيبةٌ من دوال التعامل مع الأعداد، مثل: <code>Math.max</code> للقيمة العظمى، و<code>Math.min</code> للقيمة الصغرى، و<code>Math.sqrt</code> للجذر التربيعي.
</p>

<p>
	يُستخدَم كائن <code>Math</code> على أساس حاوية لمجموعة من الوظائف المرتبطة ببعضها بعضًا، كما أنّه كائن وحيد، إذ لا يوجد كائن آخر يحمل الاسم نفسه، وهو غير مفيد أيضًا إن جاء على أساس قيمة، فهو يوفر فضاء اسم namespace لئلا تضطر الدوال والقيم لتكوّن روابط عامة global bindings، حيث تلوث كثرة هذه الروابط العامة فضاء الاسم، فكلما زاد عدد الأسماء المحجوزة زادت فرصة تغيير قيمة رابطة حالية بالخطأ، ولا بأس مثلًا بتسمية شيء ما باسم <code>max</code> في برنامج تكتبه، إذ أنّ دالة <code>max</code> المدمجة بجافاسكربت محفوظة بأمان داخل كائن <code>Math</code>، لذا فلن تتغير بفعل منك.
</p>

<p>
	لن توقفك أو تحذرك جافاسكربت -على عكس كثير من اللغات الأخرى- من تعريف رابطة باسم مأخوذ من قبل، إلا أن تكون رابطةً صرحْتَ عنها باستخدام <code>let</code>، أو <code>const</code>، أما الروابط القياسية أوالمصرَّح عنها باستخدام <code>var</code>، أو <code>function</code> فلا.
</p>

<p>
	ستحتاج إلى كائن <code>Math</code> إن أردت تنفيذ بعض العمليات المتعلِّقة بحساب المثلثات، وذلك لاحتوائه على دوال الجيب <code>sin</code>، وجيب التمام <code>cos</code>، والظل <code>tan</code>، إضافةً إلى دوالها المعكوسة، وهي: <code>asin</code>، و<code>acos</code>، و<code>atan</code>، كما أن العدد باي π متاح أيضًا في جافاسكربت في صورة <code>Math.PI</code>؛ وكُتِبت بحروف إنجليزية كبيرة تطبيقًا لعادة قديمة في البرمجة، إذ تُكتَب أسماء القيم الثابتة بالحروف الكبيرة.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_81" style=""><span class="kwd">function</span><span class="pln"> randomPointOnCircle</span><span class="pun">(</span><span class="pln">radius</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let angle </span><span class="pun">=</span><span class="pln"> </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="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">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">x</span><span class="pun">:</span><span class="pln"> radius </span><span class="pun">*</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">cos</span><span class="pun">(</span><span class="pln">angle</span><span class="pun">),</span><span class="pln">
          y</span><span class="pun">:</span><span class="pln"> radius </span><span class="pun">*</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">sin</span><span class="pun">(</span><span class="pln">angle</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">randomPointOnCircle</span><span class="pun">(</span><span class="lit">2</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → {x: 0.3667, y: 1.966}</span></pre>

<p>
	ولا تقلق إن لم تكن قد تعرضت لهذه الدوال من قبل، حيث ستُشرح حين يأتي ذكرها في المقال الرابع عشر من هذه السلسلة، وقد استخدمنا الدالة <code>Math.random</code> في المثال أعلاه، إذ تعيد عددًا عشوائيًا وهميًا بين الصفر والواحد في كل مرة تستدعيها، مع استثناء الواحد نفسه فلا تعيده. انظر المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_83" style=""><span class="pln">console</span><span class="pun">.</span><span class="pln">log</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="com">// → 0.36993729369714856</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</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="com">// → 0.727367032552138</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</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="com">// → 0.40180766698904335</span></pre>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_85" style=""><span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</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="lit">10</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 2</span></pre>

<p>
	سيعطيك ضرب العدد العشوائي في 10 عددًا أكبر من أو يساوي الصفر، وأصغر من العشرة؛ وبما أن <code>Math.floor</code> تقرِّبه، فسينتج هذا التعبيرأعدادًا من 0 إلى 9 باحتمالات متساوية.
</p>

<p>
	تقرِّب الدالة <code>Math.ceil</code> إلى عدد صحيح، كما تقرِّب الدالة <code>Math.round</code> إلى أقرب رقم صحيح، وتأخذ الدالة <code>Math.abs</code> القيمة المطلقة لعدد ما، أي تنفي القيمة السالبة وتترك القيمة الموجبة كما هي.
</p>

<h2>
	التفكيك
</h2>

<p>
	انظر الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_87" style=""><span class="kwd">function</span><span class="pln"> phi</span><span class="pun">(</span><span class="pln">table</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">table</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"> table</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> table</span><span class="pun">[</span><span class="lit">2</span><span class="pun">]</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> table</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">
    </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">sqrt</span><span class="pun">((</span><span class="pln">table</span><span class="pun">[</span><span class="lit">2</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> table</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">
              </span><span class="pun">(</span><span class="pln">table</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> table</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">
              </span><span class="pun">(</span><span class="pln">table</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"> table</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">
              </span><span class="pun">(</span><span class="pln">table</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> table</span><span class="pun">[</span><span class="lit">2</span><span class="pun">]));</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إذا عدنا إلى دالة <code>phi</code> السابقة، فإن أحد الأسباب التي يجعل هذه الدالة صعبةً في قراءتها، هو أنّه لدينا رابطة تشير إلى مصفوفتنا، بينما نريد رابطات لعناصر المصفوفة، أي <code>let n00 = table[0]‎</code>، وهكذا.
</p>

<p>
	لحسن الحظ لدينا طريقة مختصرة في جافاسكربت تفعل ذلك:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_89" style=""><span class="kwd">function</span><span class="pln"> phi</span><span class="pun">([</span><span class="pln">n00</span><span class="pun">,</span><span class="pln"> n01</span><span class="pun">,</span><span class="pln"> n10</span><span class="pun">,</span><span class="pln"> n11</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">n11 </span><span class="pun">*</span><span class="pln"> n00 </span><span class="pun">-</span><span class="pln"> n10 </span><span class="pun">*</span><span class="pln"> n01</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">sqrt</span><span class="pun">((</span><span class="pln">n10 </span><span class="pun">+</span><span class="pln"> n11</span><span class="pun">)</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">(</span><span class="pln">n00 </span><span class="pun">+</span><span class="pln"> n01</span><span class="pun">)</span><span class="pln"> </span><span class="pun">*</span><span class="pln">
              </span><span class="pun">(</span><span class="pln">n01 </span><span class="pun">+</span><span class="pln"> n11</span><span class="pun">)</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="pun">(</span><span class="pln">n00 </span><span class="pun">+</span><span class="pln"> n10</span><span class="pun">));</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	هذا يصلح أيضًا للرابطات التي أنشئت باستخدام <code>let</code>، و<code>var</code>، و<code>const</code>، فإذا كانت القيمة التي تربطها مصفوفةً تستطيع استخدام الأقواس المربعة لتنظر داخلها لتربط محتوياتها.
</p>

<p>
	كذلك بالنسبة للكائنات إذ نستخدم الأقواس العادية بدلًا من المربعة، انظر المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_91" style=""><span class="pln">let </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">name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Faraji"</span><span class="pun">,</span><span class="pln"> age</span><span class="pun">:</span><span class="pln"> </span><span class="lit">23</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">name</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → Faraji</span></pre>

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

<h2>
	صيغة JSON
</h2>

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

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

<p>
	والذي نستطيع فعله هو تحويل سَلسلة البيانات، بمعنى تحويلها إلى وصف بسيط، وثَمَّ استخدام طريقة مشهورة تسمى JSON -تنطق جَيسون- وهي اختصار لصيغة الكائنات في جافاسكربت JavaScipt Object Notation، وتُستخدَم استخدامًا واسعًا على أساس طريقة لتخزين البيانات، وصيغة للتواصل في الإنترنت حتى في اللغات الأخرى غير جافاسكربت.
</p>

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

<p>
	وإذا عدنا -مرةً أخرى- إلى مثال سمير المتحوِّل، وأردنا تمثيل مدخلًا للسجل الذي يحتفظ به في صورة بيانات JSON، فسيبدو هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_93" style=""><span class="pun">{</span><span class="pln">
  </span><span class="str">"squirrel"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"events"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">"work"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"touched tree"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"pizza"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"running"</span><span class="pun">]</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_1400_95" style=""><span class="pln">let string </span><span class="pun">=</span><span class="pln"> JSON</span><span class="pun">.</span><span class="pln">stringify</span><span class="pun">({</span><span class="pln">squirrel</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln">
                             events</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">"weekend"</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">string</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → {"squirrel":false,"events":["weekend"]}</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</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">string</span><span class="pun">).</span><span class="pln">events</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → ["weekend"]</span></pre>

<h2>
	خاتمة
</h2>

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

<p>
	تملك أغلب القيم في جافاسكربت خصائص، باستثناء: <code>null</code>، و<code>undefined</code>، ونستطيع الوصول إلى تلك الخصائص باستخدام <code>value.prop</code>، أو <code>value["prop"]‎</code>.
</p>

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

<p>
	تستطيع تطبيق التكرار على المصفوفات باستخدام نوع خاص من حلقة <code>for</code> التكرارية، أي <code>for(let element of array)‎</code>.
</p>

<p>
	ترجمة -بتصرف- <a href="https://eloquentjavascript.net/04_data.html" rel="external nofollow" target="_blank">للفصل الرابع من كتاب Elequent Javascript</a> لصاحبه Marijn Haverbeke.
</p>

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

<ul>
	<li>
		المقال التالي: <a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%B9%D9%84%D9%8A%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-r1242/" 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>
	<li>
		الدليل الشامل: <a href="https://academy.hsoub.com/programming/general/%D8%B9%D9%84%D9%85-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA/" rel="">علم البيانات</a>
	</li>
</ul>

<p>
	 
</p>
]]></description><guid isPermaLink="false">1234</guid><pubDate>Sun, 30 May 2021 15:07:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62F;&#x648;&#x627;&#x644; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x64A;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1233/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_05/60ade6649cb19_.png.4402d7709e084af8a6a4dc56e7da1d8b.png" /></p>

<p>
	يقول دونالد كنوث Donald Knuth
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

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

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="67613" data-ss1623319220="1" href="https://academy.hsoub.com/uploads/monthly_2021_05/chapter_picture_3.jpg.2cfe4c7cd0acdb2714f7822d62f0129b.jpg" rel=""><img alt="chapter_picture_3.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="67613" data-unique="ognnsfc1i" src="https://academy.hsoub.com/uploads/monthly_2021_05/chapter_picture_3.jpg.2cfe4c7cd0acdb2714f7822d62f0129b.jpg"></a>
</p>

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

<p>
	فمثلًا، تحتوي اللغة الإنجليزية -وهي المكتوب بحروفها أوامر لغات البرمجة-، على <a data-ss1623319220="1" href="https://en.wiktionary.org/wiki/Special:Statistics" rel="external nofollow">نصف مليون كلمة</a> تقريبًا، وقد لا يعلم المتحدث الأصلي لها إلا بـ 20 ألف كلمة منها فقط، وقَلَّ ما تجد لغةً من لغات البرمجة التي يصل عدد أوامرها إلى عشرين ألفًا. وعليه، ستكون المصطلحات المتوفرة فيها دقيقة المعنى للغاية، وبالتالي فهي جامدة وغير مرنة، ولهذا نحتاج إلى إدخال مصطلحات جديدة على هيئة دوال، وذلك بحسب حاجة كل مشروع أو برنامج.
</p>

<h2>
	تعريف الدالة
</h2>

<p>
	الدالة هي رابطة منتظمة، حيث تكون قيمة هذه الرابطة هي الدالة نفسها، إذ تُعرِّف الشيفرة التالية مثلًا، الثابت <code>square</code> لتشير إلى دالة تنتج مربع أي عدد مُعطَى:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4981_6" style="">
<span class="kwd">const</span><span class="pln"> square </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">x</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"> x </span><span class="pun">*</span><span class="pln"> x</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">square</span><span class="pun">(</span><span class="lit">12</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 144</span></pre>

<p>
	وتُنشأ الدالة بتعبير يبدأ بكلمة <code>function</code> المفتاحية، كما يكون للدوال مجموعة معامِلات parameters -معامِل وحيد هو <code>x</code> حسب المثال السابق-، ومتن body لاحتواء التعليمات التي يجب تنفيذها عند استدعاء الدالة، كما يُغلَّف متن الدالة بقوسين معقوصين حتى ولو لم يكن فيه إلا تعليمة واحدة. كذلك يجوز للدالة أن يكون لها عدة معامِلات، أو لا يكون لها أيّ معامِل، ففي المثال التالي، لا تحتوي دالة <code>makenoise</code> على أيّ معاملات، بينما تحتوي <code>power</code> على معاملين اثنين:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4981_8" style="">
<span class="kwd">const</span><span class="pln"> makeNoise </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</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="str">"Pling!"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

makeNoise</span><span class="pun">();</span><span class="pln">
</span><span class="com">// → Pling!</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> power </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">base</span><span class="pun">,</span><span class="pln"> exponent</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let result </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">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let count </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> count </span><span class="pun">&lt;</span><span class="pln"> exponent</span><span class="pun">;</span><span class="pln"> count</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    result </span><span class="pun">*=</span><span class="pln"> base</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"> result</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">power</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">10</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 1024</span></pre>

<p>
	وتنتج بعض الدوال قيمًا، مثل: دالتي <code>power</code>، و<code>square</code>، ولكن هذا ليس قاعدة، إذ لا تعطي بعض الدوال الأخرى قيمةً، مثل دالة <code>makenoise</code>، ونتيجتها الوحيدة هي أثر جانبي side effect. تحدِّد تعليمة <code>return</code> القيمة التي تعيدها الدالة، فحين تمر بُنية تحكُّم control -مثل التعليمات الشرطية- على تعليمة مشابهة لهذه، فستقفز مباشرةً من الدالة الحالية، وتعطي القيمة المعادة إلى الشيفرة التي استدعت الدالة. وإن لم يتبع كلمة <code>return</code> المفتاحية أيّ تعبير، فستعيد الدالة قيمة غير معرفة <code>undefined</code>، كما تعيد الدوال التي ليس فيها تعليمة <code>return</code> قيمة غير معرفة <code>undefined</code>، مثل دالة <code>makenoise</code>. تتصرف معامِلات الدالة على أساس الرابطات المنتظمة regular bindings، غير أنّه يحدِّد مستدعي الدالة قيمتها الأولية، وليس الشيفرة التي بداخل الدالة.
</p>

<h2>
	الرابطات Bindings والنطاقات Scopes
</h2>

<p>
	نطاق الرابطة في البرنامج هو الجزء الذي تكون الرابطة ظاهرةً فيه، حيث كل رابطة لها نطاق. وإذا عرَّفنا الرابطة خارج دالة أو كتلة شيفرات، فيكون نطاق هذه الرابطة البرنامج كاملًا، ويمكنك الإشارة إلى مثل تلك الرابطات أينما تشاء، وتسمى رابطات عامة Global Bindings؛ أما الرابطات المنشأة لمعامِلات الدالة، أو المصرح عنها داخل دالة ما، فيمكن الإشارة إليها داخل تلك الدالة فقط، وعليه تسمّى رابطات محلية Local bindings، وتُنشأ نسخ جديدة من تلك الرابطات في كل مرة تُستدعَى الدالة فيها، وذلك يوفر نوعًا من العزل بين الدوال بما أنّ كل دالة تتصرف في عالمها الخاص -بيئتها المحلية-، وييسّر فهم المراد منها دون الحاجة إلى العلم بكل ما في البيئة العامة. كما تكون الرابطات المصرح عنها باستخدام <code>let</code>، و<code>const</code> رابطات محلية لكتلة الشيفرة التي صُرح عن تلك الرابطات فيها، فإن أنشأت أحد تلك الرابطات داخل حلقة تكرارية، فلن تتمكن الشيفرات الموجودة قبل هذه الحلقة وبعدها، من رؤية تلك الرابطة. ولم يُسمح إنشاء نطاقات جديدة لغير الدوال في إصدارات جافاسكربت قبل 2015، لذا كانت الرابطات التي أُنشِئت باستخدام كلمة <code>var</code> المفتاحية، مرئيةً في كل الدالة التي تظهر فيها هذه الرابطات، أو في النطاق العام إن لم تكن داخل دالة ما. كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4981_10" style="">
<span class="pln">let x </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">if</span><span class="pln"> </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">
  let y </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">var</span><span class="pln"> z </span><span class="pun">=</span><span class="pln"> </span><span class="lit">30</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">x </span><span class="pun">+</span><span class="pln"> y </span><span class="pun">+</span><span class="pln"> z</span><span class="pun">);</span><span class="pln">
  </span><span class="com">// → 60</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">// y is not visible here</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">x </span><span class="pun">+</span><span class="pln"> z</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 40</span></pre>

<p>
	يستطيع كل نطاق البحث في النطاق الذي يحيط به، لذا تكون <code>x</code> ظاهرة داخل كتلة الشيفرة في المثال السابق مع استثناء وجود عدة رابطات بالاسم نفسه، ففي تلك الحالة لا تستطيع الشيفرة إلا رؤية الأقرب لها، كما في المثال التالي، حيث تشير الشيفرة داخل دالة <code>halve</code> إلى <code>n</code> الخاصة بها وليس إلى <code>n</code> العامة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4981_12" style="">
<span class="kwd">const</span><span class="pln"> halve </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</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">return</span><span class="pln"> n </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">

let n </span><span class="pun">=</span><span class="pln"> </span><span class="lit">10</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">halve</span><span class="pun">(</span><span class="lit">100</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 50</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">n</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 10</span></pre>

<h3>
	النطاق المتشعب
</h3>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4981_14" style="">
<span class="kwd">const</span><span class="pln"> hummus </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">factor</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"> ingredient </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">amount</span><span class="pun">,</span><span class="pln"> unit</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">
    let ingredientAmount </span><span class="pun">=</span><span class="pln"> amount </span><span class="pun">*</span><span class="pln"> factor</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">ingredientAmount </span><span class="pun">&gt;</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">
      unit </span><span class="pun">+=</span><span class="pln"> </span><span class="str">"s"</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">$</span><span class="pun">{</span><span class="pln">ingredientAmount</span><span class="pun">}</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">unit</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="pun">};</span><span class="pln">
  ingredient</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="str">"can"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"chickpeas"</span><span class="pun">);</span><span class="pln">
  ingredient</span><span class="pun">(</span><span class="lit">0.25</span><span class="pun">,</span><span class="pln"> </span><span class="str">"cup"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"tahini"</span><span class="pun">);</span><span class="pln">
  ingredient</span><span class="pun">(</span><span class="lit">0.25</span><span class="pun">,</span><span class="pln"> </span><span class="str">"cup"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"lemon juice"</span><span class="pun">);</span><span class="pln">
  ingredient</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="str">"clove"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"garlic"</span><span class="pun">);</span><span class="pln">
  ingredient</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="str">"tablespoon"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"olive oil"</span><span class="pun">);</span><span class="pln">
  ingredient</span><span class="pun">(</span><span class="lit">0.5</span><span class="pun">,</span><span class="pln"> </span><span class="str">"teaspoon"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"cumin"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	تستطيع شيفرة الدالة <code>ingredient</code> أن ترى رابطة <code>factor</code> من الدالة الخارجية، على عكس رابطتيها المحليتين الغير مرئيتين من الدالة الخارجية، وهما: <code>unit</code>، و<code>ingredientAmount</code>. ويُحدِّد مكان كتلة الشيفرة في البرنامج الرابطات التي ستكون مرئيةً داخل تلك الكتلة، حيث يستطيع النطاق المحلي رؤية جميع النطاقات المحلية التي تحتويه، كما تستطيع جميع النطاقات رؤية النطاق العام، ويُسمّى هذا المنظور لمرئية الرابطات، المراقبة المُعجَمية Lexical Scoping.
</p>

<h2>
	الدوال على أساس قيم
</h2>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4981_16" style="">
<span class="pln">let launchMissiles </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  missileSystem</span><span class="pun">.</span><span class="pln">launch</span><span class="pun">(</span><span class="str">"now"</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">safeMode</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  launchMissiles </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="com">/* لا تفعل شيئًا */</span><span class="pun">};</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<h2>
	مفهوم التصريح
</h2>

<p>
	توجد طريقة أقصر لإنشاء رابطة للدالة، حيث تُستخدم كلمة <code>function</code> المفتاحية في بداية التعليمة، أي كما يلي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4981_18" style="">
<span class="kwd">function</span><span class="pln"> square</span><span class="pun">(</span><span class="pln">x</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"> x </span><span class="pun">*</span><span class="pln"> x</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ويسمى هذا بتصريح الدالة function declaration، فتعرِّف التعليمة الرابطة "<code>square</code>" وتوجهها إلى الدالة المعطاة، وذلك أسهل قليلًا في الكتابة، ولا يتطلب فاصلة منقوطة بعد الدالة، لكن قد يكون هذا الأسلوب من التصريح عن الدوال خدّاعًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4981_21" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"يقول لنا المستقبل"</span><span class="pun">,</span><span class="pln"> future</span><span class="pun">());</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> future</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="str">"لن تكون هناك سيارات تطير"</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<h2>
	الدوال السهمية Arrow Functions
</h2>

<p>
	لدينا مفهوم ثالث للتصريح عن الدوال، وقد يبدو مختلفًا عن البقية، حيث يستخدِم سهمًا مكتوبًا في صورة إشارة التساوي ومحرف "أكبر من"، أي على الصورة (<code>‎=&gt;‎</code>)، لهذا انتبه من الخلط بينها وبين محرف "أكبر من أو يساوي"، الذي يُكتب على الصورة (<code>‎&gt;=‎</code>)، ويوضح المثال التالي هذا المفهوم:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4981_23" style="">
<span class="kwd">const</span><span class="pln"> power </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">base</span><span class="pun">,</span><span class="pln"> exponent</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">
  let result </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">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let count </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> count </span><span class="pun">&lt;</span><span class="pln"> exponent</span><span class="pun">;</span><span class="pln"> count</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    result </span><span class="pun">*=</span><span class="pln"> base</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"> result</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	يأتي السهم <strong>بعد</strong> قائمة المعامِلات ويُتبع بمتن الدالة، ويكون على صورة: "هذا الدخل (المعامِلات) يُنتِج هذا الخرج (المتن)"، وحين يكون لدينا اسم معامِل واحد، فيمكنك إهمال الأقواس المحيطة بقائمة المعامِلات، وإن كان المتن تعبيرًا واحدًا بدلًا من كتلة بين قوسين، فستعيد الدالة ذلك التعبير، وعليه ينفذ التعريفين التاليين لـ <code>square</code>، الشيء نفسه:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4981_25" style="">
<span class="kwd">const</span><span class="pln"> square1 </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">x</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">return</span><span class="pln"> x </span><span class="pun">*</span><span class="pln"> x</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"> square2 </span><span class="pun">=</span><span class="pln"> x </span><span class="pun">=&gt;</span><span class="pln"> x </span><span class="pun">*</span><span class="pln"> x</span><span class="pun">;</span></pre>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4981_27" style="">
<span class="kwd">const</span><span class="pln"> horn </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">"Toot"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span></pre>

<p>
	ليس ثمة سبب لوجود الدوال السهمية وتعبيرات <code>function</code> معًا في اللغة، إذ يملكان الوظيفة نفسها بصرف النظر عن التفاصيل الصغيرة، وسنتحدث عن ذلك في المقال السادس، كما لم تُضَف الدوال السهمية إلا في عام 2015، وذلك من أجل السماح بكتابة تعبيرات دوال صغيرة بأسلوب قليل الصياغة، حيث سنستخدمها كثيرًا في المقال الخامس.
</p>

<h2>
	مكدس الاستدعاء The call stack
</h2>

<p>
	قد تبدو طريقة تدفُق التحكم خلال الدوال متداخلة قليلا، انظر المثال التالي للتوضيح، حيث ينفذ بعض الاستدعاءات من الدوال المعطاة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4981_29" style="">
<span class="kwd">function</span><span class="pln"> greet</span><span class="pun">(</span><span class="pln">who</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="str">"Hello "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> who</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
greet</span><span class="pun">(</span><span class="str">"Harry"</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">"Bye"</span><span class="pun">);</span></pre>

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

<p>
	عند استدعاء <code>greet</code>، يقفز التحكم إلى بداية تلك الدالة (السطر الثاني في الشيفرة)، وتستدَعي بدورها <code>console.log</code> التي ينتقل التحكم إليها لتُنفّذ مهمتها، ثم تعود إلى المكان الذي استدعاها، وهو السطر الرابع. ثم يستدعي السطر الذي يليه، أي <code>console.log</code> مرةً أخرى، ويصل البرنامج إلى نهايته بعد إعادة ذلك. وإذا أردنا تمثيل مخطط تدفق التحكم لفظيًا، فسيكون كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4981_31" style="">
<span class="pun">خارج</span><span class="pln"> </span><span class="pun">الدالة</span><span class="pln">
   </span><span class="pun">في</span><span class="pln"> greet
        </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"> greet
</span><span class="pun">خارج</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">log
</span><span class="pun">خارج</span><span class="pln"> </span><span class="pun">الدالة</span></pre>

<p>
	ونظرًا لوجوب قفز الدالة إلى المكان الذي استدعاها، فلابد للحاسوب من تذكُّر السياق الذي حدث منه الاستدعاء، ففي إحدى الحالات أعلاه، كان على <code>console.log</code> العودة إلى دالة <code>greet</code> عند انتهاء تنفيذها، بينما تعود إلى نهاية البرنامج في الحالة الأخرى. يسمى المكان الذي يخزن فيه الحاسوب هذا السياق، بمكدس الاستدعاء call stack، ويُخزَّن السياق الحالي في قمة ذلك المكدس في كل مرة تُستدعى دالة، كما تزيل السياق الأعلى من المكدس، وتستخدمه لمتابعة التنفيذ عندما تعود الدالة.، حيث يحتاج هذا المكدس إلى مساحة في ذاكرة الحاسوب، وبما أنّ تلك المساحة محدودة، فقد يعطيك الحاسوب رسالة فشل، مثل: عدم وجود ذاكرة كافية في المكدس "out of stack space"، أو تكرارات تفوق الحد المسموح به "too much recursion". حيث لدينا المثال الآتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4981_33" style="">
<span class="kwd">function</span><span class="pln"> chicken</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"> egg</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"> egg</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"> chicken</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">chicken</span><span class="pun">()</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">" came first."</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → ??</span></pre>

<p>
	إذ توضح الشيفرة السابقة هذا الأمر بسؤال الحاسوب أسئلة صعبة، تجعله يتنقّل بين الدالتين ذهابًا وإيابًا إلى ما لا نهاية، وفي حالة المكدس اللانهائي هذه، فستنفذ ذاكرة الحاسوب المتاحة، أو ستطيح بالمكدس blow the stack.
</p>

<h2>
	الوسائط الاختيارية Optional arguments
</h2>

<p>
	انظر الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4981_35" style="">
<span class="kwd">function</span><span class="pln"> square</span><span class="pun">(</span><span class="pln">x</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"> x </span><span class="pun">*</span><span class="pln"> x</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">square</span><span class="pun">(</span><span class="lit">4</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln"> </span><span class="str">"hedgehog"</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 16</span></pre>

<p>
	تسمح لغة جافاسكربت بتنفيذ الشيفرة السابقة دون أدنى مشكلة، ورغم أننا عرَّفنا فيها <code>square</code> بمعامِل واحد فقط، ثم استدعيناها بثلاثة معامِلات، فقد تجاهلت الوسائط الزائدة، وحسبت مربع الوسيط الأول فقط. وتستنتج من هذا أن جافاسكربت لديها سعة -إن صح التعبير- في شأن الوسائط التي تمررها إلى الدالة، فإن مررت وسائط أكثر من اللازم، فستُتجاهل الزيادة، أما إن مرّرت وسائط أقل من المطلوب، فتُسنَد المعامِلات المفقودة إلى القيمة <code>undefined</code>. وسيّئة ذلك أنك قد تُمرِّر عدد خاطئ من الوسائط، ولن تعرف بذلك، ولن يخبرك أحد ولا حتى جافاسكربت نفسها، أما حسنته فيمكن استخدام هذا السلوك للسماح لدالة أن تُستدعى مع عدد مختلف من الوسائط. انظر المثال التالي حيث تحاول دالة <code>minus</code> محاكاة معامِل <code>-</code> من خلال وسيط واحد أو وسيطين:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4981_37" style="">
<span class="kwd">function</span><span class="pln"> minus</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> b</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">b </span><span class="pun">===</span><span class="pln"> </span><span class="kwd">undefined</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">a</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> a </span><span class="pun">-</span><span class="pln"> b</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">minus</span><span class="pun">(</span><span class="lit">10</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → -10</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">minus</span><span class="pun">(</span><span class="lit">10</span><span class="pun">,</span><span class="pln"> </span><span class="lit">5</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 5</span></pre>

<p>
	وإذا كتبنا عامل <code>=</code> بعد معامِل ما، ثم أتبعنا ذلك بتعبير، فستحل قيمة التعبير محل الوسيط إذا لم يكن معطى مسبقًا، إذ تجعل دالة الأس <code>power</code> مثلًا، وسيطها الثاني اختياريًا، فإن لم تعطها أنت ذلك الوسيط أو تمرر قيمة <code>undefined</code>، فسيتغير تلقائيًا إلى 2، وستتصرف الدالة مثل دالة التربيع <code>square</code> بالضبط، كما يأتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4981_39" style="">
<span class="kwd">function</span><span class="pln"> power</span><span class="pun">(</span><span class="pln">base</span><span class="pun">,</span><span class="pln"> exponent </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">
  let result </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">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let count </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> count </span><span class="pun">&lt;</span><span class="pln"> exponent</span><span class="pun">;</span><span class="pln"> count</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    result </span><span class="pun">*=</span><span class="pln"> base</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"> result</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">power</span><span class="pun">(</span><span class="lit">4</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 16</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">power</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">6</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 64</span></pre>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4981_42" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"C"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"O"</span><span class="pun">,</span><span class="pln"> </span><span class="lit">2</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → C O 2</span></pre>

<h2>
	التغليف Closure
</h2>

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

<p>
	توضح الشيفرة التالية مثالًا على هذا، فهي تعرِّف دالة <code>wrapValue</code>، والتي تنشئ رابطةً محليةً، ثم تعيد دالةً تصل إلى تلك الرابطة وتعيدها. انظر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4981_44" style="">
<span class="kwd">function</span><span class="pln"> wrapValue</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">
  let local </span><span class="pun">=</span><span class="pln"> n</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"> </span><span class="pun">=&gt;</span><span class="pln"> local</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

let wrap1 </span><span class="pun">=</span><span class="pln"> wrapValue</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln">
let wrap2 </span><span class="pun">=</span><span class="pln"> wrapValue</span><span class="pun">(</span><span class="lit">2</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">wrap1</span><span class="pun">());</span><span class="pln">
</span><span class="com">// → 1</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">wrap2</span><span class="pun">());</span><span class="pln">
</span><span class="com">// → 2</span></pre>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4981_46" style="">
<span class="kwd">function</span><span class="pln"> multiplier</span><span class="pun">(</span><span class="pln">factor</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"> number </span><span class="pun">=&gt;</span><span class="pln"> number </span><span class="pun">*</span><span class="pln"> factor</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

let twice </span><span class="pun">=</span><span class="pln"> multiplier</span><span class="pun">(</span><span class="lit">2</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">twice</span><span class="pun">(</span><span class="lit">5</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 10</span></pre>

<p>
	وبما أنّ المعامِل نفسه يُعَدّ رابطةً محليةً، فلم نعد بحاجة إلى الرابطة الصريحة <code>local</code> من دالة <code>wrapValue</code> السابقة. ويحتاج التفكير في برامج مثل هذا إلى بعض التمرس، والنموذج الذهني المعين على هذا هو التفكير في قيم الدالة على أنها تحتوي شيفرة المتن وبيئتها التي أُنشئَت فيها، وحين تُستدعى الدالة، يرى متن الدالة البيئة التي أنشئت فيها وليس البيئة التي استدعيَت فيها. وفي المثال السابق، تُستدعى الدالة <code>multiplier</code>، وتُنشئ بيئة يكون فيها المعامل <code>factor</code> مقيدًا بـ 2، وتتذكر قيمة الدالة التي تعيدها، وتكون مخزنة في <code>twice</code>، هذه البيئة، لذا حين تُستدعى فستضاعف وسيطها بمقدار 2.
</p>

<h2>
	التعاود Recursion
</h2>

<p>
	تستطيع الدالة استدعاء نفسها طالما أنها لا تكثر من ذلك إلى الحد الذي يطفح المكدس، وتسمى هذه الدالة المستدعية نفسها باسم العودية recursive، حيث يسمح التعاود لبعض الدوال بأن تُكتب في صور مختلفة كما في المثال التالي، إذ نرى استخدامًا مختلفًا لدالة الأس <code>power</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4981_48" style="">
<span class="kwd">function</span><span class="pln"> power</span><span class="pun">(</span><span class="pln">base</span><span class="pun">,</span><span class="pln"> exponent</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">exponent </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="lit">1</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="pln"> base </span><span class="pun">*</span><span class="pln"> power</span><span class="pun">(</span><span class="pln">base</span><span class="pun">,</span><span class="pln"> exponent </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">
</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">power</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 8</span></pre>

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

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

<p>
	يمكنك النظر إلى الأحجية التالية كمثال على هذا، فإذا بدأنا من العدد 1 وأضفنا 5 أو ضربنا في 3 باستمرار ، فسينتج لدينا مجموعة لا نهائية من الأعداد. كيف تكتب دالةً نعطيها عددًا فتحاول إيجاد تسلسل عمليات الجمع والضرب التي تنتج هذا العدد؟
</p>

<p>
	<strong>إرشاد</strong>: يمكن التوصل إلى العدد 13 بضرب 1 في 3، ثم إضافة 5 مرتين، في حين أننا لن نستطيع الوصول إلى العدد 15 مطلقًا.
</p>

<p>
	انظر الحل الآن بأسلوب التعاود:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4981_51" style="">
<span class="kwd">function</span><span class="pln"> findSolution</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">
  </span><span class="kwd">function</span><span class="pln"> find</span><span class="pun">(</span><span class="pln">current</span><span class="pun">,</span><span class="pln"> history</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"> target</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"> history</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">current </span><span class="pun">&gt;</span><span class="pln"> target</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">null</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="pln"> find</span><span class="pun">(</span><span class="pln">current </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">$</span><span class="pun">{</span><span class="pln">history</span><span class="pun">}</span><span class="pln"> </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">
             find</span><span class="pun">(</span><span class="pln">current </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">history</span><span class="pun">}</span><span class="pln"> </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">return</span><span class="pln"> find</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="str">"1"</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">findSolution</span><span class="pun">(</span><span class="lit">24</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → (((1 * 3) + 5) * 3)</span></pre>

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

<p>
	تنفذ دالة <code>find</code> الداخلية التعاود الحقيقي، فتأخذ وسيطين: العدد الحالي، وسلسلة نصية string لتسجل كيف وصلنا إلى هذا العدد، فإن وجدت حلًا، فستعيد سلسلةً نصيةً توضح كيفية الوصول إلى الهدف؛ أما إن لم تجد حلا بالبدء من هذا العدد، فستعيد <code>null</code>.
</p>

<p>
	ولتحقيق ذلك، تنفِّذ الدالة أحد ثلاثة إجراءات:
</p>

<ul>
<li>
		يُعاد العدد الحالي إن كان هو العدد الهدف، حيث يُعَد السجل الحالي طريقة للوصول إليه.
	</li>
	<li>
		تُعاد <code>null</code> إن كان العدد الحالي أكبر من الهدف، فليس من المنطق أن نبحث في هذا الفرع، حيث ستجعل عملية الإضافة أو الضرب، العدد أكبر مما هو عليه.
	</li>
	<li>
		تُعاد النتيجة إن كنا لا نزال أقل من العدد الهدف، فتحاول الدالة كلا الطريقتين اللتين تبدءان من العدد الحالي باستدعاء نفسها مرتين، واحدة للإضافة وأخرى للضرب، وتُعاد نتيجة الاستدعاء الأول إن كان أي شيء غير <code>null</code>، وإلا فيُعاد الاستدعاء الثاني بغض النظر عن إخراجها لسلسلة نصية أم <code>null</code>.
	</li>
</ul>
<p>
	ولفهم كيفية إخراج هذه الدالة للأثر الذي نريده، دعنا ننظر في الاستدعاءات التي تُجرى على دالة <code>find</code>، عند البحث عن حل للعدد 13:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4981_53" style="">
<span class="pln">find</span><span class="pun">(</span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="str">"1"</span><span class="pun">)</span><span class="pln">
  find</span><span class="pun">(</span><span class="lit">6</span><span class="pun">,</span><span class="pln"> </span><span class="str">"(1 + 5)"</span><span class="pun">)</span><span class="pln">
    find</span><span class="pun">(</span><span class="lit">11</span><span class="pun">,</span><span class="pln"> </span><span class="str">"((1 + 5) + 5)"</span><span class="pun">)</span><span class="pln">
      find</span><span class="pun">(</span><span class="lit">16</span><span class="pun">,</span><span class="pln"> </span><span class="str">"(((1 + 5) + 5) + 5)"</span><span class="pun">)</span><span class="pln">
        too big
      find</span><span class="pun">(</span><span class="lit">33</span><span class="pun">,</span><span class="pln"> </span><span class="str">"(((1 + 5) + 5) * 3)"</span><span class="pun">)</span><span class="pln">
        too big
    find</span><span class="pun">(</span><span class="lit">18</span><span class="pun">,</span><span class="pln"> </span><span class="str">"((1 + 5) * 3)"</span><span class="pun">)</span><span class="pln">
      too big
  find</span><span class="pun">(</span><span class="lit">3</span><span class="pun">,</span><span class="pln"> </span><span class="str">"(1 * 3)"</span><span class="pun">)</span><span class="pln">
    find</span><span class="pun">(</span><span class="lit">8</span><span class="pun">,</span><span class="pln"> </span><span class="str">"((1 * 3) + 5)"</span><span class="pun">)</span><span class="pln">
      find</span><span class="pun">(</span><span class="lit">13</span><span class="pun">,</span><span class="pln"> </span><span class="str">"(((1 * 3) + 5) + 5)"</span><span class="pun">)</span><span class="pln">
        found</span><span class="pun">!</span></pre>

<p>
	لاحظ أنّ الإزاحة في المثال السابق توضح عمق مكدس الاستدعاء. حيث تستدعي <code>find</code> في أول استدعاء لها باستدعاء نفسها للبحث عن حل يبدأ بـ (1+5)، وسيتعاود هذا الاستدعاء للبحث في كل حل ينتج عددًا أقل أو يساوي العدد الهدف. وتعيد <code>null</code> إلى الاستدعاء الأول بما أنها لن تجد ما يطابق الهدف، وهنا يتدخل عامل <code>||</code> ليتسبب في الاستدعاء الذي يبحث في (1*3)، ويكون هذا البحث هو أول استدعاء تعاودي داخل استدعاء تعاودي آخَر يصيب العدد الهدف. ويعيد آخر استدعاء فرعي سلسلة نصية، وتُمرر هذه السلسلة من قبل عامليْ <code>||</code> في الاستدعاء البيني intermediate call، مما يعيد لنا الحل في النهاية.
</p>

<h2>
	الدوال النامية Growing Functions
</h2>

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

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4981_55" style="">
<span class="lit">007</span><span class="pln"> </span><span class="typ">Cows</span><span class="pln">
</span><span class="lit">011</span><span class="pln"> </span><span class="typ">Chickens</span></pre>

<p>
	وهذا يتطلب دالة من وسيطين، هما: عدد الأبقار، وعدد الدجاج.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4981_57" style="">
<span class="kwd">function</span><span class="pln"> printFarmInventory</span><span class="pun">(</span><span class="pln">cows</span><span class="pun">,</span><span class="pln"> chickens</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let cowString </span><span class="pun">=</span><span class="pln"> </span><span class="typ">String</span><span class="pun">(</span><span class="pln">cows</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">cowString</span><span class="pun">.</span><span class="pln">length </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">
    cowString </span><span class="pun">=</span><span class="pln"> </span><span class="str">"0"</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> cowString</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">$</span><span class="pun">{</span><span class="pln">cowString</span><span class="pun">}</span><span class="pln"> </span><span class="typ">Cows</span><span class="pun">`);</span><span class="pln">
  let chickenString </span><span class="pun">=</span><span class="pln"> </span><span class="typ">String</span><span class="pun">(</span><span class="pln">chickens</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">chickenString</span><span class="pun">.</span><span class="pln">length </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">
    chickenString </span><span class="pun">=</span><span class="pln"> </span><span class="str">"0"</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> chickenString</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">$</span><span class="pun">{</span><span class="pln">chickenString</span><span class="pun">}</span><span class="pln"> </span><span class="typ">Chickens</span><span class="pun">`);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
printFarmInventory</span><span class="pun">(</span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="lit">11</span><span class="pun">);</span></pre>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4981_59" style="">
<span class="kwd">function</span><span class="pln"> printZeroPaddedWithLabel</span><span class="pun">(</span><span class="pln">number</span><span class="pun">,</span><span class="pln"> label</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  let numberString </span><span class="pun">=</span><span class="pln"> </span><span class="typ">String</span><span class="pun">(</span><span class="pln">number</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">numberString</span><span class="pun">.</span><span class="pln">length </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">
    numberString </span><span class="pun">=</span><span class="pln"> </span><span class="str">"0"</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> numberString</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">$</span><span class="pun">{</span><span class="pln">numberString</span><span class="pun">}</span><span class="pln"> $</span><span class="pun">{</span><span class="pln">label</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"> printFarmInventory</span><span class="pun">(</span><span class="pln">cows</span><span class="pun">,</span><span class="pln"> chickens</span><span class="pun">,</span><span class="pln"> horses</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  printZeroPaddedWithLabel</span><span class="pun">(</span><span class="pln">cows</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Cows"</span><span class="pun">);</span><span class="pln">
  printZeroPaddedWithLabel</span><span class="pun">(</span><span class="pln">chickens</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Chickens"</span><span class="pun">);</span><span class="pln">
  printZeroPaddedWithLabel</span><span class="pun">(</span><span class="pln">horses</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Horses"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

printFarmInventory</span><span class="pun">(</span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="lit">11</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">);</span></pre>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4981_61" style="">
<span class="kwd">function</span><span class="pln"> zeroPad</span><span class="pun">(</span><span class="pln">number</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">
  let string </span><span class="pun">=</span><span class="pln"> </span><span class="typ">String</span><span class="pun">(</span><span class="pln">number</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">string</span><span class="pun">.</span><span class="pln">length </span><span class="pun">&lt;</span><span class="pln"> width</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    string </span><span class="pun">=</span><span class="pln"> </span><span class="str">"0"</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> string</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"> string</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"> printFarmInventory</span><span class="pun">(</span><span class="pln">cows</span><span class="pun">,</span><span class="pln"> chickens</span><span class="pun">,</span><span class="pln"> horses</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">$</span><span class="pun">{</span><span class="pln">zeroPad</span><span class="pun">(</span><span class="pln">cows</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)}</span><span class="pln"> </span><span class="typ">Cows</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">$</span><span class="pun">{</span><span class="pln">zeroPad</span><span class="pun">(</span><span class="pln">chickens</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)}</span><span class="pln"> </span><span class="typ">Chickens</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">$</span><span class="pun">{</span><span class="pln">zeroPad</span><span class="pun">(</span><span class="pln">horses</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">)}</span><span class="pln"> </span><span class="typ">Horses</span><span class="pun">`);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

printFarmInventory</span><span class="pun">(</span><span class="lit">7</span><span class="pun">,</span><span class="pln"> </span><span class="lit">16</span><span class="pun">,</span><span class="pln"> </span><span class="lit">3</span><span class="pun">);</span></pre>

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

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

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

<h2>
	الدوال والآثار الجانبية
</h2>

<p>
	يمكن تقسيم الدوال إلى تلك التي تُستدعَى لآثارها الجانبية side effects، وتلك التي تُستدعَى لقيمتها المعادة -رغم أنه قد يكون للدالة آثار جانبية، وقيم معادة في الوقت نفسه-، فالدالة الأولى هي دالة مساعدة في مثال المزرعة السابق، حيث تُستدعَى <code>printZeroPaddedWithLabel</code> لأثرها الجانبي، فتطبع سطرًا؛ أما النسخة الثانية <code>zeroPad</code>، فتُستدعى لقيمتها المعادة. ولا شك أنّ الحالة الثانية مفيدة أكثر من الأولى، فتكون الدوال التي تنشئ قيمًا، أسهل في إدخالها وتشكيلها في صور جديدة عن تلك التي تنتج آثارًا جانبية مباشرة.
</p>

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

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

<h2>
	خاتمة
</h2>

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

<p>
	يُعَدّ أحد الجوانب الرئيسية في فهم الدوال هو فهم النطاقات، حيث تنشئ كل كتلة نطاقًا جديدًا، وتكون المعامِلات والرابطات المصرَّح عنها في نطاق معين محلية وغير مرئية من الخارج. كما تتصرف الرابطات المصرَّح عنها بـ <code>var</code> تصرفًا مختلفًا، حيث ينتهي بهم الأمر في أقرب نطاق دالي أو في النطاق العام.
</p>

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

<p>
	ترجمة -بتصرف- <a data-ss1623319220="1" href="https://eloquentjavascript.net/03_functions.html" rel="external nofollow">للفصل الثالث من كتاب Elequent Javascript</a> لصاحبه Marijn Haverbeke.
</p>
]]></description><guid isPermaLink="false">1233</guid><pubDate>Sun, 23 May 2021 15:00:00 +0000</pubDate></item><item><title>&#x647;&#x64A;&#x643;&#x644; &#x627;&#x644;&#x628;&#x631;&#x646;&#x627;&#x645;&#x62C; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x64A;&#x628;&#x62A;</title><link>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/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_05/60ade021b6c5b_---(1).png.17cb650a85c9e293ceaad6df1652b50a.png" /></p>

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

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="67607" href="https://academy.hsoub.com/uploads/monthly_2021_05/chapter_picture_2.jpg.057d10ca2bf701babf86555723ec1995.jpg" rel=""><img alt="chapter_picture_2.jpg" class="ipsImage ipsImage_thumbnailed" data-fileid="67607" data-unique="p38zkdiv4" src="https://academy.hsoub.com/uploads/monthly_2021_05/chapter_picture_2.jpg.057d10ca2bf701babf86555723ec1995.jpg"></a>
</p>

<h2>
	التعابير والتعليمات البرمجية
</h2>

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

<p>
	يُسمّى الجزء الذي يُنتِج قيمةً حقيقيةً في الشيفرة البرمجية، تعبيرًا expression، وتُعَدّ كل قيمة مكتوبة حرفيًّا، تعبيرًا، مثل: <code>22</code>، أو <code>psychoanalysis</code>، كما يُعَدّ التعبير الموجود بين قوسين أيضًا تعبيرًا، كما هو الحال بالنسبة للعامل الثنائي المُطبَّق على تعبيرين، أو العامل الأحادي المُطبَّق على تعبير واحد. وهذا يُبيّن أحد أوجه الجمال في واجهة مبنية على لغة برمجية. حيث تحتوي التعبيرات على تعبيرات أخرى، وذلك بالطريقة ذاتها المتّبعة في سرد الجمل الفرعية في لغات البشر المنطوقة، إذ تحتوي الجملة الفرعية على جملة فرعية أخرى داخلها، مما يسمح لنا ببناء تعبيرات تصف الحسابات المعقدة. فإذا استجاب تعبير لجزء من جملة، فستستجيب تعليمة من جافاسكربت لجملة كاملة. وما البرنامج إلا مجموعةٌ من التعليمات الموضوعة في قائمة مرتبة! وأبسط نوع من التعليمات، هو تعبير متبوع بفاصلة منقوطة، حيث ستحصل بذلك على برنامج بسيط، لكن لا فائدة منه في الواقع العملي، كما تستطيع تعديل الشيفرة التالية وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى <a href="https://codepen.io" rel="external nofollow">codepen</a>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_7" style="">
<span class="lit">1</span><span class="pun">;</span><span class="pln">
</span><span class="pun">!</span><span class="kwd">false</span><span class="pun">;</span></pre>

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

<h2>
	الرابطة Binding
</h2>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_9" style="">
<span class="pln">let caught </span><span class="pun">=</span><span class="pln"> </span><span class="lit">5</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">5</span><span class="pun">;</span></pre>

<p>
	وهذا هو النوع الثاني من التعليمات، إذ تشير الكلمة المفتاحية <code>let</code> إلى أنّ هذه الجملة ستُحدِّد رابطة، ويتبع هذه الكلمة اسم الرابطة، كما نستطيع إعطاء قيمة لها على الفور، وذلك بإضافة عامل <code>=</code> مع تعبير بعده. حيث تُنشِئ التعليمة السابقة رابطةً، اسمها: <code>caught</code>، وتستخدمها لتمسك بالعدد الناتج عن ضرب 5 بـ 5، كما يُستخدَم اسم الرابطة بعد تحديدها على أساس تعبير، وقيمته هي قيمة الرابطة التي يحملها حاليًا، كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_11" style="">
<span class="pln">let ten </span><span class="pun">=</span><span class="pln"> </span><span class="lit">10</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">ten </span><span class="pun">*</span><span class="pln"> ten</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 100</span></pre>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_13" style="">
<span class="pln">let mood </span><span class="pun">=</span><span class="pln"> </span><span class="str">"light"</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">mood</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → light</span><span class="pln">
mood </span><span class="pun">=</span><span class="pln"> </span><span class="str">"dark"</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">mood</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → dark</span></pre>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_15" style="">
<span class="pln">let </span><span class="typ">HasanDebt</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">140</span><span class="pun">;</span><span class="pln">
</span><span class="typ">HasanDebt</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">HasanDebt</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">35</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">HasanDebt</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 105</span></pre>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_17" style="">
<span class="pln">let one </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> two </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</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">one </span><span class="pun">+</span><span class="pln"> two</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 3</span></pre>

<p>
	كذلك تُستخدَم الكلمتان <code>var</code>، و<code>const</code>، لإنشاء رابطات بطريقة قريبة من <code>let</code>، أي كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_19" style="">
<span class="kwd">var</span><span class="pln"> name </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Ayda"</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> greeting </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Hello "</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">greeting </span><span class="pun">+</span><span class="pln"> name</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → Hello Ayda</span></pre>

<p>
	وكلمة <code>var</code> الأولى والمختَصرة من كلمة <strong>متغير</strong> variable بالإنجليزية، هي المُصرَّح بها عن الرابطات في جافاسكربت قبل 2015، وسنعود إلى الاختلاف بينها وبين <code>let</code> في المقال التالي، لكن نريدك أن تتذكر الآن أنها تفعل الشيء نفسه تقريبًا، رغم أننا لن نستخدمها في هذه السلسلة إلا نادرًا، وذلك بسبب خصائصها التي قد تربكك أثناء العمل. أما كلمة <code>const</code>، فهي مختصرة من كلمة <strong>ثابت</strong> الإنجليزية Constant، حيث تعرِّف رابطةً ثابتةً تشير إلى القيمة ذاتها دائمًا، وهذا مستَخدم في الرابطات التي تُسنِد اسمًا إلى قيمة، وذلك لتستطيع الإشارة إليها فيما بعد بسهولة.
</p>

<h2>
	أسماء الرابطة
</h2>

<p>
	يمكن تسمية الرابطة بأي كلمة كانت، ويجوز إدخال الأرقام في أسماء الرابطات مثل<code>catch22</code>، لكن شريطة عدم بدء الاسم برقم. يمكن كذلك إدخال علامة الدولار <code>$</code> والشرطة السفلية <code>_</code> فيه، ولا يُسمح بأي علامة ترقيم، أو محرف خاص آخر. كما لا تُستَخدم الكلمات التي لها معنى خاص مثل <code>let</code>، على أساس اسم لرابطة بما أنها كلمات مفتاحية ومحجوزة، إضافةً إلى مجموعة من الكلمات المحجوزة للاستخدام في الإصدارات التالية من جافاسكربت، والقائمة الكاملة لها طويلة نوعًا ما، وهي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_22" style="">
<span class="kwd">break</span><span class="pln"> </span><span class="kwd">case</span><span class="pln"> </span><span class="kwd">catch</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">continue</span><span class="pln"> </span><span class="kwd">debugger</span><span class="pln"> </span><span class="kwd">default</span><span class="pln">
</span><span class="kwd">delete</span><span class="pln"> </span><span class="kwd">do</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="kwd">enum</span><span class="pln"> </span><span class="kwd">export</span><span class="pln"> extends </span><span class="kwd">false</span><span class="pln"> finally </span><span class="kwd">for</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> </span><span class="kwd">if</span><span class="pln"> implements </span><span class="kwd">import</span><span class="pln"> interface in instanceof let
</span><span class="kwd">new</span><span class="pln"> package </span><span class="kwd">private</span><span class="pln"> </span><span class="kwd">protected</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">static</span><span class="pln"> super
</span><span class="kwd">switch</span><span class="pln"> </span><span class="kwd">this</span><span class="pln"> </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="kwd">try</span><span class="pln"> </span><span class="kwd">typeof</span><span class="pln"> </span><span class="kwd">var</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="kwd">while</span><span class="pln"> </span><span class="kwd">with</span><span class="pln"> yield</span></pre>

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

<h2>
	البيئة
</h2>

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

<h2>
	الدوال
</h2>

<p>
	تملك العديد من القيم الموجودة في البيئة الافتراضية، نوع دالة function، والدالة هي قطعة من برنامج ملفوفة بقيمة ما، بحيث قد تُطبَّق تلك القيمة لتشغيل البرنامج الذي تلتف حوله، ففي بيئة المتصفِّح مثلًا، تحمل الرابطة المسمّاة <code>prompt</code>، دالةً تُظهِر صندوقًا حواريًا صغيرًا يطلب إدخالًا من المستخدِم، ويُستخدَم هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_24" style="">
<span class="pln">prompt</span><span class="pun">(</span><span class="str">"Enter passcode"</span><span class="pun">);</span></pre>

<p>
	وناتج تنفيذ الشيفرة السابقة (سواءً في codepen أو في طرفية متصفحك)، هو مربع حوار يشبه ما تعرضه الصورة التالية:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="67612" href="https://academy.hsoub.com/uploads/monthly_2021_05/prompt.png.18d668ad809d26f243978258721e0f57.png" rel=""><img alt="prompt.png" class="ipsImage ipsImage_thumbnailed" data-fileid="67612" data-unique="f91hsezkz" src="https://academy.hsoub.com/uploads/monthly_2021_05/prompt.png.18d668ad809d26f243978258721e0f57.png"></a>
</p>

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

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

<h2>
	دالة console.log
</h2>

<p>
	استخدمنا دالة <code>console.log</code> لإخراج القيم في الأمثلة السابقة، حيث تُوفِّر أغلب أنظمة جافاسكربت بما فيها المتصفحات الحديثة كلها، وNode.js، دالة <code>console.log</code>، حيث تكتب هذه الدالة وُسطاءها لتعرضها على أداة عرض نصية، ويكون الخرج في المتصفحات، في طرفية جافاسكربت، وهو جزء مخفي في المتصفح افتراضيًّا، حيث تستطيع الوصول إليه في أغلب المتصفِّحات إذا ضغطت على F12 في ويندوز، أو command-option-l في ماك، وإن لم تستطع الوصول إليه، فابحث في قوائم المتصفِّح عن عنصر اسمه أدوات المطوِر Developer Tools، أو ما يشابهها. وسيظهر خرج <code>console.log</code> عند تشغيل أمثلة هذه السلسلة في صفحاته، أو عند تشغيل شيفرات خاصة بك، وسيظهر بعد المثال بدلًا من طرفية جافاسكربت في المتصفح.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_26" style="">
<span class="pln">let x </span><span class="pun">=</span><span class="pln"> </span><span class="lit">30</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">"the value of x is"</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → the value of x is 30</span></pre>

<p>
	ورغم عدم احتواء أسماء الرابطات على محرف النقطة، إلا أنّ <code>console.log</code> بها واحدة، وذلك لأنها ليست اسم رابطة بسيطة، بل تعبير يجلب السجل <code>log</code> من القيمة التي تحتفظ بها اسم الرابطة <code>console</code>، وستعرف معنى ذلك بالضبط في المقال الرابع.
</p>

<h2>
	القيم المعادة
</h2>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_28" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">max</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">));</span><span class="pln">
</span><span class="com">// → 4</span></pre>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_30" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">min</span><span class="pun">(</span><span class="lit">2</span><span class="pun">,</span><span class="pln"> </span><span class="lit">4</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">100</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 102</span></pre>

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

<h2>
	تدفق التحكم
</h2>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_32" style="">
<span class="pln">let theNumber </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Number</span><span class="pun">(</span><span class="pln">prompt</span><span class="pun">(</span><span class="str">"اختر عددًا"</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">"عددك هو الجذر التربيعي لـ"</span><span class="pln"> </span><span class="pun">+</span><span class="pln">
            theNumber </span><span class="pun">*</span><span class="pln"> theNumber</span><span class="pun">);</span></pre>

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

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="67611" href="https://academy.hsoub.com/uploads/monthly_2021_05/controlflow-straight.png.6a57f2e4169858856964c81ffa58bba1.png" rel=""><img alt="controlflow-straight.png" class="ipsImage ipsImage_thumbnailed" data-fileid="67611" data-unique="my3rtvi75" src="https://academy.hsoub.com/uploads/monthly_2021_05/controlflow-straight.png.6a57f2e4169858856964c81ffa58bba1.png"></a>
</p>

<h2>
	تنفيذ شرطي
</h2>

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

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="67608" href="https://academy.hsoub.com/uploads/monthly_2021_05/controlflow-if.png.f106d392367059510b98675db928a52d.png" rel=""><img alt="controlflow-if.png" class="ipsImage ipsImage_thumbnailed" data-fileid="67608" data-unique="8zht2honx" src="https://academy.hsoub.com/uploads/monthly_2021_05/controlflow-if.png.f106d392367059510b98675db928a52d.png"></a>
</p>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_34" style="">
<span class="pln">let theNumber </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Number</span><span class="pun">(</span><span class="pln">prompt</span><span class="pun">(</span><span class="str">"اختر عددًا"</span><span class="pun">));</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="typ">Number</span><span class="pun">.</span><span class="pln">isNaN</span><span class="pun">(</span><span class="pln">theNumber</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="str">"عددك هو الجذر التربيعي للعدد "</span><span class="pln"> </span><span class="pun">+</span><span class="pln">
              theNumber </span><span class="pun">*</span><span class="pln"> theNumber</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	وبتعديل الدخل ليكون كلمةً مثل "ببغاء"، فلن تحصل على أيّ خرج. حيث تُنفِّذ كلمة <code>if</code> المفتاحية تعليمةً ما أو تتخطاها وفقًا لقيمة التعبير البولياني، إذ يُكتب التعبير المقرِّر بعد الكلمة المفتاحية بين قوسين ويُتبع بالتعليمة المطلوب تنفيذها. وتُعَّد دالة <code>Number.isNaN</code> دالةً قياسية في جافاسكربت، حيث تُعيد <code>true</code> إذا كان الوسيط المعطى لها ليس عددًا <code>NaN</code>، كما تُعيد دالة <code>Number</code> القيمة <code>NaN</code> إذا أعطيتها نصًا لا يُمثّل عددًا صالحًا، وعليه يُترجم الشرط إلى "إن كانت القيمة <code>theNumber</code> المدخلة عددًا، افعل هذا". حيث تُغلَّف التعليمة التي تلي <code>if</code> بين قوسين معقوصين، هما: <code>{}</code>، كما في المثال السابق، ويمكن استخدام الأقواس لجمع أيّ عدد من التعليمات في تعليمة واحدة، وتُسمّى تلك المجموعة بالكتلة Block، والتي تستطيع إهمالها جميعًا في ذلك المثال بما أنها تحمل تعليمةً واحدةً فقط، لكن يستخدمهما أغلب مبرمجي جافاسكربت في كلّ تعليمة مغلفة مثل هذه، وسنتّبع هذا الأسلوب في السلسلة غالبًا، إلا في حالات شاذة عندما تكون من سطر واحد، أي كما في المثال الآتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_36" style="">
<span class="kwd">if</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"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">2</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">"صحيح"</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → صحيح</span></pre>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_38" style="">
<span class="pln">let theNumber </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Number</span><span class="pun">(</span><span class="pln">prompt</span><span class="pun">(</span><span class="str">"اختر عددًا"</span><span class="pun">));</span><span class="pln">
</span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="typ">Number</span><span class="pun">.</span><span class="pln">isNaN</span><span class="pun">(</span><span class="pln">theNumber</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="str">"عددك هو الجذر التربيعي للعدد "</span><span class="pln"> </span><span class="pun">+</span><span class="pln">
              theNumber </span><span class="pun">*</span><span class="pln"> theNumber</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">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"لمَ لم تعطني عددًا؟"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أما إن كان لديك أكثر من مسارين لتختار منهما، فيمكن استخدام سلسلة أزواج من <code>if/else</code> معًا، أي كما في المثال الآتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_40" style="">
<span class="pln">let num </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Number</span><span class="pun">(</span><span class="pln">prompt</span><span class="pun">(</span><span class="str">"اختر عددًا"</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">num </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">10</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="str">"صغير"</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">num </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">100</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="str">"وسط"</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">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"كبير"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سينظر البرنامج أولًا إن كان <code>num</code> أصغر من 10، فسيختار هذا الفرع، ويظهر لك "صغير" وانتهى الأمر؛ أما إن كان أكبر من 10، فسيأخذ مسار else الذي يحتوي على if أخرى أيضًا. فإن تحقق الشرط الثاني (‎&lt; 100)، فهذا يعني أنّ العدد بين 10 و 100، وسيظهر لك "وسط"؛ أما إن لم يكن كذلك، فسيختار مسار else الثاني والأخير. ويوضَّح مسار هذا البرنامج بالمخطط التالي: 
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="67610" href="https://academy.hsoub.com/uploads/monthly_2021_05/controlflow-nested-if.png.cdc383172f6aea9b57b8df93f30f9394.png" rel=""><img alt="controlflow-nested-if.png" class="ipsImage ipsImage_thumbnailed" data-fileid="67610" data-unique="zoaxadebj" src="https://academy.hsoub.com/uploads/monthly_2021_05/controlflow-nested-if.png.cdc383172f6aea9b57b8df93f30f9394.png"></a>
</p>

<h2>
	حلقات while وdo
</h2>

<p>
	لنقُل أنه لدينا برنامجًا يخرج كل الأرقام الزوجية من 0 حتى 12، حيث يُكتب هذا البرنامج كما يأتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_42" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="lit">0</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="lit">2</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="lit">4</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="lit">6</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="lit">8</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="lit">10</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="lit">12</span><span class="pun">);</span></pre>

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

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="67609" href="https://academy.hsoub.com/uploads/monthly_2021_05/controlflow-loop.png.ace88f1852cf08fd6fdb38fb3d603608.png" rel=""><img alt="controlflow-loop.png" class="ipsImage ipsImage_thumbnailed" data-fileid="67609" data-unique="6o6sf1hvb" src="https://academy.hsoub.com/uploads/monthly_2021_05/controlflow-loop.png.ace88f1852cf08fd6fdb38fb3d603608.png"></a>
</p>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_44" style="">
<span class="pln">let number </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">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">number </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="lit">12</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">number</span><span class="pun">);</span><span class="pln">
  number </span><span class="pun">=</span><span class="pln"> number </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="com">// → 0</span><span class="pln">
</span><span class="com">// → 2</span><span class="pln">
</span><span class="com">//   … etcetera</span></pre>

<p>
	حيث تنشئ الكلمة المفتاحية <code>while</code> حلقةً تكرارية، ويتبع <code>while</code> تعبيرًا داخل أقواس، ثم تعبير (البنية عمومًا تشبه الشرط <code>if</code>)، وتستمر الحلقة في التكرار طالما أنّ خرج التعبير عند تُحويله إلى النوع البولياني، هو <code>true</code>. كما توضح رابطة <code>number</code> الطريقة التي يمكن لرابطة ما تتبُّع سير البرنامج، حيث يحصل <code>number</code> على قيمة أكثر من القيمة السابقة بمقدار 2 عند كل عملية تكرار، كما يُوازَن برقم 12 في بداية الحلقة ليقرر ما إذا كان عمل البرنامج قد انتهى أم لا. كما يمكننا كتابة برنامج يحسب قيمة 2<sup>10</sup> ( مرفوعة للأس العاشر) كمثال عن برنامج يُنفِّذ أمرًا نافعًا حقًا، وسنستخدم رابطتين لكتابة هذا البرنامج، الأولى لتتبّع سير النتيجة، والثانية لحساب عدد مرات ضرب النتيجة في 2. وتختبر حلقة التكرار وصول الرابطة الثانية إلى 10، حيث تحدِّث كلا الرابطتين طالما أن الرابطة الثانية أصغر من 10.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_46" style="">
<span class="pln">let result </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span><span class="pln">
let counter </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">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">counter </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">10</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  result </span><span class="pun">=</span><span class="pln"> result </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
  counter </span><span class="pun">=</span><span class="pln"> counter </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">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">result</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 1024</span></pre>

<p>
	ويمكن أن يبدأ العدّاد (الرابطة الثانية) من 1، ويتحقق من شرط <code>‎&lt;= 10</code>، لكن الأفضل أن تعتاد على بدء العد من الصفر، وذلك لأسباب مذكورة في المقال الرابع. أما حلقة <code>do</code> فهي بنية تحكّم مماثلة لـ <code>while</code>، ولكن تختلف في نقطة واحدة فقط، حيث تُنفِّذ متنها مرةً واحدةً على الأقل، إذ تختبر شرط التوقف بعد أول تنفيذ، ويظهر ذلك الاختبار بعد متن الحلقة. كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_48" style="">
<span class="pln">let yourName</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">do</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  yourName </span><span class="pun">=</span><span class="pln"> prompt</span><span class="pun">(</span><span class="str">"Who are you?"</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">yourName</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">yourName</span><span class="pun">);</span></pre>

<p>
	حيث يجبرك البرنامج السابق على إدخال اسم ما، وسيسألك مرةً بعد مرة إلى أن تدخل نصًّا غير فارغ، كما سيحول عامل النفي <code>!</code> القيمة إلى قيمة بوليانية قبل نفيها، وستُحوَّل جميع النصوص إلى <code>true</code> ما عدا <code>" "</code>، مما يعني استمرار الحلقة بالتكرار إلى أن تُدخل اسمًا غير فارغ.
</p>

<h2>
	الشيفرة المزاحة
</h2>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_50" style="">
<span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">false</span><span class="pln"> </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">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"That makes sense."</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="lit">1</span><span class="pln"> </span><span class="pun">&lt;</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">
    console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"No surprise there."</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<h2>
	حلقات for
</h2>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_52" style="">
<span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let number </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> number </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="lit">12</span><span class="pun">;</span><span class="pln"> number </span><span class="pun">=</span><span class="pln"> number </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">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">number</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
</span><span class="com">// → 0</span><span class="pln">
</span><span class="com">// → 2</span><span class="pln">
</span><span class="com">//   … etcetera</span></pre>

<p>
	يطابق هذا البرنامج، المثال السابق لطباعة الأرقام الزوجية، لكن مع فرق أنّ كل التعليمات المتعلِّقة بحالة الحلقة، مجموعة معًا بعد <code>for</code>. حيث يجب احتواء الأقواس الموجودة بعد <code>for</code> على فاصلتين منقوطتين، ليُهيِِّئ الجزء الذي يسبق الفاصلة المنقوطة الأولى، الحلقة من خلال تعريف رابطة لها، كما يتحقق الجزء الثاني الذي يكون تعبيرًا، من استمرارية الحلقة، ويُحدِّث الجزء الاخير حالة الحلقة بعد كل تكرار. فنجد في معظم الحالات، هذه البنية أقصر وأوضح من بنية <code>while</code>. وتحسب الشيفرة التالية قيمة 2<sup>10</sup>، باستخدام <code>for</code> بدلًا عن <code>while</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_54" style="">
<span class="pln">let result </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">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let counter </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> counter </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">10</span><span class="pun">;</span><span class="pln"> counter </span><span class="pun">=</span><span class="pln"> counter </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">
  result </span><span class="pun">=</span><span class="pln"> result </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">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">result</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 1024</span></pre>

<h2>
	الهروب من حلقة
</h2>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_56" style="">
<span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let current </span><span class="pun">=</span><span class="pln"> </span><span class="lit">20</span><span class="pun">;</span><span class="pln"> </span><span class="pun">;</span><span class="pln"> current </span><span class="pun">=</span><span class="pln"> current </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">
  </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"> </span><span class="lit">7</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">
    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">
    </span><span class="kwd">break</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">// → 21</span></pre>

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

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

<p>
	وبالمثل فإن كلمة <code>continue</code> المفتاحية تشبه <code>break</code> في كونها تؤثر على مجرى الحلقة، فإن وجدت الحلقة كلمة <code>continue</code> في متنها فإن التحكم يقفز من المتن لينتقل إلى التكرار التالي للحلقة.
</p>

<h2>
	تحديث الرابطة بإيجاز
</h2>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_58" style="">
<span class="pln">counter </span><span class="pun">=</span><span class="pln"> counter </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span></pre>

<p>
	توفّر جافاسكربت اختصارًا لهذا، ويُكتب كما يأتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_60" style="">
<span class="pln">counter </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">1</span><span class="pun">;</span></pre>

<p>
	وبالمثل، فهناك اختصارات لعوامل أخرى، مثل: <code>result *= 2</code> الذي يضاعف <code>result</code>، أو <code>counter -= 1</code> الذي يَعُدّ تنازليًا. ويسمح هذا باختصار مثال العدّ السابق أكثر، بحيث يصبح كما يأتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_62" style="">
<span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">let number </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> number </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="lit">12</span><span class="pun">;</span><span class="pln"> number </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">
  console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">number</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أمّا بالنسبة لـ <code>counter += 1</code>، و<code>counter -= 1</code>، فلدينا نسخ أكثر إيجازًا منهما، وهما: <code>counter++‎</code> و<code>counter--‎</code>.
</p>

<h2>
	الإرسال إلى قيمة باستخدام التعليمة switch
</h2>

<p>
	من المتوقع استخدام الشيفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_64" style="">
<span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">x </span><span class="pun">==</span><span class="pln"> </span><span class="str">"value1"</span><span class="pun">)</span><span class="pln"> action1</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">x </span><span class="pun">==</span><span class="pln"> </span><span class="str">"value2"</span><span class="pun">)</span><span class="pln"> action2</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">x </span><span class="pun">==</span><span class="pln"> </span><span class="str">"value3"</span><span class="pun">)</span><span class="pln"> action3</span><span class="pun">();</span><span class="pln">
</span><span class="kwd">else</span><span class="pln"> defaultAction</span><span class="pun">();</span></pre>

<p>
	تُسمى هذه البنية باسم التبديل الشرطي <code>switch</code>، وصُمّمت للتعبير عن هذا الإرسال بطريقة مباشرة -وهي مكتسبة من أسلوب جافا وC-، لكن صياغة جافاسكربت تستخدمها بغرابة، ومن الأفضل استخدامها بدلًا من سلسلة متتالية من تعليمات <code>if</code>، أي كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_66" style="">
<span class="kwd">switch</span><span class="pln"> </span><span class="pun">(</span><span class="pln">prompt</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="kwd">case</span><span class="pln"> </span><span class="str">"ممطر"</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">"لا تنس إحضار شمسية"</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">case</span><span class="pln"> </span><span class="str">"مشمس"</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">"البس ثيابًا خفيفة"</span><span class="pun">);</span><span class="pln">
  </span><span class="kwd">case</span><span class="pln"> </span><span class="str">"غائم"</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">"اخرج لتتمشى"</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">default</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">"هذا جو غير معروف!"</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">break</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<h2>
	الحالة الكبيرة للأحرف
</h2>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_68" style="">
<span class="pln">fuzzylittleturtle
fuzzy_little_turtle
</span><span class="typ">FuzzyLittleTurtle</span><span class="pln">
fuzzyLittleTurtle</span></pre>

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

<h2>
	التعليقات
</h2>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_70" style="">
<span class="pln">let accountBalance </span><span class="pun">=</span><span class="pln"> calculateBalance</span><span class="pun">(</span><span class="pln">account</span><span class="pun">);</span><span class="pln">
</span><span class="com">// خذ من أخيك العفو واغفر ذنوبه ولا تك في كل الأمور تعاتبه</span><span class="pln">
accountBalance</span><span class="pun">.</span><span class="pln">adjust</span><span class="pun">();</span><span class="pln">
</span><span class="com">// فإنك لن تلقى أخاك مهذبًا وأي امرئ ينجو من العيب صاحبه</span><span class="pln">
let report </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Report</span><span class="pun">();</span><span class="pln">
</span><span class="com">// أخوك الذي لا ينقضُ النأيُ عهدَه ولا عند صرف الدهر يزوَرُّ جانبه</span><span class="pln">
addToReport</span><span class="pun">(</span><span class="pln">accountBalance</span><span class="pun">,</span><span class="pln"> report</span><span class="pun">);</span><span class="pln">
</span><span class="com">// وليس الذي يلقاك بالبشر والرضا وإن غبت عنه لسعتك عقاربه</span></pre>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7445_72" style="">
<span class="com">/*
 لقد وجدت هذا العدد يزحف إلى ظهر دفتري قبل مدة، ومن 
 يومها وهو يظهر لي بين الحين والآخر.
 فمرةً في منتج أشتريه، ومرةً في جهات اتصالي.
 يبدو أنه صار ملازمًا لي!
*/</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> myNumber </span><span class="pun">=</span><span class="pln"> </span><span class="lit">11213</span><span class="pun">;</span></pre>

<h2>
	خاتمة
</h2>

<p>
	اطلعنا على كيفية بناء البرنامج من تعليمات قد تحتوي هي نفسها على تعليمات أخرى، وقد تحتوي على تعبيرات ربما تتكون من تعبيرات أخرى. ويعطيك وضع التعليمات بالتتالي، برنامجًا يُنفَّذ من الأعلى إلى الأسفل، كما تستطيع إدخال مغيرات لتغيير تدفق التحكم هذا باستخدام التعليمات الشرطية، مثل: if، وelse، وswitch؛ وتعليمات الحلقات التكرارية، مثل: while، وdo، وfor. كما تستطيع استخدام الرابطة لأجزاء البيانات في الملف تحت اسم معين، وتستفيد من هذا في تتبع حالة سير برنامجك، وكذلك علمت أن البيئة هي مجموعة من الرابطات المعرَّفة، وتضع أنظمة جافاسكربت عددًا من الرابطات القياسية في بيئتك دائمًا. تعرّفنا أيضًا على الدوال التي هي عبارة عن قيم خاصة لتغليف جزء من برنامج ما، وتُستدعى بكتابة اسمها والوسائط التي تحملها، كما يُعَدّ استدعاؤها تعبيرًا، وقد ينتج قيمة.
</p>

<p>
	ترجمة -بتصرف- <a href="https://eloquentjavascript.net/02_program_structure.html" rel="external nofollow">للفصل الثاني من كتاب Elequent Javascript</a> لصاحبه Marijn Haverbeke.
</p>
]]></description><guid isPermaLink="false">1232</guid><pubDate>Sun, 16 May 2021 15:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x642;&#x64A;&#x645; &#x648;&#x627;&#x644;&#x623;&#x646;&#x648;&#x627;&#x639; &#x648;&#x627;&#x644;&#x639;&#x648;&#x627;&#x645;&#x644; &#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%82%D9%8A%D9%85-%D9%88%D8%A7%D9%84%D8%A3%D9%86%D9%88%D8%A7%D8%B9-%D9%88%D8%A7%D9%84%D8%B9%D9%88%D8%A7%D9%85%D9%84-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D9%8A%D8%A8%D8%AA-r1226/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_05/60910d8803e13_--.png.726d67308995cc2c42f1e337f8d9effc.png" /></p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

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

	<p>
		ــــ يوان ما Yuan-Ma، كتاب البرمجة The Book of Programming.
	</p>
</blockquote>

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

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2164_7" style="">
<span class="pun">|</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </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"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </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"> </span><span class="lit">0</span><span class="pln"> </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"> </span><span class="lit">0</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"> </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"> </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="lit">1</span><span class="pln"> </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">4</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> </span><span class="lit">8</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> </span><span class="lit">16</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> </span><span class="lit">32</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> </span><span class="lit">64</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> </span><span class="lit">128</span><span class="pln"> </span><span class="pun">|</span></pre>

<p>
	وبذلك، مُثِّل العدد 13 بالعدد الثنائي 00001101، والذي نحصل عليه بجمع أوزان البِتّات الغير صفرية، أي بجمع: 8، و4، و1.
</p>

<h2>
	القيم
</h2>

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

<h2>
	الأعداد
</h2>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2164_9" style="">
<span class="lit">13</span></pre>

<p>
	وإذا استخدمت القيمة أعلاه في برنامج ما، فسيُنشِئ النمط البِتّي لها في ذاكرة الحاسوب، وتستخدم جافاسكربت، لتخزين قيمة عددية واحدة، عددًا ثابتًا من البِتّات، ومقداره تحديدًا 64 بِتّ، مما يجعل عدد الأنماط التي يمكن إنشاؤها بأربع وستين بِتِّ، محدودًا نوعًا ما، وعدد الأعداد المختلفة التي يمكن تمثيلها، محدودًا أيضًا، إذ يمكنك تمثيل 10<sup>N</sup> عدد باستخدام رقم N من الأرقام العشرية، كما يمكنك تمثيل 2<sup>64</sup> عدد مختلِف باستخدام 64 رقم ثنائي أيضًا، ويُترجم هذا إلى نحو 18 كوينتليون (18 أمامها 18 صفرًا) عدد. ويُعَدّ حجم ذواكر الحاسوب قديمًا، موازنَةً بالأحجام والسعات الحالية، صغيرًا للغاية، حيث استخدم الناس، لتمثيل الأعداد، مجموعات من 8-بِتّ، أو 16-بِتّ، فكان من السهل جدًا تجاوز الحد المسموح به للأعداد التي يمكن تمثيلها فيها، لينتهي الأمر بعدد لا يمكن تمثيله ضمن العدد المعطى من البِتّات. أما الآن، فتملك الحواسيب التي تضعها في جيبك، ذواكرًا كبيرةً، مما يتيح لك استخدام مجموعات من 64-بت، ولا تقلق بشأن تجاوز ذلك الحد إلا إذا تعاملت مع أعداد كبيرة جدًا. ولكن رغم ما سبق، لا يمكن تمثيل كل الأعداد الأقل من 18 كوينتليون بعدد في جافاسكربت، حيث تُخزِّن البِتّات أيضًا أعدادًا سالبة، مما يعني أنه سيُخصَّص أحد البِتّات لتمثيل إشارة العدد، والمشكلة الأكبر هنا أنه يجب أيضًا تمثيل الأعداد الكسرية، وبالتالي سيُخصَّص بِتّ آخر لموضع الفاصلة العشرية، وذلك سيقلل عدد الأعداد التي يمكن تمثيلها بجافاسكربت، إلى 9 كوادريليون عدد (15 صفرًا هذه المرة)، وتُكتب الأعداد الكسرية fractional numbers على الصورة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2164_11" style="">
<span class="lit">9.81</span></pre>

<p>
	وتستطيع استخدام الترميز العلمي لكتابة الأعداد الكبيرة جدًا أو الصغيرة جدًا، وذلك بإضافة e (للإشارة إلى وجود أس)، متبوعةً بأس العدد، حيث يُكتَب العدد 2.998 × 10<sup>8</sup> والمساوي لـ 299,800,000، بالشكل الآتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2164_13" style="">
<span class="lit">2.998e8</span></pre>

<p>
	وتُضمَن الدقة في العمليات الحسابية المستَخدَمة مع الأعداد الصحيحة integers والأصغر من العدد المذكور آنفًا -9 كوادريليون-، على عكس العمليات الحسابية المستخدَمة مع الأعداد الكسرية، فكما أنه لا يمكن تمثيل الثابت باي π بعدد محدود من الأرقام العشرية decimal digits بعد الفاصلة، فإن كثيرًا من الأعداد تفقد دقتها عند تمثيلها بأربع وستين بِتّ فقط. ولكن لا يُمثِّل هذا مشكلةً إلا في حالات محددة. ومن المهم أن تكون على دراية بهذا، وتُعامِل الأعداد الكسرية معاملةً تقريبية، وليس على أنها قيم دقيقة.
</p>

<h3>
	العمليات الحسابية
</h3>

<p>
	العملية الحسابية هي العملية الأساسية المستَخدَمة مع الأعداد، وتأخذ المعاملات الحسابية arithmetic operations عددان، وتُنتِج عددًا جديدًا، مثل: الجمع، والضرب؛ وتُمثَّل هذه المعاملات في جافاسكربت، كما يأتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2164_15" style="">
<span class="lit">100</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">4</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">11</span></pre>

<p>
	وتُسمّى الرموز <code>+</code> و<code>*</code> بالعوامل operators، فالعامل الأول هو عامل الجمع، والعامل الثاني هو عامل الضرب، ولدينا <code>-</code> للطرح، و <code>/</code> للقسمة، وبمجرّد وضع العامل بين قيمتين، تُنفَّذ العملية الحسابية وتَنتُج قيمة جديدة، ولكن هل يعني المثال السابق أن نضيف 4 إلى 100 ونضرب النتيجة في 11، أم أن الأولوية للضرب أولًا؟ لعلّك خمّنت أن الضرب أولًا، وهذا صحيح، لكن يمكنك تغيير هذا الترتيب مثل الرياضيات، وذلك بوضع عملية الجمع بين قوسين، مما يرفع من أولوية تنفيذها، كما يأتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2164_17" style="">
<span class="pun">(</span><span class="lit">100</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">4</span><span class="pun">)</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">11</span></pre>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2164_19" style="">
<span class="lit">1</span><span class="pln"> </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">1</span></pre>

<p>
	حيث تُنفَّذ العمليات من اليسار إلى اليمين، مثل عملية الطرح بين أقواس، أي هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2164_21" style="">
<span class="pun">(</span><span class="lit">1</span><span class="pln"> </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="lit">1</span></pre>

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

<p>
	لدينا عامل حسابي آخر قد لا تميّزه للوهلة الأولى، وهو الرمز <code>%</code>، والذي يُستخدَم لتمثيل عملية الباقي remainder، فيكون <code>X % Y</code> هو باقي قسمة X على Y، فمثلًا: نتيجة <code>314 % 100</code> هي <code>14</code>، أما نتيجة <code>144 % 12</code> فتساوي <code>0</code>. وأولوية عامل الباقي هي الأولوية ذاتها للضرب والقسمة، ويُشار عادةً إلى هذا العامل باسم modulo.
</p>

<h3>
	الأعداد الخاصة
</h3>

<p>
	لدينا ثلاثة قيم خاصة في جافاسكربت، ننظر إليها على أنها أعداد، ولكنها لا تُعَدّ أعدادًا طبيعية. وأول قيمتين هما: <code>infinity</code>، و<code>-infinity</code>، وتُمثِّلان اللانهاية بموجبها وسالبها، وبالمثل، فإن <code>infinity -1</code> لا تزال تشير إلى اللانهاية. ولا تثق كثيرًا بالحسابات المبنيَّة على اللانهاية، لأنها ليست منطقيةً رياضيًا، وستقود إلى القيمة الخاصة التالية، وهي: <code>NaN</code>، والتي تُشير إلى "ليس عددًا" Not A Number، رغم كونه قيمةً من نوع عددي بذاته، وستحصل عليه مثلًا: إذا حاولت قسمة صفر على صفر، أو طرح لانهايتين، أو أيّ عدد من العمليات العددية التي لا تُنتِج قيمةً مفيدة.
</p>

<h2>
	السلاسل النصية
</h2>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2164_23" style="">
<span class="pun">`</span><span class="typ">Down</span><span class="pln"> on the sea</span><span class="pun">`</span><span class="pln">
</span><span class="str">"Lie on the ocean"</span><span class="pln">
</span><span class="str">'Float on the ocean'</span></pre>

<p>
	وتستطيع استخدام العلامة الخلفية <code>`</code>، أو علامات الاقتباس المفردة <code>'</code>، أو المزدوجة <code>''</code>، لتحديد السلاسل النصية، طالما أن العلامة التي وضعتها في بداية السلسلة هي ذاتها الموجودة في نهايتها. وتُوضع تقريبًا جميع أنواع البيانات داخل علامات الاقتباس تلك، وستعاملها جافاسكربت على أنها سلسلة نصية، ولكن ستجد صعوبةً في التعامل مع بعض المحارف، فمثلًا: كيف تضع علامات اقتباس داخل علامات الاقتباس المحدِّدة للسلسلة النصية؟ وكذلك محرف السطر الجديد Newlines، وهو ما تحصل عليه حين تضغط زرّ الإدخال؟ ولكتابة هذه المحارف داخل سلسلة نصية، يُنفَّذ الترميز التالي:
</p>

<ul>
<li>
		إذا وجدت شَرطةً مائلةً خلفيةً <code>\</code> داخل نص مُقتَبس، فهذه تشير إلى أن المحرف الذي يليها، له معنى خاص، ويسمّى هذا تهريب المحرف Escaping the character؛ إذ لن تنتهي السلسلة النصية عند احتوائها على علامات الاقتباس المسبوقة بشرطة مائلة خلفية، بل ستكون جزءًا منها؛ وحين يقع محرف <code>n</code> بعد شرطة مائلة خلفية فإنه يُفسَّر على أنه سطر جديد، وبالمِثل، فإذا جاء محرف <code>t</code> بعد شرطة مائلة خلفية، فإنه يعني محرف الجدولة tab، وtd مثال على ذلك، لدينا السلسلة النصية التالية:
	</li>
</ul>
<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2164_25" style="">
<span class="str">"هذا سطر\nوهذا سطر جديد"</span></pre>

<p>
	حيث سيبدو النص بعد تفسيره، كما يأتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2164_27" style="">
<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>\</code>، وليست مِحرفًا خاصًّا، فإذا جاءت شرطتان مائلتان خلفيتان متتابعتان، فستلغيان بعضهما، بحيث تظهر واحدة منهما فقط في القيمة الناتجة عن السلسلة النصية. فمثلًا، تُكتَب السلسلة النصية التالية: "يُكتَب محرف السطر الجديد هكذا <code>"</code>‎\n<code>"</code>." في جافاسكربت، كما يأتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2164_29" style="">
<span class="str">"يُكتب محرف السطر الجديد هكذا \"\\n\"."</span></pre>

<p>
	وينطبق هنا ما ذكرناه سابقًا في شأن البِتّات وتخزين الأعداد، حيث يجب تخزين السلاسل النصية على هيئة بِتّات داخل الحاسوب. تُخزِّن جافاسكربت السلاسل النصية بناءً على معيار يونيكود Unicode، الذي يُعيِِّن عددًا لكل محرف تقريبًا قد تحتاجه ، بما في ذلك المحارف التي في اللغة العربية، واليونانية، واليابانية، والأرمنية، وغيرها. وتُمثَّل السلسلة النصية بمجموعة من الأعداد، بما أنه لدينا عدد لكل محرف، وهذا ما تفعله جافاسكربت تحديدًا، لكن لدينا مشكلة، فتمثيل جافاسكربت يستخدِم 16بت لكل عنصر من عناصر السلسلة النصية، ما يعني أنه لدينا 2<sup>16</sup> محرفًا مختلفًا، ولكن يُعرِّف اليونيكود أكثر من ذلك، أي بمقدار الضعف تقريبًا هنا، لذا تشغل بعض المحارف مثل الصور الرمزية emoji، موقعين من مواقع المحارف في سلاسل جافاسكربت النصية، وسنعود لهذا مرةً أخرى في الفصل الخامس. ولا يمكن تقسيم السلسلة النصية أو ضربها أو الطرح منها، لكن يمكن استخدام عامل <code>+</code> عليها، حيث لا يضيف بعضها إلى بعض كما تتوقّع من <code>+</code>، وإنما يجمعها إلى بعضها ويسلسلها معًا، أو يلصق إن صحّ التعبير بعضها ببعض، فمثلًا، سينتج السطر التالي، كلمة "concatenate":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2164_32" style="">
<span class="str">"con"</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">"cat"</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">"e"</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">"nate"</span></pre>

<p>
	تملك القيم النصية عددًا من الدوال المصاحبة لها -التوابع methods- التي تُستخدَم لإجراء عمليات أخرى عليها، وسنذكُر هذا بمزيد من التفصيل في الفصل الرابع. حيث تتصرّف السلاسل النصية المحاطة بعلامات اقتباس مفردة أو مزدوجة تصرّفًا متشابهًا تقريبًا، والاختلاف الوحيد بينهما هو نوع الاقتباس الذي تحتاج تهريبه داخلها. أما السلاسل المحاطة بعلامة خلفية (`)، والتي تُسمّى عادةً بالقوالب المجرّدة template literals، فيمكن تنفيذ عمليات إضافية عليها، مثل الأسطر الجديدة التي ذكرناها، أو تضمين قيم أخرى، كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2164_34" style="">
<span class="pun">`</span><span class="pln">half of </span><span class="lit">100</span><span class="pln"> is $</span><span class="pun">{</span><span class="lit">100</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">2</span><span class="pun">}`</span></pre>

<p>
	حين تكتب شيئًا داخل <code>{}$</code> في قالب مجرّد، ستُحسب نتيجته، ثم تُحوَّل هذه النتيجة إلى سلسلة نصية وتُدمَج في ذلك الموضع، وعليه يخرج المثال السابق "half of 100 is 50".
</p>

<h2>
	العوامل الأحادية
</h2>

<p>
	تُكتَب بعض العوامل على هيئة كلمات، فليست كلها رموزًا، وأحد الأمثلة على ذلك هو عامل <code>typeof</code>، والذي يُنتِج قيمةً نصيةً تُمثِّل اسم نوع القيمة الممررة إليه. انظر الشيفرة التالية، تستطيع تعديلها وتشغيلها في طرفية المتصفِّح إن كنت تقرأ من متصفح، أو بنسخها إلى <a href="https://codepen.io" rel="external nofollow">codepen</a>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2164_36" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="kwd">typeof</span><span class="pln"> </span><span class="lit">4.5</span><span class="pun">)</span><span class="pln">
</span><span class="com">// → number</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="kwd">typeof</span><span class="pln"> </span><span class="str">"x"</span><span class="pun">)</span><span class="pln">
</span><span class="com">// → string</span></pre>

<p>
	استخدمنا <code>console.log</code> في المثال التوضيحي السابق، لبيان أننا نريد أن نرى نتيجة تقييم شيء ما، وسنبيُّن ذلك لاحقًا في الفصل التالي. وتُنفَّذ العوامل التي بيّناها في هذا الفصل اعتمادًا على قيمتين، لكن العامل <code>typeof</code> يأخذ قيمةً واحدةً فقط، وتُسمّى العوامل التي تستخدم قيمتين، بالعوامل الثنائية binary operators، أما تلك التي تأخذ عاملًا واحدًا فقط، فتسمى العوامل الأحادية unary operators، مع ملاحظة أن عامل الطرح <code>-</code> يمكن استخدامه كعامل أحادي أو ثنائي، كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2164_38" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(-</span><span class="pln"> </span><span class="pun">(</span><span class="lit">10</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">2</span><span class="pun">))</span><span class="pln">
</span><span class="com">// → -8</span></pre>

<h2>
	القيم البوليانية
</h2>

<p>
	حين يكون لدينا احتمالان، فمن المفيد إيجاد قيمة تفرّق بين الاحتمالين، مثل: "yes" و "no"، أو "on" و "off"، وتِستخدِم جافاسكربت النوع البولياني Boolean لهذا الغرض، ويتكون هذا النوع من قيمتين فقط، هما: القيمة true والقيمة false، وتُكتبان بهاتين الكلمتين فقط.
</p>

<h3>
	الموازنة
</h3>

<p>
	انظر الطريقة التالية لإنتاج قيم بوليانية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2164_40" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="lit">3</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln">
</span><span class="com">// → true</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="lit">3</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln">
</span><span class="com">// → false</span></pre>

<p>
	علامتي <code>&lt;</code>، و<code>&gt;</code> هما اللتان تعرفهما من الرياضيات للإشارة إلى الموازنة "أصغر من"، أو "أكبر من"، وكلاهما عاملان ثنائيّان، ويُنتِجان قيمةً بوليانيةً تُوضِّح هل الشرط مُتحقِق أم لا، ويمكن موازنة السلاسل النصية بالطريقة نفسها، كما في المثال التالي:
</p>

<pre class="ipsCode">
console.log("Aardvark" &lt; "Zoroaster")
// → true
</pre>

<p>
	تُعَدّ الطريقة التي تُرتَّب بها السلاسل النصية أبجدية في الغالب، لكن على خلاف ما قد تراه في القاموس، تكون الحروف الكبيرة أقل من الحروف الصغيرة، فمثلًا، <code>Z</code> أقل من <code>a</code>، والمحارف غير الأبجدية (!، -، …إلخ) مدمجة أيضًا في الترتيب، وتمر جافاسكربت على المحارف من اليسار إلى اليمين موازِنةً محارف يونيكود واحدًا تلو الآخر. والعوامل الأخرى التي تُستخدَم في الموازنة هي: <code>‎&lt;=‎</code> (أقل من أو يساوي)، و <code>=&lt;</code> (أكبر من أو يساوي)، و <code>==</code> (يساوي)، و<code>=!</code> (لا يساوي).
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2164_42" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Itchy"</span><span class="pln"> </span><span class="pun">!=</span><span class="pln"> </span><span class="str">"Scratchy"</span><span class="pun">)</span><span class="pln">
</span><span class="com">// → true</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Apple"</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="str">"Orange"</span><span class="pun">)</span><span class="pln">
</span><span class="com">// → false</span></pre>

<p>
	وتوجد قيمة واحدة في جافاسكربت لا تساوي نفسها، وهي <code>NaN</code> بمعنى "ليس عددًا"، أي كما يأتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2164_44" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="kwd">NaN</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="kwd">NaN</span><span class="pun">)</span><span class="pln">
</span><span class="com">// → false</span></pre>

<p>
	وبما أن <code>NaN</code> تشير إلى نتيجة عملية حسابية غير منطقية، فهي لن تساوي أيّ نتيجة أخرى لحساب غير منطقي.
</p>

<h3>
	العوامل المنطقية
</h3>

<p>
	كذلك لدينا في جافاسكربت بعض العوامل التي قد تُطبَّق على القيم البوليانية نفسها، وتدعم جافاسكربت ثلاثةً منها، وهي: and، وor، وnot، ويمكن استخدامها في منطق القيم البوليانية. ويُمثَّل عامل "and" بالرمز <code>&amp;&amp;</code>، وهو عامل ثنائي نتيجته صحيحة true إن كانت القيمتان المعطتان صحيحتان معًا.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2164_46" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="kwd">true</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">)</span><span class="pln">
</span><span class="com">// → false</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="kwd">true</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">)</span><span class="pln">
</span><span class="com">// → true</span></pre>

<p>
	أما عامل الاختيار "or"، فيُمثَّل بالرمز <code>||</code>، ويُخرِج true إذا تحققت صحة إحدى القيمتين أو كليهما، كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2164_48" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="kwd">false</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">)</span><span class="pln">
</span><span class="com">// → true</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="kwd">false</span><span class="pln"> </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">// → false</span></pre>

<p>
	أما "Not" فتُكتب على صورة تعجب <code>!</code>، وهي عامل أحادي يقلب القيمة المعطاة له، فالصحيح المتحقِّق منه <code>true!</code> يَخرج لنا خطأً غير متحقِّق <code>false</code>، والعكس بالعكس. وعند دمج هذه العوامل البوليانية مع العوامل الحسابية والعوامل الأخرى، فلن نستطيع تَبيُّن متى نضع الأقواس في كل حالة أو متى نحتاج إليها، والحل هنا يكون بالعلم بحال العوامل التي ذكرناها حتى الآن لشق طريقك في البرامج التي تكتبها، والشيفرات التي تقرؤها، إذ أن عامل الاختيار <code>||</code> هو أقل العوامل أولوية، ثم يليه عامل <code>&amp;&amp;</code>، ثم عوامل الموازنة (<code>&lt;</code>، <code>==</code>، …إلخ)، ثم بقية العوامل، واختيرت هذه الأسبقية أو الأولوية، كي يقل استخدام الأقواس إلى أدنى حد ممكن، انظر المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2164_50" style="">
<span class="lit">1</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">2</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> </span><span class="lit">10</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">10</span><span class="pln"> </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">50</span></pre>

<p>
	والعامل الأخير الذي لدينا ليس أحاديًّا ولا ثنائيًّا، بل i; عامل ثلاثي يعمل على ثلاث قيم، ويُكتب على صورة علامة استفهام <code>?</code>، ثم نقطتين رأسيّتين <code>:</code>، أي على الصورة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2164_52" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="kwd">true</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="lit">2</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 1</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="kwd">false</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> </span><span class="lit">1</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="lit">2</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → 2</span></pre>

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

<h2>
	القيم الفارغة
</h2>

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

<h2>
	التحويل التلقائي للنوع
</h2>

<p>
	ذكرنا في المقدمة أن جافاسكربت تقبل أيَّ برنامج تعطيه إياها، حتى البرامج التي تُنفِّذ أمورًا غريبة، وتوضح التعبيرات التالية هذا المفهوم:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2164_54" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="lit">8</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">)</span><span class="pln">
</span><span class="com">// → 0</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"5"</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln">
</span><span class="com">// → 4</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"5"</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln">
</span><span class="com">// → 51</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"five"</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">2</span><span class="pun">)</span><span class="pln">
</span><span class="com">// → NaN</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="kwd">false</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="com">// → true</span></pre>

<p>
	وحين يُطبَّق عامل ما على النوع الخطأ من القيم، فستُحوِّل جافاسكربت تلك القيمة إلى النوع المطلوب باستخدام مجموعة قواعد قد لا تريدها أو تتوقعها، ويسمّى ذلك تصحيح النوع القسري type coercion. إذ تُحوِّل <code>null</code> في التعبير الأول من المثال السابق إلى <code>0</code>، وتُحوَّل <code>"5"</code> إلى <code>5</code> أي من سلسلة نصية إلى عدد، أما في التعبير الثالث الذي يحوي عامل الجمع <code>+</code> بين نص وعدد، فنفّذت جافاسكربت الربط Concatenation قبل الإضافة العددية، وحوّلت <code>1</code> إلى <code>"1"</code> أي من عدد إلى نص. أما عند تحويل قيمة لا تُعبِّر بوضوح على أنها عدد إلى عدد، مثل:<code>"five"</code> أو <code>undefined</code>، فسنحصل على <code>NaN</code>، ولذا فإن حصلت على هذه القيمة في مثل هذا الموقف، فابحث عن تحويلات نوعية من هذا القبيل. كذلك حين نوازن قيمتين من النوع نفسه باستخدام <code>==</code>، فسيسهل توقّع الناتج، إذ يجب أن تحصل على <code>true</code> عند تطابق القيمتين إلا في حالة <code>NaN</code>، أما حين تختلف القيم، فتَستخدِم جافاسكربت مجموعة معقدّة من القواعد لتحديد الإجراء الذي يجب تنفيذه، وتحاول في أغلب الحالات أن تحوّل قيمةً أو أكثر إلى نوع القيمة الأخرى. لكن حين تكون إحدى القيمتين <code>null</code>، أو <code>undefined</code>، فستكون النتيجة تكون صحيحةً فقط إذا كان كل من الجانبين <code>null</code> أو <code>undefined</code>. كما في المثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2164_56" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="kwd">null</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="kwd">undefined</span><span class="pun">);</span><span class="pln">
</span><span class="com">// → true</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="kwd">null</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="com">// → false</span></pre>

<p>
	وهذا السلوك مفيد حين تريد اختبار أيُّ القيم فيها قيمةً حقيقيةً بدلًا من <code>null</code> أو <code>undefined</code>، فتوازنهما بعامل <code>==</code> أو <code>=!</code>. لكن إن أردت اختبار شيئ يشير إلى قيمة بعينها مثل <code>false</code>، فإن التعبيرات مثل <code>‎0 == false</code> و<code>‎" " == false</code> تكون صحيحةً أيضًا، وذلك بسبب التحويل التلقائي للنوع، أما إذا كنت لا تريد حدوث أيّ تحويل نوعي، فاستخدم هذين العاملَيْن: <code>===</code>، و <code>==!</code>. حيث يَنظر أول هذين العاملَين هل القيمة مطابقة للقيمة الثانية المقابلة أم لا، والعامل الثاني ينظر أهي غير مطابقة أم لا، وعليه يكون التعبير <code>‎" " === false</code> خطأ كما توقعت. وإني أنصح باستخدام العامل ذي المحارف الثلاثة تلقائيًّا، وذلك لتجنُّب حدوث أي تحويل نوعي يعطِّل عملك، لكن إن كنت واثقًا من الأنواع التي على جانبي العامل، فليس هناك ثمة مشكلة في استخدام العوامل الأقصر.
</p>

<h3>
	قصر العوامل المنطقية
</h3>

<p>
	يعالج العاملان <code>&amp;&amp;</code> و<code>||</code> القيم التي من أنواع مختلفة، معالجةً غريبة، إذ يحوِّلان القيمة التي على يسارهما إلى نوع بولياني لتحديد ما يجب فعله، لكنهما يعيدان إما القيمة الأصلية للجانب الأيسر أو قيمة الجانب الأيمن، وذلك وفقًا لنوع العامل، ونتيجة التحويل للقيمة اليسرى، سيُعيد عامل <code>||</code> مثلًا قيمة جانبه الأيسر إذا أمكن تحويله إلى <code>true</code>، وإلا فسيعيد قيمة جانبه الأيمن. يُحدِث هذا النتيجةَ المتوقّعة إن كانت القيم بوليانية، ونتيجةً مشابهةً إن كانت القيم من نوع آخر. كما في المثال الآتي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2164_58" style="">
<span class="pln">console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="kwd">null</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> </span><span class="str">"user"</span><span class="pun">)</span><span class="pln">
</span><span class="com">// → user</span><span class="pln">
console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Agnes"</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> </span><span class="str">"user"</span><span class="pun">)</span><span class="pln">
</span><span class="com">// → Agnes</span></pre>

<p>
	نستطيع استخدام هذا السلوك على أنه طريقة للرجوع إلى القيمة الافتراضية، فإن كانت لديك قيمة قد تكون فارغةً، فيمكنك وضع <code>||</code> بعدها مع قيمة بدل، حيث إذا كان من الممكن تحويل القيمة الابتدائية إلى <code>false</code> فستحصل على البدل. وتنص قواعد تحويل النصوص والأعداد، إلى قيم بوليانية، على أن <code>0</code>، و<code>NaN</code>، والنص الفارغ <code>" "</code>، جميعها خطأً <code>false</code>، بينما تُعَدّ القيم الأخرى <code>true</code>، لذا فإن <code>‎0 || -1</code> تخرج <code>1-</code>، و <code>‎" " || "!?"‎</code> تخرج <code>"?!"</code>. ويتصرّف عامل <code>&amp;&amp;</code> تصرّفًا قريبًا من ذلك، ولكن بطريقة أخرى، فإذا كان من الممكن تحويل القيمة اليسرى إلى <code>false</code> فسعيد تلك القيمة، وإلا سيعيد القيمة التي على يمينه. وهذان العاملان لهما خاصيّةً أخرى مُهمة، وهي أن الجزء الذي على يمينهما يُقيَّم عند الحاجة فقط، ففي حالة <code>true || x</code> ستكون النتيجة القيمة true مهما كانت قيمة x، حتى لو كانت جزءًا من برنامج يُنفِّذ شيئًا مستَهجنًا، بحيث لا تُقيَّم x عندها، ويمكن قول الشيء نفسه فيما يخص <code>false &amp;&amp; x</code> والتي ستعيد القيمة false دومًا وتتجاهل x. ويسمّى هذا بالتقييم المقصور Short-circuit Evaluation. إذ يتصرَّف العامل الشرطي تصرّفًا قريبًا من ذلك، فالقيمة المختارة من بين القيم الثلاثة هي التي تُقيَّم فقط.
</p>

<h2>
	خاتمة
</h2>

<p>
	اطلعنا في هذا الفصل على أربعة أنواع من قيم جافاسكربت، وهي: الأرقام، والسلاسل النصية، والقيم البوليانية، والغير معرَّفة، حيث تُنشَأ هذه القيم بكتابة اسمها، كما في: <code>true</code>، و <code>null</code>، أو قيمتها، كما في: <code>13</code>، و<code>"abc"</code>، وتُحوَّل وتُجمَع هذه القيم باستخدام عوامل أحادية، أو ثنائية، أو ثلاثية. كما رأينا عوامل حسابية، مثل: <code>+</code>، و<code>-</code>، و<code>*</code>، و<code>/</code>، و<code>%</code>، وعامل الضم النصي <code>+</code>، وعوامل الموازنة، وهي: <code>==</code>، و <code>=!</code>، و<code>===</code>، و<code>==!</code>، و<code>&gt;</code>، و<code>&lt;</code>، و<code>=&gt;</code>، و<code>=&lt;</code>، والعوامل المنطقية، وهي:<code>&amp;&amp;</code>، و<code>||</code>، إلى جانب تعرُّفنا على عدّة عوامل أحادية، مثل: <code>-</code>، الذي يجعل العدد سالبًا، أو <code>!</code>، المُستخدَم في النفي المنطقي، و<code>typeof</code> لإيجاد نوع القيمة، وكذلك العامل الثلاثي <code>:?</code> الذي يختار إحدى القيمتين وفقًا لقيمة ثالثة. ويعطيك ما سبق ذكره بيانات كافيةً لتستخدم جافاسكربت على أساس حاسبة جيب صغيرة، وفي الفصل التالي، سنبدأ بربط هذه التعبيرات لنكتب برامج بسيطة بها.
</p>

<p>
	ترجمة -بتصرف- <a href="https://eloquentjavascript.net/01_values.html" rel="external nofollow">للفصل الأول من كتاب Elequent Javascript</a> لصاحبه Marijn Haverbeke.
</p>

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

<ul>
<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>
	<li>
		<a href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AF%D9%88%D8%A7%D9%84-%D8%A7%D9%84%D8%B9%D9%84%D9%8A%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-r1242/" rel="">الدوال العليا في جافاسكريبت</a>
	</li>
</ul>
]]></description><guid isPermaLink="false">1226</guid><pubDate>Tue, 11 May 2021 15:05:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x627;&#x62C;&#x62A;&#x632;&#x627;&#x621;&#x627;&#x62A; &#x648;&#x627;&#x644;&#x627;&#x634;&#x62A;&#x631;&#x627;&#x643;&#x627;&#x62A; &#x641;&#x64A; GraphQL &#x648;&#x62A;&#x646;&#x641;&#x64A;&#x630;&#x647;&#x627; &#x641;&#x64A; &#x62A;&#x637;&#x628;&#x64A;&#x642; React</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%A7%D8%AC%D8%AA%D8%B2%D8%A7%D8%A1%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D8%A7%D8%B4%D8%AA%D8%B1%D8%A7%D9%83%D8%A7%D8%AA-%D9%81%D9%8A-graphql-%D9%88%D8%AA%D9%86%D9%81%D9%8A%D8%B0%D9%87%D8%A7-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-react-r1213/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_04/608295666a2a6_--GraphQL--.png.a5793b7314e2430b4c02f0d7db2120a0.png" /></p>

<p>
	سنصل قريبًا إلى نهاية هذا المنهاج، لنختم أفكاره باستعراض بعض التفاصيل الأخرى في GraphQL.
</p>

<h2>
	الاجتزاءات
</h2>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7511_8" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  findPerson</span><span class="pun">(</span><span class="pln">name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Pekka Mikkola"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    name
    phone
    address</span><span class="pun">{</span><span class="pln">
      street 
      city
    </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-javascript prettyprinted" id="ips_uid_7511_10" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  allPersons </span><span class="pun">{</span><span class="pln">
    name
    phone
    address</span><span class="pun">{</span><span class="pln">
      street 
      city
    </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>
	في مثل هذه الحالات يمكن تسهيل الأمور باستخدام الاجتزاءات <a data-ss1621334354="1" href="https://graphql.org/learn/queries/#fragments" rel="external nofollow">fragments</a>. لنعرّف إذا اجتزاءً لاختيار كل حقول الشخص:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7511_12" style="">
<span class="pln">fragment </span><span class="typ">PersonDetails</span><span class="pln"> on </span><span class="typ">Person</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  name
  phone 
  address </span><span class="pun">{</span><span class="pln">
    street 
    city
  </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_7511_14" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  allPersons </span><span class="pun">{</span><span class="pln">
    </span><span class="pun">...</span><span class="typ">PersonDetails</span><span class="pln">  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

query </span><span class="pun">{</span><span class="pln">
  findPerson</span><span class="pun">(</span><span class="pln">name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Pekka Mikkola"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="pun">...</span><span class="typ">PersonDetails</span><span class="pln">  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	<strong>لا تُعرّف</strong> الاجتزاءات ضمن تخطيط GrapQL، بل ضمن شيفرة العميل. إذ ينبغي التصريح عن الاجتزاء عندما يريد العميل تنفيذ الاستعلام.
</p>

<p>
	وكمبدأ، يمكننا التصريح عن الاجتزاء داخل كل استعلام كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7511_16" style="">
<span class="kwd">const</span><span class="pln"> ALL_PERSONS </span><span class="pun">=</span><span class="pln"> gql</span><span class="pun">`</span><span class="pln">
  </span><span class="pun">{</span><span class="pln">
    allPersons  </span><span class="pun">{</span><span class="pln">
      </span><span class="pun">...</span><span class="typ">PersonDetails</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  fragment </span><span class="typ">PersonDetails</span><span class="pln"> on </span><span class="typ">Person</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    name
    phone 
    address </span><span class="pun">{</span><span class="pln">
      street 
      city
    </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-javascript prettyprinted" id="ips_uid_7511_18" style="">
<span class="kwd">const</span><span class="pln"> PERSON_DETAILS </span><span class="pun">=</span><span class="pln"> gql</span><span class="pun">`</span><span class="pln">
  fragment </span><span class="typ">PersonDetails</span><span class="pln"> on </span><span class="typ">Person</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    id
    name
    phone 
    address </span><span class="pun">{</span><span class="pln">
      street 
      city
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">`</span></pre>

<p>
	وإن صُرِّح عن الاجتزاء كما سبق، أمكن وضعه ضمن أي استعلام أو طفرة باستخدام <a data-ss1621334354="1" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals" rel="external nofollow">إشارة الدولار $ والأقواس المعقوصة</a>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7511_20" style="">
<span class="kwd">const</span><span class="pln"> ALL_PERSONS </span><span class="pun">=</span><span class="pln"> gql</span><span class="pun">`</span><span class="pln">
  </span><span class="pun">{</span><span class="pln">
    allPersons  </span><span class="pun">{</span><span class="pln">
      </span><span class="pun">...</span><span class="typ">PersonDetails</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">PERSON_DETAILS</span><span class="pun">}</span><span class="pln">  
</span><span class="pun">`</span></pre>

<h2>
	الاشتراكات
</h2>

<p>
	تقدم GraphQL بالإضافة إلى الاستعلامات والطفرات، آلية ثالثة هي الاشتراكات <a data-ss1621334354="1" href="https://www.apollographql.com/docs/react/data/subscriptions/" rel="external nofollow">subscriptions</a>. حيث تُمكّن الاشتراكات العملاء من تتبع التحديثات التي قد تطرأ على الخادم.
</p>

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

<p>
	ولو أردنا التحدث بلغة تقنية سنقول أن البروتوكول HTTP لا يلائم تمامًا الاتصال من الخادم إلى المتصفح، لذا فقد استخدمت Apollo تحت الغطاء مايسمى بمقابس الويب <a data-ss1621334354="1" href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API" rel="external nofollow">WebSockets</a> للاتصال بين المشتركين والخادم.
</p>

<h3>
	الاشتراكات من جانب الخادم
</h3>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7511_22" style="">
<span class="pln">type </span><span class="typ">Subscription</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  personAdded</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Person</span><span class="pun">!</span><span class="pln">
</span><span class="pun">}</span><span class="pln">    </span></pre>

<p>
	فعندما يُضاف شخص جديد، سترسل جميع تفاصيله إلى جميع المشتركين.
</p>

<p>
	يحتاج الاشتراك <code>personAdded</code> إلى محلل. كما يجب تعديل المحلل <code>addPerson</code> لكي يرسل تنبيهًا إلى المشتركين. ستتغير الشيفرة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7511_24" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">PubSub</span><span class="pln"> </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">'apollo-server'</span><span class="pun">)</span><span class="kwd">const</span><span class="pln"> pubsub </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">PubSub</span><span class="pun">()</span><span class="pln">
  </span><span class="typ">Mutation</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    addPerson</span><span class="pun">:</span><span class="pln"> async </span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">,</span><span class="pln"> context</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"> person </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Person</span><span class="pun">({</span><span class="pln"> </span><span class="pun">...</span><span class="pln">args </span><span class="pun">})</span><span class="pln">
      </span><span class="kwd">const</span><span class="pln"> currentUser </span><span class="pun">=</span><span class="pln"> context</span><span class="pun">.</span><span class="pln">currentUser

      </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">currentUser</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">AuthenticationError</span><span class="pun">(</span><span class="str">"not authenticated"</span><span class="pun">)</span><span class="pln">
      </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">
        await person</span><span class="pun">.</span><span class="pln">save</span><span class="pun">()</span><span class="pln">
        currentUser</span><span class="pun">.</span><span class="pln">friends </span><span class="pun">=</span><span class="pln"> currentUser</span><span class="pun">.</span><span class="pln">friends</span><span class="pun">.</span><span class="pln">concat</span><span class="pun">(</span><span class="pln">person</span><span class="pun">)</span><span class="pln">
        await currentUser</span><span class="pun">.</span><span class="pln">save</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">
        </span><span class="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">UserInputError</span><span class="pun">(</span><span class="pln">error</span><span class="pun">.</span><span class="pln">message</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          invalidArgs</span><span class="pun">:</span><span class="pln"> args</span><span class="pun">,</span><span class="pln">
        </span><span class="pun">})</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">

      pubsub</span><span class="pun">.</span><span class="pln">publish</span><span class="pun">(</span><span class="str">'PERSON_ADDED'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> personAdded</span><span class="pun">:</span><span class="pln"> person </span><span class="pun">})</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> person
    </span><span class="pun">},</span><span class="pln">  
  </span><span class="pun">},</span><span class="pln">
  </span><span class="typ">Subscription</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      personAdded</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          subscribe</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"> pubsub</span><span class="pun">.</span><span class="pln">asyncIterator</span><span class="pun">([</span><span class="str">'PERSON_ADDED'</span><span class="pun">])</span><span class="pln">
      </span><span class="pun">},</span><span class="pln">
  </span><span class="pun">},</span></pre>

<p>
	يحدث الاتصال في الاشتراكات باستخدام مبدأ نشر- اشتراك <a data-ss1621334354="1" href="https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern" rel="external nofollow">publish-subscribe</a> عن طريق كائن يستخدم الواجهة <a data-ss1621334354="1" href="https://www.apollographql.com/docs/graphql-subscriptions/setup/#setup" rel="external nofollow">PubSub</a>. حيث ينشر إضافة شخص جديد تنبيهًا عن العملية تصل إلى جميع المشتركين وذلك بالاستفادة من التابع <code>publish</code> العائد للواجهة PubSub.
</p>

<p>
	يسجل محلل الاشتراك <code>personAdded</code> جميع المشتركين وذلك بإعادته كائن مكرّر <a data-ss1621334354="1" href="https://www.apollographql.com/docs/graphql-subscriptions/subscriptions-to-schema/" rel="external nofollow">iterator object</a> مناسب.
</p>

<p>
	لنجري التعديلات التالية على الشيفرة التي تُشغِّل الخادم:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7511_26" style="">
<span class="com">// ...</span><span class="pln">

server</span><span class="pun">.</span><span class="pln">listen</span><span class="pun">().</span><span class="pln">then</span><span class="pun">(({</span><span class="pln"> url</span><span class="pun">,</span><span class="pln"> subscriptionsUrl </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="typ">Server</span><span class="pln"> ready at $</span><span class="pun">{</span><span class="pln">url</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">Subscriptions</span><span class="pln"> ready at $</span><span class="pun">{</span><span class="pln">subscriptionsUrl</span><span class="pun">}</span><span class="pln">
</span><span class="pun">`)})</span></pre>

<p>
	سنجد أن الخادم يتنصت على الاشتراكات على العنوان ws://localhost:4000/graphq<em>l</em>
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7511_28" style="">
<span class="typ">Server</span><span class="pln"> ready at http</span><span class="pun">:</span><span class="com">//localhost:4000/</span><span class="pln">
</span><span class="typ">Subscriptions</span><span class="pln"> ready at ws</span><span class="pun">:</span><span class="com">//localhost:4000/graphql</span></pre>

<p>
	لا نحتاج لأية تعديلات أخرى على الخادم.
</p>

<p>
	يمكن اختبار الاشتراكات باستخدام أرضية عمل GraphQL كالتالي:
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="64460" data-ss1621334354="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/gpg_subscription_01.png.0e8883618b72970e0c811f55b360f682.png" rel=""><img alt="gpg_subscription_01.png" class="ipsImage ipsImage_thumbnailed" data-fileid="64460" data-unique="jdt7ax85g" src="https://academy.hsoub.com/uploads/monthly_2021_04/gpg_subscription_01.png.0e8883618b72970e0c811f55b360f682.png"></a>
</p>

<p>
	عندما تضغط على زر التشغيل play فوق الاشتراك، ستنتظر أرضية العمل التنبيهات التي قد تصل إلى الاشتراك.
</p>

<p>
	يمكن إيجاد شيفرة الواجهة الخلفية ضمن الفرع part8-6 في المستودع الخاص بالتطبيق على <a data-ss1621334354="1" href="https://github.com/fullstack-hy2020/graphql-phonebook-backend/tree/part8-6" rel="external nofollow">Github</a>.
</p>

<h3>
	الاشتراكات من جانب العميل
</h3>

<p>
	لكي نصبح قادرين على استخدام الاشتراكات في تطبيق React، لا بدّ من إجراء بعض التغييرات وخاصة على أوامر التهيئة. ينبغي تعديل أوامر التهيئة الموجودة في الملف "index.js" كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7511_32" style="">
<span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> 
  </span><span class="typ">ApolloClient</span><span class="pun">,</span><span class="pln"> </span><span class="typ">ApolloProvider</span><span class="pun">,</span><span class="pln"> </span><span class="typ">HttpLink</span><span class="pun">,</span><span class="pln"> </span><span class="typ">InMemoryCache</span><span class="pun">,</span><span class="pln"> 
  split</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@apollo/client'</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> setContext </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'apollo-link-context'</span><span class="pln">

</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> getMainDefinition </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@apollo/client/utilities'</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">WebSocketLink</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@apollo/client/link/ws'</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> authLink </span><span class="pun">=</span><span class="pln"> setContext</span><span class="pun">((</span><span class="pln">_</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> headers </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"> localStorage</span><span class="pun">.</span><span class="pln">getItem</span><span class="pun">(</span><span class="str">'phonenumbers-user-token'</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">
    headers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="pun">...</span><span class="pln">headers</span><span class="pun">,</span><span class="pln">
      authorization</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">bearer $</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">null</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="kwd">const</span><span class="pln"> httpLink </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">HttpLink</span><span class="pun">({</span><span class="pln">
  uri</span><span class="pun">:</span><span class="pln"> </span><span class="str">'http://localhost:4000'</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"> wsLink </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">WebSocketLink</span><span class="pun">({</span><span class="pln">
    uri</span><span class="pun">:</span><span class="pln"> </span><span class="pun">`</span><span class="pln">ws</span><span class="pun">:</span><span class="com">//localhost:4000/graphql`,</span><span class="pln">
    options</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        reconnect</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln">
    </span><span class="pun">}})</span><span class="kwd">const</span><span class="pln"> splitLink </span><span class="pun">=</span><span class="pln"> split</span><span class="pun">(</span><span class="pln">
    </span><span class="pun">({</span><span class="pln"> query </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"> definition </span><span class="pun">=</span><span class="pln"> getMainDefinition</span><span class="pun">(</span><span class="pln">query</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">
            definition</span><span class="pun">.</span><span class="pln">kind </span><span class="pun">===</span><span class="pln"> </span><span class="str">'OperationDefinition'</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln">
            definition</span><span class="pun">.</span><span class="pln">operation </span><span class="pun">===</span><span class="pln"> </span><span class="str">'subscription'</span><span class="pln">
        </span><span class="pun">);</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    wsLink</span><span class="pun">,</span><span class="pln">
    authLink</span><span class="pun">.</span><span class="pln">concat</span><span class="pun">(</span><span class="pln">httpLink</span><span class="pun">),)</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> client </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ApolloClient</span><span class="pun">({</span><span class="pln">
  cache</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">InMemoryCache</span><span class="pun">(),</span><span class="pln">
  link</span><span class="pun">:</span><span class="pln"> splitLink</span><span class="pun">})</span><span class="pln">

</span><span class="typ">ReactDOM</span><span class="pun">.</span><span class="pln">render</span><span class="pun">(</span><span class="pln">
  </span><span class="pun">&lt;</span><span class="typ">ApolloProvider</span><span class="pln"> client</span><span class="pun">={</span><span class="pln">client</span><span class="pun">}&gt;</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="typ">App</span><span class="pln"> </span><span class="pun">/&gt;</span><span class="pln">
  </span><span class="pun">&lt;/</span><span class="typ">ApolloProvider</span><span class="pun">&gt;,</span><span class="pln"> 
  document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">'root'</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_7511_34" style="">
<span class="pln">npm install </span><span class="lit">@apollo</span><span class="pun">/</span><span class="pln">client subscriptions</span><span class="pun">-</span><span class="pln">transport</span><span class="pun">-</span><span class="pln">ws</span></pre>

<p>
	إنّ السبب الكامن خلف إجراء التعديلات على أوامر التهيئة، هو ضرورة امتلاك التطبيق اتصال HTTP بالإضافة إلى مقبس ويب إلى خادم GraphQL.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7511_36" style="">
<span class="kwd">const</span><span class="pln"> wsLink </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">WebSocketLink</span><span class="pun">({</span><span class="pln">
  uri</span><span class="pun">:</span><span class="pln"> </span><span class="pun">`</span><span class="pln">ws</span><span class="pun">:</span><span class="com">//localhost:4000/graphql`,</span><span class="pln">
  options</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> reconnect</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</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"> httpLink </span><span class="pun">=</span><span class="pln"> createHttpLink</span><span class="pun">({</span><span class="pln">
  uri</span><span class="pun">:</span><span class="pln"> </span><span class="str">'http://localhost:4000'</span><span class="pun">,</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	تُنفَّذ الاشتراكات باستخدام دالة الخطاف <a data-ss1621334354="1" href="https://www.apollographql.com/docs/react/api/react/hooks/#usesubscription" rel="external nofollow">useSubscription</a>.
</p>

<p>
	لنعدل الشيفرة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7511_38" style="">
<span class="kwd">export</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> PERSON_ADDED </span><span class="pun">=</span><span class="pln"> gql</span><span class="pun">`</span><span class="pln">
subscription </span><span class="pun">{</span><span class="pln">
personAdded </span><span class="pun">{</span><span class="pln">
</span><span class="pun">...</span><span class="typ">PersonDetails</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">PERSON_DETAILS</span><span class="pun">}`</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  useQuery</span><span class="pun">,</span><span class="pln"> useMutation</span><span class="pun">,</span><span class="pln"> useSubscription</span><span class="pun">,</span><span class="pln"> useApolloClient</span><span class="pun">}</span><span class="pln"> from </span><span class="str">'@apollo/client'</span><span class="pln">

</span><span class="kwd">const</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </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">

  useSubscription</span><span class="pun">(</span><span class="pln">PERSON_ADDED</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    onSubscriptionData</span><span class="pun">:</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> subscriptionData </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">subscriptionData</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="pun">}</span></pre>

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

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="64459" data-ss1621334354="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/console_person_details_print_02.png.c5a60d6d9dcafbfb159e7f45a5cdea08.png" rel=""><img alt="console_person_details_print_02.png" class="ipsImage ipsImage_thumbnailed" data-fileid="64459" data-unique="kmaadnow3" src="https://academy.hsoub.com/uploads/monthly_2021_04/console_person_details_print_02.png.c5a60d6d9dcafbfb159e7f45a5cdea08.png"></a>
</p>

<p>
	سيرسل الشخص تنبيهًا إلى العميل عندما يُضاف شخص جديد، ثم تستدعى الدالة المعرّفة في الصفة <code>onSubscriptionData</code> وتُزوّد بتفاصيل الشخص الجديد على أساس معاملات.
</p>

<p>
	لنوسِّع حلنا بحيث يضاف الشخص الجديد إلى الذاكرة المؤقتة لمكتبة Apollo عندما تُستقبل تفاصيله.
</p>

<p>
	لكن لابدّ من الانتباه إلى عدم إضافة الشخص الجديد إلى الذاكرة المؤقته مرتين عندما يُنشئه المستخدم:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7511_40" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">App</span><span class="pln"> </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">const</span><span class="pln"> updateCacheWith </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">addedPerson</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"> includedIn </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">set</span><span class="pun">,</span><span class="pln"> object</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> 
      </span><span class="kwd">set</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">p </span><span class="pun">=&gt;</span><span class="pln"> p</span><span class="pun">.</span><span class="pln">id</span><span class="pun">).</span><span class="pln">includes</span><span class="pun">(</span><span class="pln">object</span><span class="pun">.</span><span class="pln">id</span><span class="pun">)</span><span class="pln">  

    </span><span class="kwd">const</span><span class="pln"> dataInStore </span><span class="pun">=</span><span class="pln"> client</span><span class="pun">.</span><span class="pln">readQuery</span><span class="pun">({</span><span class="pln"> query</span><span class="pun">:</span><span class="pln"> ALL_PERSONS </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">includedIn</span><span class="pun">(</span><span class="pln">dataInStore</span><span class="pun">.</span><span class="pln">allPersons</span><span class="pun">,</span><span class="pln"> addedPerson</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      client</span><span class="pun">.</span><span class="pln">writeQuery</span><span class="pun">({</span><span class="pln">
        query</span><span class="pun">:</span><span class="pln"> ALL_PERSONS</span><span class="pun">,</span><span class="pln">
        data</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> allPersons </span><span class="pun">:</span><span class="pln"> dataInStore</span><span class="pun">.</span><span class="pln">allPersons</span><span class="pun">.</span><span class="pln">concat</span><span class="pun">(</span><span class="pln">addedPerson</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">

  useSubscription</span><span class="pun">(</span><span class="pln">PERSON_ADDED</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    onSubscriptionData</span><span class="pun">:</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> subscriptionData </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"> addedPerson </span><span class="pun">=</span><span class="pln"> subscriptionData</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">personAdded
      notify</span><span class="pun">(`</span><span class="pln">$</span><span class="pun">{</span><span class="pln">addedPerson</span><span class="pun">.</span><span class="pln">name</span><span class="pun">}</span><span class="pln"> added</span><span class="pun">`)</span><span class="pln">
      updateCacheWith</span><span class="pun">(</span><span class="pln">addedPerson</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="pun">}</span></pre>

<p>
	يمكن استخدام الدالة <code>updateCacheWith</code> ضمن الكائن <code>PersonForm</code> لتحديث الذاكرة المؤقتة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7511_42" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="typ">PersonForm</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="pun">({</span><span class="pln"> setError</span><span class="pun">,</span><span class="pln"> updateCacheWith </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"> </span><span class="pun">[</span><span class="pln"> createPerson </span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> useMutation</span><span class="pun">(</span><span class="pln">CREATE_PERSON</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    onError</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">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      setError</span><span class="pun">(</span><span class="pln">error</span><span class="pun">.</span><span class="pln">graphQLErrors</span><span class="pun">[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">message</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    update</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">store</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">
      updateCacheWith</span><span class="pun">(</span><span class="pln">response</span><span class="pun">.</span><span class="pln">data</span><span class="pun">.</span><span class="pln">addPerson</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="pun">}</span><span class="pln"> </span></pre>

<p>
	يمكن إيجاد شيفرة العميل بشكلها الكامل ضمن الفرع part8-9 في المستودع المخصص للتطبيق على <a data-ss1621334354="1" href="https://github.com/fullstack-hy2020/graphql-phonebook-frontend/tree/part8-9" rel="external nofollow">Github</a>.
</p>

<h2>
	مسألة n+1
</h2>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7511_44" style="">
<span class="pln">type </span><span class="typ">Person</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">String</span><span class="pun">!</span><span class="pln">
  phone</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pln">
  address</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Address</span><span class="pun">!</span><span class="pln">
  friendOf</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">User</span><span class="pun">!]!</span><span class="pln">
  id</span><span class="pun">:</span><span class="pln"> ID</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_7511_46" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  findPerson</span><span class="pun">(</span><span class="pln">name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Leevi Hellas"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    friendOf</span><span class="pun">{</span><span class="pln">
      username
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	وبما أنّ الحقل <code>friendOf</code> ليس حقلًا من حقول الكائن <code>Person</code> ضمن قاعدة البيانات، لابدّ من إنشاء محلل للعملية يحل هذا الموضوع. لننشئ أولًا محللًا يعيد قائمة فارغة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7511_48" style="">
<span class="typ">Person</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  address</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">root</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">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> 
      street</span><span class="pun">:</span><span class="pln"> root</span><span class="pun">.</span><span class="pln">street</span><span class="pun">,</span><span class="pln">
      city</span><span class="pun">:</span><span class="pln"> root</span><span class="pun">.</span><span class="pln">city
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  friendOf</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">root</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">return</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>root</code> الكائن الخاص بالشخص الذي تُنشأ من أجله قائمة أصدقاء، وبالتالي سنبحث ضمن كل الكائنات من النوع <code>User</code> على تلك التي تمتلك فيها <code>root</code> معرّفًا فريدًا id كالتالي <code>root._id</code> في قائمة الأصدقاء الخاصة به:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7511_50" style="">
<span class="pln">  </span><span class="typ">Person</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// ...</span><span class="pln">
    friendOf</span><span class="pun">:</span><span class="pln"> async </span><span class="pun">(</span><span class="pln">root</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"> friends </span><span class="pun">=</span><span class="pln"> await </span><span class="typ">User</span><span class="pun">.</span><span class="pln">find</span><span class="pun">({</span><span class="pln">
        friends</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          $in</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">root</span><span class="pun">.</span><span class="pln">_id</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"> friends
    </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_7511_53" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  allPersons </span><span class="pun">{</span><span class="pln">
    name
    friendOf </span><span class="pun">{</span><span class="pln">
      username
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7511_56" style="">
<span class="typ">Person</span><span class="pun">.</span><span class="pln">find
</span><span class="typ">User</span><span class="pun">.</span><span class="pln">find
</span><span class="typ">User</span><span class="pun">.</span><span class="pln">find
</span><span class="typ">User</span><span class="pun">.</span><span class="pln">find
</span><span class="typ">User</span><span class="pun">.</span><span class="pln">find
</span><span class="typ">User</span><span class="pun">.</span><span class="pln">find</span></pre>

<p>
	فعلى الرغم من أننا نفّذنا استعلامًا واحدًا رئيسيًا، فسيُنفِّذ كل شخص ضمن قاعدة البيانات استعلامًا آخر من خلال المحلل الخاص به، وهذا ما يعرف <a data-ss1621334354="1" href="https://www.google.com/search?q=n%2B1+problem" rel="external nofollow">بمشكلة n+1</a> الشهيرة والتي تظهر بين الفينة والأخرى بشكل مختلف، وقد تتسلل إلى شيفرة المطورين دون الانتباه إليها.
</p>

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

<p>
	إنّ الحل الأسهل لحالتنا هي تخزين الأشخاص الذين تتواجد قائمة أصدقائهم ضمن كل كائن من النوع <code>Person</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7511_58" style="">
<span class="kwd">const</span><span class="pln"> schema </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> mongoose</span><span class="pun">.</span><span class="typ">Schema</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">
    type</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln">
    required</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
    unique</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
    minlength</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  phone</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    type</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln">
    minlength</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  street</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    type</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln">
    required</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
    minlength</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">  
  city</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    type</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">,</span><span class="pln">
    required</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
    minlength</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pln">
  </span><span class="pun">},</span><span class="pln">
  friendOf</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
      </span><span class="pun">{</span><span class="pln">
          type</span><span class="pun">:</span><span class="pln"> mongoose</span><span class="pun">.</span><span class="typ">Schema</span><span class="pun">.</span><span class="typ">Types</span><span class="pun">.</span><span class="typ">ObjectId</span><span class="pun">,</span><span class="pln">
          ref</span><span class="pun">:</span><span class="pln"> </span><span class="str">'User'</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>friendOf</code> للأشخاص عندما نحضر الكائنات <code>Person</code>:
</p>

<pre class="ipsCode">
Query: {
  allPersons: (root, args) =&gt; {    
    console.log('Person.find')
    if (!args.phone) {
      return Person.find({}).populate('friendOf')
    }
 return Person.find({ phone: { $exists: args.phone === 'YES' } })
      .populate('friendOf')
  },
  // ...
}
</pre>

<p>
	لن نحتاج بعد التغييرات التي أجريناها إلى محلل مستقل للحقل <code>friendOf</code>.
</p>

<p>
	لن يسبب الاستعلام <code>allPersons</code> مشكلة n+1 إن أحضرنا اسم الشخص ورقم هاتفه فقط:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7511_60" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  allPersons </span><span class="pun">{</span><span class="pln">
    name
    phone
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لو عدّلنا الاستعلام لتنفيذ استعلام مشترك لأنه يسبب أحيانا مشكلة n+1، فسيتطلب تنفيذه جهدًا أكبر، وخاصةً عندما لا نحتاج المعلومات عن الأشخاص المعنيين. يمكن استمثال الاستعلام باستخدام <a data-ss1621334354="1" href="https://www.apollographql.com/docs/apollo-server/data/data/#resolver-type-signature" rel="external nofollow">المعامل الرابع</a> لدوال المحللات. إذ يستخدم هذا المعامل للتحقق من الاستعلام بحد ذاته، وبالتالي يمكن تنفيذ الاستعلام المشترك في بعض الحالات فقط عندما نتنبأ بحدوث مشكلة n+1. لكن لا تستعجل بالقفز إلى هذه المرحلة قبل أن تتأكد بأنك تحتاج ذلك فعلًا.
</p>

<p>
	<a data-ss1621334354="1" href="https://en.wikiquote.org/wiki/Donald_Knuth" rel="external nofollow">وكما يقول دونالد كنوث</a>:
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		اقتباس
	</div>

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

<p>
	تقدم المكتبة <a data-ss1621334354="1" href="https://github.com/facebook/dataloader" rel="external nofollow">DataLoader</a> التي طورتها Facebook حلًا جيدًا لمشكلة n+1 بالإضافة إلى مسائل أخرى. يمكن إيجاد معلومات أكثر عن استخدامها مع Apollo من خلال الانترنت وننصح بالمقالة <a data-ss1621334354="1" href="https://www.robinwieruch.de/graphql-apollo-server-tutorial/#graphql-server-data-loader-caching-batching" rel="external nofollow">graphql server data loader caching batching</a> والمقالة <a data-ss1621334354="1" href="http://www.petecorey.com/blog/2017/08/14/batching-graphql-queries-with-dataloader" rel="external nofollow">batching graphq queries with dataloader</a>.
</p>

<h2>
	خاتمة
</h2>

<p>
	لم تخطط هيكلية التطبيق الذي طورناه في هذا القسم بالشكل الأمثل. فيجب على الأقل نقل التخطيط والاستعلامات والطفرات خارج شيفرة التطبيق. يمكنك إيجاد هيكيليات أفضل لتطبيقات GraphQL من خلال الانترنت <a data-ss1621334354="1" href="https://blog.apollographql.com/modularizing-your-graphql-schema-code-d7f71d5ed5f2" rel="external nofollow">لشيفرة الخادم</a> أو <a data-ss1621334354="1" href="https://medium.com/@peterpme/thoughts-on-structuring-your-apollo-queries-mutations-939ba4746cd8" rel="external nofollow">لشيفرة العميل</a>.
</p>

<p>
	لقد أصبحت GraphQL تقنية قديمة نوعًا ما، إذ بدأت Facebbok باستخدامها منذ عام 2012، وقد خضعت بالفعل لاختبارات صعبة. تزايد الاهتمام بهذه المكتبة شيئًا فشيئًا منذ أن نشرتها FaceBook عام 2015، وقد تهدد سيطرة REST في المستقبل القريب. إنه تلاشي <a data-ss1621334354="1" href="https://www.stridenyc.com/podcasts/52-is-2018-the-year-graphql-kills-rest" rel="external nofollow">متوقع</a>، لكنه لن يحصل قريبًا. وبالتالي <a data-ss1621334354="1" href="https://blog.graphqleditor.com/javascript-predictions-for-2019-by-npm" rel="external nofollow">تعلم</a> GrapQL أمر يستحق المحاولة بكل تأكيد.
</p>

<h2>
	تمارين
</h2>

<h3>
	1 الاشتراكات من جهة الخادم
</h3>

<p>
	أضف الاشتراك <code>bookAdded</code> إلى الواجهة الخلفية بحيث يعيد تفاصيل كل الكتب الجديدة للمشترك.
</p>

<h3>
	2 الاشتراكات من جهة العميل: القسم الأول
</h3>

<p>
	ابدأ باستخدام الاشتراكات من جهة العميل. أضف الاشتراك <code>bookAdded</code>، وأبلغ المستخدم عندما تُضاف كتب جديدة. يمكنك أن تنبه المستخدم بطرق عدة، مثل استعمال الدالة <a data-ss1621334354="1" href="https://developer.mozilla.org/en-US/docs/Web/API/Window/alert" rel="external nofollow">window.alert</a> على سبيل المثال.
</p>

<h3>
	3 الاشتراكات من ناحية العميل: القسم الثاني
</h3>

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

<h3>
	مشكلة n+1
</h3>

<p>
	جد حلًا لمشكلة n+1 في الاستعلام التالي بأي طريقة تريد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7511_63" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  allAuthors </span><span class="pun">{</span><span class="pln">
    name 
    bookCount
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	وهكذا نكون وصلنا إلى آخر تمرين في هذا القسم وحان الوقت لرفع إجاباتك إلى GitHub. لا تنس تحديد التمارين التي أنجزت حلها ضمن <a data-ss1621334354="1" href="https://studies.cs.helsinki.fi/stats/courses/fullstackopen" rel="external nofollow">منظومة تسليم التمارين</a>.
</p>

<p>
	ترجمة -وبتصرف- للفصل <a data-ss1621334354="1" href="https://fullstackopen.com/en/part8/fragments_and_subscriptions" rel="external nofollow">Fragments and Subscriptions</a> من سلسلة <a data-ss1621334354="1" href="https://fullstackopen.com/en/" rel="external nofollow">Deep Dive Into Modern Web Development</a>
</p>
]]></description><guid isPermaLink="false">1213</guid><pubDate>Sun, 09 May 2021 09:01:00 +0000</pubDate></item><item><title>[&#x641;&#x64A;&#x62F;&#x64A;&#x648;] &#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x645;&#x62F;&#x64A;&#x631; &#x627;&#x644;&#x62D;&#x632;&#x645; npm</title><link>https://academy.hsoub.com/programming/javascript/%D9%81%D9%8A%D8%AF%D9%8A%D9%88-%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D9%85%D8%AF%D9%8A%D8%B1-%D8%A7%D9%84%D8%AD%D8%B2%D9%85-npm-r1225/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_05/608d5abb9afd1_----npm.png.11b181f1fcbd08706cbc6c668f2df832.png" /></p>
<p style="text-align: center;">
	<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="450" src="https://academy.hsoub.com/applications/core/interface/index.html" title="YouTube video player" width="560" data-embed-src="https://www.youtube.com/embed/NseEi4a8_rY"></iframe>
</p>

<p>
	نشرح في هذا الفيديو عن نظام إدارة الحزم وفوائد استخدامه. ولماذا نحن بحاجة إلى مثل هذه الأنظمة. نشرح في هذا الدرس كيفية استخدام سطر الأوامر لتنزيل حزم باستخدام مدير الحزم npm. ستتعرف أيضًا على الملف package.json وما هي الفائدة منه. وكذلك الأمر بالنسبة لمجلد node_modules وكيف يمكنك تضمين مكتبة من داخل هذا المجلد في مشروعك.
</p>
]]></description><guid isPermaLink="false">1225</guid><pubDate>Sat, 01 May 2021 13:42:00 +0000</pubDate></item><item><title>&#x645;&#x62F;&#x62E;&#x644; &#x625;&#x644;&#x649; &#x627;&#x644;&#x645;&#x643;&#x62A;&#x628;&#x629; GraphQL &#x648;&#x627;&#x633;&#x62A;&#x639;&#x645;&#x627;&#x644;&#x627;&#x62A;&#x647;&#x627; &#x641;&#x64A; &#x628;&#x646;&#x627;&#x621; &#x62A;&#x637;&#x628;&#x64A;&#x642;&#x627;&#x62A; &#x627;&#x644;&#x648;&#x64A;&#x628; &#x627;&#x644;&#x62D;&#x62F;&#x64A;&#x62B;&#x629;</title><link>https://academy.hsoub.com/programming/javascript/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%A7%D9%84%D9%85%D9%83%D8%AA%D8%A8%D8%A9-graphql-%D9%88%D8%A7%D8%B3%D8%AA%D8%B9%D9%85%D8%A7%D9%84%D8%A7%D8%AA%D9%87%D8%A7-%D9%81%D9%8A-%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-%D8%A7%D9%84%D8%AD%D8%AF%D9%8A%D8%AB%D8%A9-r1208/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_04/60815b052ceae_-GraphQL-Server.png.524a8864d7728df3274c0ee23fc127ef.png" /></p>

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

<p>
	وازدادت خلال السنوات الأخيرة شهرة المكتبة <a data-ss1621333846="1" href="http://graphql.org/" rel="external nofollow">GraphQL</a> التي طوّرتها فيسبوك Facebook للاتصال بين الخادم وتطبيق الويب.
</p>

<p>
	تختلف فلسفة GraphQL عن REST بشكل واضح. حيث يعتمد المعيار REST على الموارد، ولكل مورد (كالمستخدم مثلًا) عنوانه الخاص الذي يعرّفه users/10/ مثلًا. وتجري كل العمليات على الموارد باستخدام طلب HTTP إلى عنوان موقعه. ويعتمد الفعل الذي سيُنفّذ على نوع الطلب HTTP.
</p>

<p>
	تعمل آلية REST بشكل جيد في معظم الحالات. لكنها قد تتصرف بغرابة في بعض الأحيان.
</p>

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

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

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

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

<p>
	يمكن أن نحضر البيانات التي ناقشناها في السيناريو السابق كالتالي (تقريبيًا):
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_9" style="">
<span class="pln">query </span><span class="typ">FetchBlogsQuery</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  user</span><span class="pun">(</span><span class="pln">username</span><span class="pun">:</span><span class="pln"> </span><span class="str">"mluukkai"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    followedUsers </span><span class="pun">{</span><span class="pln">
      blogs </span><span class="pun">{</span><span class="pln">
        comments </span><span class="pun">{</span><span class="pln">
          user </span><span class="pun">{</span><span class="pln">
            blogs </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="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>
	سيستجيب الخادم بإعادته كائن JSON له تقريبًا البنية التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_11" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"data"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"followedUsers"</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="str">"blogs"</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="str">"comments"</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="str">"user"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
                  </span><span class="str">"blogs"</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="str">"title"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Goto considered harmful"</span><span class="pln">
                    </span><span class="pun">},</span><span class="pln">
                    </span><span class="pun">{</span><span class="pln">
                      </span><span class="str">"title"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"End to End Testing with Cypress is most enjoyable"</span><span class="pln">
                    </span><span class="pun">},</span><span class="pln">
                    </span><span class="pun">{</span><span class="pln">
                      </span><span class="str">"title"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Navigating your transition to GraphQL"</span><span class="pln">
                    </span><span class="pun">},</span><span class="pln">
                    </span><span class="pun">{</span><span class="pln">
                      </span><span class="str">"title"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"From REST to GraphQL"</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">
        </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>

<h2>
	التخطيطات والاستعلامات
</h2>

<p>
	سنتعرف على أساسيات GraphQL، بإنجاز نسخة من تطبيق دليل الهاتف الذي عملنا عليه في القسمين 2و3، بالاعتماد على GraphQL.
</p>

<p>
	في صميم كل تطبيقات GraphQL <a data-ss1621333846="1" href="https://graphql.org/learn/schema/" rel="external nofollow">تخطيطًا</a> يصف البيانات المرسلة بين العميل والخادم. سيكون التخطيط الأولي لتطبيق دليل الهاتف كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_13" style="">
<span class="pln">type </span><span class="typ">Person</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">String</span><span class="pun">!</span><span class="pln">
  phone</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pln">
  street</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">
  city</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">
  id</span><span class="pun">:</span><span class="pln"> ID</span><span class="pun">!</span><span class="pln"> 
</span><span class="pun">}</span><span class="pln">

type </span><span class="typ">Query</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  personCount</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Int</span><span class="pun">!</span><span class="pln">
  allPersons</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">Person</span><span class="pun">!]!</span><span class="pln">
  findPerson</span><span class="pun">(</span><span class="pln">name</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!):</span><span class="pln"> </span><span class="typ">Person</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يصف التخطيط <a data-ss1621333846="1" href="https://graphql.org/learn/schema/#type-system" rel="external nofollow">نوعين</a>. يحد أولهما Person أن للشخص خمسة حقول، أربعة منها "نصية" من النوع "String"، وهو أحد <a data-ss1621333846="1" href="https://graphql.org/learn/schema/#scalar-types" rel="external nofollow">الأنماط السلّمية</a> scalar types في GraphQL. ويجب أن تُعطى كل الحقول النصية قيمًا ما عدا الحقل phone. وستجد أنّ هذه الحقول مُعلّمة بإشارة تعجّب في التخطيط. أما نوع الحقل id فهو "ID". هذا النوع نصي أيضًا، لكن المكتبة GraphQL ستضمن أن قيمه فريدة.
</p>

<p>
	أما النوع الثاني فهو <a data-ss1621333846="1" href="https://graphql.org/learn/schema/#the-query-and-mutation-types" rel="external nofollow">Query</a> أي استعلام. حيث يصف كل تخطيط في الواقع استعلامًا يحدد أنواع الاستعلامات التي يمكن طلبها من الواجهة البرمجية <abbr title="Application Programming Interface | واجهة برمجية">API</abbr>.
</p>

<p>
	يصف دليل الهاتف ثلاث استعلامات مختلفة: يعيد الأول personCount عددًا صحيحًا، ويعيد الثاني allPersons قائمة من الكائنات Person، أما الثالث findPerson فيتلقى معاملًا نصيًا ويعيد كائنًا من النوع Person. استخدمنا هنا أيضًا إشارة التعجّب لتحديد أي استعلام سيعيد قيمة، وأية معاملات ينبغي أن تحمل قيمة.
</p>

<p>
	سيعيد الاستعلام personCount قيمة صحيحة، ويجب أن يتلقى الاستعلام findPerson معاملًا نصيًا وأن يعيد كائن من النوع Person أو "Null- لاشيء". كما سيعيد الاستعلام allPersons قائمة كائنات من النوع Person بحيث لا تحتوي القائمة على أية قيم من النوع "Null- لاشيء".
</p>

<p>
	إذًا سيصف التخطيط الاستعلامات التي يمكن للمستخدم إرسالها إلى الخادم، والمعاملات التي تأخذها، وأنواع المعطيات التي تعيدها.
</p>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_19" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  personCount
</span><span class="pun">}</span></pre>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_17" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"data"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"personCount"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	أما الاستعلام allPersons الذي يحضر معلومات كل الأشخاص، فهو أعقد بقليل. وطالما أنه سيعيد قائمة من الكائنات، فعليه أن يحدد أية <a data-ss1621333846="1" href="ttps://graphql.org/learn/queries/#fields" rel="external nofollow">حقول</a> من الكائن سوف يعيدها:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_21" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  allPersons </span><span class="pun">{</span><span class="pln">
    name
    phone
  </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_5953_23" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"data"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"allPersons"</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="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Arto Hellas"</span><span class="pun">,</span><span class="pln">
        </span><span class="str">"phone"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"040-123543"</span><span class="pln">
      </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln">
        </span><span class="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Matti Luukkainen"</span><span class="pun">,</span><span class="pln">
        </span><span class="str">"phone"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"040-432342"</span><span class="pln">
      </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln">
        </span><span class="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Venla Ruuska"</span><span class="pun">,</span><span class="pln">
        </span><span class="str">"phone"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">null</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>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_25" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  allPersons</span><span class="pun">{</span><span class="pln">
    name
    city
    street
  </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_5953_27" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  findPerson</span><span class="pun">(</span><span class="pln">name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Arto Hellas"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    phone 
    city 
    street
    id
  </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_5953_29" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"data"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"findPerson"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="str">"phone"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"040-123543"</span><span class="pun">,</span><span class="pln">
      </span><span class="str">"city"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Espoo"</span><span class="pun">,</span><span class="pln">
      </span><span class="str">"street"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Tapiolankatu 5 A"</span><span class="pln">
      </span><span class="str">"id"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"3d594650-3436-11e9-bc57-8b80ba54c431"</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>
	يمكن أن يعيد الاستعلام القيمة "Null"، فلو حاولنا البحث عن شخص غير موجود:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_32" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  findPerson</span><span class="pun">(</span><span class="pln">name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Donald Trump"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    phone 
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ستكون النتيجة Null
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_34" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"data"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"findPerson"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">null</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	وكما رأينا، هنالك علاقة مباشرة بين استعلام GraphQL وكائن JSON الذي سيعيده. وبالتالي يمكننا أن نعتبر أن الاستعلام سيحدد نوع البيانات التي يريدها في الاستجابة. إن اختلاف هذا الاستعلام عن استعلام REST كبير، فلا علاقة لعنوان المورد ونوع البيانات التي يعيدها الاستعلام في REST بشكل البيانات المعادة.
</p>

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

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

<h2>
	خادم Apollo
</h2>

<p>
	لننجز خادم GraphQL بمساعدة المكتبة الرائدة في هذا المجال <a data-ss1621333846="1" href="https://www.apollographql.com/docs/apollo-server/" rel="external nofollow">Apollo -server</a>. سننشئ مشروع npm جديد بتنفيذ الأمر npm init وسنثبت اعتمادياته اللازمة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_36" style="">
<span class="pln">npm install apollo</span><span class="pun">-</span><span class="pln">server graphql</span></pre>

<p>
	ستكون الشيفرة الأولية كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_38" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">ApolloServer</span><span class="pun">,</span><span class="pln"> gql </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">'apollo-server'</span><span class="pun">)</span><span class="pln">

let persons </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">"Arto Hellas"</span><span class="pun">,</span><span class="pln">
    phone</span><span class="pun">:</span><span class="pln"> </span><span class="str">"040-123543"</span><span class="pun">,</span><span class="pln">
    street</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Tapiolankatu 5 A"</span><span class="pun">,</span><span class="pln">
    city</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Espoo"</span><span class="pun">,</span><span class="pln">
    id</span><span class="pun">:</span><span class="pln"> </span><span class="str">"3d594650-3436-11e9-bc57-8b80ba54c431"</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">"Matti Luukkainen"</span><span class="pun">,</span><span class="pln">
    phone</span><span class="pun">:</span><span class="pln"> </span><span class="str">"040-432342"</span><span class="pun">,</span><span class="pln">
    street</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Malminkaari 10 A"</span><span class="pun">,</span><span class="pln">
    city</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Helsinki"</span><span class="pun">,</span><span class="pln">
    id</span><span class="pun">:</span><span class="pln"> </span><span class="str">'3d599470-3436-11e9-bc57-8b80ba54c431'</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">"Venla Ruuska"</span><span class="pun">,</span><span class="pln">
    street</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Nallemäentie 22 C"</span><span class="pun">,</span><span class="pln">
    city</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Helsinki"</span><span class="pun">,</span><span class="pln">
    id</span><span class="pun">:</span><span class="pln"> </span><span class="str">'3d599471-3436-11e9-bc57-8b80ba54c431'</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"> typeDefs </span><span class="pun">=</span><span class="pln"> gql</span><span class="pun">`</span><span class="pln">
  type </span><span class="typ">Person</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">String</span><span class="pun">!</span><span class="pln">
    phone</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pln">
    street</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">
    city</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln"> 
    id</span><span class="pun">:</span><span class="pln"> ID</span><span class="pun">!</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  type </span><span class="typ">Query</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    personCount</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Int</span><span class="pun">!</span><span class="pln">
    allPersons</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">Person</span><span class="pun">!]!</span><span class="pln">
    findPerson</span><span class="pun">(</span><span class="pln">name</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!):</span><span class="pln"> </span><span class="typ">Person</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"> resolvers </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="typ">Query</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    personCount</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"> persons</span><span class="pun">.</span><span class="pln">length</span><span class="pun">,</span><span class="pln">
    allPersons</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"> persons</span><span class="pun">,</span><span class="pln">
    findPerson</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln">
      persons</span><span class="pun">.</span><span class="pln">find</span><span class="pun">(</span><span class="pln">p </span><span class="pun">=&gt;</span><span class="pln"> p</span><span class="pun">.</span><span class="pln">name </span><span class="pun">===</span><span class="pln"> args</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">const</span><span class="pln"> server </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ApolloServer</span><span class="pun">({</span><span class="pln">
  typeDefs</span><span class="pun">,</span><span class="pln">
  resolvers</span><span class="pun">,</span><span class="pln">
</span><span class="pun">})</span><span class="pln">

server</span><span class="pun">.</span><span class="pln">listen</span><span class="pun">().</span><span class="pln">then</span><span class="pun">(({</span><span class="pln"> url </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="typ">Server</span><span class="pln"> ready at $</span><span class="pun">{</span><span class="pln">url</span><span class="pun">}`)</span><span class="pln">
</span><span class="pun">})</span></pre>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_40" style="">
<span class="kwd">const</span><span class="pln"> server </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ApolloServer</span><span class="pun">({</span><span class="pln">
  typeDefs</span><span class="pun">,</span><span class="pln">
  resolvers</span><span class="pun">,</span><span class="pln">
</span><span class="pun">})</span></pre>

<p>
	الأول هو typeDefs ويتضمن تخطيط GraphQL، والثاني كائن يحتوي على <a data-ss1621333846="1" href="https://www.apollographql.com/docs/tutorial/resolvers/" rel="external nofollow">محللات</a> Resolvers لاستجابة الخادم، وهي شيفرة تحدد كيف سيستجيب الخادم لاستعلامات GraphQL. لشيفرة المحللات resolvers الشكل التالي:
</p>

<p>
	وكما نرى، تتعلق المحللات بوصف الاستعلامات الوارد في التخطيط:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_42" style="">
<span class="pln">type </span><span class="typ">Query</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  personCount</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Int</span><span class="pun">!</span><span class="pln">
  allPersons</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">Person</span><span class="pun">!]!</span><span class="pln">
  findPerson</span><span class="pun">(</span><span class="pln">name</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!):</span><span class="pln"> </span><span class="typ">Person</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	إذًا، هناك حقل لكل استعلام يصفه التخطيط داخل النوع Query. فالاستعلام التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_44" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  personCount
</span><span class="pun">}</span></pre>

<p>
	سيمتلك المحلل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_46" style="">
<span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> persons</span><span class="pun">.</span><span class="pln">length</span></pre>

<p>
	وبالتالي سيكون طول المصفوفة persons هو الرد على الاستعلام.
</p>

<p>
	أما الاستعلام الذي سيحضر بيانات كل الأشخاص:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_48" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  allPersons </span><span class="pun">{</span><span class="pln">
    name
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سيمتلك محللًا يعيد كل الكائنات الموجودة في المصفوفة persons.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_51" style="">
<span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> persons</span></pre>

<h2>
	أرضية عمل GraphQL
</h2>

<p>
	عندما يعمل خادم Apollo في وضع التطوير node filename.js، سيُشغّل <a data-ss1621333846="1" href="https://www.apollographql.com/docs/apollo-server/testing/graphql-playground/" rel="external nofollow">أرضية عمل(playground (GraphQL</a> على العنوان <a data-ss1621333846="1" href="http://localhost:4000/graphql." ipsnoembed="false" rel="external nofollow">http://localhost:4000/graphql.</a> إنّ هذه الميزة مفيدة جدًا للمطورين، ويمكن استخدامها لإنشاء استعلامات إلى الخادم.
</p>

<p>
	لنجرّب ذلك:
</p>

<p style="text-align: center;">
	<a data-fileid="64319" data-ss1621333846="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/Gql-playground_01.png.d7bf028ad20f43e0289b4b9fd86cafed.png" rel=""><img alt="Gql-playground_01.png" data-fileid="64319" data-unique="6gzkrutg6" src="https://academy.hsoub.com/uploads/monthly_2021_04/Gql-playground_01.png.d7bf028ad20f43e0289b4b9fd86cafed.png"></a>
</p>

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

<p style="text-align: center;">
	<a data-fileid="64321" data-ss1621333846="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/gql-playground_query_error_02.png.5a449d4e35266ab22ba3d0ece2d12817.png" rel=""><img alt="gql-playground_query_error_02.png" data-fileid="64321" data-unique="sw6bs2839" src="https://academy.hsoub.com/uploads/monthly_2021_04/gql-playground_query_error_02.png.5a449d4e35266ab22ba3d0ece2d12817.png"></a>
</p>

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

<p style="text-align: center;">
	<a data-fileid="64320" data-ss1621333846="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/gql-playground_error_message_03.png.e6e88459428fa2ab0f39527d9c2c2bdd.png" rel=""><img alt="gql-playground_error_message_03.png" data-fileid="64320" data-unique="9h3fk1lud" src="https://academy.hsoub.com/uploads/monthly_2021_04/gql-playground_error_message_03.png.e6e88459428fa2ab0f39527d9c2c2bdd.png"></a>
</p>

<p>
	إن بدا لك أن أرضية العمل لا تستجيب، فقد يساعدك تحديث الصفحة refresh.
</p>

<p>
	سيظهر لك بالنقر على النص DOCS على يمين أرضية العمل تخطيط GraphQL على الخادم.
</p>

<p style="text-align: center;">
	<a data-fileid="64322" data-ss1621333846="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/gql_schema_04.png.d8040fd8b4c099c751945836756d3b38.png" rel=""><img alt="gql_schema_04.png" data-fileid="64322" data-unique="l9ciy7woq" src="https://academy.hsoub.com/uploads/monthly_2021_04/gql_schema_04.png.d8040fd8b4c099c751945836756d3b38.png"></a>
</p>

<h2>
	معاملات المحلل
</h2>

<p>
	يمتلك الاستعلام التالي الذي يحضر بيانات شخص واحد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_53" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  findPerson</span><span class="pun">(</span><span class="pln">name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Arto Hellas"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    phone 
    city 
    street
  </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_5953_55" style="">
<span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> persons</span><span class="pun">.</span><span class="pln">find</span><span class="pun">(</span><span class="pln">p </span><span class="pun">=&gt;</span><span class="pln"> p</span><span class="pun">.</span><span class="pln">name </span><span class="pun">===</span><span class="pln"> args</span><span class="pun">.</span><span class="pln">name</span><span class="pun">)</span></pre>

<p>
	يحتوي المعامل الثاني args معاملات الاستعلام. حيث يعيد المحلل عندها شخصًا من قائمة الأشخاص عندما يتطابق اسمه مع القيمة args.name. ولا يحتاج المحلل إلى المعامل الأول root.
</p>

<p>
	تعطى كل المحللات عمليًا <a data-ss1621333846="1" href="https://www.graphql-tools.com/docs/resolvers#resolver-function-signature" rel="external nofollow">أربع معاملات</a>. لكن ليس من الضروري في تعريف المعاملات إن لم نحتاجها. وسنرى كيف سنستعمل المعاملين الأول والثالث فقط لاحقًا في هذا القسم.
</p>

<h2>
	المحلل الافتراضي
</h2>

<p>
	عندما نرسل الاستعلام التالي على سبيل المثال:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_57" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  findPerson</span><span class="pun">(</span><span class="pln">name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Arto Hellas"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    phone 
    city 
    street
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	كيف يمكن للخادم أن يعيد الحقول التي يطلبها الاستعلام تمامًا؟
</p>

<p>
	ينبغي على خادم GraphQL أن يحدد محللًا لكل حقل من كل نوع في التخطيط. لقد عرفنا حاليًا محللات للحقول من النوع "Query" فقط وذلك من أجل كل استعلام في التطبيق.
</p>

<p>
	ولأننا لم نعرف محللات لحقول النوع "Person"، يحدد لها Apollo <a data-ss1621333846="1" href="https://www.graphql-tools.com/docs/resolvers/#default-resolver" rel="external nofollow">محللات افتراضية</a>. تعمل هذه المحللات كالمثال التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_59" style="">
<span class="kwd">const</span><span class="pln"> resolvers </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="typ">Query</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    personCount</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"> persons</span><span class="pun">.</span><span class="pln">length</span><span class="pun">,</span><span class="pln">
    allPersons</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"> persons</span><span class="pun">,</span><span class="pln">
    findPerson</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> persons</span><span class="pun">.</span><span class="pln">find</span><span class="pun">(</span><span class="pln">p </span><span class="pun">=&gt;</span><span class="pln"> p</span><span class="pun">.</span><span class="pln">name </span><span class="pun">===</span><span class="pln"> args</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="typ">Person</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="pun">(</span><span class="pln">root</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> root</span><span class="pun">.</span><span class="pln">name</span><span class="pun">,</span><span class="pln">
      phone</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">root</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> root</span><span class="pun">.</span><span class="pln">phone</span><span class="pun">,</span><span class="pln">
      street</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">root</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> root</span><span class="pun">.</span><span class="pln">street</span><span class="pun">,</span><span class="pln">
      city</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">root</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> root</span><span class="pun">.</span><span class="pln">city</span><span class="pun">,</span><span class="pln">
      id</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">root</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> root</span><span class="pun">.</span><span class="pln">id
  </span><span class="pun">}}</span></pre>

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

<p>
	لا يتوجب عليك تعريف محللات خاصة إن كانت المحللات الافتراضية كافية لتنفيذ المطلوب. كما يمكنك تعريف محللات ترتبط ببعض حقول نوع معين، وترك المحللات الافتراضية تتعامل مع بقية الحقول.
</p>

<p>
	يمكنك على سبيل المثال تحديد عنوان واحد لكل الأشخاص وليكن Manhattan New York بكتابته مباشرة ضمن الشيفرة، وكتابة التالي لمحللات الحقلين street وcity العائدين للنوع "Person":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_61" style="">
<span class="typ">Person</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  street</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">root</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="str">"Manhattan"</span><span class="pun">,</span><span class="pln">
  city</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">root</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="str">"New York"</span><span class="pln">
</span><span class="pun">}</span></pre>

<h2>
	كائنات ضمن الكائنات
</h2>

<p>
	لنعدّل التخطيط السابق قليلًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_63" style="">
<span class="pln">type </span><span class="typ">Address</span><span class="pln"> </span><span class="pun">{</span><span class="pln">  street</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">  city</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
type </span><span class="typ">Person</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">String</span><span class="pun">!</span><span class="pln">
  phone</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pln">
  address</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Address</span><span class="pun">!</span><span class="pln">
  id</span><span class="pun">:</span><span class="pln"> ID</span><span class="pun">!</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

type </span><span class="typ">Query</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  personCount</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Int</span><span class="pun">!</span><span class="pln">
  allPersons</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="typ">Person</span><span class="pun">!]!</span><span class="pln">
  findPerson</span><span class="pun">(</span><span class="pln">name</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!):</span><span class="pln"> </span><span class="typ">Person</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	سيمتلك الشخص حقلًا من النوع "Address" الذي يحتوي على المدينة والشارع. سيتغير الاستعلام الذي يتطلب العنوان إلى الشكل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_65" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  findPerson</span><span class="pun">(</span><span class="pln">name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Arto Hellas"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    phone 
    address </span><span class="pun">{</span><span class="pln">
      city 
      street
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	وستكون الاستجابة الآن على شكل كائن من النوع "person" يحتوي على كائن من النوع "Address".
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_67" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"data"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"findPerson"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="str">"phone"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"040-123543"</span><span class="pun">,</span><span class="pln">
      </span><span class="str">"address"</span><span class="pun">:</span><span class="pln">  </span><span class="pun">{</span><span class="pln">
        </span><span class="str">"city"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Espoo"</span><span class="pun">,</span><span class="pln">
        </span><span class="str">"street"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Tapiolankatu 5 A"</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>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_69" style="">
<span class="pln">let persons </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">"Arto Hellas"</span><span class="pun">,</span><span class="pln">
    phone</span><span class="pun">:</span><span class="pln"> </span><span class="str">"040-123543"</span><span class="pun">,</span><span class="pln">
    street</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Tapiolankatu 5 A"</span><span class="pun">,</span><span class="pln">
    city</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Espoo"</span><span class="pun">,</span><span class="pln">
    id</span><span class="pun">:</span><span class="pln"> </span><span class="str">"3d594650-3436-11e9-bc57-8b80ba54c431"</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>
	إذًا، فالكائن "person" الذي يُخزَّن في الخادم ليس مطابقًا لكائن GraphQL من النوع "person" والموصوف في التخطيط.
</p>

<p>
	وعلى النقيض، لا يمتلك النوع "Address" الحقل id لأنه لا يُخزَّن ضمن بنية خاصة به على الخادم. وطالما أنّ الكائنات التي خُزّنت داخل المصفوفة لا تمتلك الحقل address، لن يكون المحلل الافتراضي كافيًا. لنضيف إذًا محللًا للحقل address العائد للنوع "Person".
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_71" style="">
<span class="kwd">const</span><span class="pln"> resolvers </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="typ">Query</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    personCount</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"> persons</span><span class="pun">.</span><span class="pln">length</span><span class="pun">,</span><span class="pln">
    allPersons</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"> persons</span><span class="pun">,</span><span class="pln">
    findPerson</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln">
      persons</span><span class="pun">.</span><span class="pln">find</span><span class="pun">(</span><span class="pln">p </span><span class="pun">=&gt;</span><span class="pln"> p</span><span class="pun">.</span><span class="pln">name </span><span class="pun">===</span><span class="pln"> args</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="typ">Person</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      address</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">root</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">return</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
              street</span><span class="pun">:</span><span class="pln"> root</span><span class="pun">.</span><span class="pln">street</span><span class="pun">,</span><span class="pln">
              city</span><span class="pun">:</span><span class="pln"> root</span><span class="pun">.</span><span class="pln">city
          </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}}</span></pre>

<p>
	وهكذا وفي كل مرة يُعاد فيها كائن من النوع "Person"، ستُعاد الحقول name وid وphone باستخدام محللاتها الخاصة أما الحقل address فسيتشكل باستخدام المحلل الذي عرّفناه. وطالما أنّ المعامل root لدالة المحلل هو كائن من النوع "Person"، فيمكن الوصول إلى الشارع والمدينة من حقوله.
</p>

<p>
	ستجد شيفرة التطبيق بوضعه الحالي ضمن الفرع part8-1 في المستودع المخصص للتطبيق على <a data-ss1621333846="1" href="https://github.com/fullstack-hy2020/graphql-phonebook-backend/tree/part8-1" rel="external nofollow">GitHub</a>
</p>

<h2>
	الطفرات
</h2>

<p>
	لنضف وظيفة إدخال مستخدم جديد إلى دليل الهاتف. تُنفَّذ جميع العمليات التي تسبب تغييرات في GraphQL باستخدام الطفرات (<a data-ss1621333846="1" href="https://graphql.org/learn/queries/#mutations" rel="external nofollow">mutations</a>). تُوصف الطفرات كمفاتيح من النوع "’Mutation" ضمن تخطيط GraphQL.
</p>

<p>
	سيبدو تخطيط إضافة شخص جديد كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_73" style="">
<span class="pln">type </span><span class="typ">Mutation</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  addPerson</span><span class="pun">(</span><span class="pln">
    name</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">
    phone</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pln">
    street</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">
    city</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">
  </span><span class="pun">):</span><span class="pln"> </span><span class="typ">Person</span><span class="pln">
</span><span class="pun">}</span></pre>

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

<p>
	تتطلب الطفرات محللات أيضًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_75" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> v1</span><span class="pun">:</span><span class="pln"> uuid </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">'uuid'</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"> resolvers </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">Mutation</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    addPerson</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> args</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"> person </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">...</span><span class="pln">args</span><span class="pun">,</span><span class="pln"> id</span><span class="pun">:</span><span class="pln"> uuid</span><span class="pun">()</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
      persons </span><span class="pun">=</span><span class="pln"> persons</span><span class="pun">.</span><span class="pln">concat</span><span class="pun">(</span><span class="pln">person</span><span class="pun">)</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> person
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تضيف الطفرة الكائن الذي يمرر إليها عبر الوسيط args إلى المصفوفة persons، وتعيد الكائن الذي أضافته إلى المصفوفة. يُعطى الحقل id قيمة فريدة بالاستعانة بالمكتبة <a data-ss1621333846="1" href="https://github.com/kelektiv/node-uuid#readme" rel="external nofollow">uuid</a>.
</p>

<p>
	يمكن أن يُضاف شخص جديد من خلال الطفرة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_77" style="">
<span class="pln">mutation </span><span class="pun">{</span><span class="pln">
  addPerson</span><span class="pun">(</span><span class="pln">
    name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Pekka Mikkola"</span><span class="pln">
    phone</span><span class="pun">:</span><span class="pln"> </span><span class="str">"045-2374321"</span><span class="pln">
    street</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Vilppulantie 25"</span><span class="pln">
    city</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Helsinki"</span><span class="pln">
  </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    name
    phone
    address</span><span class="pun">{</span><span class="pln">
      city
      street
    </span><span class="pun">}</span><span class="pln">
    id
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لاحظ أن الشخص الجديد سيوضع ضمن المصفوفة persons على الشكل التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_79" style="">
<span class="pun">{</span><span class="pln">
  name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Pekka Mikkola"</span><span class="pun">,</span><span class="pln">
  phone</span><span class="pun">:</span><span class="pln"> </span><span class="str">"045-2374321"</span><span class="pun">,</span><span class="pln">
  street</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Vilppulantie 25"</span><span class="pun">,</span><span class="pln">
  city</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Helsinki"</span><span class="pun">,</span><span class="pln">
  id</span><span class="pun">:</span><span class="pln"> </span><span class="str">"2b24e0b0-343c-11e9-8c2a-cb57c2bf804f"</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	لكن استجابة الخادم على الطفرة ستكون بالشكل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_81" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"data"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"addPerson"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Pekka Mikkola"</span><span class="pun">,</span><span class="pln">
      </span><span class="str">"phone"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"045-2374321"</span><span class="pun">,</span><span class="pln">
      </span><span class="str">"address"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="str">"city"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Helsinki"</span><span class="pun">,</span><span class="pln">
        </span><span class="str">"street"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Vilppulantie 25"</span><span class="pln">
      </span><span class="pun">},</span><span class="pln">
      </span><span class="str">"id"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"2b24e0b0-343c-11e9-8c2a-cb57c2bf804f"</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>
	إذًا، سيُنسّق محلل الحقل address للنوع "Person " الكائن الذي يُعيده الخادم بالشكل الصحيح.
</p>

<h2>
	معالجة الأخطاء
</h2>

<p>
	إذا لم تتوافق المعاملات عند إنشاء شخص جديد مع الوصف المرافق للتخطيط، سيعطينا الخادم رسالة الخطأ التالية:
</p>

<p style="text-align: center;">
	<a data-fileid="64324" data-ss1621333846="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/server_error_message_05.png.fddaac972aa1b7cb9997d6d24e1b544c.png" rel=""><img alt="server_error_message_05.png" data-fileid="64324" data-unique="15adqjow8" src="https://academy.hsoub.com/uploads/monthly_2021_04/server_error_message_05.png.fddaac972aa1b7cb9997d6d24e1b544c.png"></a>
</p>

<p>
	يمكن معالجة بعض الأخطاء تلقائيًا باستخدام <a data-ss1621333846="1" href="https://graphql.org/learn/validation/" rel="external nofollow">تقييم</a> GraphQL، لكنها لا يمكن أن تعالج كل شيء تلقائيًا. إذ ينبغي إضافة القواعد الأكثر تشددًا لإرسال البيانات إلى الطفرات يدويًا. تُعالج الأخطاء الناتجة عن تلك القواعد وفق <a data-ss1621333846="1" href="https://www.apollographql.com/docs/apollo-server/data/errors" rel="external nofollow">آلية معالجة الأخطاء لخادم Apollo</a>.
</p>

<p>
	لنمنع إضافة نفس الاسم عدة مرات إلى دليل الهاتف:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_83" style="">
<span class="kwd">const</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">ApolloServer</span><span class="pun">,</span><span class="pln"> </span><span class="typ">UserInputError</span><span class="pun">,</span><span class="pln"> gql </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">'apollo-server'</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"> resolvers </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">Mutation</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    addPerson</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> args</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">persons</span><span class="pun">.</span><span class="pln">find</span><span class="pun">(</span><span class="pln">p </span><span class="pun">=&gt;</span><span class="pln"> p</span><span class="pun">.</span><span class="pln">name </span><span class="pun">===</span><span class="pln"> args</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="kwd">throw</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">UserInputError</span><span class="pun">(</span><span class="str">'Name must be unique'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> 
                              invalidArgs</span><span class="pun">:</span><span class="pln"> args</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">const</span><span class="pln"> person </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">...</span><span class="pln">args</span><span class="pun">,</span><span class="pln"> id</span><span class="pun">:</span><span class="pln"> uuid</span><span class="pun">()</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
      persons </span><span class="pun">=</span><span class="pln"> persons</span><span class="pun">.</span><span class="pln">concat</span><span class="pun">(</span><span class="pln">person</span><span class="pun">)</span><span class="pln">
      </span><span class="kwd">return</span><span class="pln"> person
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	فلو كان الاسم موجودًا مسبقًا، فسيرمي التطبيق الخطأ UserInputError.
</p>

<p style="text-align: center;">
	<a data-fileid="64325" data-ss1621333846="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/user_input_error_06.png.d9242dc2d11db5da5cd787a4f9c906d5.png" rel=""><img alt="user_input_error_06.png" data-fileid="64325" data-unique="u7pegr6wf" src="https://academy.hsoub.com/uploads/monthly_2021_04/user_input_error_06.png.d9242dc2d11db5da5cd787a4f9c906d5.png"></a>
</p>

<p>
	ستجد شيفرة التطبيق بوضعه الحالي ضمن الفرع part8-2 في المستودع المخصص للتطبيق على <a data-ss1621333846="1" href="https://github.com/fullstack-hy2020/graphql-phonebook-backend/tree/part8-2" rel="external nofollow">GitHub</a>.
</p>

<h2>
	التعداد Enum في GraphQL
</h2>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_85" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  allPersons</span><span class="pun">(</span><span class="pln">phone</span><span class="pun">:</span><span class="pln"> YES</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    name
    phone 
  </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_5953_87" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  allPersons</span><span class="pun">(</span><span class="pln">phone</span><span class="pun">:</span><span class="pln"> NO</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="pun">}</span></pre>

<p>
	ستغير التخطيط على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_91" style="">
<span class="kwd">enum</span><span class="pln"> </span><span class="typ">YesNo</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> YES  NO</span><span class="pun">}</span><span class="pln">
type </span><span class="typ">Query</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  personCount</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Int</span><span class="pun">!</span><span class="pln">
  allPersons</span><span class="pun">(</span><span class="pln">phone</span><span class="pun">:</span><span class="pln"> </span><span class="typ">YesNo</span><span class="pun">):</span><span class="pln"> </span><span class="pun">[</span><span class="typ">Person</span><span class="pun">!]!</span><span class="pln">  findPerson</span><span class="pun">(</span><span class="pln">name</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!):</span><span class="pln"> </span><span class="typ">Person</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	يمثل النوع YesNo <a data-ss1621333846="1" href="https://graphql.org/learn/schema/#enumeration-types" rel="external nofollow">تعدادًا</a> GraphQL، أو معدِّدًا يأخذ إحدى قيمتين YES أو NO. يعتبر المعامل phone في الاستعلام allPerson من النوع "YesNO"، لكنه قد يحمل القيمة "null".
</p>

<p>
	سيتغير المحلل كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_93" style="">
<span class="typ">Query</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  personCount</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"> persons</span><span class="pun">.</span><span class="pln">length</span><span class="pun">,</span><span class="pln">
  allPersons</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> args</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">args</span><span class="pun">.</span><span class="pln">phone</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"> persons
      </span><span class="pun">}</span><span class="pln">
      </span><span class="kwd">const</span><span class="pln"> byPhone </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">person</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln">
      args</span><span class="pun">.</span><span class="pln">phone </span><span class="pun">===</span><span class="pln"> </span><span class="str">'YES'</span><span class="pln"> </span><span class="pun">?</span><span class="pln"> person</span><span class="pun">.</span><span class="pln">phone </span><span class="pun">:</span><span class="pln"> </span><span class="pun">!</span><span class="pln">person</span><span class="pun">.</span><span class="pln">phone
      </span><span class="kwd">return</span><span class="pln"> persons</span><span class="pun">.</span><span class="pln">filter</span><span class="pun">(</span><span class="pln">byPhone</span><span class="pun">)</span><span class="pln">  </span><span class="pun">},</span><span class="pln">
          findPerson</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> args</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln">
    persons</span><span class="pun">.</span><span class="pln">find</span><span class="pun">(</span><span class="pln">p </span><span class="pun">=&gt;</span><span class="pln"> p</span><span class="pun">.</span><span class="pln">name </span><span class="pun">===</span><span class="pln"> args</span><span class="pun">.</span><span class="pln">name</span><span class="pun">)</span><span class="pln">
</span><span class="pun">},</span></pre>

<h2>
	تغيير رقم الهاتف
</h2>

<p>
	لنضف طفرة لتغيير رقم هاتف شخص محدد. سيبدو تخطيط هذه الطفرة كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_95" style="">
<span class="pln">type </span><span class="typ">Mutation</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  addPerson</span><span class="pun">(</span><span class="pln">
    name</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">
    phone</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pln">
    street</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">
    city</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">
  </span><span class="pun">):</span><span class="pln"> </span><span class="typ">Person</span><span class="pln">
  editNumber</span><span class="pun">(</span><span class="pln">    name</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">    phone</span><span class="pun">:</span><span class="pln"> </span><span class="typ">String</span><span class="pun">!</span><span class="pln">  </span><span class="pun">):</span><span class="pln"> </span><span class="typ">Person</span><span class="pun">}</span></pre>

<p>
	وتنفذ هذه الطفرة باستخدام المحلل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_97" style="">
<span class="typ">Mutation</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// ...</span><span class="pln">
  editNumber</span><span class="pun">:</span><span class="pln"> </span><span class="pun">(</span><span class="pln">root</span><span class="pun">,</span><span class="pln"> args</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"> person </span><span class="pun">=</span><span class="pln"> persons</span><span class="pun">.</span><span class="pln">find</span><span class="pun">(</span><span class="pln">p </span><span class="pun">=&gt;</span><span class="pln"> p</span><span class="pun">.</span><span class="pln">name </span><span class="pun">===</span><span class="pln"> args</span><span class="pun">.</span><span class="pln">name</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">person</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">null</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">

    </span><span class="kwd">const</span><span class="pln"> updatedPerson </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">...</span><span class="pln">person</span><span class="pun">,</span><span class="pln"> phone</span><span class="pun">:</span><span class="pln"> args</span><span class="pun">.</span><span class="pln">phone </span><span class="pun">}</span><span class="pln">
    persons </span><span class="pun">=</span><span class="pln"> persons</span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">p </span><span class="pun">=&gt;</span><span class="pln"> p</span><span class="pun">.</span><span class="pln">name </span><span class="pun">===</span><span class="pln"> args</span><span class="pun">.</span><span class="pln">name </span><span class="pun">?</span><span class="pln"> updatedPerson </span><span class="pun">:</span><span class="pln"> p</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> updatedPerson
  </span><span class="pun">}</span><span class="pln">   
</span><span class="pun">}</span></pre>

<p>
	ستجد الطفرة الشخص الذي ستُحدَّث بياناته من خلال الحقل name.
</p>

<p>
	ستجد شيفرة التطبيق بوضعه الحالي ضمن الفرع part8-3 في المستودع المخصص للتطبيق على <a data-ss1621333846="1" href="https://github.com/fullstack-hy2020/graphql-phonebook-backend/tree/part8-3" rel="external nofollow">GitHub</a>.
</p>

<h2>
	المزيد عن الاستعلامات
</h2>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_100" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  personCount
  allPersons </span><span class="pun">{</span><span class="pln">
    name
  </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_5953_102" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"data"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"personCount"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"allPersons"</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="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Arto Hellas"</span><span class="pln">
      </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln">
        </span><span class="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Matti Luukkainen"</span><span class="pln">
      </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln">
        </span><span class="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Venla Ruuska"</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>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_104" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  havePhone</span><span class="pun">:</span><span class="pln"> allPersons</span><span class="pun">(</span><span class="pln">phone</span><span class="pun">:</span><span class="pln"> YES</span><span class="pun">){</span><span class="pln">
    name
  </span><span class="pun">}</span><span class="pln">
  phoneless</span><span class="pun">:</span><span class="pln"> allPersons</span><span class="pun">(</span><span class="pln">phone</span><span class="pun">:</span><span class="pln"> NO</span><span class="pun">){</span><span class="pln">
    name
  </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_5953_106" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"data"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"havePhone"</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="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Arto Hellas"</span><span class="pln">
      </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln">
        </span><span class="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Matti Luukkainen"</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">],</span><span class="pln">
    </span><span class="str">"phoneless"</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="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Venla Ruuska"</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>
	ومن المفيد أن نعطي الاستعلام اسمًا في بعض الأحيان. وخاصة عندما تمتلك الاستعلامات أو الطفرات <a data-ss1621333846="1" href="https://graphql.org/learn/queries/#variables" rel="external nofollow">معاملات</a>. وسنتطرق إلى هذا الموضوع قريبًا.
</p>

<p>
	إن ظهرت في شيفرتك استعلامات متعددة، ستسألك أرضية العمل أن تحدد الاستعلام الذي ستنفّذه:
</p>

<p style="text-align: center;">
	<a data-fileid="64323" data-ss1621333846="1" href="https://academy.hsoub.com/uploads/monthly_2021_04/playground_query_to_run_07.png.bc1f37a0a4997910911262a3a6b92fd0.png" rel=""><img alt="playground_query_to_run_07.png" data-fileid="64323" data-unique="gaklfw7ym" src="https://academy.hsoub.com/uploads/monthly_2021_04/playground_query_to_run_07.png.bc1f37a0a4997910911262a3a6b92fd0.png"></a>
</p>

<h2>
	التمارين
</h2>

<p>
	سننجز خلال هذه التمارين واجهة خلفية باستخدام GraphQL لمكتبة صغيرة. استخدم <a data-ss1621333846="1" href="https://github.com/fullstack-hy2020/misc/blob/master/library-backend.js" rel="external nofollow">الملف المخصص للتمرين</a> والموجود على GitHub كنقطة انطلاق، وتذكر أن تستخدم أمر التهيئة npm init وأن تُثبّت الاعتماديات.
</p>

<p>
	انتبه لفكرة أن الشيفرة لن تعمل بشكلها الحالي لأن تعريف التخطيط الخاص بالواجهة غير مكتمل بعد.
</p>

<h3>
	1. عدد الكتب المؤلفين
</h3>

<p>
	أنجز الاستعلامين bookCount وauthorCount اللذين يعيدان عدد الكتب وعدد المؤلفين.
</p>

<p>
	ينبغي للاستعلام التالي:
</p>

<pre class="ipsCode">
<code class="bash language-bash">query {
  bookCount
  authorCount
}
</code></pre>

<p>
	أن يعيد النتيجة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_108" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"data"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"bookCount"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">7</span><span class="pun">,</span><span class="pln">
    </span><span class="str">"authorCount"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">5</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	2. جميع الكتب
</h3>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_110" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  allBooks </span><span class="pun">{</span><span class="pln"> 
    title 
    author
    published 
    genres
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	3. جميع المؤلفين
</h3>

<p>
	أنجز الاستعلام allAuthors الذي يعيد تفاصيل جميع المؤلفين. يجب أن تتضمن الاستجابة الحقل bookCount الذي يضم عدد الكتب التي ألّفها الكاتب. فسيعيد الاستعلام التالي على سبيل المثال:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_112" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  allAuthors </span><span class="pun">{</span><span class="pln">
    name
    bookCount
  </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_5953_114" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"data"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"allAuthors"</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="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Robert Martin"</span><span class="pun">,</span><span class="pln">
        </span><span class="str">"bookCount"</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="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Martin Fowler"</span><span class="pun">,</span><span class="pln">
        </span><span class="str">"bookCount"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
      </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln">
        </span><span class="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Fyodor Dostoevsky"</span><span class="pun">,</span><span class="pln">
        </span><span class="str">"bookCount"</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="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Joshua Kerievsky"</span><span class="pun">,</span><span class="pln">
        </span><span class="str">"bookCount"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pln">
      </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln">
        </span><span class="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Sandi Metz"</span><span class="pun">,</span><span class="pln">
        </span><span class="str">"bookCount"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</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>

<h3>
	4. الكتب التي أنجزها مؤلف
</h3>

<p>
	عدل الاستعلام allBooks لكي يتمكن المستخدم من تمرير المعامل الاختياري author إلى الاستعلام. يجب أن تحتوي الاستجابة على الكتب التي أنجزها المؤلف الذي مُرِّر من خلال المعامل.
</p>

<p>
	سيعيد مثلًا الاستعلام التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_116" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  allBooks</span><span class="pun">(</span><span class="pln">author</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Robert Martin"</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">
</span><span class="pun">}</span></pre>

<p>
	النتيجة التالية:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_118" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"data"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"allBooks"</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="str">"title"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Clean Code"</span><span class="pln">
      </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln">
        </span><span class="str">"title"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Agile software development"</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>

<h3>
	5. الحصول على الكتب من خلال نوعها
</h3>

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

<p>
	سيعطي الاستعلام التالي على سبيل المثال:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_120" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  allBooks</span><span class="pun">(</span><span class="pln">genre</span><span class="pun">:</span><span class="pln"> </span><span class="str">"refactoring"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    title
    author
  </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_5953_122" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"data"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"allBooks"</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="str">"title"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Clean Code"</span><span class="pun">,</span><span class="pln">
        </span><span class="str">"author"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Robert Martin"</span><span class="pln">
      </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln">
        </span><span class="str">"title"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Refactoring, edition 2"</span><span class="pun">,</span><span class="pln">
        </span><span class="str">"author"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Martin Fowler"</span><span class="pln">
      </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln">
        </span><span class="str">"title"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Refactoring to patterns"</span><span class="pun">,</span><span class="pln">
        </span><span class="str">"author"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Joshua Kerievsky"</span><span class="pln">
      </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">{</span><span class="pln">
        </span><span class="str">"title"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Practical Object-Oriented Design, An Agile Primer Using Ruby"</span><span class="pun">,</span><span class="pln">
        </span><span class="str">"author"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Sandi Metz"</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>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_124" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  allBooks</span><span class="pun">(</span><span class="pln">author</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Robert Martin"</span><span class="pun">,</span><span class="pln"> genre</span><span class="pun">:</span><span class="pln"> </span><span class="str">"refactoring"</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    title
    author
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<h3>
	6. إضافة كتاب
</h3>

<p>
	أنجز الطفرة addBook التي ستستخدم كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_126" style="">
<span class="pln">mutation </span><span class="pun">{</span><span class="pln">
  addBook</span><span class="pun">(</span><span class="pln">
    title</span><span class="pun">:</span><span class="pln"> </span><span class="str">"NoSQL Distilled"</span><span class="pun">,</span><span class="pln">
    author</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Martin Fowler"</span><span class="pun">,</span><span class="pln">
    published</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2012</span><span class="pun">,</span><span class="pln">
    genres</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">"database"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"nosql"</span><span class="pun">]</span><span class="pln">
  </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">
    author
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	من المفترض أن تعمل الطفرة mutation حتى لو لم يُخزَّن اسم المؤلف على الخادم بعد:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_139" style="">
<span class="pln">mutation </span><span class="pun">{</span><span class="pln">
  addBook</span><span class="pun">(</span><span class="pln">
    title</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Pimeyden tango"</span><span class="pun">,</span><span class="pln">
    author</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Reijo Mäki"</span><span class="pun">,</span><span class="pln">
    published</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1997</span><span class="pun">,</span><span class="pln">
    genres</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">"crime"</span><span class="pun">]</span><span class="pln">
  </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">
    author
  </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_5953_136" style="">
<span class="pln">query </span><span class="pun">{</span><span class="pln">
  allAuthors </span><span class="pun">{</span><span class="pln">
    name
    born
    bookCount
  </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_5953_134" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"data"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"allAuthors"</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="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Reijo Mäki"</span><span class="pun">,</span><span class="pln">
        </span><span class="str">"born"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">,</span><span class="pln">
        </span><span class="str">"bookCount"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</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>

<h3>
	7. تحديث عام ولادة المؤلف
</h3>

<p>
	أنجز الطفرة editAuthor التي ستستخدم لتحديد عام ولادة المؤلف. يمكن استخدام الطفرة على النحو التالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_132" style="">
<span class="pln">mutation </span><span class="pun">{</span><span class="pln">
  editAuthor</span><span class="pun">(</span><span class="pln">name</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Reijo Mäki"</span><span class="pun">,</span><span class="pln"> setBornTo</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1958</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    name
    born
  </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_5953_130" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"data"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"editAuthor"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Reijo Mäki"</span><span class="pun">,</span><span class="pln">
      </span><span class="str">"born"</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1958</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>
	إن لم يكن المؤلف موجودًا، ستعيد العملية القيمة "null".
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5953_128" style="">
<span class="pun">{</span><span class="pln">
  </span><span class="str">"data"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"editAuthor"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">null</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ترجمة -وبتصرف- للفصل <a data-ss1621333846="1" href="https://fullstackopen.com/en/part8/Graphql_server" rel="external nofollow">GraphQl-Server</a> من سلسلة <a data-ss1621333846="1" href="https://fullstackopen.com/en/" rel="external nofollow">Deep Dive Into Modern Web Development</a>
</p>
]]></description><guid isPermaLink="false">1208</guid><pubDate>Wed, 28 Apr 2021 09:09:00 +0000</pubDate></item><item><title>&#x625;&#x631;&#x633;&#x627;&#x644; &#x627;&#x644;&#x627;&#x633;&#x62A;&#x645;&#x627;&#x631;&#x627;&#x62A; (form submit) &#x648;&#x645;&#x639;&#x627;&#x644;&#x62C;&#x62A;&#x647;&#x627; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A5%D8%B1%D8%B3%D8%A7%D9%84-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D9%85%D8%A7%D8%B1%D8%A7%D8%AA-form-submit-%D9%88%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%AA%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-r1196/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/60622e68adceb_--(form-submit)---.png.e63f2bc4ac833cc0c760304637a0170b.png" /></p>

<p>
	يقع الحدث <code>submit</code> عندما تُسلَّم الاستمارة، ويُستخدم عادة للتحقّق من الاستمارة قبل إرسالها إلى الخادم، أو لإلغاء التسليم والقيام بمعالجته في جافاسكربت.
</p>

<p>
	يمكّن التابع <code>form.submit()‎</code> من إرسال الاستمارة بواسطة جافاسكربت. ويمكننا استخدامه لإنشاء وإرسال استماراتنا إلى الخادم ديناميكيًّا.
</p>

<p>
	لنرى المزيد من التفاصيل حولهما.
</p>

<h2>
	الحدث: submit
</h2>

<p>
	هناك طريقتان رئيسيّتان لتسليم الاستمارة:
</p>

<ol>
<li>
		الأولى بالنقر على <code>&lt;input type="submit"</code>‎<code>&gt;</code> أو <code>&lt;input type="image"</code>‎<code>&gt;</code>.
	</li>
	<li>
		الثانية بالضغط على المفتاح <code>Enter</code> داخل حقل المُدخل.
	</li>
</ol>
<p>
	يؤدّي كلا هذين الفعلين إلى وقوع الحدث <code>submit</code> على الاستمارة. يمكن للمعالج التحقّق من المعطيات، وإذا وجد أنّ هناك أخطاء، فإنّه يبيّنها ويستدعى <code>event.preventDefault()‎</code>، لكيلا تُرسل الاستمارة إلى الخادم.
</p>

<p>
	في الاستمارة أدناه، يمكن النقر على <code>&lt;input type="submit"‎&gt;</code> أو الذهاب إلى داخل الحقل النصّيّ والضغط على <code>Enter</code>. سيؤدّي كلا الفعلين إلى إظهار <code>alert</code> ولا تُرسَل الاستمارة إلى أيّ مكان بسبب وجود <code>return false</code>.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2737_7" style="">
<span class="tag">&lt;form</span><span class="pln"> </span><span class="atn">onsubmit</span><span class="pun">=</span><span class="atv">"</span><span class="pln">alert</span><span class="pun">(</span><span class="str">'submit!'</span><span class="pun">);</span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">false</span><span class="atv">"</span><span class="tag">&gt;</span><span class="pln">
  First: Enter in the input field </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">value</span><span class="pun">=</span><span class="atv">"text"</span><span class="tag">&gt;&lt;br&gt;</span><span class="pln">
  Second: Click "submit": </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"submit"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"Submit"</span><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;/form&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1617047141="1" data-ss1617047309="1" frameborder="no" height="177" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/jOyMawy?height=177&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-forms-submit-ex1">See the Pen JS-forms-submit-ex1 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<hr>
<p>
	<strong>ملاحظة: العلاقة بين <code>submit</code> و<code>click</code></strong>
</p>

<p>
	عندما تُرسَل الاستمارة باستخدام المفتاح <code>Enter</code> في حقل المُدخل، فإنّ الحدث <code>click</code> يقع على العنصر <code>&lt;input type="submit"‎&gt;</code>. هذا طريف، إذ لم يكن هناك نقرٌ على الإطلاق. يمكن تجربة ذلك بواسطة هذه الشيفرة:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2737_9" style="">
<span class="tag">&lt;form</span><span class="pln"> </span><span class="atn">onsubmit</span><span class="pun">=</span><span class="atv">"</span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">false</span><span class="atv">"</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">size</span><span class="pun">=</span><span class="atv">"30"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"Focus here and press enter"</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">"submit"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"Submit"</span><span class="pln"> </span><span class="atn">onclick</span><span class="pun">=</span><span class="atv">"</span><span class="pln">alert</span><span class="pun">(</span><span class="str">'click'</span><span class="pun">)</span><span class="atv">"</span><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;/form&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1617047141="1" data-ss1617047309="1" frameborder="no" height="168" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/vYgXWZw?height=168&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-forms-submit-ex2">See the Pen JS-forms-submit-ex2 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<hr>
<h2>
	التابع: submit
</h2>

<p>
	لتسليم استمارة ما إلى الخادم يدويّا، يمكننا استدعاء <code>form.submit()‎</code>. لا يتولّد الحدث <code>submit</code> بذلك، لأنّ المبرمج إذا استدعى<code>form.submit()‎</code>، يُفترض أنّ السكربت قد أدّى كلّ المعالجة المطلوبة. يُستخدم ذلك أحيانا لإنشاء وإرسال الاستمارة يدويّا، هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2737_11" style="">
<span class="pln">let form </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">'form'</span><span class="pun">);</span><span class="pln">
form</span><span class="pun">.</span><span class="pln">action </span><span class="pun">=</span><span class="pln"> </span><span class="str">'https://google.com/search'</span><span class="pun">;</span><span class="pln">
form</span><span class="pun">.</span><span class="pln">method </span><span class="pun">=</span><span class="pln"> </span><span class="str">'GET'</span><span class="pun">;</span><span class="pln">

form</span><span class="pun">.</span><span class="pln">innerHTML </span><span class="pun">=</span><span class="pln"> </span><span class="str">'&lt;input name="q" value="test"&gt;'</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">body</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">form</span><span class="pun">);</span><span class="pln">

form</span><span class="pun">.</span><span class="pln">submit</span><span class="pun">();</span></pre>

<h2>
	التمارين
</h2>

<h3>
	استمارة في نافذة منبثقة شرطية (modal)
</h3>

<p>
	<em>اﻷهميّة: 5</em>
</p>

<p>
	أنشئ الدالّة <code>showPrompt(html, callback)‎</code> التي تُظهر استمارة فيها الرسالة <code>html</code> وحقل مُدخلٍ والأزار <code>OK/CANCEL</code>.
</p>

<ul>
<li>
		يجب أن يكتب المستخدم شيئا ما في الحقل النصّيّ ثمّ يضغط على المفتاح <code>Enter</code> أو الزرّ OK، ليُستدعى <code>callback(value)‎</code> بالقيمة التي أُدخلت.
	</li>
	<li>
		وإلاّ فإذا ضغط المستخدم على المفتاح <code>Esc</code> أو الزرّ CANCEL، يُستدعى <code>callback(null)‎</code>
	</li>
</ul>
<p>
	في كلتا الحالتين تنتهي عمليّة الإدخال وتُزال الاستمارة.
</p>

<p>
	المتطلّبات:
</p>

<ul>
<li>
		يجب أن تكون الاستمارة في وسط النافذة.
	</li>
	<li>
		الاستمارة هي <em>نافذة منبثقة شرطيّة</em>. بعبارة أخرى، ليس من الممكن التفاعل مع بقيّة الصفحة إلى أن يغلقها المستخدم.
	</li>
	<li>
		عندما تظهر الاستمارة، يجب أن يكون التركيز داخل العنصر <code>&lt;input&gt;</code> للمستخدم.
	</li>
	<li>
		يجب أن تعمل المفاتيح <code>Tab</code>/<code>Shift+Tab</code> على نقل التركيز بين حقول الاستمارة، لكن لا تسمح بمغادرته إلى عناصر الصفحة الأخرى.
	</li>
</ul>
<p>
	مثال عن كيفيّة الاستخدام:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2737_13" style="">
<span class="pln">showPrompt</span><span class="pun">(</span><span class="str">"Enter something&lt;br&gt;...smart :)"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</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">
  alert</span><span class="pun">(</span><span class="pln">value</span><span class="pun">);</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	يمكن مشاهدة النتيجة من <a data-ss1617047141="1" data-ss1617047309="1" href="https://en.js.cx/task/modal-dialog/solution/" rel="external nofollow">هنا</a>.
</p>

<p>
	<strong>ملاحظة</strong>: في المستند المصدريّ، يوجد HTML/CSS لاستمارة بتموضع ثابت، وترجع لك مهمّة جعلها نافذة منبثقة شرطيّة.
</p>

<p>
	<a data-ss1617047141="1" data-ss1617047309="1" href="https://plnkr.co/edit/uZtz29nBZSMuAixB?p=preview" rel="external nofollow">أنجز التمرين في البيئة التجريبيّة</a>.
</p>

<h3>
	الحل
</h3>

<p>
	يُمكن إنجاز نافذة منبثقة شرطيّة بواسط عنصر <code>&lt;div id="cover-div"‎&gt;</code> نصف شفّاف، يغطّي كامل النافذة، هكذا:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_2737_15" style="">
<span class="com">#cover-div {</span><span class="pln">
  position</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">fixed</span><span class="pun">;</span><span class="pln">
  top</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  left</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
  z</span><span class="pun">-</span><span class="pln">index</span><span class="pun">:</span><span class="pln"> </span><span class="lit">9000</span><span class="pun">;</span><span class="pln">
  width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">%;</span><span class="pln">
  height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">100</span><span class="pun">%;</span><span class="pln">
  background</span><span class="pun">-</span><span class="pln">color</span><span class="pun">:</span><span class="pln"> gray</span><span class="pun">;</span><span class="pln">
  opacity</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0.3</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	ولأنّ هذا الـ<code>&lt;div&gt;</code> يغطّي كلّ شيء، فإنّه هو الذي يتلقّى جميع النقرات، وليست الصفحة التي تحته. يمكننا أيضا منع تمرير الصفحة بوضع <code>body.style.overflowY='hidden'‎</code>.
</p>

<p>
	يجب أن لا تكون الاستمارة داخل <code>&lt;div&gt;</code>، ولكن بجانبه، لأنّنا لا نريد أن تكون له الشفافيّة <code>opacity</code>.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a data-ss1617047141="1" data-ss1617047309="1" href="https://javascript.info/forms-submit" rel="external nofollow">Forms: event and method submit</a> من سلسلة <a data-ss1617047141="1" data-ss1617047309="1" href="https://javascript.info/ui" rel="external nofollow">Browser: Document, Events, Interfaces</a> لصاحبها Ilya Kantor
</p>
]]></description><guid isPermaLink="false">1196</guid><pubDate>Thu, 22 Apr 2021 13:00:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x623;&#x62D;&#x62F;&#x627;&#x62B; &#x627;&#x644;&#x645;&#x62A;&#x639;&#x644;&#x642;&#x629; &#x628;&#x627;&#x644;&#x646;&#x635;&#x648;&#x635;: &#x627;&#x644;&#x646;&#x633;&#x62E; &#x648;&#x627;&#x644;&#x644;&#x635;&#x642; &#x648;&#x627;&#x644;&#x642;&#x635; &#x648;&#x627;&#x644;&#x643;&#x62A;&#x627;&#x628;&#x629; &#x648;&#x627;&#x644;&#x62A;&#x639;&#x62F;&#x64A;&#x644; &#x648;&#x645;&#x639;&#x627;&#x644;&#x62C;&#x62A;&#x647;&#x627; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%A3%D8%AD%D8%AF%D8%A7%D8%AB-%D8%A7%D9%84%D9%85%D8%AA%D8%B9%D9%84%D9%82%D8%A9-%D8%A8%D8%A7%D9%84%D9%86%D8%B5%D9%88%D8%B5-%D8%A7%D9%84%D9%86%D8%B3%D8%AE-%D9%88%D8%A7%D9%84%D9%84%D8%B5%D9%82-%D9%88%D8%A7%D9%84%D9%82%D8%B5-%D9%88%D8%A7%D9%84%D9%83%D8%AA%D8%A7%D8%A8%D8%A9-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%AF%D9%8A%D9%84-%D9%88%D9%85%D8%B9%D8%A7%D9%84%D8%AC%D8%AA%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-r1195/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/60622cf3c3f61_-----------.png.34197455b0e31b3976ea4068ab057433.png" /></p>

<p>
	لنتناول مختلف اﻷحداث التي ترافق تحديث المُعطيات.
</p>

<h2>
	الحدث: change
</h2>

<p>
	يقع الحدث <code>change</code> عند تمام تغيّر العنصر.
</p>

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

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5196_7" style="">
<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">onchange</span><span class="pun">=</span><span class="atv">"</span><span class="pln">alert</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">value</span><span class="pun">)</span><span class="atv">"</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">"button"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"Button"</span><span class="tag">&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1617046761="1" data-ss1617047004="1" frameborder="no" height="154" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/bGgwYgE?height=154&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-events-change-input-ex1">See the Pen JS-events-change-input-ex1 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	بالنسبة للعناصر الأخرى <code>select</code> و<code>input type=checkbox/radio</code> ، فإنّ الحدث يقع بعد تغيّر التحديد مباشرة:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5196_9" style="">
<span class="tag">&lt;select</span><span class="pln"> </span><span class="atn">onchange</span><span class="pun">=</span><span class="atv">"</span><span class="pln">alert</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">value</span><span class="pun">)</span><span class="atv">"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;option</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">""</span><span class="tag">&gt;</span><span class="pln">Select something</span><span class="tag">&lt;/option&gt;</span><span class="pln">
  </span><span class="tag">&lt;option</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"1"</span><span class="tag">&gt;</span><span class="pln">Option 1</span><span class="tag">&lt;/option&gt;</span><span class="pln">
  </span><span class="tag">&lt;option</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"2"</span><span class="tag">&gt;</span><span class="pln">Option 2</span><span class="tag">&lt;/option&gt;</span><span class="pln">
  </span><span class="tag">&lt;option</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"3"</span><span class="tag">&gt;</span><span class="pln">Option 3</span><span class="tag">&lt;/option&gt;</span><span class="pln">
</span><span class="tag">&lt;/select&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1617046761="1" data-ss1617047004="1" frameborder="no" height="203" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/ExZgbZE?height=203&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-events-change-input-ex2">See the Pen JS-events-change-input-ex2 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<h2>
	الحدث: input
</h2>

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

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5196_11" style="">
<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">"input"</span><span class="tag">&gt;</span><span class="pln"> oninput: </span><span class="tag">&lt;span</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"result"</span><span class="tag">&gt;&lt;/span&gt;</span><span class="pln">
</span><span class="tag">&lt;script&gt;</span><span class="pln">
  input</span><span class="pun">.</span><span class="pln">oninput </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    result</span><span class="pun">.</span><span class="pln">innerHTML </span><span class="pun">=</span><span class="pln"> input</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="tag">&lt;/script&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1617046761="1" data-ss1617047004="1" frameborder="no" height="152" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/ExZgbWL?height=152&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-events-change-input-ex3">See the Pen JS-events-change-input-ex3 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	إذا أردنا معالجة جميع التغيّرات في <code>&lt;input&gt;</code> فإنّ هذا الحدث هو الخيار اﻷمثل.
</p>

<p>
	في المقابل، لا يقع <code>input</code> عند أفعال لوحة المفاتيح أو غيرها من اﻷفعال التي لا تؤدّي إلى تغيّر القيمة، مثل الضغط على مفاتيح اﻷسهم <code>⇦</code> و<code>⇨</code> في المُدخل.
</p>

<hr>
<p>
	<strong>ملاحظة: لا يمكن منع أيّ شيء في <code>oninput</code></strong>
</p>

<p>
	يقع الحدث <code>input</code> بعد تغيّر القيمة، ولذا لا يمكننا استعمال <code>event.preventDefault()‎</code> هناك -- قد فات الآوان، لن يكون لذلك أيّ أثر.
</p>

<hr>
<h2>
	الأحداث: cut وcopy وpaste
</h2>

<p>
	تقع هذه اﻷحداث عند قصّ/نسخ/لصق قيمة ما، وهي تنتمي إلى الصنف <a data-ss1617046761="1" data-ss1617047004="1" href="https://www.w3.org/TR/clipboard-apis/#clipboard-event-interfaces" rel="external nofollow">ClipboardEvent</a>، وتُمكّن من الوصول إلى المُعطيات التي قُصّت/أُلصقت. يمكننا أيضا استخدام <code>event.preventDefault()‎</code> معها لإلغاء الفعل، فلا يتمّ بذلك نسخ/لصق أيّ شيء. على سبيل المثال، تمنع الشيفرة أدناه وقوع أيّ من هذه اﻷحداث، وتُظهر ماذا نحاول قصّه/نسخه/لصقه:
</p>

<pre class="ipsCode">
&lt;input type="text" id="input"&gt;
&lt;script&gt;
  input.oncut = input.oncopy = input.onpaste = function(event) {
    alert(event.type + ' - ' + event.clipboardData.getData('text/plain'));
    return false;
  };
&lt;/script&gt;
</pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1617046761="1" data-ss1617047004="1" frameborder="no" height="153" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/gOgwXWO?height=153&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-events-change-input-ex4">See the Pen JS-events-change-input-ex4 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	يُرجى التنبّه إلى أنّه لا يمكن نسخ/لصق النصّ فقط، بل أيّ شيء، مثل نسخ ملفّ في مدير الملفّات في نظام التشغيل، ولصقه. هذا لأنّ <code>clipboardData</code> تتضمّن الواجهة <code>DataTransfer</code>، التي تُستخدم في السحب والإفلات والنسخ/اللصق. تُعدّ هذه الأمور خارجة قليلا عن موضوعنا الآن، لكن يمكنك أن تجد التوابع الخاصّة بها <a data-ss1617046761="1" data-ss1617047004="1" href="https://www.w3.org/TR/clipboard-apis/#dfn-datatransfer" rel="external nofollow">في المواصفة</a>. .
</p>

<hr>
<p>
	<strong>تنبيه: ClipboardAPI: قيود تتعلّق بسلامة المستخدم</strong>
</p>

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

<p>
	ويُمنع كذلك توليد أحداث "مخصّصّة" للحافظة باستخدام <code>dispatchEvent</code> في جميع المتصفّحات باستثناء Firefox.
</p>

<hr>
<h2>
	الملخص
</h2>

<p>
	أحداث تغيير المعطيات:
</p>

<table>
<thead><tr>
<th>
				الحدث
			</th>
			<th>
				الوصف
			</th>
			<th>
				الخصائص
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				<code>change</code>
			</td>
			<td>
				عند تغيّر قيمة ما
			</td>
			<td>
				بالنسبة للمُدخلات النصّيّة، يقع عند فقدانها التركيز.
			</td>
		</tr>
<tr>
<td>
				<code>input</code>
			</td>
			<td>
				عند كلّ تغيّر في المُدخلات النصّية
			</td>
			<td>
				يقع مباشرة، على خلاف <code>change</code>.
			</td>
		</tr>
<tr>
<td>
				<code>cut/copy/paste</code>
			</td>
			<td>
				عند أفعال القصّ/النسخ/اللصق.
			</td>
			<td>
				يمكن منع الفعل، وتتيح الخاصّيّةُ <code>event.clipboardData</code> إمكانيّة القراءة من/الكتابة في الحافظة.
			</td>
		</tr>
</tbody>
</table>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;
    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<h2>
	التمارين
</h2>

<h3>
	حاسبة الإيداع
</h3>

<p>
	<em>اﻷهميّة: 5</em>
</p>

<p>
	أنشئ واجهة تُمكّن من إدخال القيمة المودعة في البنك إضافة إلى النسبة، ثمّ تحسب كم ستصبح القيمة بعد مدّة مُعيّنة من الزمن. كما في المثال من <a data-ss1617046761="1" data-ss1617047004="1" href="https://en.js.cx/task/deposit-calculator/solution/" rel="external nofollow">هنا</a>.
</p>

<p>
	يجب أن يُعالج أيّ تغيّر في المُدخل مباشرة.
</p>

<p>
	المعادلة هي كالتالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_5196_15" style="">
<span class="com">// القيمة الابتدائيّة للنقود :initial</span><span class="pln">
</span><span class="com">// تعني 0.05 مثلا، %5 سنويّا :interest</span><span class="pln">
</span><span class="com">// كم عاما من الانتظار :years</span><span class="pln">
let result </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">round</span><span class="pun">(</span><span class="pln">initial </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"> interest </span><span class="pun">*</span><span class="pln"> years</span><span class="pun">));</span></pre>

<p>
	<a data-ss1617046761="1" data-ss1617047004="1" href="https://plnkr.co/edit/m91aRhjRUmEfzctR?p=preview" rel="external nofollow">أنجز التمرين في البيئة التجريبيّة</a>
</p>

<h3>
	الحل
</h3>

<p>
	<a data-ss1617046761="1" data-ss1617047004="1" href="https://plnkr.co/edit/RDYGjvI0orX6oZfU?p=preview" rel="external nofollow">افتح الحلّ في البيئة التجريبيّة</a>
</p>

<p>
	ترجمة -وبتصرف- للمقال <a data-ss1617046761="1" data-ss1617047004="1" href="https://javascript.info/events-change-input" rel="external nofollow">Events: change, input, cut, copy, paste</a> من سلسلة <a data-ss1617046761="1" data-ss1617047004="1" href="https://javascript.info/ui" rel="external nofollow">Browser: Document, Events, Interfaces</a> لصاحبها Ilya Kantor
</p>
]]></description><guid isPermaLink="false">1195</guid><pubDate>Mon, 19 Apr 2021 13:02:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x631;&#x643;&#x64A;&#x632; &#x639;&#x644;&#x649; &#x639;&#x646;&#x627;&#x635;&#x631; &#x627;&#x644;&#x627;&#x633;&#x62A;&#x645;&#x627;&#x631;&#x627;&#x62A; &#x627;&#x644;&#x625;&#x644;&#x643;&#x62A;&#x631;&#x648;&#x646;&#x64A;&#x629; &#x648;&#x627;&#x644;&#x62A;&#x646;&#x642;&#x644; &#x628;&#x64A;&#x646;&#x647;&#x627; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%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%A7%D9%84%D8%A7%D8%B3%D8%AA%D9%85%D8%A7%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D8%A5%D9%84%D9%83%D8%AA%D8%B1%D9%88%D9%86%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AA%D9%86%D9%82%D9%84-%D8%A8%D9%8A%D9%86%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-r1194/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/60622b7c3b8c5_---------.png.82a0ff6742a86dd6ddbb35fdd3850f86.png" /></p>

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

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

<p>
	قد تكون لحظة فقدان التركيز (أو ما يُعرف بـ "blur" أي "الضبابيّة") أكثر أهميّة. وتحصل عندما ينقر المستخدم في مكان آخر أو يضغط على المفتاح <code>Tab</code> لينتقل إلى الحقل الموالي في الاستمارة، أو غير ذلك من الوسائل الممكنة الأخرى.
</p>

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

<h2>
	الأحداث focus/blur
</h2>

<p>
	يُستدعى الحدث <code>focus</code> عند التركيز على العنصر، ويُستدعى <code>blur</code> عندما يفقد العنصر التركيز. لنستخدم هذه الأحداث لغرض التحقّق من القيم المُدخَلة في حقول المدخلات. إليك المثال التالي:
</p>

<ul>
<li>
		يتحقّق معالج <code>blur</code> من أنّ الحقل قد أُدخل فيه بريد الكترونيّ، فإذا لم يكن قد أُدخل فيه بريد الكترونيّ، يظهر رسالة خطأ.
	</li>
	<li>
		يُخفي معالج <code>focus</code> رسالة الخطأ (ليجري التحقّق عند <code>blur</code> مرّة أخرى).
	</li>
</ul>
<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5500_7" style="">
<span class="tag">&lt;style&gt;</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">invalid </span><span class="pun">{</span><span class="pln"> border</span><span class="pun">-</span><span class="pln">color</span><span class="pun">:</span><span class="pln"> red</span><span class="pun">;</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
  </span><span class="com">#error</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> color</span><span class="pun">:</span><span class="pln"> red </span><span class="pun">}</span><span class="pln">
</span><span class="tag">&lt;/style&gt;</span><span class="pln">

Your email please: </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">"input"</span><span class="tag">&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">"error"</span><span class="tag">&gt;&lt;/div&gt;</span><span class="pln">

</span><span class="tag">&lt;script&gt;</span><span class="pln">
input</span><span class="pun">.</span><span class="pln">onblur </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</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">input</span><span class="pun">.</span><span class="pln">value</span><span class="pun">.</span><span class="pln">includes</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="com">// ليس بريدا الكترونيّا</span><span class="pln">
    input</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">'invalid'</span><span class="pun">);</span><span class="pln">
    error</span><span class="pun">.</span><span class="pln">innerHTML </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Please enter a correct email.'</span><span class="pln">
  </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">onfocus </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</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="kwd">this</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">'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">
    </span><span class="kwd">this</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">'invalid'</span><span class="pun">);</span><span class="pln">
    error</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">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">};</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1617046390="1" data-ss1617046629="1" frameborder="no" height="164" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/gOgwXpN?height=164&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-focus-blur-ex1">See the Pen JS-focus-blur-ex1 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	تُمكّننا لغة HTML الحديثة من إجراء عدّة تحقّقات بواسطة سمات العناصر input مثل <code>required</code> و<code>pattern</code> وغيرها. وهي أحيانا كلّ ما نحتاجه. يُمكن أن تُستخدم جافاسكربت إذا أردنا المزيد من المرونة. ويمكننا أيضا إرسال القيمة المُعدّلة إلى الخادم تلقائيّا إذا كانت صحيحة.
</p>

<h2>
	التوابع focus/blur
</h2>

<p>
	تجعل التوابعُ <code>elem.focus()‎</code> و<code>elem.blur()‎</code> العنصر يكتسب/يفقد التركيز. على سبيل المثال، يمكن أن نمنع الزائر من مغادرة حقل المُدخَل إذا كانت القيمة غير صحيحة:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5500_9" style="">
<span class="tag">&lt;style&gt;</span><span class="pln">
  </span><span class="pun">.</span><span class="pln">error </span><span class="pun">{</span><span class="pln">
    background</span><span class="pun">:</span><span class="pln"> red</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="tag">&lt;/style&gt;</span><span class="pln">

Your email please: </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">"input"</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">style</span><span class="pun">=</span><span class="atv">"</span><span class="pln">width</span><span class="pun">:</span><span class="lit">220px</span><span class="atv">"</span><span class="pln"> </span><span class="atn">placeholder</span><span class="pun">=</span><span class="atv">"make email invalid and try to focus here"</span><span class="tag">&gt;</span><span class="pln">

</span><span class="tag">&lt;script&gt;</span><span class="pln">
  input</span><span class="pun">.</span><span class="pln">onblur </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</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="kwd">this</span><span class="pun">.</span><span class="pln">value</span><span class="pun">.</span><span class="pln">includes</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="com">// ليس بريدا الكترونيّا</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">classList</span><span class="pun">.</span><span class="pln">add</span><span class="pun">(</span><span class="str">"error"</span><span class="pun">);</span><span class="pln">
      </span><span class="com">// وأرجع التركيز كما كان...</span><span class="pln">
      input</span><span class="pun">.</span><span class="pln">focus</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">this</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">"error"</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="tag">&lt;/script&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1617046390="1" data-ss1617046629="1" frameborder="no" height="177" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/rNjMYOz?height=177&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-focus-blur-ex2">See the Pen JS-focus-blur-ex2 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	يعمل ذلك على جميع المتصفّحات باستثناء فايرفكس (<a data-ss1617046390="1" data-ss1617046629="1" href="https://bugzilla.mozilla.org/show_bug.cgi?id=53579" rel="external nofollow">خطأ برمجيّ</a>).
</p>

<p>
	إذا أدخلنا شيئا ما في الحقل ثمّ حاولنا استعمال المفتاح <code>Tab</code> أو النقر خارج الـ<code>&lt;input&gt;</code>، فإنّ <code>onblur</code> يعمل على استعادة التركيز.
</p>

<p>
	يُرجى التنبّه أنّه لا يمكننا "منع فقدان التركيز" بواسطة استدعاء <code>event.preventDefault()‎</code> عند <code>onblur</code>، لأنّ <code>onblur</code> يعمل <em>بعد</em> أن يفقد العنصر التركيز.
</p>

<hr>
<p>
	<strong>تنبيه: فقدان التركيز الناشئ من جافاسكربت</strong>
</p>

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

<ul>
<li>
		يجلب <code>alert</code> التركيز إليه، وبذلك فهو يتسبّب في فقدان التركيز عن العنصر (الحدث <code>blur</code>)، وعندما يُزال <code>alert</code> فإن التركيز يرجع حيث كان (حدث <code>focus</code>).
	</li>
	<li>
		إذا حُذف عنصر ما من DOM، فإنّ ذلك يتسبّب في فقدان التركيز أيضا. لكن إذا أُدرج ثانية، لا يرجع معه التركيز.
	</li>
</ul>
<p>
	تتسبّب هذه الميزات أحيانا في سوء سلوك معالجات <code>focus/blur</code> -- تشتغل دون الحاجة إليها.
</p>

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

<hr>
<h2>
	تمكين التركيز على أيّ عنصر: tabindex
</h2>

<p>
	هناك العديد من العناصر التي لا تدعم التركيز افتراضيًّا. تختلف القائمة قليلا بين المتصفّحات، لكنّ هناك شيئا ثابتا: يكون دعم <code>focus/blur</code> مضمونا بالنسبة للعناصر التي يمكن للمستخدم أن يتفاعل معها، مثل <code>&lt;button&gt;</code> و<code>&lt;input&gt;</code> و<code>&lt;select&gt;</code> و<code>&lt;a&gt;</code> وما إلى ذلك.
</p>

<p>
	في المقابل، لا تقبل العناصر التي أوجدت بغرض التنسيق، مثل <code>&lt;div&gt;</code> و<code>&lt;span&gt;</code> و<code>&lt;table&gt;</code>، التركيز افتراضيًّا. لا يعمل التابع <code>elem.focus()‎</code> معها، ولا تقع عليها الأحداث <code>focus/blur</code> أبدا.
</p>

<p>
	يمكن تغيير ذلك بواسطة السمة <code>tabindex</code> في HTML.
</p>

<p>
	يصير أيّ عنصر قابلا للتركيز إذا كانت له السّمة <code>tabindex</code>. تمثّل قيمة السمة رقم ترتيب العنصر بين العناصر عند استخدام المفتاح <code>Tab</code> (أو شيء من هذا القبيل) للتنقّل بينها.
</p>

<p>
	هذا يعني لو أنّ لدينا عنصرين، أحدهما له <code>tabindex="1"‎</code>، والثاني له <code>tabindex="2"‎</code>، فإنّ ضغط المفتاح <code>Tab</code> في العنصر الأوّل ينقل التركيز إلى العنصر الثاني.
</p>

<p>
	يكون ترتيب التنقّل على النحو التالي: العناصر التي لها <code>tabindex</code> من 1 فصاعدا تأتي أوّلا (حسب ترتيب <code>tabindex</code>)، ثمّ العناصر التي ليس لها <code>tabindex</code> (أي عناصر <code>&lt;input&gt;</code> العاديّة).
</p>

<p>
	يُنتقل إلى العناصر التي لها نفس قيمة <code>tabindex</code> حسب ترتيبها في الشيفرة المصدريّة للمستند (الترتيب الافتراضيّ).
</p>

<p>
	وهناك قيمتان خاصّتان:
</p>

<p>
	<code>tabindex="0"‎</code> تجعل العنصر ضمن العناصر التي ليس لها <code>tabindex</code>. بمعنى، عند التنقّل بين العناصر، تأتي العناصر التي لها <code>tabindex=0</code> بعد العناصر التي لها <code>tabindex ≥ 1</code>. تُستخدم عادة لجعل عنصر ما قابلا للتركيز، مع المحافظة على ترتيب التنقّل الافتراضيّ. لجعل العنصر جزءًا من الاستمارة ومضاهيا لـ <code>&lt;input&gt;</code>. <code>tabindex="-1"‎</code> تمكّن من التركيز برمجيّا فقط على العنصر. يتجاهل المفتاح <code>Tab</code> مثل هذه العناصر، لكنّ التابع <code>elem.focus()‎</code> يعمل معها.
</p>

<p>
	على سبيل المثال، إليك القائمة التالية:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5500_11" style="">
<span class="pln">Click the first item and press Tab. Keep track of the order. Please note that many subsequent Tabs can move the focus out of the iframe in the example.
</span><span class="tag">&lt;ul&gt;</span><span class="pln">
  </span><span class="tag">&lt;li</span><span class="pln"> </span><span class="atn">tabindex</span><span class="pun">=</span><span class="atv">"1"</span><span class="tag">&gt;</span><span class="pln">One</span><span class="tag">&lt;/li&gt;</span><span class="pln">
  </span><span class="tag">&lt;li</span><span class="pln"> </span><span class="atn">tabindex</span><span class="pun">=</span><span class="atv">"0"</span><span class="tag">&gt;</span><span class="pln">Zero</span><span class="tag">&lt;/li&gt;</span><span class="pln">
  </span><span class="tag">&lt;li</span><span class="pln"> </span><span class="atn">tabindex</span><span class="pun">=</span><span class="atv">"2"</span><span class="tag">&gt;</span><span class="pln">Two</span><span class="tag">&lt;/li&gt;</span><span class="pln">
  </span><span class="tag">&lt;li</span><span class="pln"> </span><span class="atn">tabindex</span><span class="pun">=</span><span class="atv">"-1"</span><span class="tag">&gt;</span><span class="pln">Minus one</span><span class="tag">&lt;/li&gt;</span><span class="pln">
</span><span class="tag">&lt;/ul&gt;</span><span class="pln">

</span><span class="tag">&lt;style&gt;</span><span class="pln">
  li </span><span class="pun">{</span><span class="pln"> 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="pun">:</span><span class="pln">focus </span><span class="pun">{</span><span class="pln"> outline</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1px</span><span class="pln"> dashed green</span><span class="pun">;</span><span class="pln"> </span><span class="pun">}</span><span class="pln">
</span><span class="tag">&lt;/style&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1617046390="1" data-ss1617046629="1" frameborder="no" height="257" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/JjEROGZ?height=257&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-focus-blur-ex3">See the Pen JS-focus-blur-ex3 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	لو نقرنا على العنصر الأوّل ثم ضغطنا على <code>Tab</code>، سيكون الترتيب هكذا: <code>1 - 2 - 0</code>. عادة، لا تدعم <code>&lt;li&gt;</code> التركيز، لكنّ <code>tabindex</code> يجعل ذلك ممكنا كلّيّا، بما في ذلك الأحداث وإمكانيّة التنسيق بواسطة <code>focus:</code>.
</p>

<hr>
<p>
	<strong>ملاحظة: تعمل الخاصيّة <code>elem.tabIndex</code> أيضا</strong>
</p>

<p>
	يمكننا إضافة <code>tabindex</code> من خلال جافاسكربت باستخدام الخاصيّة <code>elem.tabIndex</code>. يكون لذلك نفس الأثر.
</p>

<hr>
<h2>
	التفويض: focusin/focusout
</h2>

<p>
	لا تنتشر الأحداث <code>focus</code> و<code>blur</code> نحو الأعلى. على سبيل المثال، لا يمكننا إسناد <code>onfocus</code> إلى <code>&lt;form&gt;</code> لإبرازه، هكذا:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5500_13" style="">
<span class="com">&lt;!-- عند التركيز على الاستمارة، أضف الصنف --&gt;</span><span class="pln">
</span><span class="tag">&lt;form</span><span class="pln"> </span><span class="atn">onfocus</span><span class="pun">=</span><span class="atv">"</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">className</span><span class="pun">=</span><span class="str">'focused'</span><span class="atv">"</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">name</span><span class="pun">=</span><span class="atv">"name"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"Name"</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">name</span><span class="pun">=</span><span class="atv">"surname"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"Surname"</span><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;/form&gt;</span><span class="pln">

</span><span class="tag">&lt;style&gt;</span><span class="pln"> </span><span class="pun">.</span><span class="pln">focused </span><span class="pun">{</span><span class="pln"> outline</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1px</span><span class="pln"> solid red</span><span class="pun">;</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="tag">&lt;/style&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1617046390="1" data-ss1617046629="1" frameborder="no" height="163" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/PoWGONY?height=163&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-focus-blur-ex4">See the Pen JS-focus-blur-ex4 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	لا يعمل المثال أعلاه، لأنّه عندما يركّز المستخدم على <code>&lt;input&gt;</code>، فإنّ الحدث <code>focus</code> يقع على ذلك المُدخَل فقط. لا ينتشر نحو الأعلى. وبذلك لا يشتغل <code>form.onfocus</code> أبدا.
</p>

<p>
	يوجد حلّان.
</p>

<p>
	أوّلا، هناك ميزة تاريخيّة طريفة: لا تنتشر الأحداث <code>focus/blur</code> نحو الأعلى، لكنّها تنتشر نحو الأسفل.
</p>

<p>
	سيعمل هذا:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5500_15" style="">
<span class="tag">&lt;form</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"form"</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">name</span><span class="pun">=</span><span class="atv">"name"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"Name"</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">name</span><span class="pun">=</span><span class="atv">"surname"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"Surname"</span><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;/form&gt;</span><span class="pln">

</span><span class="tag">&lt;style&gt;</span><span class="pln"> </span><span class="pun">.</span><span class="pln">focused </span><span class="pun">{</span><span class="pln"> outline</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1px</span><span class="pln"> solid red</span><span class="pun">;</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="tag">&lt;/style&gt;</span><span class="pln">

</span><span class="tag">&lt;script&gt;</span><span class="pln">
  </span><span class="com">// (true ضع المعالج في مرحلة الانتشار نحو اﻷسفل (قيمة الوسيط اﻷخير </span><span class="pln">
  form</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"focus"</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"> form</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">'focused'</span><span class="pun">),</span><span class="pln"> </span><span class="kwd">true</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">"blur"</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"> form</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">'focused'</span><span class="pun">),</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">);</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1617046390="1" data-ss1617046629="1" frameborder="no" height="150" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/zYNKPqp?height=150&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-focus-blur-ex5">See the Pen JS-focus-blur-ex5 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	ثانيا، توجد هناك الأحداث <code>focusin</code> و<code>focusout</code> التي هي مثل <code>focus/blur</code> تماما غير أنّها تنتشر نحو الأعلى.
</p>

<p>
	تنبّه أنّه يجب إسنادها باستخدام <code>elem.addEventListener</code>، وليس <code>on&lt;event&gt;‎</code>.
</p>

<p>
	وبذلك هذا هو الحلّ البديل:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_5500_17" style="">
<span class="tag">&lt;form</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"form"</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">name</span><span class="pun">=</span><span class="atv">"name"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"Name"</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">name</span><span class="pun">=</span><span class="atv">"surname"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"Surname"</span><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;/form&gt;</span><span class="pln">

</span><span class="tag">&lt;style&gt;</span><span class="pln"> </span><span class="pun">.</span><span class="pln">focused </span><span class="pun">{</span><span class="pln"> outline</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1px</span><span class="pln"> solid red</span><span class="pun">;</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="tag">&lt;/style&gt;</span><span class="pln">

</span><span class="tag">&lt;script&gt;</span><span class="pln">
  form</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">"focusin"</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"> form</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">'focused'</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">"focusout"</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"> form</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">'focused'</span><span class="pun">));</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1617046390="1" data-ss1617046629="1" frameborder="no" height="162" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/qBRaVNy?height=162&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-focus-blur-ex6">See the Pen JS-focus-blur-ex6 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

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

<p>
	تقع الأحداث <code>focus</code> و<code>blur</code> على العنصر عندما يكتسب/يفقد التركيز.
</p>

<p>
	خصائصها هي:
</p>

<ul>
<li>
		لا تنتشر نحو الأعلى. يمكن استخدام حالة الانتشار نحو الأسفل بدل ذلك أو <code>focusin/focusout</code>.
	</li>
	<li>
		لا تدعم معظم العناصر التركيز افتراضيًّا. استخدم <code>tabindex</code> لجعل أيّ شيء قابلا للتركيز.
	</li>
</ul>
<p>
	يوجد العنصر المُركّز عليه حاليا في <code>document.activeElement</code>.
</p>

<h2>
	التمارين
</h2>

<h3>
	عنصر div قابل للتحرير
</h3>

<p>
	<em>الأهميّة:5</em>
</p>

<p>
	أنشئ عنصر<code>&lt;div&gt;</code> بحيث يتحوّل إلى مساحة نصّيّة <code>&lt;textarea&gt;</code> عند النقر عليه.
</p>

<p>
	تُمكّن المساحة النصّيّة من التعديل على شيفرة HTML التي بداخل العنصر <code>&lt;div&gt;</code>.
</p>

<p>
	عندما يضغط المستخدم على المفتاح <code>Enter</code> أو عندما تفقد المساحة النصّيّة التركيز، فإنّها تعود <code>&lt;div&gt;</code>، ويحلّ محتواها محلّ شيفرة HTML التي كانت بداخل <code>&lt;div&gt;</code>.
</p>

<p>
	كما هو مبيّن من <a data-ss1617046390="1" data-ss1617046629="1" href="https://en.js.cx/task/editable-div/solution/" rel="external nofollow">هنا</a>.
</p>

<p>
	<a data-ss1617046390="1" data-ss1617046629="1" href="https://plnkr.co/edit/ZjUep0nX4AjfVvcZ?p=preview" rel="external nofollow">أنجز التمرين في البيئة التجريبية</a>.
</p>

<h3>
	الحل
</h3>

<p>
	<a data-ss1617046390="1" data-ss1617046629="1" href="https://plnkr.co/edit/hi6zoBbDbBiSRftV?p=preview" rel="external nofollow">افتح الحلّ في البيئة التجريبية</a>.
</p>

<h3>
	التحرير في خانة الجدول عند النقر عليها
</h3>

<p>
	<em>الأهميّة: 5</em>
</p>

<p>
	اجعل خانات الجدول قابلة للتحرير عند النقر عليها.
</p>

<ul>
<li>
		عند النقر، يجب أن تصير الخانة "قابلة للتحرير" (تظهر مساحة نصّيّة داخلها)، ويمكننا عندها التعديل على HTML. يجب أن لا يكون هناك أي تغيّر في الحجم، تبقى جميع الأبعاد على حالها.
	</li>
	<li>
		تظهر الأزرار OK وCANCEL تحت الخانة لإتمام/إلغاء التحرير.
	</li>
	<li>
		يمكن التحرير في خانة واحدة فقط في نفس الوقت. عندما تكون خانة ما في "وضع التحرير"، تُتجاهل النقرات على الخانات الأخرى.
	</li>
	<li>
		قد يكون في الجدول العديد من الخانات. استخدم تفويض الأحداث.
	</li>
</ul>
<p>
	يمكن مشاهدة المثال من <a data-ss1617046390="1" data-ss1617046629="1" href="https://en.js.cx/task/edit-td-click/solution/" rel="external nofollow">هنا</a>.
</p>

<p>
	<a data-ss1617046390="1" data-ss1617046629="1" href="https://plnkr.co/edit/8svbkXOrvh6EAMhR?p=preview" rel="external nofollow">أنجز التمرين في البيئة التجريبية</a>
</p>

<h3>
	الحل
</h3>

<ol>
<li>
		عند النقر، استبدل <code>innerHTML</code> الخاص بالخانة بـ <code>&lt;textarea&gt;</code> بنفس الحجم وبدون إطار. يمكن استخدام جافاسكربت أو CSS لضبط الحجم الصحيح.
	</li>
	<li>
		اجعل <code>textarea.value</code> تساوي <code>td.innerHTML</code>.
	</li>
	<li>
		ركّز على المساحة النصّيّة.
	</li>
	<li>
		أظهر الأزرار OK/CANCEL تحت الخانة، وعالج النقر عليها.
	</li>
</ol>
<p>
	<a data-ss1617046390="1" data-ss1617046629="1" href="https://plnkr.co/edit/GJlVn2ir1dhNilFc?p=preview" rel="external nofollow">افتح الحل في البيئة التجريبية</a>.
</p>

<h3>
	التحكّم في الفأرة باستخدام لوحة المفاتيح
</h3>

<p>
	<em>الأهميّة: 4</em>
</p>

<p>
	ركّز على الفأرة، ثمّ استخدم مفاتيح الأسهم لتحريكها.
</p>

<p>
	كما في المثال من <a data-ss1617046390="1" data-ss1617046629="1" href="https://en.js.cx/task/keyboard-mouse/solution/" rel="external nofollow">هنا</a>.
</p>

<p>
	ملاحظة: لا تضع معالجات الأحداث في غير العنصر <code>‎#mouse</code>.
</p>

<p>
	ملاحظة أخرى: لا تغيّر HTML/CSS. يجب أن تكون الطريقة عامّة وتعمل مع أيّ عنصر كا.
</p>

<p>
	<a data-ss1617046390="1" data-ss1617046629="1" href="https://plnkr.co/edit/mWShSzKepRZFzHVn?p=preview" rel="external nofollow">أنجز التمرين في البيئة التجريبية</a>
</p>

<h3>
	الحل
</h3>

<p>
	يمكننا استخدام <code>mouse.onclick</code> لمعالجة النقر، وجعل الفأرة "قابلة للتحريك" بواسطة <code>position:fixed</code>، ثمّ استخدام <code>mouse.onkeydown</code> لمعالجة مفاتيح الأسهم.
</p>

<p>
	المزلق الوحيد هو أنّ <code>keydown</code> يقع فقط على العناصر التي فيها التركيز. لذا نحتاج إلى إضافة <code>tabindex</code> للعنصر. وبما أنّه يُمنع تغيير HTML، يمكننا استخدام الخاصيّة <code>mouse.tabIndex</code> لذلك.
</p>

<p>
	<strong>ملاحظة</strong>: يمكننا أيضا استبدال <code>mouse.onclick</code> بـ<code>mouse.onfocus</code>.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a data-ss1617046390="1" data-ss1617046629="1" href="https://javascript.info/focus-blur" rel="external nofollow">Focusing: focus/blur</a> من سلسلة <a data-ss1617046390="1" data-ss1617046629="1" href="https://javascript.info/ui" rel="external nofollow">Browser: Document, Events, Interfaces</a> لصاحبها Ilya Kantor
</p>
]]></description><guid isPermaLink="false">1194</guid><pubDate>Thu, 15 Apr 2021 13:08:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x627;&#x633;&#x62A;&#x645;&#x627;&#x631;&#x627;&#x62A; (forms) &#x641;&#x64A; &#x645;&#x62A;&#x635;&#x641;&#x62D; &#x627;&#x644;&#x648;&#x64A;&#x628; &#x648;&#x643;&#x64A;&#x641;&#x64A;&#x629; &#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639;&#x647;&#x627; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D9%85%D8%A7%D8%B1%D8%A7%D8%AA-forms-%D9%81%D9%8A-%D9%85%D8%AA%D8%B5%D9%81%D8%AD-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D9%88%D9%83%D9%8A%D9%81%D9%8A%D8%A9-%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9%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-r1193/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/606227ba62744_-(forms)--------.png.c9601a1b394183aa01adeef2ded62523.png" /></p>

<p>
	تمتلك الاستمارات وعناصر التحكّم فيها، مثل <code>&lt;input&gt;</code>، الكثير من الخاصيّات والأحداث الخاصّة بها. يساعد تعلّم هذه الخاصّيات والأحداث على التعامل مع الاستمارات براحة أكثر.
</p>

<h2>
	التنقل: الاستمارة والعناصر
</h2>

<p>
	تنتمي استمارات المستند إلى المجموعة الخاصّة <code>document.forms</code>، وهي ما يُطلق عليها "مجموعةً مُسمّاة (named collection)"، لأنّها مُرتّبة و مُسمّاة في آن واحد. يمكننا الحصول على استمارة ما في المستند إمّا بواسطة الرقم أو الاسم.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7528_11" style="">
<span class="pln">document</span><span class="pun">.</span><span class="pln">forms</span><span class="pun">.</span><span class="pln">my</span><span class="pun">;</span><span class="pln"> </span><span class="com">// the form with name="my"</span><span class="pln">
document</span><span class="pun">.</span><span class="pln">forms</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln"> </span><span class="com">// the first form in the document</span></pre>

<p>
	إذا كان لدينا استمارة ما، فإنّ جميع العناصر التي فيها تكون موجودة في المجموعة المُسمّاة <code>form.elements</code>. على سبيل المثال:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7528_15" style="">
<span class="tag">&lt;form</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"my"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"one"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"1"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"two"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"2"</span><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;/form&gt;</span><span class="pln">

</span><span class="tag">&lt;script&gt;</span><span class="pln">
  </span><span class="com">// الحصول على الاستمارة</span><span class="pln">
  let form </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">forms</span><span class="pun">.</span><span class="pln">my</span><span class="pun">;</span><span class="pln"> </span><span class="com">// &lt;form name="my"&gt; العنصر</span><span class="pln">

  </span><span class="com">// الحصول على العنصر</span><span class="pln">
  let elem </span><span class="pun">=</span><span class="pln"> form</span><span class="pun">.</span><span class="pln">elements</span><span class="pun">.</span><span class="pln">one</span><span class="pun">;</span><span class="pln"> </span><span class="com">// &lt;input name="one"&gt; العنصر</span><span class="pln">

  alert</span><span class="pun">(</span><span class="pln">elem</span><span class="pun">.</span><span class="pln">value</span><span class="pun">);</span><span class="pln"> </span><span class="com">// 1</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1617045273="1" data-ss1617046259="1" frameborder="no" height="173" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/QWdKqXj?height=173&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-form-elements-ex1">See the Pen JS-form-elements-ex1 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	إذا كانت لدينا عدّة عناصر لها نفس الاسم، كما هو الحال عادة مع أزرار الانتقاء (radio buttons)، فإنّ <code>form.elements[name]‎</code> تكون عبارة عن مجموعة (set). على سبيل المثال:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7528_13" style="">
<span class="tag">&lt;form&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">name</span><span class="pun">=</span><span class="atv">"age"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"10"</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">name</span><span class="pun">=</span><span class="atv">"age"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"20"</span><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;/form&gt;</span><span class="pln">

</span><span class="tag">&lt;script&gt;</span><span class="pln">
let form </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">forms</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln">

let ageElems </span><span class="pun">=</span><span class="pln"> form</span><span class="pun">.</span><span class="pln">elements</span><span class="pun">.</span><span class="pln">age</span><span class="pun">;</span><span class="pln">

alert</span><span class="pun">(</span><span class="pln">ageElems</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]);</span><span class="pln"> </span><span class="com">// [object HTMLInputElement]</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1617045273="1" data-ss1617046259="1" frameborder="no" height="153" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/JjERrQp?height=153&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-form-elements-ex2">See the Pen JS-form-elements-ex2 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	لا تتعلّق خاصيّات التنقّل هذه بهيكل الوسوم. جميع عناصر التحكّم، مهما كان عمقها في الاستمارة، موجودة في <code>form.elements</code>.
</p>

<hr>
<p>
	<strong>ملاحظة: العناصر Fieldset كاستمارات فرعيّة</strong>
</p>

<p>
	قد تحتوي الاستمارة على عناصر <code>&lt;fieldset&gt;</code>، التي تمتلك بدورها الخاصيّة <code>elements</code> التي تسرد عناصر التحكّم التي داخلها. على سبيل المثال:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7528_17" style="">
<span class="tag">&lt;body&gt;</span><span class="pln">
  </span><span class="tag">&lt;form</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"form"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;fieldset</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"userFields"</span><span class="tag">&gt;</span><span class="pln">
      </span><span class="tag">&lt;legend&gt;</span><span class="pln">info</span><span class="tag">&lt;/legend&gt;</span><span class="pln">
      </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"login"</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text"</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;/form&gt;</span><span class="pln">

  </span><span class="tag">&lt;script&gt;</span><span class="pln">
    alert</span><span class="pun">(</span><span class="pln">form</span><span class="pun">.</span><span class="pln">elements</span><span class="pun">.</span><span class="pln">login</span><span class="pun">);</span><span class="pln"> </span><span class="com">// &lt;input name="login"&gt;</span><span class="pln">

    let fieldset </span><span class="pun">=</span><span class="pln"> form</span><span class="pun">.</span><span class="pln">elements</span><span class="pun">.</span><span class="pln">userFields</span><span class="pun">;</span><span class="pln">
    alert</span><span class="pun">(</span><span class="pln">fieldset</span><span class="pun">);</span><span class="pln"> </span><span class="com">// HTMLFieldSetElement</span><span class="pln">

   </span><span class="com">// fieldset يمكننا الحصول على المُدخل بواسطة الاسم سواء من الاستمارة أو من</span><span class="pln">
    alert</span><span class="pun">(</span><span class="pln">fieldset</span><span class="pun">.</span><span class="pln">elements</span><span class="pun">.</span><span class="pln">login </span><span class="pun">==</span><span class="pln"> form</span><span class="pun">.</span><span class="pln">elements</span><span class="pun">.</span><span class="pln">login</span><span class="pun">);</span><span class="pln"> </span><span class="com">// true</span><span class="pln">
  </span><span class="tag">&lt;/script&gt;</span><span class="pln">
</span><span class="tag">&lt;/body&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1617045273="1" data-ss1617046259="1" frameborder="no" height="178" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/OJWRxee?height=178&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-form-elements-ex3">See the Pen JS-form-elements-ex3 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<hr>
<hr>
<p>
	<strong>تنبيه: الصيغة المختصرة <code>form.name</code></strong>
</p>

<p>
	هناك صيغة مختصرة تمكّننا من الوصول إلى العنصر بواسطة <code>form[index/name]‎</code>. بعبارة أخرى، بدل <code>form.elements.login</code>، يمكننا كتابة <code>form.login</code>.
</p>

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

<pre class="ipsCode">
&lt;form id="form"&gt;
  &lt;input name="login"&gt;
&lt;/form&gt;

&lt;script&gt;
  alert(form.elements.login == form.login); // &lt;input&gt; نفس الـ ،true 

  form.login.name = "username"; // تغيير اسم المُدخل

  // :الاسم form.elements غيّرت
  alert(form.elements.login); // undefined
  alert(form.elements.username); // input

  // تتيح الاستمارة كلا الاسمين، القديم والجديد
  alert(form.username == form.login); // true
&lt;/script&gt;
</pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1617045273="1" data-ss1617046259="1" frameborder="no" height="178" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/XWpjeva?height=178&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-form-elements-ex4">See the Pen JS-form-elements-ex4 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	لا يشكّل ذلك مشكلة في الغالب، لأنّنا قلّ ما نغّير أسماء العناصر في الاستمارات.
</p>

<hr>
<h2>
	الإشارة العكسيّة (Backreference)‏: element.form
</h2>

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

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="61121" data-ss1617045273="1" data-ss1617046259="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/form-navigation.png.48cc26cb64c75097783d05ba142d7c13.png" rel=""><img alt="form-navigation.png" class="ipsImage ipsImage_thumbnailed" data-fileid="61121" data-unique="n5ggcxq2g" src="https://academy.hsoub.com/uploads/monthly_2021_03/form-navigation.png.48cc26cb64c75097783d05ba142d7c13.png"></a>
</p>

<p>
	على سبيل المثال:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7528_19" style="">
<span class="tag">&lt;form</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"form"</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">name</span><span class="pun">=</span><span class="atv">"login"</span><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;/form&gt;</span><span class="pln">

</span><span class="tag">&lt;script&gt;</span><span class="pln">
  </span><span class="com">// element &lt;- form</span><span class="pln">
  let login </span><span class="pun">=</span><span class="pln"> form</span><span class="pun">.</span><span class="pln">login</span><span class="pun">;</span><span class="pln">

  </span><span class="com">// form &lt;- element</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln">login</span><span class="pun">.</span><span class="pln">form</span><span class="pun">);</span><span class="pln"> </span><span class="com">// HTMLFormElement</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1617045273="1" data-ss1617046259="1" frameborder="no" height="147" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/xxgEPKY?height=147&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-form-elements-ex5">See the Pen JS-form-elements-ex5 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<h2>
	عناصر الاستمارة
</h2>

<p>
	لنتحدّث عن عناصر التحكّم في الاستمارة.
</p>

<h3>
	input و textarea
</h3>

<p>
	يمكننا الوصول إلى القيم التي في هذه العناصر بواسطة <code>input.value</code> (سلسلة نصيّة) أو <code>input.checked</code> (منطقيّة) بالنسبة لصناديق التأشير (checkboxes)، هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7528_21" style="">
<span class="pln">input</span><span class="pun">.</span><span class="pln">value </span><span class="pun">=</span><span class="pln"> </span><span class="str">"New value"</span><span class="pun">;</span><span class="pln">
textarea</span><span class="pun">.</span><span class="pln">value </span><span class="pun">=</span><span class="pln"> </span><span class="str">"New text"</span><span class="pun">;</span><span class="pln">

input</span><span class="pun">.</span><span class="pln">checked </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln"> </span><span class="com">// بالنسبة لصناديق التأشير أو أزرار الانتقاء</span></pre>

<hr>
<p>
	<strong>تنبيه: استخدم <code>textarea.value</code>، وليس <code>textarea.innerHTML</code></strong>
</p>

<p>
	يُرجى التنبّه إلى أنّه حتى لو كانت القيمة التي في داخل <code>&lt;textarea&gt;...&lt;/textarea&gt;</code> هي عبارة عن عناصر HTML متداخلة، فلا ينبغي أبدا أن نستخدم الخاصيّة <code>textarea.innerHTML</code> للوصول إليها. ذلك لأنّ هذه الخاصيّة تحتوي على شيفرة HTML التي كانت في الصفحة بدايةً، وليس القيمة الحاليّة.
</p>

<hr>
<h3>
	elect وoptions
</h3>

<p>
	للعنصر <code>&lt;select&gt;</code> ثلاث خاصيّات مهمّة:
</p>

<ol>
<li>
		<code>select.options</code> -- مجموعة عناصر <code>&lt;option&gt;</code> الفرعيّة.
	</li>
	<li>
		<code>select.value</code> -- قيمة <code>&lt;option&gt;</code> المحدّدة حاليّا.
	</li>
	<li>
		<code>select.selectedIndex</code> -- عدد عناصر <code>&lt;option&gt;</code> المحدّدة حاليّا.
	</li>
</ol>
<p>
	وتوفّر هذه الخاصّيات ثلاث طرق مختلفة لإعطاء قيمة إلى <code>&lt;select&gt;</code>:
</p>

<ol>
<li>
		العثور على العنصر المُراد <code>&lt;option&gt;</code>، ثمّ إعطاء <code>option.selected</code> القيمة <code>true</code>.
	</li>
	<li>
		وضع القيمة في <code>select.value</code>.
	</li>
	<li>
		جعل قيمة <code>select.selectedIndex</code> هي رقم الخيار option المُراد.
	</li>
</ol>
<p>
	قد تكون الطريقة الأولى هي الأوضح، لكنّ الطريقتين <code>(2)</code> و<code>(3)</code> أكثر ملائمة في الغالب. إليك هذا المثال:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7528_23" style="">
<span class="tag">&lt;select</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"select"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;option</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"apple"</span><span class="tag">&gt;</span><span class="pln">Apple</span><span class="tag">&lt;/option&gt;</span><span class="pln">
  </span><span class="tag">&lt;option</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"pear"</span><span class="tag">&gt;</span><span class="pln">Pear</span><span class="tag">&lt;/option&gt;</span><span class="pln">
  </span><span class="tag">&lt;option</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"banana"</span><span class="tag">&gt;</span><span class="pln">Banana</span><span class="tag">&lt;/option&gt;</span><span class="pln">
</span><span class="tag">&lt;/select&gt;</span><span class="pln">

</span><span class="tag">&lt;script&gt;</span><span class="pln">
  </span><span class="com">// تؤدي جميع اﻷسطر الثلاث نفس الوظيفة</span><span class="pln">
  select</span><span class="pun">.</span><span class="pln">options</span><span class="pun">[</span><span class="lit">2</span><span class="pun">].</span><span class="pln">selected </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
  select</span><span class="pun">.</span><span class="pln">selectedIndex </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln">
  select</span><span class="pun">.</span><span class="pln">value </span><span class="pun">=</span><span class="pln"> </span><span class="str">'banana'</span><span class="pun">;</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1617045273="1" data-ss1617046259="1" frameborder="no" height="186" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/BapLmaB?height=186&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-form-elements-ex6">See the Pen JS-form-elements-ex6 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	بخلاف معظم عناصر التحكّم الأخرى، يتيح <code>&lt;select&gt;</code> تحديد عدّة خيارات في نفس الوقت إذا كانت له السمة <code>multiple</code>. قلّ ما تُستخدم هذه الوظيفة، لكن إن كان ولابد، اعتمد الطريقة الأولى: أضف/أزل الخاصيّة <code>selected</code> في عناصر <code>&lt;option&gt;</code> الفرعيّة. يمكن الحصول على مجموعة العناصر الفرعيّة بواسطة <code>select.options</code>، على سبيل المثال:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7528_25" style="">
<span class="tag">&lt;select</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"select"</span><span class="pln"> </span><span class="atn">multiple</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;option</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"blues"</span><span class="pln"> </span><span class="atn">selected</span><span class="tag">&gt;</span><span class="pln">Blues</span><span class="tag">&lt;/option&gt;</span><span class="pln">
  </span><span class="tag">&lt;option</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"rock"</span><span class="pln"> </span><span class="atn">selected</span><span class="tag">&gt;</span><span class="pln">Rock</span><span class="tag">&lt;/option&gt;</span><span class="pln">
  </span><span class="tag">&lt;option</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"classic"</span><span class="tag">&gt;</span><span class="pln">Classic</span><span class="tag">&lt;/option&gt;</span><span class="pln">
</span><span class="tag">&lt;/select&gt;</span><span class="pln">

</span><span class="tag">&lt;script&gt;</span><span class="pln">
  </span><span class="com">// الحصول على جميع القيم المُحدّدة من التحديد المتعدّد</span><span class="pln">
  let selected </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Array</span><span class="pun">.</span><span class="pln">from</span><span class="pun">(</span><span class="pln">select</span><span class="pun">.</span><span class="pln">options</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">filter</span><span class="pun">(</span><span class="pln">option </span><span class="pun">=&gt;</span><span class="pln"> option</span><span class="pun">.</span><span class="pln">selected</span><span class="pun">)</span><span class="pln">
    </span><span class="pun">.</span><span class="pln">map</span><span class="pun">(</span><span class="pln">option </span><span class="pun">=&gt;</span><span class="pln"> option</span><span class="pun">.</span><span class="pln">value</span><span class="pun">);</span><span class="pln">

  alert</span><span class="pun">(</span><span class="pln">selected</span><span class="pun">);</span><span class="pln"> </span><span class="com">// blues,rock  </span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1617045273="1" data-ss1617046259="1" frameborder="no" height="202" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/preview/eYgdeYW?height=202&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-form-elements-ex7">See the Pen JS-form-elements-ex7 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	يمكن الاطلاع على المواصفة الكاملة للعنصر <code>&lt;select&gt;</code> من <a data-ss1617045273="1" data-ss1617046259="1" href="https://html.spec.whatwg.org/multipage/forms.html#the-select-element" rel="external nofollow">هنا</a>.
</p>

<h3>
	new Option
</h3>

<p>
	من النادر استعمال هذه الصيغة على انفراد، لكنّ هناك شيئا مثيرا للاهتمام.
</p>

<p>
	تحتوي <a data-ss1617045273="1" data-ss1617046259="1" href="https://html.spec.whatwg.org/multipage/forms.html#the-option-element" rel="external nofollow">المواصفة</a> على صيغة مختصرة وجميلة لإنشاء عناصر <code>&lt;option&gt;</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7528_28" style="">
<span class="pln">option </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Option</span><span class="pun">(</span><span class="pln">text</span><span class="pun">,</span><span class="pln"> value</span><span class="pun">,</span><span class="pln"> defaultSelected</span><span class="pun">,</span><span class="pln"> selected</span><span class="pun">);</span></pre>

<p>
	حيث الوسائط:
</p>

<ul>
<li>
		<code>text</code> -- النصّ داخل الخيار،
	</li>
	<li>
		<code>value</code> -- قيمة الخيار،
	</li>
	<li>
		<code>defaultSelected</code> -- إذا كانت <code>true</code>، تُنشأ السمة <code>selected</code> في HTML،
	</li>
	<li>
		<code>selected</code> -- إذا كانت <code>true</code>، فإنّ الخيار يكون محدَّدا.
	</li>
</ul>
<p>
	قد يحصل التباس طفيف بين <code>defaultSelected</code> و<code>selected</code>. الأمر بسيط: تهيّئ <code>defaultSelected</code> سمة HTML التي يمكننا الحصول عليها بواسطة <code>option.getAttribute('selected')‎</code>. بينما تبيّن <code>selected</code> ما إذا كان الخيار مُحدّدا أو لا، ولها أهمّية أكبر. قد تكون للخاصيّتين القيمة <code>true</code> أو لا تكون لها قيم بتاتا (وهو كما لو أعطيت القيمة <code>false</code>). على سبيل المثال:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7528_30" style="">
<span class="pln">let option </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Option</span><span class="pun">(</span><span class="str">"Text"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"value"</span><span class="pun">);</span><span class="pln">
</span><span class="com">// &lt;option value="value"&gt;Text&lt;/option&gt; تنشئ</span></pre>

<p>
	تحديد نفس العنصر:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_7528_32" style="">
<span class="pln">let option </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Option</span><span class="pun">(</span><span class="str">"Text"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"value"</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">true</span><span class="pun">);</span></pre>

<p>
	لعناصر option الخاصّيات التالية:
</p>

<ul>
<li>
		<strong><code>option.selected</code> :</strong> هل الخيار مُحددّ؟
	</li>
	<li>
		<strong><code>option.index</code> :</strong> رقم الخيار بين خيارات <code>&lt;select&gt;</code> الأخرى.
	</li>
	<li>
		<strong><code>option.text</code> :</strong> المحتوى النصّي للخيار (الذي يراه الزائر).
	</li>
</ul>
<h2>
	الملخص
</h2>

<p>
	التنقّل في الاستمارة:
</p>

<ul>
<li>
		<strong><code>document.forms</code>:</strong> يمكن الوصول إلى الاستمارة بواسطة <code>document.forms[name/index]‎</code>.
	</li>
	<li>
		<strong><code>form.elements</code>:</strong> يمكن الوصول إلى عناصر الاستمارة بواسطة <code>form.elements[name/index]‎</code>، أو فقط باستخدام <code>form[name/index]‎</code>. تعمل الخاصيّة <code>elements</code> مع <code>&lt;fieldset&gt;</code> كذلك.
	</li>
	<li>
		<strong><code>element.form</code>:</strong> تشير إلى الاستمارة الخاصّة بها بواسطة الخاصيّة <code>form</code>.
	</li>
</ul>
<p>
	يمكن الوصول إلى القيمة بواسطة <code>input.value</code> و<code>textarea.value</code> و<code>select.value</code> وغيرها، بالإضافة إلى <code>input.checked</code> بالنسبة لصناديق التأشير وأزرار الانتقاء.
</p>

<p>
	بالنسبة لـ <code>&lt;select&gt;</code>، يمكننا أيضا الحصول على القيمة بواسطة الرقم الاستدلاليّ <code>select.selectedIndex</code> أو من خلال مجموعة الخيارات <code>select.options</code>.
</p>

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

<p>
	في المقال التالي، سنتناول الأحداث <code>focus</code> و<code>blur</code> التي يمكن أن تقع على أيّ عنصر، لكنّها تُعالج غالبا في الاستمارات.
</p>

<h2 id="التمارين">
	التمارين
</h2>

<h3 id="أضف-خيارا-إلى-select">
	أضف خيارا إلى select
</h3>

<p>
	الأهميّة: 5
</p>

<p>
	هناك عنصر <code>&lt;select&gt;</code>:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7528_38" style="">
<span class="tag">&lt;select</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"genres"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;option</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"rock"</span><span class="tag">&gt;</span><span class="pln">Rock</span><span class="tag">&lt;/option&gt;</span><span class="pln">
  </span><span class="tag">&lt;option</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"blues"</span><span class="pln"> </span><span class="atn">selected</span><span class="tag">&gt;</span><span class="pln">Blues</span><span class="tag">&lt;/option&gt;</span><span class="pln">
</span><span class="tag">&lt;/select&gt;</span></pre>

<p>
	باستخدام جافاسكربت:
</p>

<p>
	أظهر القيمة والنصّ للخيار المُحدّد. أضف الخيار <code>&lt;option value="classic"&gt;Classic&lt;/option&gt;</code>. اجعله قابلا للتحديد.
</p>

<p>
	لاحظ أنّك إذا أنجزت كلّ شيء بشكل صحيح، يجب أن تُظهِر alert الكلمة <code>blues</code>.
</p>

<h3 id="الحلّ">
	الحل
</h3>

<p>
	إليك الحلّ خطوة بخطوة:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7528_40" style="">
<span class="tag">&lt;select</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"genres"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;option</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"rock"</span><span class="tag">&gt;</span><span class="pln">Rock</span><span class="tag">&lt;/option&gt;</span><span class="pln">
  </span><span class="tag">&lt;option</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"blues"</span><span class="pln"> </span><span class="atn">selected</span><span class="tag">&gt;</span><span class="pln">Blues</span><span class="tag">&lt;/option&gt;</span><span class="pln">
</span><span class="tag">&lt;/select&gt;</span><span class="pln">

</span><span class="tag">&lt;script&gt;</span><span class="pln">
  </span><span class="com">// 1)</span><span class="pln">
  let selectedOption </span><span class="pun">=</span><span class="pln"> genres</span><span class="pun">.</span><span class="pln">options</span><span class="pun">[</span><span class="pln">genres</span><span class="pun">.</span><span class="pln">selectedIndex</span><span class="pun">];</span><span class="pln">
  alert</span><span class="pun">(</span><span class="pln"> selectedOption</span><span class="pun">.</span><span class="pln">value </span><span class="pun">);</span><span class="pln">

  </span><span class="com">// 2)</span><span class="pln">
  let newOption </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Option</span><span class="pun">(</span><span class="str">"Classic"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"classic"</span><span class="pun">);</span><span class="pln">
  genres</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">newOption</span><span class="pun">);</span><span class="pln">

  </span><span class="com">// 3)</span><span class="pln">
  newOption</span><span class="pun">.</span><span class="pln">selected </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
</span><span class="tag">&lt;/script&gt;</span></pre>

<h2 id="المراجع">
	المراجع
</h2>

<ul>
<li>
		<a data-ss1617045273="1" data-ss1617046259="1" href="https://html.spec.whatwg.org/multipage/forms.html" rel="external nofollow">المواصفة forms</a>
	</li>
</ul>
<p>
	ترجمة -وبتصرف- للمقال <a data-ss1617045273="1" data-ss1617046259="1" href="https://javascript.info/form-elements" rel="external nofollow">Form properties and methods</a> من سلسلة <a data-ss1617045273="1" data-ss1617046259="1" href="https://javascript.info/ui" rel="external nofollow">Browser: Document, Events, Interfaces</a> لصاحبها Ilya Kantor
</p>
]]></description><guid isPermaLink="false">1193</guid><pubDate>Mon, 12 Apr 2021 13:07:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x645;&#x631;&#x64A;&#x631; (scrolling) &#x648;&#x623;&#x62D;&#x62F;&#x627;&#x62B;&#x647; &#x648;&#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639;&#x647; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%AA%D9%85%D8%B1%D9%8A%D8%B1-scrolling-%D9%88%D8%A3%D8%AD%D8%AF%D8%A7%D8%AB%D9%87-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9%D9%87-%D9%81%D9%8A-%D8%AC%D8%A7%D9%81%D8%A7%D8%B3%D9%83%D8%B1%D8%A8%D8%AA-r1156/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/6042302725dbe_-(scrolling)-----.png.1db1fa806edb18c7d627d76cab45b0c2.png" /></p>

<p>
	يمكّن الحدث <code>scroll</code> من الاستجابة لتمرير الصفحة أو عنصرٍ ما. يمكننا فعل العديد من الأمور الجيّدة في هذا الباب. على سبيل المثال:
</p>

<ul>
<li>
		إظهار/إخفاء أزرار التحكّم أو معلوماتٍ إضافيّة حسب مكان المستخدم في المستند.
	</li>
	<li>
		تحميل المزيد من البيانات عندما يمرّر المستخدم الصفحة حتى النهاية.
	</li>
</ul>
<p>
	إليك دالّة صغيرة تعرض الموضع الحاليّ للتمرير:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8919_7" style="">
<span class="pln">window</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">'scroll'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</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">getElementById</span><span class="pun">(</span><span class="str">'showScroll'</span><span class="pun">).</span><span class="pln">innerHTML </span><span class="pun">=</span><span class="pln"> window</span><span class="pun">.</span><span class="pln">pageYOffset </span><span class="pun">+</span><span class="pln"> </span><span class="str">'px'</span><span class="pun">;</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	عند اشتغالها:
</p>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1614951238="1" frameborder="no" height="236" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/xxRJmNE?height=236&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-onscroll-ex01">See the Pen JS-onscroll-ex01 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	يعمل الحدث <code>scroll</code> سواءً مع النافذة <code>window</code> أو العناصر القابلة للتمرير.
</p>

<h2>
	منع التمرير
</h2>

<p>
	كيف يمكن أن نجعل شيئا ما غير قابل للتمرير؟
</p>

<p>
	لا يمكن منع التمرير بمجرّد استخدام <code>event.preventDefault()‎</code> في منصت الحدث <code>onscroll</code>، لأنّ هذا الحدث يقع <em>بعد</em> حصول التمرير.
</p>

<p>
	لكن يمكننا منع التمرير باستخدام <code>event.preventDefault()‎</code> مع الحدث الذي يتسبّب في التمرير، مثل حدث <code>keydown</code> عند الضغط على المفاتيح <code>pageUp</code> و<code>pageDown</code>. فإذا وضعنا معالجات لهذه اﻷحداث مع <code>event.preventDefault()‎</code> داخلها، لن يحصل التمرير.
</p>

<p>
	هناك عدّة طرق لحصول التمرير، لذا يجدر استخدام الخاصّيّة <code>overflow</code> في CSS.
</p>

<p>
	هذه بعض التمارين التي يمكنك حلّها أو النظر فيها للاطلاع على بعض تطبيقات <code>onscroll</code>.
</p>

<h2>
	التمارين
</h2>

<h3>
	صفحة لا متناهية
</h3>

<p>
	أنشئ صفحة لا متناهية. عندما يقوم الزائر بالتمرير حتى النهاية، يُلحَق التاريخ والوقت الحاليّان بالنصّ تلقائيّا (ليستطيع الزائر تمرير المزيد)، كما يمكنك مشاهدة ذلك من <a data-ss1614950393="1" data-ss1614951238="1" href="https://en.js.cx/task/endless-page/solution/" rel="external nofollow">هنا</a>.
</p>

<p>
	يُرجى التنبّه إلى ميزتين مهمّتين للتمرير:
</p>

<ol>
<li>
		<strong>التمرير "متمدّد".</strong> يمكننا مواصلة التمرير قليلا إلى ما وراء بداية أو نهاية المستند في بعض المتصفّحات/الأجهزة (تظهر مساحة فارغة في اﻷسفل، قبل أن "يرتدّ" المستند كما كان).
	</li>
	<li>
		<strong>التمرير غير دقيق.</strong> عندما نمرّر الصفحة حتى النهاية، قد نكون في الواقع على بعد حوالي 0-50px من نهاية المستند الحقيقيّة.
	</li>
</ol>
<p>
	وبذلك، فإنّ "التمرير حتى النهاية" يعني أنّه لا بدّ للزائر ألّا يبعد أكثر من 100px من نهاية المستند.
</p>

<p>
	ملاحظة: قد يكون الغرض في الحالات الواقعيّة عرض "المزيد من الرسائل" أو "المزيد من السلع".
</p>

<p>
	<a data-ss1614950393="1" data-ss1614951238="1" href="https://plnkr.co/edit/9og7bth6RKWq4fnw?p=preview" rel="external nofollow">أنجز التمرين في البيئة التجريبيّة</a>.
</p>

<h4>
	الحلّ
</h4>

<p>
	يكمن الحلّ في إنشاء دالّة تضيف المزيد من التواريخ إلى الصفحة (أو تحمّل المزيد من الأشياء في الحالات الواقعيّة) عندما نكون في آخر الصفحة. يمكننا استدعاؤها مباشرة وإضافتها كمعالج <code>window.onscroll</code>.
</p>

<p>
	لكنّ أهمّ سؤال هو: "كيف نعرف أنّ الصفحة قد وصلت إلى اﻷسفل؟".
</p>

<p>
	لنستخدم في ذلك الإحداثيّات بالنسبة إلى النافذة.
</p>

<p>
	يمكننا الحصول على الإحداثيّات بالنسبة إلى النافذة في كامل المستند بواسطة <code>document.documentElement.getBoundingClientRect()‎</code>، وتكون الخاصيّة <code>bottom</code> هي إحداثيّات أسفل المستند بالنسبة إلى النافذة.
</p>

<p>
	على سبيل المثال، إذا افترضنا أنّ ارتفاع كامل مستند HTML هو <code>2000px</code>، يكون لدينا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8919_9" style="">
<span class="com">// عندما نكون في أعلى الصفحة</span><span class="pln">
</span><span class="com">// القمّة بالنسبة للنافذة = 0</span><span class="pln">
document</span><span class="pun">.</span><span class="pln">documentElement</span><span class="pun">.</span><span class="pln">getBoundingClientRect</span><span class="pun">().</span><span class="pln">top </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pln">

</span><span class="com">// الأسفل بالنسبة للنافذة = 2000</span><span class="pln">
</span><span class="com">// المستند طويل، لذا فإنّه يتجاوز على اﻷرجح أسفل النافذة بكثير</span><span class="pln">
document</span><span class="pun">.</span><span class="pln">documentElement</span><span class="pun">.</span><span class="pln">getBoundingClientRect</span><span class="pun">().</span><span class="pln">bottom </span><span class="pun">=</span><span class="pln"> </span><span class="lit">2000</span></pre>

<p>
	إذا مرّرنا <code>500px</code>، فإنّ:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8919_11" style="">
<span class="com">// 500px أعلى المستند فوق النافذة بـ</span><span class="pln">
document</span><span class="pun">.</span><span class="pln">documentElement</span><span class="pun">.</span><span class="pln">getBoundingClientRect</span><span class="pun">().</span><span class="pln">top </span><span class="pun">=</span><span class="pln"> </span><span class="pun">-</span><span class="lit">500</span><span class="pln">
</span><span class="com">// 500px أسفل المستند أقرب بـ </span><span class="pln">
document</span><span class="pun">.</span><span class="pln">documentElement</span><span class="pun">.</span><span class="pln">getBoundingClientRect</span><span class="pun">().</span><span class="pln">bottom </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1500</span></pre>

<p>
	إذا مرّرنا إلى النهاية، بافتراض أنّ ارتفاع النافذة هو <code>600px</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8919_13" style="">
<span class="com">// 1400px أعلى المستند فوق النافذة بـ</span><span class="pln">
document</span><span class="pun">.</span><span class="pln">documentElement</span><span class="pun">.</span><span class="pln">getBoundingClientRect</span><span class="pun">().</span><span class="pln">top </span><span class="pun">=</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1400</span><span class="pln">
</span><span class="com">// 600px أسفل المستند تحت النافذة بـ </span><span class="pln">
document</span><span class="pun">.</span><span class="pln">documentElement</span><span class="pun">.</span><span class="pln">getBoundingClientRect</span><span class="pun">().</span><span class="pln">bottom </span><span class="pun">=</span><span class="pln"> </span><span class="lit">600</span></pre>

<p>
	يُرجى التنبّه أنّ اﻷسفل <code>bottom</code> لا يمكن أن يكون <code>0</code> لأنّه لا يرتقي إلى أعلى النافذة أبدا. أقلّ حدّ للإحداثيّة <code>bottom</code> هو ارتفاع النافذة (افترضنا أنّها <code>600</code>)، لا نستطيع تمريرها أعلى من ذلك.
</p>

<p>
	يمكننا الحصول على ارتفاع النافذة بواسطة <code>document.documentElement.clientHeight</code>.
</p>

<p>
	في هذا التمرين، نحتاج أن نعرف متى نبعد عن أسفل المستند بأقلّ من <code>100px</code> (بمعنى يكون أسفل المستند <code>600-700px</code> بالنسبة للنافذة، إذا كان ارتفاعها <code>600</code>).
</p>

<p>
	هذه هي الدالّة إذًا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8919_15" style="">
<span class="kwd">function</span><span class="pln"> populate</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">while</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="com">// أسفل المستند</span><span class="pln">
    let windowRelativeBottom </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">documentElement</span><span class="pun">.</span><span class="pln">getBoundingClientRect</span><span class="pun">().</span><span class="pln">bottom</span><span class="pun">;</span><span class="pln">

    </span><span class="com">// على النهاية)‏‎ 100px إذا لم يمرّر المستخدم بعيدا بما يكفي (أكثر من</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">windowRelativeBottom </span><span class="pun">&gt;</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">documentElement</span><span class="pun">.</span><span class="pln">clientHeight </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">break</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">body</span><span class="pun">.</span><span class="pln">insertAdjacentHTML</span><span class="pun">(</span><span class="str">"beforeend"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">`&lt;</span><span class="pln">p</span><span class="pun">&gt;</span><span class="typ">Date</span><span class="pun">:</span><span class="pln"> $</span><span class="pun">{</span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Date</span><span class="pun">()}&lt;/</span><span class="pln">p</span><span class="pun">&gt;`);</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	<a data-ss1614950393="1" data-ss1614951238="1" href="https://plnkr.co/edit/VVJZRWimYbGiMkqJ?p=preview" rel="external nofollow">شاهد الحلّ في البيئة التجريبيّة</a>.
</p>

<h3>
	زرّ اﻷعلى/اﻷسفل
</h3>

<p>
	<em>اﻷهميّة:5</em>
</p>

<p>
	أنشئ زرّ "الانتقال نحو اﻷعلى" للمساعدة على تمرير الصفحة. يجب أن يعمل كالتالي:
</p>

<ul>
<li>
		إذا لم تُمرَّر النافذة نحو اﻷسفل بمقدار ارتفاع النافذة على اﻷقلّ، فإنّه لا يظهر.
	</li>
	<li>
		إذا مُرّرت الصفحة نحو اﻷسفل بمقدار يزيد على ارتفاع النافذة، يظهر سهم يشير نحو اﻷعلى في الزاوية العليا من اليسار. إذا أعيد تمرير الصفحة نحو اﻷعلى، فإنّه يختفي.
	</li>
	<li>
		عند الضغط على السهم، فإنّ الصّفحة تُمرّر حتى القمّة. كما هو مبيّن <a data-ss1614950393="1" data-ss1614951238="1" href="https://en.js.cx/task/updown-button/solution/" rel="external nofollow">هنا</a> (في الزاوية العليا من اليسار، مرّر الصفحة لتراه).
	</li>
</ul>
<p>
	<a data-ss1614950393="1" data-ss1614951238="1" href="https://plnkr.co/edit/DqgU6NJ1rB6Wdq42?p=preview" rel="external nofollow">أنجز التمرين في البيئة التجريبيّة</a>.
</p>

<h4>
	الحل
</h4>

<p>
	<a data-ss1614950393="1" data-ss1614951238="1" href="https://plnkr.co/edit/pfARI2TpxF80roQY?p=preview" rel="external nofollow">شاهد الحلّ في البيئة التجريبيّة</a>.
</p>

<h3>
	حمّل الصور المرئيّة
</h3>

<p>
	<em>اﻷهميّة: 4</em>
</p>

<p>
	لنفترض أنّ لدينا عميلا ذا سرعة قليلة ونريد أن نحفظ له بيانات الجوّال. فقرّرنا لهذا الغرض ألّا نظهر الصور فورا، بل نضع مكانها نائبًا (placeholder)، بهذا الشكل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8919_17" style="">
<span class="pun">&lt;</span><span class="pln">img src</span><span class="pun">=</span><span class="str">"placeholder.svg"</span><span class="pln"> width</span><span class="pun">=</span><span class="str">"128"</span><span class="pln"> height</span><span class="pun">=</span><span class="str">"128"</span><span class="pln"> data</span><span class="pun">-</span><span class="pln">src</span><span class="pun">=</span><span class="str">"real.jpg"</span><span class="pun">&gt;</span></pre>

<p>
	في البداية، تكون كلّ الصّور <code>placeholder.svg</code>. وعندما تُمرّر الصفحة إلى موضع يمكن للمستخدم فيه أن يرى الصورة، نغيّر قيمة <code>src</code> إلى القيمة الموجودة في <code>data-src</code>، فتُحمَّل الصّورة.
</p>

<p>
	يمكن مشاهدة المثال حيّا من <a data-ss1614950393="1" data-ss1614951238="1" href="https://en.js.cx/task/load-visible-img/solution/" rel="external nofollow">هنا</a>. قم بالتمرير لترى الصور تُحمّل "حسب الطلب".
</p>

<p>
	المتطلّبات:
</p>

<ul>
<li>
		عندما تُحمّل الصفحة، يجب أن تُحمّل الصور التي على الشاشة فورا، قبل حصول التمرير.
	</li>
	<li>
		قد تكون بعض الصور عاديّة، دون <code>data-src</code>. يجب أن تتركها الشيفرة على حالها.
	</li>
	<li>
		بعد انتهاء تحميل الصورة، يجب ألّا يُعاد تحميلها مرّة أخرى عند تمريرها إلى اﻷسفل/اﻷعلى.
	</li>
</ul>
<p>
	ملاحظة: إذا أمكن، أنجز حلّا أكثر تقدّما يتيح "التحميل المسبق" للصور التي تكون قبل/بعد الموضع الحاليّ بمقدار صفحة.
</p>

<p>
	ملاحظة أخرى: ينبغي فقط معالجة التمرير العموديّ، لا التمرير الأفقيّ.
</p>

<p>
	<a data-ss1614950393="1" data-ss1614951238="1" href="https://plnkr.co/edit/ZmiPqJnvJuXPBPzc?p=preview" rel="external nofollow">أنجز التمرين في البيئة التجريبيّة</a>.
</p>

<h4>
	الحل
</h4>

<p>
	يجب أن يعمل المعالجُ <code>onscroll</code> على تحديد الصّور مرئيّة وعرضها. نريده أيضا أن يشتغل عند تحميل الصفحة، لاكتشاف الصور المرئيّة فورا وعرضها. يجب أن تشتغل الشيفرة بعد الانتهاء من تحميل المستند، لتتمكّن من الوصول إلى محتواه. أو توضع أسفل العنصر <code>&lt;body&gt;</code>.
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8919_19" style="">
<span class="com">// ...محتوى الصفحة الفوق...</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> isVisible</span><span class="pun">(</span><span class="pln">elem</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  let coords </span><span class="pun">=</span><span class="pln"> elem</span><span class="pun">.</span><span class="pln">getBoundingClientRect</span><span class="pun">();</span><span class="pln">

  let windowHeight </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">documentElement</span><span class="pun">.</span><span class="pln">clientHeight</span><span class="pun">;</span><span class="pln">

  </span><span class="com">// مرئيّ؟ elem هل الحدّ العلويّ لـ</span><span class="pln">
  let topVisible </span><span class="pun">=</span><span class="pln"> coords</span><span class="pun">.</span><span class="pln">top </span><span class="pun">&gt;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> coords</span><span class="pun">.</span><span class="pln">top </span><span class="pun">&lt;</span><span class="pln"> windowHeight</span><span class="pun">;</span><span class="pln">

  </span><span class="com">// مرئيّ؟ elem هل الحدّ السفليّ لـ</span><span class="pln">
  let bottomVisible </span><span class="pun">=</span><span class="pln"> coords</span><span class="pun">.</span><span class="pln">bottom </span><span class="pun">&lt;</span><span class="pln"> windowHeight </span><span class="pun">&amp;&amp;</span><span class="pln"> coords</span><span class="pun">.</span><span class="pln">bottom </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="kwd">return</span><span class="pln"> topVisible </span><span class="pun">||</span><span class="pln"> bottomVisible</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	تستخدم الدالّةُ <code>showVisible()‎</code> اختبارَ الرؤية، المُنفَّذ بواسطة <code>isVisible()‎</code>، لتحميل الصور المرئيّة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_8919_21" style="">
<span class="kwd">function</span><span class="pln"> showVisible</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="pln">let img of document</span><span class="pun">.</span><span class="pln">querySelectorAll</span><span class="pun">(</span><span class="str">'img'</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    let realSrc </span><span class="pun">=</span><span class="pln"> img</span><span class="pun">.</span><span class="pln">dataset</span><span class="pun">.</span><span class="pln">src</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">realSrc</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">continue</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">isVisible</span><span class="pun">(</span><span class="pln">img</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"> realSrc</span><span class="pun">;</span><span class="pln">
      img</span><span class="pun">.</span><span class="pln">dataset</span><span class="pun">.</span><span class="pln">src </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="pun">}</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

showVisible</span><span class="pun">();</span><span class="pln">
window</span><span class="pun">.</span><span class="pln">onscroll </span><span class="pun">=</span><span class="pln"> showVisible</span><span class="pun">;</span></pre>

<p>
	ملاحظة: هناك أيضا حلّ بديل حيث تعمل فيه <code>isVisible</code> " على "التحميل المسبق" للصور الموجودة ضمن صفحة واحدة أعلى/أسفل موضع التمرير الحاليّ للمستند.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a data-ss1614950393="1" data-ss1614951238="1" href="https://javascript.info/onscroll" rel="external nofollow">Scrolling</a> من سلسلة <a data-ss1614950393="1" data-ss1614951238="1" href="https://javascript.info/ui" rel="external nofollow">Browser: Document, Events, Interfaces</a> لصاحبها Ilya Kantor
</p>
]]></description><guid isPermaLink="false">1156</guid><pubDate>Sat, 27 Mar 2021 13:01:00 +0000</pubDate></item><item><title>&#x623;&#x62D;&#x62F;&#x627;&#x62B; &#x644;&#x648;&#x62D;&#x629; &#x627;&#x644;&#x645;&#x641;&#x627;&#x62A;&#x64A;&#x62D;: keydown &#x648; keyup &#x648;&#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639;&#x647;&#x627; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A3%D8%AD%D8%AF%D8%A7%D8%AB-%D9%84%D9%88%D8%AD%D8%A9-%D8%A7%D9%84%D9%85%D9%81%D8%A7%D8%AA%D9%8A%D8%AD-keydown-%D9%88-keyup-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9%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-r1155/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/60422e93e38bf_---keydown-keyup----.png.878dad998107b77747282254c2fed021.png" /></p>

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

<p>
	ولذلك، إذا أردنا تتبّع جميع المُدخلات في الحقل <code>&lt;input&gt;</code>، فإنّ أحداث لوحة المفاتيح لا تفي بالغرض. يوجد هناك حدث آخر اسمه <code>input</code> لتتبّع التغيّرات التي تحصل في الحقل <code>&lt;input&gt;</code>، مهما كانت الوسيلة. وقد يكون الخيار اﻷفضل لهذه المهمّة. سنتناول هذا الحدث لاحقا في مقال يشرح اﻷحداث change وinput وcut وcopy وpaste.
</p>

<p>
	ينبغي استخدام أحداث لوحة المفاتيح عند التعامل مع اﻷفعال التي تحصل بواسطة لوحة المفاتيح (يدخل في ذلك لوحة المفاتيح الافتراضيّة)، كالاستجابة للضغط على المفاتيح السهميّة <code>Up</code> و<code>Down</code> أو مفاتيح الاختصارات (بما في ذلك الجمع بين المفاتيح).
</p>

<h2>
	منصة الاختبار
</h2>

<p>
	لفهم أحداث لوحة المفاتيح بشكل أفضل، يمكنك استخدام منصّة الاختبار الموجودة <a data-ss1614949657="1" data-ss1614950031="1" data-ss1614950118="1" data-ss1614950195="1" href="https://javascript.info/article/keyboard-events/keyboard-dump/" rel="external nofollow">هنا</a> أو المثال الحي الآتي. جرّب تجميعات مختلفة من المفاتيح في المساحة النصيّة.
</p>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1614949657="1" data-ss1614950031="1" data-ss1614950118="1" data-ss1614950195="1" frameborder="no" height="512" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/wvoxRQx?height=512&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-keyboard-events-ex01">See the Pen JS-keyboard-events-ex01 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<h2>
	Keydown وkeyup
</h2>

<p>
	يقع حدث <code>keydown</code> عندما يُضغط مفتاح ما، ثمّ يقع <code>keyup</code> عندما يُحرّر.
</p>

<h3>
	event.code وevent.key
</h3>

<p>
	تسمح الخاصّيّة <code>key</code> لكائن الحدث بالحصول على المحرف، بينما تُمكّنه الخاصّيّة <code>code</code> من الحصول على "كود المفتاح الملموس". على سبيل المثال، يمكن أن يُضغط نفس المفتاح <code>Z</code> مع أو بدون المفتاح <code>Shift</code>. سيعطينا ذلك محرفان مختلفان: <code>z</code> الصغير و<code>Z</code> الكبير.
</p>

<p>
	تعبّر الخاصّيّة <code>event.key</code> عن المحرف، وقد تختلف. لكنّ <code>event.code</code> تبقى نفسها:
</p>

<table>
<thead><tr>
<th>
				Key
			</th>
			<th>
				‏ <code>event.key</code>
			</th>
			<th>
				‏ <code>event.code</code>
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				<code>Z</code>
			</td>
			<td>
				‏<code>z</code> (الصغير)
			</td>
			<td>
				<code>KeyZ</code>
			</td>
		</tr>
<tr>
<td>
				<code>Shift+Z</code>
			</td>
			<td>
				‏<code>Z</code> (الكبير)
			</td>
			<td>
				<code>KeyZ</code>
			</td>
		</tr>
</tbody>
</table>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;

    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<p>
	إذا كان المستخدم يستعمل لغات مختلفة، فإنّ تغييره للغة أخرى سيعطي محرفا مختلفا تماما بدل <code>"Z"</code>. وسيصبح ذلك هو قيمة <code>event.key</code>، بينما تبقى <code>event.code</code> هي نفسها دائما: <code>"KeyZ".</code>
</p>

<hr>
<p>
	<strong>ملاحظة: "KeyZ" وغيرها من أكواد المفاتيح</strong>
</p>

<p>
	لكلّ مفتاحٍ كود يتعلّق بمكانه على لوحة المفاتيح. أكواد المفاتيح موضّحة في <a data-ss1614949657="1" data-ss1614950031="1" data-ss1614950118="1" data-ss1614950195="1" href="https://www.w3.org/TR/uievents-code" rel="external nofollow">مواصفة أكواد أحداث واجهة المستخدم</a>.
</p>

<p>
	على سبيل المثال:
</p>

<ul>
<li>
		مفاتيح الأحرف لها أكواد من الشكل <code>"Key&lt;letter&gt;‎"</code>‏: <code>"KeyA"</code> و<code>"KeyB"</code> وهكذا.
	</li>
	<li>
		مفاتيح اﻷرقام لها أكواد من الشكل <code>"Digit&lt;number&gt;‎"</code>: ‏<code>"Digit0"</code> و<code>"Digit1"</code> وهكذا.
	</li>
	<li>
		المفاتيح الخاصّة مكوّدة بأسمائها <code>"Enter"</code> و<code>"Backspace"</code> و<code>"Tab"</code> وهكذا.
	</li>
</ul>
<p>
	هناك عدّة تخطيطات (layouts) شائعة للوحات المفاتيح، وتعطي المواصفة أكواد مفاتيح لكلّ منها؛ طالع <a data-ss1614949657="1" data-ss1614950031="1" data-ss1614950118="1" data-ss1614950195="1" href="https://www.w3.org/TR/uievents-code/#key-alphanumeric-section" rel="external nofollow">قسم الحروف اﻷبجديّة واﻷرقام في المواصفة</a> لمزيد من اﻷكواد، أو اضغط فقط على المفتاح في منصّة الاختبار الموجودة <a data-ss1614949657="1" data-ss1614950031="1" data-ss1614950118="1" data-ss1614950195="1" href="https://javascript.info/article/keyboard-events/keyboard-dump/" rel="external nofollow">هنا</a>.
</p>

<hr>
<hr>
<p>
	<strong>تنبيه: حجم الأحرف مهمّ: <code>"KeyZ"</code> وليس <code>"keyZ"</code></strong>
</p>

<p>
	يبدو ذلك واضحا، لكن لا يزال النّاس يخطئون في ذلك. يُرجى تجنّب الأخطاء عند الكتابة: إنّه <code>KeyZ</code> وليس <code>keyZ</code>. لن ينجح التحقّق <code>event.code=="keyZ"</code> مثلا، إذ يجب أن يكون أوّل حرف من <code>"Key"</code> كبيرا.
</p>

<hr>
<p>
	لكن ماذا لو لم يكن المفتاح يعطي أيّ محرف؟ مثل <code>Shift</code> أو <code>F1</code> أو غير ذلك. بالنسبة لتلك المفاتيح، <code>event.key</code> هي في الغالب نفس <code>event.code</code>:
</p>

<table>
<thead><tr>
<th>
				Key
			</th>
			<th>
				‏ <code>event.key</code>
			</th>
			<th>
				‏<code>event.code</code>
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				<code>F1</code>
			</td>
			<td>
				‏<code>F1</code> ‏
			</td>
			<td>
				‏‏<code>F1</code>
			</td>
		</tr>
<tr>
<td>
				<code>Backspace</code>
			</td>
			<td>
				‏<code>Backspace</code>
			</td>
			<td>
				‏<code>Backspace</code>
			</td>
		</tr>
<tr>
<td>
				<code>Shift</code>
			</td>
			<td>
				‏<code>Shift</code>
			</td>
			<td>
				‏<code>ShiftRight</code> أو <code>ShiftLeft</code>
			</td>
		</tr>
</tbody>
</table>
<p>
	يُرجى التنبّه أنّ <code>event.code</code> يبيّن المفتاح قد ضُعط بالضبط. فعلى سبيل المثال، تملك أغلب لوحات المفاتيح مفتاحي <code>Shift</code>، على اليمين وعلى اليسار. يخبرنا <code>event.code</code> أيّ واحد قد ضُغط بالضبط، أمّا <code>event.key</code> فهو مسؤول عن "معنى" المفتاح: ما هو (إنّه "تبديل").
</p>

<p>
	لنفترض أنّنا نريد معالجة أحد مفاتيح الاختصارات: <code>Ctrl+Z</code> (أو <code>Cmd+Z</code> في ماك). تربط معظم محرّرات النّصوص فعل "التراجع" بها. يمكننا وضع منصت إلى <code>keydown</code> وتفحّص المفتاح الذي ضُغط.
</p>

<p>
	لكنّ هناك معضلة هنا: في مثل هذه المنصتات، هل يجب علينا تفحّص قيمة <code>event.key</code> أو <code>event.code</code>؟
</p>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4958_7" style="">
<span class="pln">document</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(</span><span class="str">'keydown'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">event</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">event</span><span class="pun">.</span><span class="pln">code </span><span class="pun">==</span><span class="pln"> </span><span class="str">'KeyZ'</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> </span><span class="pun">(</span><span class="pln">event</span><span class="pun">.</span><span class="pln">ctrlKey </span><span class="pun">||</span><span class="pln"> event</span><span class="pun">.</span><span class="pln">metaKey</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    alert</span><span class="pun">(</span><span class="str">'Undo!'</span><span class="pun">)</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">});</span></pre>

<p>
	في الجهة المقابلة، هناك مشكلة مع <code>event.code</code>. مع اختلاف تخطيطات لوحة المفاتيح، قد يكون لنفس المفتاح محرفات مختلفة. على سبيل المثال، إليك التخطيط اﻷمريكيّ ("QWERTY") والتخطيط الألمانيّ ("QWERTZ") تحته (من ويكيبيديا):
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="58955" data-ss1614949657="1" data-ss1614950031="1" data-ss1614950118="1" data-ss1614950195="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/us-layout.png.420a4a1354d4354ed9fdac1fadfd2272.png" rel=""><img alt="us-layout.png" class="ipsImage ipsImage_thumbnailed" data-fileid="58955" data-unique="p5n5hffnx" src="https://academy.hsoub.com/uploads/monthly_2021_03/us-layout.png.420a4a1354d4354ed9fdac1fadfd2272.png"></a>
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="58954" data-ss1614949657="1" data-ss1614950031="1" data-ss1614950118="1" data-ss1614950195="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/german-layout.png.a81fd495b36d098c9d23745c7523b5a0.png" rel=""><img alt="german-layout.png" class="ipsImage ipsImage_thumbnailed" data-fileid="58954" data-unique="78ska5qva" src="https://academy.hsoub.com/uploads/monthly_2021_03/german-layout.png.a81fd495b36d098c9d23745c7523b5a0.png"></a>
</p>

<p>
	عند نفس المفتاح، نجد في التخطيط اﻷمريكيّ "Z"، بينما في التخطيط اﻷلمانيّ "Y" (اﻷحرف منعكسة).
</p>

<p>
	حرفيًّا، تكون قيمة <code>event.code</code> هي <code>KeyZ</code> بالنسبة للذين عندهم التخطيط اﻷلماني عند الضغط على المفتاح <code>Y</code>.
</p>

<p>
	لو أجرينا التحقّق <code>event.code == 'KeyZ'</code>‎ في شيفرتنا، فإنّ الناس الذين عندهم التخطيط اﻷلمانيّ سينجح معهم هذا التحقّق إذا ضغطوا على المفتاح "Y".
</p>

<p>
	يبدو هذا غريبا حقّا، لكنّه كذلك. تذكر <a data-ss1614949657="1" data-ss1614950031="1" data-ss1614950118="1" data-ss1614950195="1" href="https://www.w3.org/TR/uievents-code/#table-key-code-alphanumeric-writing-system" rel="external nofollow">المواصفة</a> هذا السلوك صراحة.
</p>

<p>
	من الممكن إذًا أن يوافق <code>event.code</code> المحرفَ الخطأ في التخطيط غير المتوقّع. فقد تقابل نفس اﻷحرف مفاتيح ملموسة مختلفة باختلاف التخطيطات، مما يؤدّي إلى أكواد مختلفة. لحسن الحظّ، يحدث هذا مع بعض اﻷكواد فقط مثل <code>keyA</code> و<code>keyQ</code> و<code>keyZ</code> (كما رأينا)، ولا يحدث مع المفاتيح الخاصّة مثل <code>Shift</code>. يمكن أن تجد القائمة في <a data-ss1614949657="1" data-ss1614950031="1" data-ss1614950118="1" data-ss1614950195="1" href="https://www.w3.org/TR/uievents-code/#table-key-code-alphanumeric-writing-system" rel="external nofollow">المواصفة</a>.
</p>

<p>
	لتتبّع المحرفات التي تتعلّق بالتخطيط بشكل موثوق، قد يكون <code>event.key</code> هو الطريقة اﻷفضل.
</p>

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

<p>
	إذا كنّا نودّ معالجة المفاتيح التي تتعلّق بالتخطيط، فإنّ <code>event.key</code> هو الحلّ. وإذا كنّا نريد لمفاتيح الاختصارات أن تعمل ولو عند تغيير اللغة، فقد يكون <code>event.code</code> هو اﻷفضل.
</p>

<h2>
	التكرار التلقائي
</h2>

<p>
	إذا استغرق الضغط على مفتاح ما مدّة طويلة بما يكفي، فإنّه يبتدئ "التكرار التلقائيّ": يقع الحدث <code>keydown</code> مرار وتكرار، إلى أن يُحرّر المفتاح فنحصل عندها على <code>keyup</code>. لذلك قد يكون من الطبيعيّ أن نحصل على العديد من <code>keydown</code> بالإضافة إلى <code>keyup</code> وحيد.
</p>

<p>
	إذا وقعت الأحداث بواسطة التكرار التلقائيّ، فإنّ قيمة الخاصّية <code>event.repeat</code> لكائن الحدث تكون <code>true</code>.
</p>

<h2>
	اﻷفعال الافتراضيّة
</h2>

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

<ul>
<li>
		يظهر محرف على الشاشة (النتيجة اﻷظهر).
	</li>
	<li>
		يُحذف محرف (مفتاح <code>Delete</code>).
	</li>
	<li>
		تُمرَّر الصفحة (مفتاح <code>PageDown</code>).
	</li>
	<li>
		يفتح المتصفّح حوار "حفظ الصفحة" (<code>Ctrl+S</code>).
	</li>
	<li>
		…وهكذا.
	</li>
</ul>
<p>
	قد يؤدّي منع الفعل الافتراضيّ عند <code>keydown</code> إلى إلغاء معظمها، باستثناء المفاتيح الخاصّة المضمّنة في نظام التشغيل. على سبيل المثال، يعمل <code>Alt+F4</code> في ويندوز على إغلاق نافذة المتصفّح الحاليّة. ولا سبيل إلى إيقاف ذلك بواسطة منع الفعل الافتراضيّ في جافاسكربت.
</p>

<p>
	مثلا، يتوقّع المُدخل <code>&lt;input&gt;</code> أدناه رقم هاتف، فلا يقبل المفاتيح التي بخلاف اﻷرقام أو <code>+</code> أو <code>()</code> أو <code>-</code>:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4958_9" style="">
<span class="pun">&lt;</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> checkPhoneKey</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">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">key </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="str">'0'</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> key </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="str">'9'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> key </span><span class="pun">==</span><span class="pln"> </span><span class="str">'+'</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> key </span><span class="pun">==</span><span class="pln"> </span><span class="str">'('</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> key </span><span class="pun">==</span><span class="pln"> </span><span class="str">')'</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> key </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="pun">&lt;/</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">input onkeydown</span><span class="pun">=</span><span class="str">"return checkPhoneKey(event.key)"</span><span class="pln"> placeholder</span><span class="pun">=</span><span class="str">"Phone, please"</span><span class="pln"> type</span><span class="pun">=</span><span class="str">"tel"</span><span class="pun">&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1614949657="1" data-ss1614950031="1" data-ss1614950118="1" data-ss1614950195="1" frameborder="no" height="155" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/xxRJmMO?height=155&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-keyboard-events-ex02">See the Pen JS-keyboard-events-ex02 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	يُرجى التنبّه أنّ المفاتيح الخاصّة، مثل <code>Backspace</code> و<code>Left</code> و<code>Right</code> و<code>Ctrl+V</code>، لا تعمل في المُدخل. هذا أثر جانبيّ للمرشّح <code>checkPhoneKey</code> الصارم. لنقم بإرخائه بعض الشيء:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4958_11" style="">
<span class="pun">&lt;</span><span class="pln">script</span><span class="pun">&gt;</span><span class="pln">
</span><span class="kwd">function</span><span class="pln"> checkPhoneKey</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">
  </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="pln">key </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="str">'0'</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span class="pln"> key </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="str">'9'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> key </span><span class="pun">==</span><span class="pln"> </span><span class="str">'+'</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> key </span><span class="pun">==</span><span class="pln"> </span><span class="str">'('</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> key </span><span class="pun">==</span><span class="pln"> </span><span class="str">')'</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> key </span><span class="pun">==</span><span class="pln"> </span><span class="str">'-'</span><span class="pln"> </span><span class="pun">||</span><span class="pln">
    key </span><span class="pun">==</span><span class="pln"> </span><span class="str">'ArrowLeft'</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> key </span><span class="pun">==</span><span class="pln"> </span><span class="str">'ArrowRight'</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> key </span><span class="pun">==</span><span class="pln"> </span><span class="str">'Delete'</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> key </span><span class="pun">==</span><span class="pln"> </span><span class="str">'Backspace'</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">script</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">&lt;</span><span class="pln">input onkeydown</span><span class="pun">=</span><span class="str">"return checkPhoneKey(event.key)"</span><span class="pln"> placeholder</span><span class="pun">=</span><span class="str">"Phone, please"</span><span class="pln"> type</span><span class="pun">=</span><span class="str">"tel"</span><span class="pun">&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1614949657="1" data-ss1614950031="1" data-ss1614950118="1" data-ss1614950195="1" frameborder="no" height="133" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/gOLjZqe?height=133&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-keyboard-events-ex03">See the Pen JS-keyboard-events-ex03 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	تعمل كلّ من مفاتيح اﻷسهم والحذف جيّدا الآن.
</p>

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

<h2>
	الأحداث والخاصيات القديمة
</h2>

<p>
	في القديم، كان هناك الحدث <code>keypress</code>، بالإضافة إلى خاصّيّات كائن الحدث <code>keyCode</code> و<code>charCode</code> و <code>which</code>. كان هناك الكثير من حالات عدم توافق المتصفّح عند العمل بها، مما أجبر العاملين على المواصفة على التخلّي عنها جميعا وإنشاء أحداث جديدة (كما بُيّنت في أعلى هذا المقال). لا تزال الشيفرات القديمة تعمل، إذ بقيت المتصفّحات تدعمها، لكن ليس هناك سبب إطلاقا لاستخدامها الآن.
</p>

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

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

<p>
	أحداث لوحة المفاتيح:
</p>

<ul>
<li>
		<code>keydown</code> -- عند الضغط على المفتاح (يتكرّر تلقائيّا إذا ضُغط المفتاح طويلا)،
	</li>
	<li>
		<code>keyup</code> -- عند تحرير المفتاح.
	</li>
</ul>
<p>
	خاصّيات أحداث لوحة المفاتيح اﻷساسيّة:
</p>

<ul>
<li>
		<code>code</code> -- "كود المفتاح" (<code>"KeyA"</code> و<code>"ArrowLeft"</code> وهكذا)، متعلّق بالمكان الملموس للمفتاح على لوحة المفاتيح.
	</li>
	<li>
		<code>key</code> -- المحرف (<code>"A"</code> و<code>"a"</code> وهكذا). لمفاتيح غير المحارف، مثل <code>Esc</code>، تكون له نفس قيمة <code>code</code> عادة.
	</li>
</ul>
<p>
	في السابق، كانت أحداث لوحة المفاتيح تُستعمل أحيانا لتتبّع مُدخلات المستخدم في حقول النماذج. هذا غير موثوق، لأنّ المُدخلات قد تأتي من مصادر متنوّعة. لذلك، لدينا الأحداث <code>input</code> و<code>change</code> التي تمكّننا من معالجة أيّ مدخل كان (سنتطرّق لها لاحقا في مقال اﻷحداث change وinput وcut وcopy وpaste). تقع تلك اﻷحداث عند إدخال أيّ نوع من المدخلات، بما في ذلك النسخ واللصق والتعرّف على الكلام.
</p>

<h2>
	التمارين
</h2>

<h3>
	مفاتيح الاختصارات الموسعة
</h3>

<p>
	<em>الأهميّة: 5</em>
</p>

<p>
	أنشئ الدالّة <code>runOnKeys(func, code1, code2, ... code_n)‎</code> التي تعمل على بتنفيذ <code>func</code> عند الضغط في نفس الوقت على المفاتيح التي لها اﻷكواد <code>code1</code>, <code>code2</code>, …, <code>code_n</code>.
</p>

<p>
	على سبيل المثال، تظهر الشيفرة أدناه <code>alert</code> عندما تُضغط المفاتيح <code>"Q"</code> و<code>"W"</code> معا (في أيّ لغة، مع أو بدون تفعيل الأحرف الكبيرة).
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4958_14" style="">
<span class="pln">runOnKeys</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"> alert</span><span class="pun">(</span><span class="str">"Hello!"</span><span class="pun">),</span><span class="pln">
  </span><span class="str">"KeyQ"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"KeyW"</span><span class="pln">
</span><span class="pun">);</span></pre>

<p>
	يمكن مشاهدة المثال يعمل من <a data-ss1614949657="1" data-ss1614950031="1" data-ss1614950118="1" data-ss1614950195="1" href="https://en.js.cx/task/check-sync-keydown/solution/" rel="external nofollow">هنا</a>.
</p>

<h3>
	الحل
</h3>

<p>
	لابدّ أن نستخدم اثنين من المعالجات: <code>document.onkeydown</code> و<code>document.onkeyup</code>.
</p>

<p>
	لننشئ المجموعة <code>pressed = new Set()‎</code> لحفظ المفاتيح المضغوطة حاليّا. يعمل المعالج اﻷوّل على الإضافة إليها، بينما يعمل المعالج الثاني على النزع منها. وكلّما وقع الحدث <code>keydown</code> نتحقّق إن كان لدينا ما يكفي من المفاتيح المضغوطة، وننفّذ الدالّة إذا كان الحال كذلك.
</p>

<p>
	<a data-ss1614949657="1" data-ss1614950031="1" data-ss1614950118="1" data-ss1614950195="1" href="https://plnkr.co/edit/W0sa74x8GS59kXwb?p=preview" rel="external nofollow">افتح الحلّ في البيئة التجريبيّة</a>
</p>

<p>
	ترجمة -وبتصرف- للمقال <a data-ss1614949657="1" data-ss1614950031="1" data-ss1614950118="1" data-ss1614950195="1" href="https://javascript.info/keyboard-events" rel="external nofollow">Keyboard: keydown and keyup</a> من سلسلة <a data-ss1614949657="1" data-ss1614950031="1" data-ss1614950118="1" data-ss1614950195="1" href="https://javascript.info/ui" rel="external nofollow">Browser: Document, Events, Interfaces</a> لصاحبها Ilya Kantor
</p>
]]></description><guid isPermaLink="false">1155</guid><pubDate>Tue, 23 Mar 2021 13:07:00 +0000</pubDate></item><item><title>&#x623;&#x62D;&#x62F;&#x627;&#x62B; &#x627;&#x644;&#x645;&#x624;&#x634;&#x631; &#x648;&#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639;&#x647;&#x627; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A3%D8%AD%D8%AF%D8%A7%D8%AB-%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9%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-r1154/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/60422b1817e36_-----.png.7c9c3118da7b855cda73fd3579d02377.png" /></p>

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

<h2>
	لمحة تاريخية مختصرة
</h2>

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

<ul>
<li>
		<p>
			في القديم، لم تكن هناك سوى أحداث الفأرة. ثمّ انتشرت اﻷجهزة اللمسيّة، مثل الهواتف واﻷجهزة اللوحيّة. ولكي تعمل النصوص البرمجيّة القائمة، كانت هذه اﻷجهزة (ولا زالت) تولّد أحداث الفأرة. فعلى سبيل المثال، يولّد الضغط على شاشة لمسيّة الحدث <code>mousedown</code>. فكانت الأجهزة اللمسيّة بذلك تعمل جيّدا مع صفحات الويب.
		</p>

		<p>
			لكنّ اﻷجهزة اللمسيّة لها قدرات أكثر من الفأرة. فمن الممكن مثلا لمس عدّة نقاط في نفس الوقت ("تعدّد اللمسات"). مع ذلك، لا تملك أحداث الفأرة الخاصّيّات اللازمة للتعامل مع مثل هذه اللمسات المتعدّدة.
		</p>
	</li>
	<li>
		<p>
			ولذلك السبب أُدرجت أحداث اللمس، مثل <code>touchstart</code> و<code>touchend</code> و<code>touchmove</code>، التي لها خاصيّات تتعلّق باللمس (لن نتطرّق إليها بالتفصيل هنا، ﻷنّ أحداث المؤشّر أفضل منها).
		</p>

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

<p>
	حاليًّا، تدعم كافّةُ المتصفّحات المشهورة مواصفة <a data-ss1614949127="1" href="https://www.w3.org/TR/pointerevents2/" rel="external nofollow">أحداث المؤشّر المستوى 2</a> ، بينما لا زالت المواصفة اﻷحدث <a data-ss1614949127="1" href="https://w3c.github.io/pointerevents/" rel="external nofollow">أحداث المؤشّر المستوى 3</a> قيد الإنشاء، وهي في الغالب متوافقة مع أحداث المؤشّر المستوى 2.
</p>

<p>
	ما لم تكن تطوّر من أجل المتصفّحات القديمة، مثل Internet Explorer 10 أو Safari 12، فلا داعي لاستخدام أحداث الفأرة أو اللمس بعد الآن -- يمكنك التغيير نحو أحداث المؤشّر، وستعمل شيفرتك حينها مع كلٍّ من الفأرة واﻷجهزة اللمسيّة.
</p>

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

<h2>
	أنواع أحداث المؤشّر
</h2>

<p>
	تكون تسمية أحداث المؤشّر بطريقة مشابهة لأحداث الفأرة:
</p>

<table>
<thead><tr>
<th>
				حدث المؤشّر
			</th>
			<th>
				حدث الفأرة المشابه
			</th>
		</tr></thead>
<tbody>
<tr>
<td>
				<code>pointerdown</code>
			</td>
			<td>
				‏<code>mousedown</code>
			</td>
		</tr>
<tr>
<td>
				<code>pointerup</code>
			</td>
			<td>
				‏ <code>mouseup</code>
			</td>
		</tr>
<tr>
<td>
				<code>pointermove</code>
			</td>
			<td>
				‏ <code>mousemove</code>
			</td>
		</tr>
<tr>
<td>
				<code>pointerover</code>
			</td>
			<td>
				‏ <code>mouseover</code>
			</td>
		</tr>
<tr>
<td>
				<code>pointerout</code>
			</td>
			<td>
				‏ <code>mouseout</code>
			</td>
		</tr>
<tr>
<td>
				<code>pointerenter</code>
			</td>
			<td>
				‏ <code>mouseenter</code>
			</td>
		</tr>
<tr>
<td>
				<code>pointerleave</code>
			</td>
			<td>
				‏ <code>mouseleave</code>
			</td>
		</tr>
<tr>
<td>
				<code>pointercancel</code>
			</td>
			<td>
				-
			</td>
		</tr>
<tr>
<td>
				<code>gotpointercapture</code>
			</td>
			<td>
				-
			</td>
		</tr>
<tr>
<td>
				<code>lostpointercapture</code>
			</td>
			<td>
				-
			</td>
		</tr>
</tbody>
</table>
<style type="text/css">
table {
    width: 100%;
}

thead {
    vertical-align: middle;

    text-align: center;
} 

td, th {
    border: 1px solid #dddddd;
    text-align: right;
    padding: 8px;
    text-align: inherit;

}
tr:nth-child(even) {
    background-color: #dddddd;
}</style>
<p>
	كما يمكن أن نرى، لكلّ حدث فأرة <code>mouse&lt;event&gt;‎</code>، هناك حدث مؤشّر <code>pointer&lt;event&gt;‎</code> يؤدّي غرضا مشابهًا. هناك أيضا 3 أحداث مؤشّر إضافيّة ليس لها أحداث <code>mouse...‎</code> تقابلها، سنشرحها قريبا.
</p>

<hr>
<p>
	<strong>ملاحظة: استبدال <code>mouse&lt;event&gt;‎</code> بـ <code>pointer&lt;event&gt;‎</code> في الشيفرة</strong>
</p>

<p>
	يمكننا استبدال <code>mouse&lt;event&gt;‎</code> بـ <code>pointer&lt;event&gt;‎</code> في شيفرتنا مع التأكّد من بقاء اﻷمور تعمل كما ينبغي باستخدام الفأرة.
</p>

<hr>
<p>
	إضافة إلى ذلك، سيتحسّن دعم اﻷجهزة اللمسيّة تلقائيّا. بالرغم من أنّنا قد نحتاج إلى إضافة <code>touch-action: none</code> في بعض المواضع في CSS. سنتطرّق إلى ذلك في اﻷسفل في القسم الذي حول <code>pointercancel</code>.
</p>

<h2>
	خاصيات حدث المؤشر
</h2>

<p>
	لأحداث المؤشّر نفس خاصّيّات أحداث الفأرة، مثل <code>clientX/Y</code> و<code>target</code> وغيرها، بالإضافة إلى بعض الخاصّيّات اﻷخرى:
</p>

<ul>
<li>
		<code>pointerId</code> - المعرّف الفريد للمؤشّر الذي تسبّب في الحدث، وهو مولَّد من طرف المتصفّح. يمكّننا من التعامل مع عدّة مؤشّرات، مثل شاشة لمسيّة مع قلم ولمسات متعدّدة (ستأتي الأمثلة).
	</li>
	<li>
		<code>pointerType</code> - نوع جهاز التأشير. لابدّ أن يكون سلسلة نصّيّة من هذه الثلاث: "mouse" أو "pen" أو "touch". يمكننا استخدام هذه الخاصّيّة للتجاوب بشكل مختلف مع شتّى أنواع المؤشّرات.
	</li>
	<li>
		<code>isPrimary</code> - تكون <code>true</code> بالنسبة للمؤشّر اﻷوّليّ (أوّل اصبع عند تعدّد اللمسات).
	</li>
</ul>
<p>
	تقيس بعض أجهزة التأشير مساحة التلامس ومقدار الضغط، لإصبع فوق شاشة لمسيّة على سبيل المثال، وهناك خاصّيّات إضافّيّة لهذا الغرض:
</p>

<ul>
<li>
		<code>width</code> - عرض المساحة التي يلمس فيها المؤشّر (الإصبع مثلا) الجهاز. إذا لم يكن الجهاز يدعم اللمس، كالفأرة مثلا، فإنّ قيمتها تكون دائما <code>1</code>.
	</li>
	<li>
		<code>height</code> - طول المساحة التي يلمس فيها المؤشّر الجهاز. إذا لم يكن الجهاز يدعم اللمس، فإنّ قيمتها تكون دائما <code>1</code>.
	</li>
	<li>
		<code>pressure</code> - قيمة الضغط الناتج عن المؤشّر، وتكون في المجال الذي بين 0 و 1. في الأجهزة التي لا تدعم ذلك، تكون قيمتها إمّا <code>0.5</code> (مضغوط) أو <code>0</code>.
	</li>
	<li>
		<code>tangentialPressure</code> - الضغط المماسيّ المطبّع (normalized tangential pressure).
	</li>
	<li>
		<code>tiltX</code> و<code>tiltY</code> و<code>twist</code> - خاصّيات متعلّقة بالقلم، وتصف طريقة تموضعه بالنسبة للسطح.
	</li>
</ul>
<p>
	ليست هذه الخاصّيات مدعومة في معظم الأجهزة، لهذا يندر استخدامها. يمكنك أن تجد التفاصيل المتعلّقة بها في <a data-ss1614949127="1" href="https://w3c.github.io/pointerevents/#pointerevent-interface" rel="external nofollow">المواصفة</a> عند الحاجة.
</p>

<h2>
	تعدد اللمسات
</h2>

<p>
	من اﻷمور التي لا تدعمها أحداث الفأرة إطلاقا هو تعدّد اللمسات: قد يلمس المستخدم هاتفه أو جهازه اللوحيّ في عدّة أماكن في نفس الوقت، أو قد يؤدّي بعض الحركات الخاصّة. تمكّن أحداث المؤشّر من التعامل مع تعدّد اللمسات بفضل الخاصّيّات <code>pointerId</code> و<code>isPrimary</code>. هذا ما يحدث عندما يلمس المستخدم شاشة لمسيّة في مكان ما، ثمّ يضع عليها إصبعا آخر في مكان مختلف:
</p>

<ol>
<li>
		عند لمسة الإصبع الأولى:
		<ul>
<li>
				يقع الحدث <code>pointerdown</code> مع <code>isPrimary=true</code> و<code>pointerId</code> ما.
			</li>
		</ul>
</li>
	<li>
		عند وضع الإصبع الثاني والمزيد من اﻷصابع (مع افتراض أن اﻷوّل لا يزال ملامسا):
		<ul>
<li>
				يقع الحدث <code>pointerdown</code> مع <code>isPrimary=false</code> و<code>pointerId</code> مختلف لكلّ إصبع.
			</li>
		</ul>
</li>
</ol>
<p>
	يُرجى التنبّه: لا يُسند <code>pointerId</code> إلى الجهاز ككلّ، ولكن لكلّ إصبع ملامس. إذا استخدمنا 5 أصابع للمس الشاشة في نفس الوقت، سنحصل على 5 أحداث <code>pointerdown</code>، كلٌّ له إحداثيّاته الخاصّة و<code>pointerId</code> مختلف.
</p>

<p>
	للأحداث المرتبطة بالإصبع اﻷول دائما <code>isPrimary=true</code>.
</p>

<p>
	يمكننا تتبّع عدّة أصابع ملامسة باستخدام <code>pointerId</code> الخاصّ بها. عندما يحرّك المستخدم إصبعا ثم يزيله، فسنحصل على اﻷحداث <code>pointermove</code> و<code>pointerup</code> مع نفس الـ <code>pointerId</code> الذي حصلنا عليه في <code>pointerdown</code>.
</p>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1614949127="1" frameborder="no" height="265" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/bGBjOvK?height=265&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-pointer-events-ex01">See the Pen JS-p2-pointer-events-ex01 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	يمكنك مشاهدة هذا المثال من <a data-ss1614949127="1" href="https://en.js.cx/article/pointer-events/multitouch/" rel="external nofollow">هنا</a> الذي يعمل على تسجيل اﻷحداث <code>pointerdown</code> و<code>pointerup</code>.
</p>

<p>
	يُرجى التنبّه: يجب أن تستخدم جهازا لمسيّا، كهاتف أو جهاز لوحيّ، لرؤية الفرق في <code>pointerId/isPrimary</code>. بالنسبة للأجهزة الأحاديّة اللمس، كالفأرة مثلا، يكون هناك دائما نفس الـ <code>pointerId</code> و<code>isPrimary=true</code> ، عند جميع أحداث المؤشّر.
</p>

<h2>
	الحدث pointercancel
</h2>

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

<ul>
<li>
		تعطيل عتاد جهاز التأشير تعطيلا حسّيًّا.
	</li>
	<li>
		تغيير اتجاه الجهاز (كتدوير الجهاز اللوحيّ).
	</li>
	<li>
		قرار المتصفّح معالجة التفاعل بنفسه، ظنًّا منه أنّها حركة للفأرة أو حركة تكبير وتحريك أو غير ذلك.
	</li>
</ul>
<p>
	سنستعرض الحدث <code>pointercancel</code> في مثال عمليّ لنرى كيف يمكن أن يمسّنا. لنفترض أنّنا نودّ إنجاز سحب وإفلات لكرة ما، تماما كما في بداية المقال <a data-ss1614949127="1" href="https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%B3%D8%AD%D8%A8-%D9%88%D8%A7%D9%84%D8%A5%D9%81%D9%84%D8%A7%D8%AA-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A3%D8%AD%D8%AF%D8%A7%D8%AB-%D8%A7%D9%84%D9%81%D8%A3%D8%B1%D8%A9-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9%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-r1153/" rel="">السحب والإفلات باستخدام أحداث الفأرة</a>. ستأخذ أفعال المستخدم والأحداث المقابلة لها المجرى التالي:
</p>

<ol>
<li>
		يضغط المستخدم على الصورة لابتداء السحب
		<ul>
<li>
				ينطلق الحدث <code>pointerdown</code>
			</li>
		</ul>
</li>
	<li>
		ثم يبدأ بتحريك المؤشّر (وبذلك سحب الصورة)
		<ul>
<li>
				ينطلق الحدث <code>pointermove</code>، ربّما عدّة مرّات
			</li>
		</ul>
</li>
	<li>
		وبعدها تحصل المفاجأة! للمتصفّح دعم أصيل (native) لسحب وإفلات الصور، فيستجيب ويستولي على عمليّة السحب والإفلات، مولّدا بذلك الحدث <code>pointercancel</code>.
		<ul>
<li>
				يعالج المتصفّح الآن سحب وإفلات الصورة بنفسه. يستطيع المستخدم سحب صورة الكرة حتى إلى خارج المتصفّح، إلى برنامج البريد الخاصّ به أو إلى مدير الملفّات.
			</li>
			<li>
				لا مزيد من الأحداث <code>pointermove</code> بالنسبة لنا.
			</li>
		</ul>
</li>
</ol>
<p>
	فالمشكلة إذًا هي أنّ المتصفّح "يختطف" التفاعل: ينطلق الحدث <code>pointercancel</code> في بداية عمليّة "السحب والإفلات"، ولا تتولّد المزيد من أحداث <code>pointermove</code>.
</p>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1614949127="1" frameborder="no" height="310" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/bGBjOMK?height=310&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-pointer-events-ex02">See the Pen JS-p2-pointer-events-ex02 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	يمكن مشاهدة السحب والإفلات من <a data-ss1614949127="1" href="https://en.js.cx/article/pointer-events/ball/" rel="external nofollow">هنا</a>، مع تسجيلٍ لأحداث المؤشّر (<code>up/down</code> و<code>move</code> و<code>cancel</code> فقط) في المساحة النصيّة <code>textarea</code>.
</p>

<p>
	نريد الآن إنجاز السحب والإفلات بأنفسنا، فلنطلب من المتصفّح إذًا عدم الاستيلاء عليه.
</p>

<p>
	<strong>منع فعل المتصفّح الافتراضيّ لتجنّب <code>pointercancel</code>.</strong>
</p>

<p>
	نحتاج إلى فعل أمرين:
</p>

<ol>
<li>
		منع السحب والإفلات اﻷصيل من الوقوع:
		<ul>
<li>
				يمكننا فعل ذلك عن طريق وضع <code>ball.ondragstart = () =&gt; false</code>، تماما كما هو موضّح في المقال <a data-ss1614949127="1" href="#" rel="">السحب والإفلات باستخدام أحداث الفأرة</a>.
			</li>
			<li>
				يعمل ذلك جيّدا مع أحداث الفأرة.
			</li>
		</ul>
</li>
	<li>
		بالنسبة للأجهزة اللمسيّة، هناك أفعال أخرى للمتصفّح متعلّقة باللمس (عدا السحب والإفلات). لتجنّب المشاكل معها أيضا:
		<ul>
<li>
				يمكن منعها بواسطة وضع <code>‎#ball { touch-action: none }‎</code> في CSS.
			</li>
			<li>
				عندئذ ستصير شيفرتنا تعمل على اﻷجهزة اللمسيّة.
			</li>
		</ul>
</li>
</ol>
<p>
	بعد القيام بذلك، ستعمل اﻷحداث كما ينبغي، ولن يختطف المتصفّح العمليّة ولن يرسل الحدث <code>pointercancel</code>.
</p>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1614949127="1" frameborder="no" height="265" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/BaQPvVx?height=265&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-pointer-events-ex03">See the Pen JS-p2-pointer-events-ex03 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	يضيف المثال السابق (<a data-ss1614949127="1" href="https://en.js.cx/article/pointer-events/ball-2/" rel="external nofollow">تجربه حية</a>) هذه اﻷسطر. وكما ترى، لا مزيد من اﻷحداث <code>pointercancel</code> بعد الآن.
</p>

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

<h2>
	أسر المؤشر
</h2>

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

<p>
	التابع العامّ هو:
</p>

<ul>
<li>
		<code>elem.setPointerCapture(pointerId)‎</code> - يربط اﻷحداثَ التي لها <code>pointerId</code> المُعطى بالعنصر <code>elem</code>. بعد الاستدعاء، ستكون لجميع أحداث المؤشّر التي لها نفس <code>pointerId</code> الهدفَ <code>elem</code> (كما لو أنّها وقعت على <code>elem</code>)، أيّا كان مكان وقوعها حقيقةً في المستند.
	</li>
</ul>
<p>
	بعبارة أخرى، يعيد التابع <code>elem.setPointerCapture(pointerId)‎</code> توجيه جميع ما يلي من اﻷحداث التي لها <code>pointerId</code> المعطى نحو <code>elem</code>.
</p>

<p>
	يُزال هذا الربط:
</p>

<ul>
<li>
		تلقائيّا عندما تقع اﻷحداث <code>pointerup</code> أو <code>pointercancel</code>،
	</li>
	<li>
		تلقائيّا عندما يُزال العنصر <code>elem</code> من المستند،
	</li>
	<li>
		عند استدعاء <code>elem.releasePointerCapture(pointerId)‎</code>.
	</li>
</ul>
<p>
	<strong>قد يُستخدم أسر المؤشّر لتبسيط التفاعلات التي من نوع السحب والإفلات.</strong>
</p>

<p>
	على سبيل المثال، لنتذكّر كيف يمكن إنجاز منزلق مخصّص، كما في مقال <a data-ss1614949127="1" href="#" rel="">السحب والإفلات باستخدام أحداث الفأرة</a>. ننشئ عنصر المنزلق على شكل شريط مع "زلاجة" (<code>thumb</code>) في داخله. ثم يعمل هكذا:
</p>

<ol>
<li>
		يضغط المستخدم على الزلّاجة <code>thumb</code> - يقع <code>pointerdown</code>.
	</li>
	<li>
		ثمّ يحرّك المستخدم المؤشّر - يقع <code>pointermove</code>، ونحرّك <code>thumb</code> تبعًا.
	</li>
	<li>
		…عند تحرّك المؤشّر، قد يفارق الزلّاجة <code>thumb</code>، فيذهب فوقها أو تحتها. يجب ألّا يتحرّك <code>thumb</code> إلا أفقيًّا، مع بقائه موازيًا للمؤشّر.
	</li>
</ol>
<p>
	فمن أجل تتبّع جميع حركات المؤشّر، بما في ذلك ذهابه فوق/تحت <code>thumb</code>، كان علينا عندها إسناد معالج الحدث <code>pointermove</code> إلى المستند <code>document</code>.
</p>

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

<p>
	يقدّم أسر المتصفّح وسيلة لربط الحدث <code>pointermove</code> بالعنصر <code>thumb</code> وتجنّب أيّ من هذه المشاكل:
</p>

<ul>
<li>
		يمكننا استدعاء <code>thumb.setPointerCapture(event.pointerId)‎</code> في معالج <code>pointerdown</code>،
	</li>
	<li>
		فيُعاد توجيه أحداث المؤشّر نحو <code>thumb</code> إلى حين وقوع <code>pointerup/cancel</code>.
	</li>
	<li>
		عند وقوع <code>pointerup</code> (انتهاء السحب)، يزول الربط تلقائيّا، وليس علينا أن نكترث له.
	</li>
</ul>
<p>
	فبذلك حتى لو حرّك المستخدم المؤشّر حول المستند برُمَّته، ستُستدعى معالجات اﻷحداث على <code>thumb</code>. ما عدا ذلك، تبقى خاصّيات الإحداثيّات في كائنات اﻷحداث، مثل <code>clientX/clientY</code> صحيحة - يمسّ اﻷسر <code>target/currentTarget</code> فقط.
</p>

<p>
	إليك الشيفرة اﻷساسيّة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_2928_7" style="">
<span class="pln">thumb</span><span class="pun">.</span><span class="pln">onpointerdown </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">event</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// thumb إلى (pointerup إلى حين‎) يعاد توجيه جميع أحداث المؤشّر</span><span class="pln">
  thumb</span><span class="pun">.</span><span class="pln">setPointerCapture</span><span class="pun">(</span><span class="pln">event</span><span class="pun">.</span><span class="pln">pointerId</span><span class="pun">);</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

thumb</span><span class="pun">.</span><span class="pln">onpointermove </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">event</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="com">// إذ جميع أحداث المؤشّر موجّهة نحوه ،thumb تحريك المؤشّر: يكون الإنصات في</span><span class="pln">
  let newLeft </span><span class="pun">=</span><span class="pln"> event</span><span class="pun">.</span><span class="pln">clientX </span><span class="pun">-</span><span class="pln"> slider</span><span class="pun">.</span><span class="pln">getBoundingClientRect</span><span class="pun">().</span><span class="pln">left</span><span class="pun">;</span><span class="pln">
  thumb</span><span class="pun">.</span><span class="pln">style</span><span class="pun">.</span><span class="pln">left </span><span class="pun">=</span><span class="pln"> newLeft </span><span class="pun">+</span><span class="pln"> </span><span class="str">'px'</span><span class="pun">;</span><span class="pln">
</span><span class="pun">};</span><span class="pln">

</span><span class="com">// ،thumb.releasePointerCapture ملاحظة: لا حاجة لاستدعاء</span><span class="pln">
</span><span class="com">// تلقائيّا pointerup إذ تقع عند </span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1614949127="1" frameborder="no" height="144" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/RwoBEeL?height=144&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-pointer-events-ex04">See the Pen JS-p2-pointer-events-ex04 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	يمكن مشاهدة المثال كاملا من <a data-ss1614949127="1" href="https://en.js.cx/article/pointer-events/slider/" rel="external nofollow">هنا</a>.
</p>

<p>
	في النهاية، لأسر المؤشّر فائدتان:
</p>

<ol>
<li>
		تصير الشيفرة أنظف، إذ لا نحتاج إلى إضافة/إزالة المعالجات إلى كامل المستند. يُفكّ الرّبط تلقائيّا.
	</li>
	<li>
		إذا كانت هناك أيّة معالجات <code>pointermove</code> على المستند، فلن يؤدّي المؤشّر إلى اشتغالها دون قصد عندما يسحب المستخدم المنزلق.
	</li>
</ol>
<h3>
	أحداث أسر المؤشر
</h3>

<p>
	يوجد اثنان من أحداث المؤشّر التي تتعلّق باﻷسر:
</p>

<ul>
<li>
		ينطلق <code>gotpointercapture</code> عندما يستخدم عنصرٌ ما <code>setPointerCapture</code> لتفعيل اﻷسر.
	</li>
	<li>
		ينطلق <code>lostpointercapture</code> عندما يُفكّ اﻷسر: سواءً كان ذلك صراحة بواسطة استدعاء <code>releasePointerCapture</code>، أو تلقائيّا عند <code>pointerup</code>/<code>pointercancel</code>.
	</li>
</ul>
<h3>
	الملخص
</h3>

<p>
	تمكّن أحداث المؤشّر من التعامل مع أحداث الفأرة واللمس والقلم في نفس الوقت، باستخدام شيفرة واحدة.
</p>

<p>
	أحداث المؤشّر هي توسعة لأحداث الفأرة. يمكن استبدال <code>mouse</code> بـ <code>pointer</code> في أسماء اﻷحداث وسيظلّ كلّ شيء يعمل مع الفأرة، بالإضافة إلى دعم أفضل ﻷنواع اﻷجهزة اﻷخرى.
</p>

<p>
	بالنسبة للسحب والإفلات وغيرها من تفاعلات اللمس المعقّدة التي قد يقرّر المتصفّح اختطافها ومعالجتها بنفسه، تذكّر إلغاء الفعل الافتراضيّ للأحداث ووضع <code>touch-events: none</code> في CSS للعناصر التي نتعامل معها.
</p>

<p>
	لأحداث المؤشّر القدرات الإضافيّة التالية:
</p>

<ul>
<li>
		دعم اللمس المتعدّد باستخدام <code>pointerId</code> و<code>isPrimary</code>.
	</li>
	<li>
		الخاصيّات المتعلّقة ببعض اﻷجهزة، مثل <code>pressure</code> و<code>width/height</code> وغيرها.
	</li>
	<li>
		أسر المؤشّر: يمكننا إعادة توجيه جميع أحداث المؤشر نحو عنصر محدّد إلى حين وقوع <code>pointerup</code>/<code>pointercancel</code>.
	</li>
</ul>
<p>
	حاليّا، تدعم جميع المتصفّحات المشهورة أحداث المؤشّر، وبذلك يمكننا الانتقال إليها بأمان، خاصّة إذا لم يُحتج إلى -IE10 و-Safari 12. بل حتّى مع تلك المتصفّحات، هناك ترقيعات متعدّدة (polyfills) تمكّن من دعم أحداث المؤشّر.
</p>

<p>
	ترجمة -وبتصرف- للمقال <a data-ss1614949127="1" href="https://javascript.info/pointer-events" rel="external nofollow">Pointer events</a> من سلسلة <a data-ss1614949127="1" href="https://javascript.info/ui" rel="external nofollow">Browser: Document, Events, Interfaces</a> لصاحبها Ilya Kantor
</p>
]]></description><guid isPermaLink="false">1154</guid><pubDate>Sat, 20 Mar 2021 13:03:00 +0000</pubDate></item><item><title>&#x627;&#x644;&#x633;&#x62D;&#x628; &#x648;&#x627;&#x644;&#x625;&#x641;&#x644;&#x627;&#x62A; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x623;&#x62D;&#x62F;&#x627;&#x62B; &#x627;&#x644;&#x641;&#x623;&#x631;&#x629; &#x648;&#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639;&#x647;&#x627; &#x641;&#x64A; &#x62C;&#x627;&#x641;&#x627;&#x633;&#x643;&#x631;&#x628;&#x62A;</title><link>https://academy.hsoub.com/programming/javascript/%D8%A7%D9%84%D8%B3%D8%AD%D8%A8-%D9%88%D8%A7%D9%84%D8%A5%D9%81%D9%84%D8%A7%D8%AA-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A3%D8%AD%D8%AF%D8%A7%D8%AB-%D8%A7%D9%84%D9%81%D8%A3%D8%B1%D8%A9-%D9%88%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9%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-r1153/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2021_03/603e33ed362ba_--------.png.320366900db291070b7e34fbc96d19d3.png" /></p>

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

<p>
	في مواصفة HTML الحديثة، هناك <a data-ss1614954181="1" href="https://html.spec.whatwg.org/multipage/interaction.html#dnd" rel="external nofollow">قسم حول السحب والإفلات</a> مع أحداث خاصّة بها مثل <code>dragstart</code> و<code>dragend</code> وغيرها.
</p>

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

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

<p>
	لذا سنرى هنا كيفيّة إنجاز السحب والإفلات باستخدام أحداث الفأرة.
</p>

<h2>
	خوارزمية السحب والإفلات
</h2>

<p>
	يمكن وصف الخوارزميّة اﻷساسيّة للسحب والإفلات كالتالي:
</p>

<ol>
<li>
		عند <code>mousedown</code>، جهّز العنصر لتحريكه، عند الحاجة لذلك (ربما بإنشاء نسخة عنه، أو إضافة صنف له أو أيًّا كان ذلك).
	</li>
	<li>
		ثمّ عند <code>mousemove</code>، حرّكه عن طريق تغيير <code>left/top</code> بواسطة <code>position:absolute</code>.
	</li>
	<li>
		عند <code>mouseup</code>، قم بجميع اﻷفعال المرتبطة بإنهاء السحب والإفلات.
	</li>
</ol>
<p>
	هذه هي الأساسيّات. سنرى لاحقا كيفيّة إضافة ميزات أخرى، مثل إبراز العناصر التحتيّة الحاليّة عند السحب فوقها.
</p>

<p>
	هذا تطبيق لعمليّة السحب على كرة:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3435_7" style="">
<span class="pln">ball</span><span class="pun">.</span><span class="pln">onmousedown </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">event</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> 
  </span><span class="com">// (1) z-index تجهّز للتحريك: اجعل وضعيّة الكرة مطلقة وفي الأعلى بواسطة</span><span class="pln">
  ball</span><span class="pun">.</span><span class="pln">style</span><span class="pun">.</span><span class="pln">position </span><span class="pun">=</span><span class="pln"> </span><span class="str">'absolute'</span><span class="pun">;</span><span class="pln">
  ball</span><span class="pun">.</span><span class="pln">style</span><span class="pun">.</span><span class="pln">zIndex </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">;</span><span class="pln">

  </span><span class="com">// مباشرة body افصلها عن أيّ آباءٍ لها وألحقها بـ</span><span class="pln">
  </span><span class="com">// body اجعلها متموضعة بالنسبة لـ</span><span class="pln">
  document</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">append</span><span class="pun">(</span><span class="pln">ball</span><span class="pun">);</span><span class="pln">  

  </span><span class="com">// (pageX, pageY) مركِز الكرةَ في الإحداثيّات</span><span class="pln">
  </span><span class="kwd">function</span><span class="pln"> moveAt</span><span class="pun">(</span><span class="pln">pageX</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">
    ball</span><span class="pun">.</span><span class="pln">style</span><span class="pun">.</span><span class="pln">left </span><span class="pun">=</span><span class="pln"> pageX </span><span class="pun">-</span><span class="pln"> ball</span><span class="pun">.</span><span class="pln">offsetWidth </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="str">'px'</span><span class="pun">;</span><span class="pln">
    ball</span><span class="pun">.</span><span class="pln">style</span><span class="pun">.</span><span class="pln">top </span><span class="pun">=</span><span class="pln"> pageY </span><span class="pun">-</span><span class="pln"> ball</span><span class="pun">.</span><span class="pln">offsetHeight </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="str">'px'</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">

  </span><span class="com">// حرّك الكرة المطلقة التموضع أسفل المؤشّر</span><span class="pln">
  moveAt</span><span class="pun">(</span><span class="pln">event</span><span class="pun">.</span><span class="pln">pageX</span><span class="pun">,</span><span class="pln"> event</span><span class="pun">.</span><span class="pln">pageY</span><span class="pun">);</span><span class="pln">

  </span><span class="kwd">function</span><span class="pln"> onMouseMove</span><span class="pun">(</span><span class="pln">event</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    moveAt</span><span class="pun">(</span><span class="pln">event</span><span class="pun">.</span><span class="pln">pageX</span><span class="pun">,</span><span class="pln"> event</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">

  </span><span class="com">// (2) mousemove حرّك الكرة عند</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"> onMouseMove</span><span class="pun">);</span><span class="pln">

  </span><span class="com">// (3) أفلت الكرة وأزل المعالجات غير المرغوبة</span><span class="pln">
  ball</span><span class="pun">.</span><span class="pln">onmouseup </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</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">'mousemove'</span><span class="pun">,</span><span class="pln"> onMouseMove</span><span class="pun">);</span><span class="pln">
    ball</span><span class="pun">.</span><span class="pln">onmouseup </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">;</span><span class="pln">
  </span><span class="pun">};</span><span class="pln">

</span><span class="pun">};</span></pre>

<p>
	إذا أجرينا الشيفرة، يمكننا ملاحظة شيء غريب. في بداية السحب والإفلات، "تُستنسخ" الكرة، ونقوم بسحب "نسختها". (يمكن مشاهدة المثال يعمل من <a data-ss1614689249="1" data-ss1614954181="1" href="https://en.js.cx/article/mouse-drag-and-drop/ball/" rel="external nofollow">هنا</a>).
</p>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1614689249="1" data-ss1614954181="1" frameborder="no" height="330" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/PobeaRq?height=330&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="PobeaRq">See the Pen PobeaRq by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

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

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3435_9" style="">
<span class="pln">ball</span><span class="pun">.</span><span class="pln">ondragstart </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</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></pre>

<p>
	سيكون الآن كلّ شيء على ما يرام، كما يمكن رؤية ذلك من <a data-ss1614689249="1" data-ss1614954181="1" href="https://en.js.cx/article/mouse-drag-and-drop/ball2/" rel="external nofollow">هنا</a>.
</p>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1614689249="1" data-ss1614954181="1" frameborder="no" height="338" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/YzpLvOo?height=338&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-mouse-drag-and-drop-ex02">See the Pen JS-p2-mouse-drag-and-drop-ex02 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

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

<h2>
	التموضع الصحيح
</h2>

<p>
	في الأمثلة أعلاه تتحرّك الكرة ويبقى منتصفها تحت المؤشّر على الدوام:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3435_11" style="">
<span class="pln">ball</span><span class="pun">.</span><span class="pln">style</span><span class="pun">.</span><span class="pln">left </span><span class="pun">=</span><span class="pln"> pageX </span><span class="pun">-</span><span class="pln"> ball</span><span class="pun">.</span><span class="pln">offsetWidth </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="str">'px'</span><span class="pun">;</span><span class="pln">
ball</span><span class="pun">.</span><span class="pln">style</span><span class="pun">.</span><span class="pln">top </span><span class="pun">=</span><span class="pln"> pageY </span><span class="pun">-</span><span class="pln"> ball</span><span class="pun">.</span><span class="pln">offsetHeight </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="str">'px'</span><span class="pun">;</span></pre>

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

<p>
	قد يكون من اﻷفضل الإبقاء على الإزاحة اﻷوليّة للعنصر بالنسبة للمؤشّر. على سبيل المثال، إذا بدأنا السحب من حافّة الكرة، فإنّ المؤشّر يبقى على الحافّة خلال السحب.
</p>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="58803" data-ss1614689249="1" data-ss1614954181="1" href="https://academy.hsoub.com/uploads/monthly_2021_03/ball_shift.png.756b996d1fcc7dab1a91cd470dcbd638.png" rel=""><img alt="ball_shift.png" class="ipsImage ipsImage_thumbnailed" data-fileid="58803" data-unique="fdjl4l6rz" src="https://academy.hsoub.com/uploads/monthly_2021_03/ball_shift.png.756b996d1fcc7dab1a91cd470dcbd638.png"></a>
</p>

<p>
	لنحدّث الخوارزميّة:
</p>

<p>
	<strong>أولًا</strong>، عندما يضغط الزائر على الزرّ (<code>mousedown</code>)، احفظ المسافة من المؤشّر إلى الزاوية العليا من اليسار للكرة في المتغيّرات <code>shiftX/shiftY</code>. سنحافظ على تلك المسافة خلال السحب.
</p>

<p>
	يمكننا الحصول على تلك الإزاحات عن طريق طرح الإحداثيّات:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3435_13" style="">
<span class="pln">    </span><span class="com">// onmousedown في</span><span class="pln">
    let shiftX </span><span class="pun">=</span><span class="pln"> event</span><span class="pun">.</span><span class="pln">clientX </span><span class="pun">-</span><span class="pln"> ball</span><span class="pun">.</span><span class="pln">getBoundingClientRect</span><span class="pun">().</span><span class="pln">left</span><span class="pun">;</span><span class="pln">
    let shiftY </span><span class="pun">=</span><span class="pln"> event</span><span class="pun">.</span><span class="pln">clientY </span><span class="pun">-</span><span class="pln"> ball</span><span class="pun">.</span><span class="pln">getBoundingClientRect</span><span class="pun">().</span><span class="pln">top</span><span class="pun">;</span><span class="pln">
    </span></pre>

<p>
	ثانيًا، بعد ذلك نُبقي الكرة على نفس الإزاحة بالنسبة للمؤشّر خلال السحب هكذا:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3435_15" style="">
<span class="com">// onmousemove في</span><span class="pln">
</span><span class="com">// position:absolute الكرة لها </span><span class="pln">
ball</span><span class="pun">.</span><span class="pln">style</span><span class="pun">.</span><span class="pln">left </span><span class="pun">=</span><span class="pln"> event</span><span class="pun">.</span><span class="pln">pageX </span><span class="pun">-</span><span class="pln"> </span><span class="pun">*!*</span><span class="pln">shiftX</span><span class="pun">*/!*</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">'px'</span><span class="pun">;</span><span class="pln">
ball</span><span class="pun">.</span><span class="pln">style</span><span class="pun">.</span><span class="pln">top </span><span class="pun">=</span><span class="pln"> event</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">shiftY</span><span class="pun">*/!*</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">'px'</span><span class="pun">;</span><span class="pln">
</span></pre>

<p>
	هذه الشيفرة النهائيّة لتموضع أفضل:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3435_17" style="">
<span class="pln">ball</span><span class="pun">.</span><span class="pln">onmousedown </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">event</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

  let shiftX </span><span class="pun">=</span><span class="pln"> event</span><span class="pun">.</span><span class="pln">clientX </span><span class="pun">-</span><span class="pln"> ball</span><span class="pun">.</span><span class="pln">getBoundingClientRect</span><span class="pun">().</span><span class="pln">left</span><span class="pun">;</span><span class="pln">
  let shiftY </span><span class="pun">=</span><span class="pln"> event</span><span class="pun">.</span><span class="pln">clientY </span><span class="pun">-</span><span class="pln"> ball</span><span class="pun">.</span><span class="pln">getBoundingClientRect</span><span class="pun">().</span><span class="pln">top</span><span class="pun">;</span><span class="pln">

  ball</span><span class="pun">.</span><span class="pln">style</span><span class="pun">.</span><span class="pln">position </span><span class="pun">=</span><span class="pln"> </span><span class="str">'absolute'</span><span class="pun">;</span><span class="pln">
  ball</span><span class="pun">.</span><span class="pln">style</span><span class="pun">.</span><span class="pln">zIndex </span><span class="pun">=</span><span class="pln"> </span><span class="lit">1000</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">append</span><span class="pun">(</span><span class="pln">ball</span><span class="pun">);</span><span class="pln">

  moveAt</span><span class="pun">(</span><span class="pln">event</span><span class="pun">.</span><span class="pln">pageX</span><span class="pun">,</span><span class="pln"> event</span><span class="pun">.</span><span class="pln">pageY</span><span class="pun">);</span><span class="pln">

  </span><span class="com">// (pageX, pageY) تحريك الكرة إلى الإحداثيّات</span><span class="pln">
  </span><span class="com">// مع أخذ الإزاحة اﻷوّليّة في الحسبان</span><span class="pln">
  </span><span class="kwd">function</span><span class="pln"> moveAt</span><span class="pun">(</span><span class="pln">pageX</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">
    ball</span><span class="pun">.</span><span class="pln">style</span><span class="pun">.</span><span class="pln">left </span><span class="pun">=</span><span class="pln"> pageX </span><span class="pun">-</span><span class="pln"> </span><span class="pun">*!*</span><span class="pln">shiftX</span><span class="pun">*/!*</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">'px'</span><span class="pun">;</span><span class="pln">
    ball</span><span class="pun">.</span><span class="pln">style</span><span class="pun">.</span><span class="pln">top </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">shiftY</span><span class="pun">*/!*</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">'px'</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"> onMouseMove</span><span class="pun">(</span><span class="pln">event</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    moveAt</span><span class="pun">(</span><span class="pln">event</span><span class="pun">.</span><span class="pln">pageX</span><span class="pun">,</span><span class="pln"> event</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">

  </span><span class="com">// mousemove حرّك الكرة عند</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"> onMouseMove</span><span class="pun">);</span><span class="pln">

  </span><span class="com">// أفلت الكرة وأزل المعالجات غير المرغوبة</span><span class="pln">
  ball</span><span class="pun">.</span><span class="pln">onmouseup </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</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">'mousemove'</span><span class="pun">,</span><span class="pln"> onMouseMove</span><span class="pun">);</span><span class="pln">
    ball</span><span class="pun">.</span><span class="pln">onmouseup </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">;</span><span class="pln">
  </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">ondragstart </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</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></pre>

<p>
	يمكن مشاهدة ذلك يعمل من <a data-ss1614689249="1" data-ss1614954181="1" href="https://en.js.cx/article/mouse-drag-and-drop/ball3/" rel="external nofollow">هنا</a>.
</p>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1614689249="1" data-ss1614954181="1" frameborder="no" height="326" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/OJbZErN?height=326&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-mouse-drag-and-drop-ex03">See the Pen JS-p2-mouse-drag-and-drop-ex03 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	يظهر الفرق جليًّا إذا جررنا الكرة من زاويتها السفلى من اليمين. في المثال السابق "تقفز" الكرة تحت المؤشر. أمّا الآن فهي تتبع المؤشّر بسلاسة من وضعيّتها اﻷوليّة.
</p>

<h2>
	أهداف الإفلات المحتملة (العناصر القابلة للإفلات فوقها)
</h2>

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

<p>
	نحتاج لذلك إلى معرفة:
</p>

<ul>
<li>
		مكان إفلات العنصر في نهاية السحب والإفلات -- للقيام بالفعل المناسب،
	</li>
	<li>
		ومن المستحسن، العناصر القابلة للإفلات فوقها عند السحب فوقها، لإبرازها.
	</li>
</ul>
<p>
	قد يكون الحلّ مثيرا للاهتمام نوعا ما، وشائكًا بعض الشيء لذا سنعمل على شرحه هنا.
</p>

<p>
	ما الفكرة التي يمكن أن تتبادر إلى الذهن؟ ربّما إسناد معالجات لـ <code>mouseover/mouseup</code> إلى العناصر المحتملة القابلة للإفلات فوقها؟
</p>

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

<p>
	على سبيل المثال، يوجد في اﻷسفل عنصرا <code>&lt;div&gt;</code>، حيث أنّ العنصر اﻷحمر فوق العنصر اﻷزرق (يغطّيه تماما). لا سبيل إلى التقاط حدثٍ في العنصر اﻷزرق لأنّ اﻷحمر موجود أعلاه:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3435_19" style="">
<span class="tag">&lt;style&gt;</span><span class="pln">
  div </span><span class="pun">{</span><span class="pln">
    width</span><span class="pun">:</span><span class="pln"> </span><span class="lit">50px</span><span class="pun">;</span><span class="pln">
    height</span><span class="pun">:</span><span class="pln"> </span><span class="lit">50px</span><span class="pun">;</span><span class="pln">
    position</span><span class="pun">:</span><span class="pln"> absolute</span><span class="pun">;</span><span class="pln">
    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="pun">}</span><span class="pln">
</span><span class="tag">&lt;/style&gt;</span><span class="pln">
</span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">style</span><span class="pun">=</span><span class="atv">"</span><span class="pln">background</span><span class="pun">:</span><span class="pln">blue</span><span class="atv">"</span><span class="pln"> </span><span class="atn">onmouseover</span><span class="pun">=</span><span class="atv">"</span><span class="pln">alert</span><span class="pun">(</span><span class="str">'never works'</span><span class="pun">)</span><span class="atv">"</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">style</span><span class="pun">=</span><span class="atv">"</span><span class="pln">background</span><span class="pun">:</span><span class="pln">red</span><span class="atv">"</span><span class="pln"> </span><span class="atn">onmouseover</span><span class="pun">=</span><span class="atv">"</span><span class="pln">alert</span><span class="pun">(</span><span class="str">'over red!'</span><span class="pun">)</span><span class="atv">"</span><span class="tag">&gt;&lt;/div&gt;</span></pre>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1614689249="1" data-ss1614954181="1" frameborder="no" height="171" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/eYBrKxr?height=171&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-mouse-drag-and-drop-ex04">See the Pen JS-p2-mouse-drag-and-drop-ex04 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

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

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

<p>
	يوجد هناك تابع يُطلق عليه <code>document.elementFromPoint(clientX, clientY)‎</code>. يُعيد هذا التابع العنصر السفليّ الموجود عند الإحداثيّات المُعطاة بالنسبة إلى النافذة (أو <code>null</code> إذا كانت الإحداثيّات المُعطاة خارج النافذة).
</p>

<p>
	يمكننا استخدامه في أيٍّ من معالجات أحداث الفأرة التي لدينا لاكتشاف العناصر المحتملة القابلة للإفلات فوقها تحت المؤشّر، هكذا:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_3435_21" style="">
<span class="pln">// داخل معالج حدث فأرة
ball.hidden = true; // (*) أخفِ العنصر الذي نسحبه

let elemBelow = document.elementFromPoint(event.clientX, event.clientY);
// هو العنصر الذي تحت الكرة، قد يكون قابلا للإفلات فوقه elemBelow 
ball.hidden = false;</span></pre>

<p>
	يُرجى التنبّه: نحتاج إلى إخفاء الكرة قبل الاستدعاء <code>(*)</code>، وإلّا فستكون الكرة في تلك الإحداثيّات في الغالب، لأنّها هي العنصر العلويّ تحت المؤشّر: <code>elemBelow=ball</code>. لذا نخفيها ثمّ نظهرها من جديد مباشرة.
</p>

<p>
	يمكننا استخدام هذه الشيفرة لمعرفة العنصر الذي "نحلّق فوقه" في أيّ وقت كان. ثم معالجة الإفلات عند حدوثه.
</p>

<p>
	هذه شيفرة مفصّلة لـ <code>onMouseMove</code> لإيجاد العناصر "القابلة للإفلات فوقها":
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_3435_23" style="">
<span class="com">// العنصر المحتمل القابل للإفلات فوقه الذي نحلّق فوقه الآن</span><span class="pln">
let currentDroppable </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> onMouseMove</span><span class="pun">(</span><span class="pln">event</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  moveAt</span><span class="pun">(</span><span class="pln">event</span><span class="pun">.</span><span class="pln">pageX</span><span class="pun">,</span><span class="pln"> event</span><span class="pun">.</span><span class="pln">pageY</span><span class="pun">);</span><span class="pln">

  ball</span><span class="pun">.</span><span class="pln">hidden </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln">
  let elemBelow </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">elementFromPoint</span><span class="pun">(</span><span class="pln">event</span><span class="pun">.</span><span class="pln">clientX</span><span class="pun">,</span><span class="pln"> event</span><span class="pun">.</span><span class="pln">clientY</span><span class="pun">);</span><span class="pln">
  ball</span><span class="pun">.</span><span class="pln">hidden </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">// خارج النافذة (عندما نسحب الكرة إلى خارج الشاشة) ‏ mousemove قد تقع اﻷحداث</span><span class="pln">
  </span><span class="com">// null يعيد elementFromPoint خارج النافذة، فإنّ clientX/clientY إذا كانت</span><span class="pln">
  </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">elemBelow</span><span class="pun">)</span><span class="pln"> </span><span class="kwd">return</span><span class="pun">;</span><span class="pln">

  </span><span class="com">// potential droppables are labeled with the class "droppable" (can be other logic)</span><span class="pln">
  </span><span class="com">// (قد يكون منطقًا آخر) “droppable” تُعلَّم العناصر المحتملة القابلة للسحب بالصنف </span><span class="pln">
  let droppableBelow </span><span class="pun">=</span><span class="pln"> elemBelow</span><span class="pun">.</span><span class="pln">closest</span><span class="pun">(</span><span class="str">'.droppable'</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">currentDroppable </span><span class="pun">!=</span><span class="pln"> droppableBelow</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">// null انتبه: قد تكون كلتا القيمتان</span><span class="pln">
    </span><span class="com">// (إذا لم نكن فوق عنصر قابل للإفلات فوقه قبل وقوع هذا الحدث (كمساحة فارغة currentDroppable=null</span><span class="pln">
    </span><span class="com">// إذا لم نكن فوق عنصر قابل للإفلات فوقه الآن، خلال هذا الحدث droppableBelow=null</span><span class="pln">

    </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">currentDroppable</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="com">// (المنطق المُعالَج عند “التحليق خارج” العنصر القابل للإفلات فوقه (إزالة الإبراز</span><span class="pln">
      leaveDroppable</span><span class="pun">(</span><span class="pln">currentDroppable</span><span class="pun">);</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
    currentDroppable </span><span class="pun">=</span><span class="pln"> droppableBelow</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">currentDroppable</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="com">// المنطق المُعالَج عند “التحليق داخل” العنصر القابل للإفلات فوقه</span><span class="pln">
      enterDroppable</span><span class="pun">(</span><span class="pln">currentDroppable</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>
	في المثال الذي يمكن مشاهدته من <a data-ss1614689249="1" data-ss1614954181="1" href="https://javascript.info/article/mouse-drag-and-drop/ball4/" rel="external nofollow">هنا</a>، عندما تُسحب الكرة إلى المرمى، يُبرز الهدف.
</p>

<p>
	<iframe allowfullscreen="true" allowtransparency="true" data-ss1614689249="1" data-ss1614954181="1" frameborder="no" height="265" loading="lazy" scrolling="no" src="https://codepen.io/Hsoub/embed/PobeagN?height=265&amp;theme-id=light&amp;default-tab=result" style="width: 100%;" title="JS-p2-mouse-drag-and-drop-ex05">See the Pen JS-p2-mouse-drag-and-drop-ex05 by Hsoub (@Hsoub) on CodePen.</iframe>
</p>

<p>
	نملك الآن "هدف الإفلات" الحاليّ، الذي نحلّق فوقه، في المتغيّر <code>currentDroppable</code> خلال كامل العمليّة ويمكننا استخدامه للإبراز أو لأيّ غرض آخر.
</p>

<h2>
	الملخّص
</h2>

<p>
	لقد درسنا خوارزميّة قاعديّة للسحب والإفلات، مكوّناتها الأساسيّة هي:
</p>

<ol>
<li>
		مجرى اﻷحداث: <code>ball.mousedown</code> ‏-&gt; <code>document.mousemove</code> ‏-&gt; <code>ball.mouseup</code> (لا تنس إلغاء <code>ondragstart</code> الأصيل).
	</li>
	<li>
		عند بداية السحب، تذكّر الإزاحة اﻷوّليّة للمؤشّر بالنسبة للعنصر، أي <code>shiftX/shiftY</code>، وحافظ عليها خلال السحب.
	</li>
	<li>
		اكتشف العناصر القابلة للإفلات فوقها تحت المؤشّر باستخدام <code>document.elementFromPoint</code>.
	</li>
</ol>
<p>
	يمكننا بناء الكثير فوق هذا اﻷساس.
</p>

<ul>
<li>
		عند <code>mouseup</code> يمكننا إنهاء السحب بشكل أفضل: كالقيام بتغيير المعطيات وترتيب العناصر.
	</li>
	<li>
		يمكننا إبراز العناصر التي نحلّق فوقها.
	</li>
	<li>
		يمكننا حصر السحب في مساحات أو اتجاهات معيّنة.
	</li>
	<li>
		يمكننا استخدام تفويض اﻷحداث مع <code>mousedown/up</code>. يستطيع معالج حدث على نطاق واسع أن يدير السحب والإفلات لمئات العناصر، بواسطة تفحّص <code>event.target</code>.
	</li>
	<li>
		وهكذا.
	</li>
</ul>
<p>
	هناك أطر عمل تبني هندستها على هذا اﻷساس: DragZone<code>و</code>Droppable<code>و</code>Draggable` وغيرها من الأصناف. يقوم معظمها بأمور تُشبه ما تمّ وصفه أعلاه، لذا فسيكون من السهل فهمها الآن. أو أنجز واحدا خاصّا بك، فكما رأيت إنّه من السهل فعل ذلك، بل أحيانا أسهل من محاولة تكييف حلّ من طرف ثالث.
</p>

<h2>
	التمارين
</h2>

<h3>
	المنزلق
</h3>

<p>
	اﻷهميّة: 5
</p>

<p>
	أنشئ منزلقًا كالذي هو مبيّن <a data-ss1614689249="1" data-ss1614954181="1" href="https://en.js.cx/task/slider/solution/" rel="external nofollow">هنا</a>.
</p>

<p>
	اسحب المقبض اﻷزرق بواسطة الفأرة وحرّكه.
</p>

<p>
	تفاصيل مهمّة:
</p>

<ul>
<li>
		عند الضغط على زرّ الفأرة، يمكن للمؤشّر الذهاب فوق وتحت المنزلق خلال السحب، ويبقى المنزلق يعمل بشكل عاديّ (هذا ملائم للمستخدم).
	</li>
	<li>
		عندما تتحرّك الفأرة بسرعة إلى اليمين واليسار، يجب أن يتوقّف المقبض عند الحافّة بالضبط.
	</li>
</ul>
<p>
	<a data-ss1614689249="1" data-ss1614954181="1" href="https://plnkr.co/edit/p7Ag21yFCKk3iLFe?p=preview" rel="external nofollow">أنجز التمرين في البيئة التجريبيّة</a>
</p>

<h3>
	الحل
</h3>

<p>
	كما يمكننا أن نرى من خلال HTML/CSS، أنّ المنزلق هو <code>&lt;div&gt;</code> بخلفيّة ملوّنة، يحتوي على زلّاجة -- التي هي <code>&lt;div&gt;</code> آخر له <code>position:relative</code>.
</p>

<p>
	نغيّر موضع الزلّاجة باستخدام <code>position:relative</code> بإعطاء الإحداثيّات نسبةً إلى العنصر اﻷب، وهي هنا أكثر ملائمة من استخدام <code>position:absolute</code>.
</p>

<p>
	ثمّ ننجز السحب والإفلات المحصور أفقيّا، والمحدود بالعُرض.
</p>

<p>
	<a data-ss1614689249="1" data-ss1614954181="1" href="https://plnkr.co/edit/zYSBv9zIaLpa3rQr?p=preview" rel="external nofollow">شاهد الحلّ في البيئة التجريبيّة</a>
</p>

<h3>
	اسحب اﻷبطال الخارقين حول الميدان
</h3>

<p>
	اﻷهميّة: 5
</p>

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

<p>
	اجعل جميع العناصر التي لها الصنف <code>draggable</code> قابلة للسحب. مثل الكرة في المقال.
</p>

<p>
	المتطلّبات:
</p>

<ul>
<li>
		استخدم تفويض اﻷحداث لترصّد بداية السحب: معالج واحد للحدث <code>mousedown</code> على <code>document</code>.
	</li>
	<li>
		إذا سُحبت العناصر إلى الحوافّ العلويّة/السفليّة للنافذة، تُمرَّر الصّفحة لتمكين المزيد من السحب.
	</li>
	<li>
		ليس هناك تمرير أفقيّ للصفحة (يجعل هذا التمرين أبسط قليلا، بالرغم أنّه من السهل إضافتها).
	</li>
	<li>
		يجب ألّا تفارق العناصر القابلة للسحب أو أيٌّ من أجزائها النافذة، ولو تحرّكت الفأرة بسرعة.
	</li>
</ul>
<p>
	يمكن مشاهدة النتيجة من <a data-ss1614689249="1" data-ss1614954181="1" href="https://en.js.cx/task/drag-heroes/solution/" rel="external nofollow">هنا</a>.
</p>

<p>
	<a data-ss1614689249="1" data-ss1614954181="1" href="https://plnkr.co/edit/lAng5DJhG41lE78n?p=preview" rel="external nofollow">أنجز التمرين في البيئة التجريبيّة</a>.
</p>

<h3>
	الحلّ
</h3>

<p>
	لسحب العنصر يمكننا استخدام <code>position:fixed</code>، فهي تجعل الإحداثيات أسهل في الإدارة. في النهاية يجب علينا إرجاعها إلى <code>position:absolute</code> لوضع العنصر في المستند.
</p>

<p>
	عندما تكون الإحداثيّات في أعلى/أسفل النافذة، نستخدم <code>window.scrollTo</code> لتمريرها.
</p>

<p>
	المزيد من التفاصيل في التعليقات التي في الشيفرة.
</p>

<p>
	<a data-ss1614689249="1" data-ss1614954181="1" href="https://plnkr.co/edit/KxqImMT4P0ySjN97?p=preview" rel="external nofollow">شاهد الحلّ في البيئة التجريبيّة</a>
</p>

<p>
	ترجمة -وبتصرف- للمقال <a data-ss1614689249="1" data-ss1614954181="1" href="https://javascript.info/mouse-drag-and-drop" rel="external nofollow">Drag'n'Drop with mouse events</a> من سلسلة <a data-ss1614689249="1" data-ss1614954181="1" href="https://javascript.info/ui" rel="external nofollow">Browser: Document, Events, Interfaces</a> لصاحبها Ilya Kantor
</p>
]]></description><guid isPermaLink="false">1153</guid><pubDate>Tue, 16 Mar 2021 13:01:00 +0000</pubDate></item></channel></rss>
