14 серпня 2023 р.

Внесення змін в документ

Модифікації DOM є ключем до створення “живих” сторінок.

Тут ми побачимо, як створювати нові елементи “на льоту” та змінювати ті, що вже існують.

Приклад: показати повідомлення

Розглянемо на прикладі. Ми додамо на сторінку повідомлення яке виглядатиме краще аніж alert.

Ось так воно виглядатиме:

<style>
.alert {
  padding: 15px;
  border: 1px solid #d6e9c6;
  border-radius: 4px;
  color: #3c763d;
  background-color: #dff0d8;
}
</style>

<div class="alert">
  <strong>Привіт!</strong> Ви прочитали важливе повідомлення.
</div>

Це був приклад HTML розмітки. Тепер створимо такий самий div за допомогою JavaScript (припускаємо, що стилі вже є в HTML або в окремому CSS-файлі).

Створення елементу

Є два способи створення DOM вузлів:

document.createElement(tag)

Створює новий елемент з заданим тегом:

let div = document.createElement('div');
document.createTextNode(text)

Створює новий текстовий вузол з заданим текстом:

let textNode = document.createTextNode('От і я');

У більшості випадків нам потрібно створювати саме елементи, такі як div для повідомлень.

Створення повідомлення

Створення div елементу для повідомлення складається з трьох кроків:

// 1. Створюємо елемент <div>
let div = document.createElement('div');

// 2. Задаємо йому клас "alert"
div.className = "alert";

// 3. Наповнюємо його змістом
div.innerHTML = "<strong>Всім привіт!</strong> Ви прочитали важливе повідомлення.";

Ми створили елемент, але поки що він знаходиться лише у змінній з назвою div, тому не можемо бачити його на сторінці, оскільки він не є частиною документа.

Методи вставки

Щоб div з’явився нам потрібно вставити його десь в document. Наприклад, в елемент <body> який можна отримати звернувшись до document.body.

Для цього існує спеціальний метод append: document.body.append(div).

Ось повний код:

<style>
.alert {
  padding: 15px;
  border: 1px solid #d6e9c6;
  border-radius: 4px;
  color: #3c763d;
  background-color: #dff0d8;
}
</style>

<script>
  let div = document.createElement('div');
  div.className = "alert";
  div.innerHTML = "<strong>Всім привіт!</strong> Ви прочитали важливе повідомлення.";

  document.body.append(div);
</script>

Тут ми викликали метод append на document.body, але це можна зробити на будь-якому іншому елементі, щоб вставити інший елемент в нього. Наприклад, ми можемо додати щось до <div> викликавши div.append(anotherElement).

Ось більше методів вставки, вони вказують куди саме буде вставлено вміст:

  • node.append(...вузли або рядки) – додає вузли або рядки в кінець node,
  • node.prepend(...вузли або рядки) – вставляє вузли або рядки на початку node,
  • node.before(...вузли або рядки) – вставляє вузли або рядки попереду node,
  • node.after(...вузли або рядки) – вставляє вузли або рядки після node,
  • node.replaceWith(...вузли або рядки) – замінює node заданими вузлами або рядками.

Аргументами цих методів є довільний список DOM вузлів або текстові рядки(які автоматично перетворюються на текстові вузли).

Подивимося на них в дії.

Ось приклад використання цих методів для додавання елементів до списку та тексту до/після нього:

<ol id="ol">
  <li>0</li>
  <li>1</li>
  <li>2</li>
</ol>

<script>
  ol.before('before'); // вставити рядок "before" перед <ol>
  ol.after('after'); // вставити рядок "after" після <ol>

  let liFirst = document.createElement('li');
  liFirst.innerHTML = 'prepend';
  ol.prepend(liFirst); // вставити liFirst на початку <ol>

  let liLast = document.createElement('li');
  liLast.innerHTML = 'append';
  ol.append(liLast); // вставити liLast в кінці <ol>
</script>

