DocumentFragment: ما هو وكيف (لا) محاربته

تنويه
يبدو أنني بدأت سلسلة جديدة من المقالات - مملّة قليلاً ومفيدة تمامًا. سيشرحون النقاط التي غالبًا ما تسبب صعوبات لطلابي. إذا كنت مطور ويب متمرس ، فعلى الأرجح لن تكون مهتمًا. إذا كنت تنتظر الانحرافات في قوة شبيبة الجمعة ، فلن يكونوا هنا ، للأسف.


يعد DocumentFragment أحد الأشياء التي يواجه الطلاب صعوبة في فهمها بانتظام. بشكل عام ، لا يمكنني إلقاء اللوم عليهم. مع البساطة الخارجية ، لديها العديد من الخصائص غير الواضحة وحتى المضادة للحدس. في هذه المقالة ، أريد جمع كل ما يحتاج المبتدئ إلى معرفته عنه.

الصورة

ما هذا


إن DocumentFragment عبارة عن حاوية يمكن أن تحتوي على عدد عشوائي من عناصر DOM. إذا كان الأمر ببساطة ، يمكنك تخيله كدلو. يتم تكديس العناصر فيه بحيث يمكن التخلص منها في الوقت المناسب فورًا.

كيف تصنع


ابتدائي.

var fragment = document.createDocumentFragment(); 

هناك أيضًا طرق أخرى ، ولكن حولها أدناه.

لماذا أحتاج


كما كتبت أعلاه ، من أجل تخزين عناصر DOM. قد يعترض القارئ "لكن يمكن تخزينها في مغنية عادية". صحيح أن الجزء له خاصية فريدة تجعله المرشح الأفضل لهذا الدور. خذ بعين الاعتبار الرمز التالي:

 var fragment = document.createDocumentFragment(); var parentDiv = document.createElement("div"); var div1 = document.createElement("div"); var div2 = document.createElement("div"); fragment.appendChild(div1); fragment.appendChild(div2); //   parentDiv.appendChild(fragment); console.log(parentDiv.children); 

ماذا ستخبرنا وحدة التحكم؟ قد يظن شخص غير معتاد على DocumentFragment أن parentDiv سيكون لديه طفل واحد - fragment . ولكن في الواقع ، سيكون لديها طفلان - div1 و div2 . والحقيقة هي أن الجزء نفسه ليس عنصر DOM ، إنه مجرد حاوية لعناصر DOM. وعندما يتم تمريره كحجة لطرق مثل appendChild أو insertBefore ، فإنه لا يتم تضمينه في شجرة DOM ، بل يقوم بدمج محتوياته هناك.

لكن لماذا تحتاجها؟


إن خاصية "الدلو" جيدة بالطبع ، ولكن ما مدى فائدة ذلك عمليًا؟ يحتوي DocumentFragment على مجالين رئيسيين للتطبيق.

1. تخزين أجزاء HTML التي ليس لها سلف مشترك.

هناك حالات نحتاج فيها إلى استبدال محتويات عنصر ، ولكن لا تلمس العنصر نفسه. افترض أننا نستخدم تفويض الحدث ، وأن جميع معالجات الأحداث التي تحدث على العناصر الداخلية معلقة على قسم خارجي. في هذه الحالة ، يعد DocumentFragment مثاليًا لنا:

 div.innerHTML = ""; div.appendChild(fragmentWithAllContent); 

"ولكن هل يمكننا فقط إضافة عناصر إلى div فور إنشائها؟" - يسأل القارئ التآكل. نستطيع ، لكن الأمر لا يستحق ، ولهذا السبب.

2. تحسين الأداء في حالة الإدخالات المتعددة.

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

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

من أجل الوضوح ، قمت بعمل مقياس بسيط بحيث يمكن للقارئ أن يرى الفرق شخصيًا.

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

الفروق الدقيقة


هناك ميزتان تجعل المبتدئين غالبًا ما يصعب عليهم استخدام الأجزاء. أولاً: كما كتبت أعلاه ، الجزء ليس عنصر DOM . هذا يعني أنها تفتقر إلى العديد من الأساليب والخصائص المألوفة ، على وجه الخصوص - innerHTML . لذلك ، لا يمكنك فقط تحويل سلسلة إلى محتويات جزء. كيفية القيام بذلك ليس سهلاً ، سيتم شرحه أدناه.

السمة الثانية: الجزء عند استخدام "الغنائم". بتعبير أدق ، يتم تفريغه. عندما نفعل div.appendChild(fragment) ، يتم div.appendChild(fragment) جميع أطفال الجزء في div . وبما أن العنصر لا يمكن أن يحتوي على أكثر من أحد الوالدين ، فهذا يعني أنه تمت إزالته من الجزء! لتجنب هذا السلوك عندما يكون غير مرغوب فيه ، يمكنك استخدام cloneNode .

علامة <template>


هناك مكان واحد حيث يمكنك العثور على DocumentFragment دون إنشائه من خلال JS. هذه هي خاصية content لعنصر القالب.

<template> ابتكار <template> خصيصًا لتخزين أجزاء من كود HTML ، ولكن ليس لتحميل المتصفح مسبقًا. لا يصبح ما بداخل هذه العلامة جزءًا من شجرة DOM النشطة. على وجه الخصوص (غالبًا ما querySelector القادمون الجدد هذا أيضًا) ، ولا يمكن العثور querySelector باستخدام querySelector . لا تصبح العناصر التي تم إنشاؤها من كود HTML داخل علامة <template> تابعة لها. بدلاً من ذلك ، يمكن لـ JavaScript الوصول إليها من خلال خاصية content ، وهي - مفاجأة! - فقط DocumentFragment.

باستخدام عنصر القالب ، يمكنك إنشاء جزء من سلسلة:

 function createFragmentFromString(str){ var template = document.createElement("template"); template.innerHTML = str; return template.content; } 

الخاتمة


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

Source: https://habr.com/ru/post/ar413287/


All Articles