إضافة التفاعل باستخدام JavaScript

تاريخ النشر: 31 كانون الأول (ديسمبر) 2013

تتيح لنا JavaScript تعديل كلّ جانب من جوانب الصفحة تقريبًا: المحتوى، والتصميم، والاستجابة لتفاعل المستخدِم. ومع ذلك، يمكن أن تحظر JavaScript أيضًا إنشاء نموذج كائن المستند (DOM) وتؤخّر عرض الصفحة. ولتحقيق الأداء الأمثل، ننصحك بعدم مزامنة JavaScript وإزالة أي رموز JavaScript غير ضرورية من مسار العرض الحرج.

  • يمكن لـ JavaScript الاستعلام عن DOM وCSSOM وتعديلهما.
  • وحدات تنفيذ JavaScript على CSSOM.
  • تحظر لغة JavaScript إنشاء DOM ما لم يتم الإعلان عنها صراحةً على أنّها غير متزامنة.

لغة JavaScript هي لغة ديناميكية تعمل في المتصفح وتسمح لنا بتغيير كل جانب من جوانب سلوك الصفحة: يمكننا تعديل المحتوى من خلال إضافة عناصر من شجرة DOM وإزالتها، كما يمكننا تعديل خصائص CSSOM لكل عنصر، ويمكننا معالجة إدخالات المستخدم، وغير ذلك الكثير. لتوضيح ذلك، اطّلِع على ما يحدث عند تغيير مثال "مرحبًا بك في العالم" السابق لإضافة نص برمجي مضمّن قصير:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
    <title>Critical Path: Script</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script>
      var span = document.getElementsByTagName('span')[0];
      span.textContent = 'interactive'; // change DOM text content
      span.style.display = 'inline'; // change CSSOM property
      // create a new element, style it, and append it to the DOM
      var loadTime = document.createElement('div');
      loadTime.textContent = 'You loaded this page on: ' + new Date();
      loadTime.style.color = 'blue';
      document.body.appendChild(loadTime);
    </script>
  </body>
</html>

التجربة الآن

  • تسمح لنا JavaScript بالوصول إلى نموذج DOM وإزالة الإشارة إلى عقدة span المخفية. قد لا تكون العقدة مرئية في شجرة العرض، ولكنّها لا تزال موجودة في نموذج DOM. بعد ذلك، عندما يكون لدينا المرجع، يمكننا تغيير نصه (عبر .textContent)، بل وتجاوز خاصية نمط العرض المحسوبة من "none" إلى "inline". تعرض صفحتنا الآن العبارة "مرحبًا بالطلاب التفاعليين".

  • تسمح لنا JavaScript أيضًا بإنشاء عناصر جديدة في DOM وتنسيقها وإضافتها وإزالتها. من الناحية الفنية، يمكن أن تكون صفحتنا بأكملها ملف JavaScript كبيرًا واحدًا ينشئ العناصر ويصمّمها واحدًا تلو الآخر. على الرغم من أنّ هذا الإجراء قد يكون مناسبًا، إلا أنّ استخدام HTML وCSS أسهل بكثير. في الجزء الثاني من دالة JavaScript، ننشئ عنصر div جديدًا، ونضبط محتواه النصي، ونضبط نمطه، ونُلحقه بالنص الأساسي.

معاينة لصفحة معروضة على جهاز جوّال

بعد ذلك، عدّلنا محتوى عقدة DOM الحالية وأسلوب CSS الخاص بها، وأضفنا عقدة جديدة تمامًا إلى المستند. لن تفوز صفحتنا بأي جوائز تصميم، ولكنها توضّح القوة والمرونة التي يوفّرها لنا JavaScript.

ومع ذلك، على الرغم من أنّ لغة JavaScript تمنحنا الكثير من الإمكانيات، إلا أنّها تفرض الكثير من القيود الإضافية على كيفية عرض الصفحة ووقت عرضها.

أولاً، لاحظ أنه في المثال السابق أن النص البرمجي المضمّن قريب من أسفل الصفحة. لماذا؟ عليك تجربته بنفسك، ولكن إذا نقلنا النص البرمجي فوق العنصر <span>، ستلاحظ أنّ النص البرمجي يتعذّر عليه العثور على إشارة إلى أي عناصر <span> في المستند، أي أنّ getElementsByTagName('span') يعرض null. يوضّح ذلك خاصيّة مهمة: يتم تنفيذ النص البرمجي في النقطة المحدّدة التي يتم إدراجه فيها في المستند. عندما يصادف محلّل HTML علامة نص برمجي، فإنه يوقف عملية إنشاء DOM مؤقتًا ويتخذ إجراءً يتيح التحكم في محرك JavaScript؛ وبعد انتهاء تشغيل محرك JavaScript، يستأنف المتصفح من حيث توقف ويستأنف إنشاء DOM.