Наочна ілюстрація роботи методів:

Отже, в підсумку список буде таким:

before
<ol id="ol">
  <li>prepend</li>
  <li>0</li>
  <li>1</li>
  <li>2</li>
  <li>append</li>
</ol>
after

Як вже було сказано, ці методи можуть вставляти декілька вузлів і фрагментів тексту за один виклик.

Наприклад, тут вставлено рядок та елемент одночасно.

<div id="div"></div>
<script>
  div.before('<p>Привіт!</p>', document.createElement('hr'));
</script>

Пам’ятайте, що текст вставляється «як текст», а не «як HTML», з відповідними замінами таких символів як <, >.

Тому фінальний HTML буде таким:

&lt;p&gt;Привіт&lt;/p&gt;
<hr>
<div id="div"></div>

Іншими словами, рядки вставляються безпечним способом, як це робить elem.textContent.

Тому ці методи можна використовувати лише для вставки DOM вузлів або фрагментів тексту.

Але що, як нам потрібно вставити рядок HTML «як html», з усіма тегами та іншим, так само як це робить elem.innerHTML?

insertAdjacentHTML/Text/Element

Для цього ми можемо використовувати інший, досить універсальний метод: elem.insertAdjacentHTML(where, html).

Перший параметр це кодове слово, яке вказує куди вставляти відносно elem. Його значення має бути одним з наступних:

  • "beforebegin" – вставити html безпосередньо перед elem,
  • "afterbegin" – вставити html в elem, на початку,
  • "beforeend" – вставити html в elem, в кінці,
  • "afterend" – вставити html безпосередньо після elem.

Другим параметром є рядок HTML, який вставляється “як HTML”.

Наприклад:

<div id="div"></div>
<script>
  div.insertAdjacentHTML('beforebegin', '<p>Привіт</p>');
  div.insertAdjacentHTML('afterend', '<p>Бувай</p>');
</script>

…виглядатиме як:

<p>Привіт</p>
<div id="div"></div>
<p>Бувай</p>

Ось так ми можемо додавати HTML на сторінку.

На зображенні показані всі можливі варіанти вставки.

Можна легко помітити схожість між цим та попереднім зображенням. Місця вставки насправді ті ж самі, але останній метод вставляє HTML.

Метод має двох братів:

  • elem.insertAdjacentText(куди, текст) – синтаксис той самий, але рядок тексту вставляється «як текст» замість HTML
  • elem.insertAdjacentElement(куди, текст) – синтаксис той самий, але вставляється елемент

Наведені методи існують для того, щоб синтаксис залишався “однорідним”. На практиці найчастіше використовується insertAdjacentHTML. Тому що для вставки елементів та тексту є методи append/prepend/before/after – вони коротші для написання і так само вміють вставляти вузли чи фрагменти тексту.

Отже, ось альтернативний варіант показу повідомлення:

<style>
.alert {
  padding: 15px;
  border: 1px solid #d6e9c6;
  border-radius: 4px;
  color: #3c763d;
  background-color: #dff0d8;
}
</style>

<script>
  document.body.insertAdjacentHTML("afterbegin", `<div class="alert">
    <strong>Всім привіт!</strong> Ви прочитали важливе повідомлення.
  </div>`);
</script>

Видалення вузлів

Щоб видалити вузол використовуйте метод node.remove().

Спробуємо зробити так, щоб наше повідомлення зникало через одну секунду:

<style>
.alert {
  padding: 15px;
  border: 1px solid #d6e9c6;
  border-radius: 4px;
  color: #3c763d;
  background-color: #dff0d8;
}
</style>

<script>
  let div = document.createElement('div');
  div.className = "alert";
  div.innerHTML = "<strong>Всім привіт!</strong> Ви прочитали важливе повідомлення.";

  document.body.append(div);
  setTimeout(() => div.remove(), 1000);
</script>

