Такий сценарій чудово б підійшов для еротичної новели, якби історії про інтим з програмними системами приваблювали широкого читача. Проте нас із вами цікавить протилежний розвиток подій, а саме — мінімізувати кількість взаємодій із DOM до фізично можливого мінімуму, бажано до нуля.
І в цій невдячній справі нам стане в нагоді
DocumentFragment. Це такий собі віртуальний контейнер, який існує лише в памʼяті, що дозволяє проводити різні маніпуляції зі своїм вмістом за допомогою DOM API, а потім безболісно вставляти цей вміст в реальний DOM за одну ітерацію.Так, DocumentFragment ініціює лише один цикл repaint/reflow незалежно від того, скільки вузлів та елементів в ньому зараз знаходиться.
const fragment = document.createDocumentFragment();
// а хоч 10 000 юзерів
for (const user of users) {
const li = document.createElement('li');
li.textContent = user.name;
fragment.appendChild(li);
}
document
.querySelector('ul')
.appendChild(fragment);
Звичайно, це не означає, що бравзер однаково швидко опрацює вставку одного елемента і десяти тисяч елементів через фрагмент. Але вважайте, що саме reflow може суттєво вдарити по вашій швидкодії. І чим менше ми його ініціюємо, тим краще.
При вставці в DOM фрагмент поводиться як желатинова капсула — і розчиняється, коли бравзер "ковтає" її, випускаючи в реальне дерево лише свій вміст. В самому DOM фрагмент не представлено жодним чином.
Взагалі, DocumentFragment — це саме DOM Node, а не DOM Element. Це означає, що він має appendChild, querySelector, cloneNode, але не має innerHtml, classList, id, style. Іноді і я про це забуваю, марно намагаючись щось там учудити з innerHtml.
До речі, fragment.parentNode === null, бо він не є частиною DOM.
З цікавих особливостей також можна відмітити, що при вставці фрагменту в DOM його дочірні вузли переміщаються в реальне дерево, а не копіюються.
childNodes.length === 0 і робіть що хочете. Однак якщо ви використаєте cloneNode(true) на фрагменті, то отримаєте повноцінну копію без втрати вмісту цього фрагмента. Тож, якщо ви захочете вставити фрагмент в декілька місць, клонування — ваш вибір.А взагалі ця поведінка притаманна саме методам append та appendChild, а не самому фрагменту.
Так, ну й властивість
.content template-елемента вже є DocumentFragment, саме через це скрізь рекомендують спочатку склонувати його вміст, а потім уже з ним працювати. Інакше попсуєте оригінал.Ще варто відмітити, що позаяк фрагмент не може бути частиною DOM, то поки вміст знаходиться в ньому, той не наслідує стилі документа. Такий елемент може мати лише явно присвоєні стилі, або через inline style, або через властивість style DOM-обʼєкта. Але тут є ще одне але. Якщо це стилі блочні — розміри, відступи, оце все, то вони не впливатимуть на елемент, і той же
getBoundingClientRect поверне всі нулі. Причина проста — елемент не в реальному DOM, і не пройшов layout-фазу.Також фрагмент можна вставляти в інший фрагмент, і ніхто вам за це нічого не зробить. Тож можете бавитись в композицію і тут.
Взагалі, фрагмент надзвичайно зручно використовувати тоді, коли нам треба зробити багато маніпуляцій над DOM-вузлами. Наприклад, ви їх можете "висмикнути" з реального дерева, помістити у фрагмент, поробити усе, що вам треба: додати мільйон event listeners, позмінювати усі атрибути на світі, повпихувати в усі вузли новий контент, а потім просто одним махом вставити усе назад. І це вартуватиме вас лише одного циклу reflow.
🌐 MDN: DocumentFragment
🔥 Було цікаво? Тисніть вподобайку, поширюйте сей допис, а за це вам від мене величезна подяка і цьом у лобіка.
@babichdev