بعبارة أخرى، لا يمكن لوحدة النص البرمجي العثور على أي عناصر في وقت لاحق من الصفحة لأنّه لم تتم معالجتها بعد. ويمكن القول بطريقة مختلفة قليلاً: يؤدي تنفيذ النص البرمجي المضمّن إلى حظر إنشاء DOM، ما يؤدي أيضًا إلى تأخير العرض الأولي.

من الخصائص الدقيقة الأخرى لاستخدام النصوص البرمجية في صفحتنا أنّها يمكنها قراءة وتعديل عناصر DOM، بالإضافة إلى خصائص CSSOM. في الواقع، هذا بالضبط ما نفعله في المثال الذي طرحناه عندما نغير خاصية العرض لعنصر span من none إلى inline. ما هي النتيجة النهائية؟ الآن لدينا شرط السباق.

ماذا لو لم ينتهِ المتصفّح من تنزيل CSSOM وإنشاءه عندما نريد تشغيل النص البرمجي؟ لا تؤدي هذه الطريقة إلى تحسين الأداء: يؤخّر المتصفّح تنفيذ النصوص البرمجية وإنشاء DOM إلى أن ينتهي من تنزيل CSSOM وإنشاءه.

باختصار، تُضيف JavaScript الكثير من التبعيات الجديدة بين DOM وCSSOM وتنفيذ JavaScript. يمكن أن يؤدي ذلك إلى تأخيرات كبيرة في المتصفّح أثناء معالجة الصفحة وعرضها على الشاشة:

  • إنّ موقع النص البرمجي في المستند مهم.
  • عندما يصادف المتصفّح علامة نص برمجي، يتم إيقاف إنشاء DOM مؤقتًا إلى أن ينتهي تنفيذ النص البرمجي.
  • يمكن لـ JavaScript طلب نموذج DOM وCSSOM وتعديله.
  • يتوقف تنفيذ JavaScript مؤقتًا إلى أن يصبح CSSOM جاهزًا.

يشير "تحسين مسار العرض المهم" إلى حد كبير إلى فهم الرسم البياني للتبعية بين HTML وCSS وJavaScript وتحسينه.

حظر المُحلِّل مقابل JavaScript غير المتزامن

يتم بشكل تلقائي تنفيذ JavaScript "حظر المحلِّل": عندما يصادف المتصفح نصًا برمجيًا في المستند، يجب أن يتوقف بشكل مؤقت بنية DOM، ويترك إمكانية التحكم في وقت تشغيل JavaScript، ويترك النص البرمجي ينفذه قبل متابعة إنشاء DOM. لقد رأينا ذلك أثناء استخدام نص برمجي مضمَّن في المثال السابق. في الواقع، تكون النصوص البرمجية المضمنة دائمًا حظر المحلل ما لم تكتب تعليمات برمجية إضافية لتأجيل تنفيذها.

ماذا عن النصوص البرمجية المضمّنة باستخدام علامة نص برمجي؟ خذ المثال السابق واستخرِج الرمز إلى ملف منفصل:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
    <title>Critical Path: Script External</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script src="app.js"></script>
  </body>
</html>

app.js

var span = document.getElementsByTagName('span')[0];
span.textContent = 'interactive'; // change DOM text content
span.style.display = 'inline'; // change CSSOM property
// create a new element, style it, and append it to the DOM
var loadTime = document.createElement('div');
loadTime.textContent = 'You loaded this page on: ' + new Date();
loadTime.style.color = 'blue';
document.body.appendChild(loadTime);

التجربة

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

يتم تلقائيًا حظر جميع وحدات JavaScript من خلال التحليل. وبما أنّ المتصفّح لا يعرف ما الذي سيفعله النص البرمجي على الصفحة، يفترض أسوأ سيناريو ويحظّر المُحلِّل. إنّ إرسال إشارة إلى المتصفّح بأنّه لا يلزم تنفيذ النص البرمجي في النقطة المحدّدة التي تتم الإشارة إليها تسمح للمتصفّح بمواصلة إنشاء نموذج DOM والسماح بتنفيذ النص البرمجي عندما يصبح جاهزًا، على سبيل المثال، بعد جلب الملف من ذاكرة التخزين المؤقت أو من خادم بعيد.

لتحقيق ذلك، تتم إضافة السمة async إلى العنصر <script>:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
    <title>Critical Path: Script Async</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script src="app.js" async></script>
  </body>
</html>

التجربة

تُعلِم إضافة الكلمة الرئيسية async إلى علامة البرنامج النصي المتصفّح بعدم حظر إنشاء DOM أثناء انتظار توفّر البرنامج النصي، ما يمكن أن يؤدي إلى تحسين الأداء بشكل كبير.

ملاحظات