Зверніть увагу: якщо ми хочемо перемістити елемент на інше місце в документі – тоді немає потреби його видаляти.

Всі методи вставки автоматично видаляють вузол з попереднього місця.

Наприклад, поміняємо елементи місцями:

<div id="first">Перший</div>
<div id="second">Другий</div>
<script>
  // немає потреби викликати remove
  second.after(first); // взяти #second та після нього вставити #first
</script>

Клонування вузлів: cloneNode

Як вставити ще одне схоже повідомлення?

Можна створити функцію та помістити код в неї. Але є ще один спосіб – клонувати наявний div та змінити текст всередині (якщо потрібно).

У випадку, коли наш елемент великий, це може бути швидше та простіше.

  • Виклик elem.cloneNode(true) створює «глибоку» копію елемента – з усіма атрибутами та піделементами. Якщо ми викличемо elem.cloneNode(false), тоді буде створена копія без дочірніх елементів.

Приклад копіювання повідомлення:

<style>
.alert {
  padding: 15px;
  border: 1px solid #d6e9c6;
  border-radius: 4px;
  color: #3c763d;
  background-color: #dff0d8;
}
</style>

<div class="alert" id="div">
  <strong>Всім привіт!</strong> Ви прочитали важливе повідомлення.
</div>

<script>
  let div2 = div.cloneNode(true); // клонувати елемент
  div2.querySelector('strong').innerHTML = 'Bye there!'; // змінити клона

  div.after(div2); // вставити клонований елемент після існуючого `div`
</script>

DocumentFragment

DocumentFragment це спеціальний DOM-вузол який служить обгорткою для передачі списку вузлів.

Ми можемо додавати до нього інші вузли, але коли ми вставляємо його кудись, він “зникає”, а замість нього вставляється лише його вміст (контент).

Наприклад, getListContent нижче генерує фрагмент з елементами <li>, які пізніше вставляються в <ul>:

<ul id="ul"></ul>

<script>
function getListContent() {
  let fragment = new DocumentFragment();

  for(let i=1; i<=3; i++) {
    let li = document.createElement('li');
    li.append(i);
    fragment.append(li);
  }

  return fragment;
}

ul.append(getListContent()); // (*)
</script>

Зверніть увагу, що в останньому рядку коду (*) ми додаємо DocumentFragment, але він “зникає”, тому в результаті структура буде такою:

<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
</ul>

DocumentFragment рідко використовується явно. Навіщо додавати до вузла особливого типу, якщо натомість можна повернути масив вузлів? Переписаний приклад:

<ul id="ul"></ul>

<script>
function getListContent() {
  let result = [];

  for(let i=1; i<=3; i++) {
    let li = document.createElement('li');
    li.append(i);
    result.push(li);
  }

  return result;
}

ul.append(...getListContent()); // append + оператор "..." = друзі!
</script>

Ми згадуємо DocumentFragment в основному тому, що на ньому базуються деякі концепції, як от елемент template, який ми розберемо пізніше.

Застарілі методи вставки/видалення

Стара школа
Ця інформація допомагає зрозуміти старі скрипти, але не потрібна для нової розробки.

Також є застарілі методи для маніпуляції з DOM, які існують лише з історичних причин.

Ці методи прийшли з давніх часів. Сьогодні немає жодних причин їх використовувати, оскільки сучасні методи, такі як append, prepend, before, after, remove, replaceWith набагато зручніші у використанні.

Ми перераховуємо ці методи лише тому, що вони можуть зустрітися вам в багатьох старих скриптах:

parentElem.appendChild(node)

Додає node як останній дочірній елемент parentElem.

В наведеному нижче прикладі додаємо <li> в кінець <ol>:

<ol id="list">
  <li>0</li>
  <li>1</li>
  <li>2</li>
</ol>

<script>
  let newLi = document.createElement('li');
  newLi.innerHTML = 'Привіт, світ!';

  list.appendChild(newLi);
</script>
parentElem.insertBefore(node, nextSibling)

Вставляє node перед nextSibling в parentElem.

В наведеному нижче прикладі вставляється новий елемент списку перед другим <li>:

<ol id="list">
  <li>0</li>
  <li>1</li>
  <li>2</li>
</ol>
<script>
  let newLi = document.createElement('li');
  newLi.innerHTML = 'Привіт, світ!';

  list.insertBefore(newLi, list.children[1]);
</script>

Щоб вставити newLi першим елементом, ми можемо зробити так:

list.insertBefore(newLi, list.firstChild);
parentElem.replaceChild(node, oldChild)

Замінює oldChild на node поміж дочірніми елементами parentElem.

parentElem.removeChild(node)

Видаляє node з parentElem (припускаючи, що node – це його дочірній елемент).

В наведеному нижче прикладі з <ol> видаляється перший <li> :

<ol id="list">
  <li>0</li>
  <li>1</li>
  <li>2</li>
</ol>

<script>
  let li = list.firstElementChild;
  list.removeChild(li);
</script>

Всі ці методи повертаються вставлені/видалено вузли. Іншими словами, parentElem.appendChild(node) повертає node. Але зазвичай повернуті значення не використовуються, ми лише запускаємо метод.

Декілька слів про «document.write»

Є ще один дуже застарілий метод додати вміст на вебсторінку: document.write.

Синтаксис:

<p>Десь на сторінці...</p>
<script>
  document.write('<b>Привіт від JS</b>');
</script>
<p>Кінець</p>

Виклик document.write(html) записує html на сторінку “прямо тут і зараз”. Рядок html може бути згенерований динамічно, тож метод досить гнучкий. За допомогою JavaScript ми можемо створити повноцінну вебсторінку та записати її вміст в документ.

Метод прийшов з тих часів коли не було ні DOM, ні стандартів… Справді давні часи. Метод досі живий, тому що є скрипти, в яких він використовується.

В сучасних скриптах він рідко зустрічається через наступні важливі обмеження:

Виклик document.write працює лише під час завантаження сторінки.

Якщо ми викличемо його пізніше, то чинний вміст документа буде видалений.

Наприклад:

<p>Через одну секунду вміст цієї сторінки буде замінено...</p>
<script>
  // document.write через 1 секунду
  // виклик відбувся після того як сторінка завантажилася, тому метод стирає вміст
  setTimeout(() => document.write('<b>...By this.</b>'), 1000);
</script>

Тому цей метод непридатний на стадії «після завантаження», на відміну від інших DOM-методів які ми розібрали раніше.

Це його недолік.

Окрім того, є і перевага. Технічно, коли document.write викликається поки браузер читає (“розбирає”) вхідний HTML і щось записує в документ, то браузер сприймає його так, наче він з самого початку був там, в HTML-документі.

Тому він працює надзвичайно швидко, адже не відбувається жодних DOM-модифікацій. Він записує безпосередньо в текст сторінки, поки DOM ще не сформований.

Отже, якщо нам потрібно динамічно додати в HTML велику кількість тексту, і ми на стадії завантаження сторінки, і швидкість завантаження має значення – цей метод може допомогти. Але на практиці всі ці вимоги рідко поєднуються. І зазвичай цей метод зустрічається в скриптах лише тому, що вони старі.

Підсумки

  • Методи для створення нових вузлів:

    • document.createElement(tag) – створює елемент з заданим тегом,
    • document.createTextNode(value) – створює текстовий вузол (рідко використовується),
    • elem.cloneNode(deep) – клонує елемент, якщо deep==true, то з усіма нащадками.
  • Вставка та видалення:

    • node.append(...nodes or strings) – вставляє в node, в кінець,
    • node.prepend(...nodes or strings) – вставляє в node, на початку,
    • node.before(...nodes or strings) – вставляє прямо перед node,
    • node.after(...nodes or strings) – вставляє відразу після node,
    • node.replaceWith(...nodes or strings) – замінює node.
    • node.remove() – видаляє node.

    Текстові рядки вставляються «як текст».

  • Також є застарілі методи:

    • parent.appendChild(node)
    • parent.insertBefore(node, nextSibling)
    • parent.removeChild(node)
    • parent.replaceChild(newElem, node)

    Всі вони повертають node.

  • Метод elem.insertAdjacentHTML(where, html) вставляє заданий HTML в залежності від значення параметра where:

    • "beforebegin" – вставляє html прямо перед elem,
    • "afterbegin" – вставляє html в elem, на початку,
    • "beforeend" – вставляє html в elem, в кінці,
    • "afterend" – вставляє html відразу після elem.

    Також є схожі методи, elem.insertAdjacentText та elem.insertAdjacentElement, що вставляють текстові рядки та елементи, але їх рідко використовують.

  • Щоб додати HTML на сторінку до того як вона повністю завантажиться:

    • document.write(html)

    Після завантаження сторінки такий виклик призведе до стирання документа. В основному зустрічається в старих скриптах.

Завдання

важливість: 5

У нас є пустий DOM-елемент elem та рядок text.

Які з цих трьох команд виконають одну й ту ж дію?

  1. elem.append(document.createTextNode(text))
  2. elem.innerHTML = text
  3. elem.textContent = text

Відповідь: 1 та 3.

Результатом обох команд буде text доданий до elem «як текст».

Приклад:

<div id="elem1"></div>
<div id="elem2"></div>
<div id="elem3"></div>
<script>
  let text = '<b>text</b>';

  elem1.append(document.createTextNode(text));
  elem2.innerHTML = text;
  elem3.textContent = text;
</script>
важливість: 5

Створіть функцію clear(elem) яка видаляє все з елементу.

<ol id="elem">
  <li>Привіт</li>
  <li>Світ</li>
</ol>

<script>
  function clear(elem) { /* ваш код */ }

  clear(elem); // очищує список
</script>

Перш за все, розглянемо як це не варто робити:

function clear(elem) {
  for (let i=0; i < elem.childNodes.length; i++) {
      elem.childNodes[i].remove();
  }
}

Це не спрацює, тому що виклик remove() змістить колекцію elem.childNodes таким чином, що елементи щоразу починатимуться з індексу 0. Але i зростатиме, і в результаті деякі елементи будуть пропущені.

Цикл for..of робить те саме.

Правильним варіантом може бути:

function clear(elem) {
  while (elem.firstChild) {
    elem.firstChild.remove();
  }
}

Також є більш простий спосіб:

function clear(elem) {
  elem.innerHTML = '';
}
важливість: 1

В прикладі нижче виклик table.remove() має видалити таблицю з документу.

Але, якщо ви запустите код, то побачите, що текст "aaa" не зникає.

Чому так відбувається?

<table id="table">
  aaa
  <tr>
    <td>Тест</td>
  </tr>
</table>

<script>
  alert(table); // таблиця, як і має бути

  table.remove();
  // чому текст "aaa" залишився в документі?
</script>

HTML в завданні написаний невірно. Це і є причиною дивної поведінки коду.

Браузер має виправити це автоматично. Але згідно специфікації тег <table> може містити в собі лише теги які відносяться до таблиць. Тому браузер виносить "aaa" перед тегом <table>.

Тепер очевидно, що коли ми видаляємо таблицю, текст залишається

Можна легко відповісти на це запитання, дослідивши DOM за допомогою інструментів веб-розробки в браузері. Ви побачите "aaa" перед <table>.

В стандартах HTML детально описано як обробляти некоректний HTML, тому така поведінка браузера є правильною.

важливість: 4

Напишіть інтерфейс для створення списку на основі того, що введе користувач.

Для кожного пункта списку:

  1. Запитайте текст пункту у користувача за допомогою prompt.
  2. Створіть <li> з цим текстом та додайте його до <ul>.
  3. Продовжуйте поки користувач не припинить вводити дані (натисне Esc або введе пустий рядок).

Всі елементи повинні створюватися динамічно.

Якщо користувач вводить HTML-теги, вони мають розглядатися як текст.

Демонстрація в новому вікні

Зверніть увагу на використання textContent для додавання контенту в <li>.

Відкрити рішення в пісочниці.

важливість: 5

Напишіть функцію createTree яка створює вкладений ul/li список з вкладеного об’єкта.

Наприклад:

let data = {
  Риба: {
    форель: {},
    лосось: {}
  },

  Дерево: {
    Величезні: {
      секвойя: {},
      дуб: {}
    },
    Квітучі: {
      яблуня: {},
      магнолія: {}
    }
  }
};

Синтаксис:

let container = document.getElementById('container');
createTree(container, data); // створює дерево в контейнері

Результат (дерево) має виглядати так:

Виберіть один із двох способів вирішення цього завдання:

  1. Створіть HTML для дерева, а потім призначте в container.innerHTML.
  2. Створіть вузли дерева та додайте їх за допомогою методів DOM.

Було б чудово, якби ви могли зробити обидва.

P.S. Дерево не повинно мати “зайвих” елементів, як-от порожній <ul></ul> для листя.

Відкрити пісочницю для завдання.

Найпростіший спосіб обійти об’єкт – скористатися рекурсією.

  1. Рішення з innerHTML.
  2. Рішення з DOM.
важливість: 5

Дано дерево з вкладених ul/li.

Напишіть код, який додає до кожного <li> кількість його нащадків. Пропускаючи пусті вузли (без дочірніх елементів).

Результат:

Відкрити пісочницю для завдання.

Щоб додати текст до кожного <li>, ми можемо змінювати текстовий вузол data.

Відкрити рішення в пісочниці.

важливість: 4

Напишіть функцію createCalendar(elem, year, month).

Виклик повинен створити календар на даний рік/місяць і помістити його в elem.

Календар має бути таблицею, де тиждень це <tr>, а день це <td>. Перший рядок таблиці має бути оформлений як <th> з назвами днів тижня: першим днем має бути понеділок і так до неділі.

Наприклад, createCalendar(cal, 2012, 9) має створити в елементі cal такий календар:

P.S. Для цього завдання достатньо створити календар, який поки що не має бути клікабельним.

Відкрити пісочницю для завдання.

Ми створимо таблицю як рядок "<table>...</table>", а потім присвоїмо її в innerHTML.

Алгоритм:

  1. Створіть заголовок таблиці з <th> та днями тижня.
  2. Створіть об’єкт дати d = new Date(year, month-1). Це перший день місяця month (беручи до уваги, що місяці в JavaScript починаються з 0, а не з 1).
  3. Декілька перших клітинок до d.getDay() можуть бути пустими. Заповнимо їх <td></td>.
  4. Збільшуйте день d: d.setDate(d.getDate()+1). Якщо d.getMonth() ще не досяг наступного місяця, тоді додайте нову клітинку <td> в календар. Якщо це НД, тоді додайте новий рядок “</tr><tr>”.
  5. Якщо міcяць завершився, але рядок таблиці ще не заповнений, додайте пусті <td>, щоб надати таблиці правильної форми.

Відкрити рішення в пісочниці.

важливість: 4

Створіть кольоровий годинник як у цьому прикладі:

Використовуйте HTML/CSS для стилізації, JavaScript лише оновлює час в елементах.

Відкрити пісочницю для завдання.

Спочатку, напишемо HTML/CSS.

Кожен компонент часу обгорнемо у <span>:

<div id="clock">
  <span class="hour">hh</span>:<span class="min">mm</span>:<span class="sec">ss</span>
</div>

Розфарбуємо кожен <span> за допомогою CSS.

Функція update оновлюватиме годинник, а метод setInterval викликатиме її щосекунди.

function update() {
  let clock = document.getElementById('clock');
  let date = new Date(); // (*)
  let hours = date.getHours();
  if (hours < 10) hours = '0' + hours;
  clock.children[0].innerHTML = hours;

  let minutes = date.getMinutes();
  if (minutes < 10) minutes = '0' + minutes;
  clock.children[1].innerHTML = minutes;

  let seconds = date.getSeconds();
  if (seconds < 10) seconds = '0' + seconds;
  clock.children[2].innerHTML = seconds;
}

В рядку (*) ми щоразу перевіряємо поточну дату. Виклики setInterval не надійні: вони можуть відбуватися з затримкою.

Функція для управління годинником:

let timerId;

function clockStart() { // увімкнути годинник
  if (!timerId) { // встановити новий інтервал, якщо годинник не увімкнений
    timerId = setInterval(update, 1000);
  }
  update(); // (*)
}

function clockStop() {
  clearInterval(timerId);
  timerId = null; // (**)
}

Будь ласка, зверніть увагу, що виклик update() не тільки заплановано в clockStart(), а ще й негайно виконується в рядку (*). Інакше відвідувач повинен був би чекати до першого виконання setInterval. І до того часу годинник був би порожнім.

Також важливо встановити новий інтервал у clockStart() лише тоді, коли годинник не працює. Інакше натискання кнопки «Пуск» кілька разів встановить кілька одночасних інтервалів. Ще гірше – ми запам’ятаємо лише timerID останнього інтервалу, втративши посилання на всі інші. В такому разі ми б ніколи більше не змогли зупинити годинник! Зауважте, в рядку (**) нам потрібно очистити timerID, коли годинник зупинено, щоб його можна було знову увімкнути, запустивши clockStart().

Відкрити рішення в пісочниці.

важливість: 5

Напишіть код, щоб вставити <li>2</li><li>3</li> між двома <li> тут:

<ul id="ul">
  <li id="one">1</li>
  <li id="two">4</li>
</ul>

Коли нам потрібно кудись вставити фрагмент HTML, найкраще підходить insertAdjacentHTML.

Рішення:

one.insertAdjacentHTML('afterend', '<li>2</li><li>3</li>');
важливість: 5

Ось таблиця:

<table>
<thead>
  <tr>
    <th>Ім’я</th><th>Прізвище</th><th>Вік</th>
  </tr>
</thead>
<tbody>
  <tr>
    <td>Сергій</td><td>Петренко</td><td>10</td>
  </tr>
  <tr>
    <td>Юрій</td><td>Даниленко</td><td>15</td>
  </tr>
  <tr>
    <td>Анна</td><td>Ткаченко</td><td>5</td>
  </tr>
  <tr>
    <td>...</td><td>...</td><td>...</td>
  </tr>
</tbody>
</table>

В таблиці може бути більше рядків.

Напишіть код для сортування по імені, колонка "name".

Відкрити пісочницю для завдання.

Рішення коротке, але може виглядати дещо складним, тому я надаю його з розширеними коментарями:

let sortedRows = Array.from(table.tBodies[0].rows) // 1
  .sort((rowA, rowB) => rowA.cells[0].innerHTML.localeCompare(rowB.cells[0].innerHTML));

table.tBodies[0].append(...sortedRows); // (3)

Покроковий алгоритм:

  1. Отримати всі <tr> з <tbody>.
  2. Потім відсортувати їх порівнюючи за вмістом першого <td> (поле «Ім’я»).
  3. Вставити вузли в правильному порядку .append(...sortedRows).

Нам не потрібно видаляти існуючі рядки з таблиці, лише повторно вставити і вони автоматично залишать своє попереднє місце розташування.

P.S. У нашому випадку в таблиці є явний <tbody>, але навіть якщо HTML-таблиця не має <tbody>, він завжди є в структурі DOM.

Відкрити рішення в пісочниці.

Навчальна карта