Шпаргалка по методам JS для работы с DOM, часть2

Mixin ParentNode

Это смешение предназначено для обработки родительских элементов (предков), т.е. элементов, содержащих одного и более потомков (дочерних элементов).

  • children — потомки элемента
const { children } = list // list.children
log(children)
/*
HTMLCollection(3)
  0: li#item1.item
  1: li#item2.item
  2: li#item3.item
  length: 3
*/

Такая структура называется коллекцией HTML и представляет собой объект, похожий на руки (псевдомассив). Есть еще одна похожая структура — list of Nodes(NodeList).

Массивоподобные объекты имеют свойство length с количеством потомков, метод forEach() , (NodeList), который позволяет перемещать узлы (делать итерацию). Такие объекты позволяют получать элементы по индексу, по имени (HTMLCollection) и т. д. Однако у них нет методов реальных массивов, таких как map(), filter(), reduce() и т. д., что делает работу с ними не очень удобной. Поэтому объекты-массивы рекомендуется преобразовывать в массивы с помощью метода оператора Array.from() или Spread:

const children = Array.from(list.children)
// or
const children = [...list.children]
log(children) 
// [li#item1.item, li#item2.item, li#item3.item] - Normal array
  • firstElementChild — первый потомок — элемент
  • lastElementChild — последний потомок — элемент
log(list.firstElementChild) // li#item1.item
log(list.lastElementChild) // li#item2.item

Для дальнейших манипуляций нам потребуется периодически создавать новые элементы, поэтому создадим еще одну утилиту:

const createEl = (id, text, tag = 'li', _class = 'item') => {
  const el = document.createElement(tag)
  el.id = id
  el.className = _class
  el.textContent = text
  return el
}

Наша утилита принимает 4 аргумента: идентификатор, текст, имя тега и класс CSS. 2 аргумента (тег и класс) имеют значения по умолчанию. Функция возвращает готовый к работе элемент. Впоследствии реализуем более универсальную версию этой утилиты.

  • prepend(newNode) — добавляет элемент в начало списка
  • append(newNode) — добавляет элемент в конец списка
// Create a new element
const newItem = createEl('item0', 0)
// and add it to the top of the list
list.prepend(newItem)
// Create another item
const newItem2 = createEl('item4', 4)
// and add it to the end of the list
list.append(newItem2)
log(children)
/*
HTMLCollection(5)
  0: li#item0.item
  1: li#item1.item
  2: li#item2.item
  3: li#item3.item
  4: li#item4.item
*/

Одной из интересных особенностей HTMLCollection является то, что он «живой», т.е. элементы, возвращаемые по ссылке, и их количество обновляются автоматически. Однако эту функцию нельзя использовать, например, для автоматического добавления обработчиков событий.

  • replaceChildren(nodes) — заменяет потомков новыми элементами
const newItems = [newItem, newItem2]
// replace descendants with new elements
list.replaceChildren(...newItems) // list.replaceChildren(newItem, newItem2)
log(children) // 2

Наиболее универсальными методами получения ссылок на элементы являются querySelector(selector) и querySelectorAll(selector)методы. Более того, в отличие от getElementById(), их можно вызывать на любом родительском элементе, а не только на document. Любой допустимый селектор CSS передается в качестве аргумента этим методам. (id, class, tag и т. д.):

// get an element `li` with `id === item0`
const itemWithId0 = list.querySelector('#item0')
log(itemWithId0) // li#item0.item
// get all elements `li` with `class === item`
const allItems = list.querySelectorAll('.item')
log(allItems) // array-like object
/*
NodeList(2)
  0: li#item0.item
  1: li#item4.item
  length: 2
*/

Создайте универсальную утилиту для получения предметов:

const getEl = (selector, parent = document, single = true) => single ? parent.querySelector(selector) : [...parent.querySelectorAll(selector)]

Наша утилита принимает 3 аргумента: CSS-селектор, родительский элемент и индикатор количества элементов (один или все). 2 аргумента (предок и индикатор) имеют значения по умолчанию. Функция возвращает либо один, либо все элементы (как обычный массив), совпадающие с селектором, в зависимости от значения индикатора:

const itemWithId0 = getEl('#item0', list)
log(itemWithId0) // li#item0.item
const allItems = getEl('.item', list, false)
log(allItems) // [li#item0.item, li#item4.item]

Mixin NonDocumentTypeChildNode

Это смешение предназначено для обработки дочерних элементов, не являющихся документом, т.е. всех узлов, кроме document.

  • previousElementSibling — предыдущий элемент
  • nextElementSibling —следующий элемент
log(itemWithId0.previousElementSibling) // null
log(itemWithId0.nextElementSibling) // #item4

Mixin ChildNode

Это смешение предназначено для обработки дочерних элементов, т.е. элементов, являющихся потомками других элементов.

  • before(newNode) — вставляет новый элемент перед текущим
  • after(newNode) — вставляет новый элемент после текущего
// get `li` with `id === item4`
const itemWithId4 = getEl('#item4', list)
// create new element
const newItem3 = createEl('item3', 3)
// and insert it before `itemWithId4`
itemWithId4.before(newItem3)
// create another one
const newItem4 = createEl('item2', 2)
// and insert it after `itemWithId0`
itemWithId0.after(newItem4)
  • replaceWith(newNode) — заменяет текущий элемент новым
// создаем новый элемент
const newItem5 = createEl('item1', 1)
// и заменяем им `itemWithId0`
itemWithId0.replaceWith(newItem5)
  • remove() — удаляет текущий элемент
itemWithId4.remove()

Node Interface

Этот интерфейс предназначен для обработки узлов.

  • nodeType — тип узла
log(list.nodeType) // 1
// other options
/*
 1 -> ELEMENT_NODE (element)
 3 -> TEXT_NODE (text)
 8 -> COMMENT_NODE (comment)
 9 -> DOCUMENT_NODE (document)
 10 -> DOCUMENT_TYPE_NODE (doctype)
 11 -> DOCUMENT_FRAGMENT_NODE (fragment) etc.
*/
  • nodeName — имя узла
log(list.nodeName) // UL
// other options
/*
  - qualified name of the HTML element with capital (capital) letters
  - qualified attribute name
  - #text
  - #comment
  - #document
  - doctype
  - #document-fragment
*/
  • baseURI — основной путь
log(list.baseURI) // .../dom/index.html
  • parentNode — родительский узел
  • parentElement —родительский элемент
const itemWithId1 = getEl('#item1', list)
log(itemWithId1.parentNode) // #list
log(itemWithId1.parentElement) // #list
  • hasChildNodes() — возвращает true, если у элемента есть хотя бы один потомок
  • childNodes — дочерние узлы
log(list.hasChildNodes()) // true
log(list.childNodes)
/*
NodeList(3)
  0: li#item1.item
  1: li#item2.item
  2: li#item3.item
*/
  • firstChild — первый дочерний узел
  • lastChild — последний дочерний узел
log(list.firstChild) // #item1
log(list.lastChild) // #item3
  • nextSibling — следующий узел
  • previousSibling — предыдущий узел
log(itemWithId1.nextSibling) // #item2
log(itemWithId1.previousSibling) // null
  • textContent — геттер/сеттер для извлечения/записи текста
// get text
log(itemWithId1.textContent) // 1
// edit text
itemWithId1.textContent = 'item1'
log(itemWithId1.textContent) // item1
// get text content from all children
log(list.textContent) // item123

Для извлечения/записи текста есть еще один (устаревший) геттер/сеттер — innerText.

  • cloneNode(deep) — копирует узел. Принимает логическое значение, определяющее характер копирования: поверхностное — копируется только сам узел, копируется сама сборка и все ее потомки
// create a new list by copying the existing
const newList = list.cloneNode(false)
// remove the `ID` in the avoidance of collisions
newList.removeAttribute('id')
// change its text content
newList.textContent = 'new list'
// and insert it after an existing list
list.after(newList)
// create another list
const newList2 = newList.cloneNode(true)
newList.after(newList2)
  • isEqualNode(node) — сравнивает узлы
  • isSameNode(node) — определяет идентичность узлов
log(newList.isEqualNode(newList2)) // true
log(newList.isSameNode(newList2)) // false
  • contains(node) — возвращает true, если элемент содержит указанный узел
log(list.contains(itemWithId1)) // true
  • insertBefore(newNode, existingNode) — добавляет новый узел (newNode) перед существующим (existingNode)
// create new element
const itemWithIdA = createEl('#item_a', 'a')
// and insert it before `itemWithId1`
list.insertBefore(itemWithIdA, itemWithId1)
  • appendChild(node) — добавляет узел в конец списка
// создаем новый элемент
const itemWithIdC = createEl('#item_c', 'c')
// и добавляем его в конец списка
list.appendChild(itemWithIdC)
  • replaceChild(newNode, existingNode) — заменяет существующий узел (existingNode) новым (newNode):
// создаем новый элемент
const itemWithIdB = createEl('item_b', 'b')
// и заменяем им `itemWithId1`
list.replaceChild(itemWithIdB, itemWithId1)
  • removeChild(node) — удаляет указанный дочерний узел
// получаем `li` с `id === item2`
const itemWithId2 = getEl('#item2', list)
// и удаляем его
list.removeChild(itemWithId2)

Document interface

Этот интерфейс предназначен для обработки объектаDocument .

  • URL и documentURI — адрес документа
log(document.URL) // .../dom/index.html
log(document.documentURI) // ^
  • documentElement:
log(document.documentElement) // html
  • getElementsByTagName(tag) — возвращает все элементы с указанным тегом
const itemsByTagName = document.getElementsByTagName('li')
log(itemsByTagName)
/*
HTMLCollection(4)
  0: li##item_a.item
  1: li#item_b.item
  2: li#item3.item
  3: li##item_c.item
*/
  • getElementsByClassName(className) — возвращает все элементы с указанным классом CSS
const itemsByClassName = list.getElementsByClassName('item')
log(itemsByClassName) // ^
  • createDocumentFragment() — возвращает фрагмент документа:
// create a fragment
const fragment = document.createDocumentFragment()
// create a new element
const itemWithIdD = createEl('item_d', 'd')
// add element to fragment
fragment.append(itemWithIdD)
// add fragment to list
list.append(fragment)

Фрагменты позволяют избежать создания ненужных элементов. Их часто используют при работе с разметкой, скрытой от пользователя с помощью тега template (метод cloneNode() возвращает DocumentFragment).

  • createTextNode(data) —создать текст
  • createComment(data) — создать комментарий
  • importNode(existingNode, deep) — создает новый узел на основе существующего
// Creates a new list based on existing
const newList3 = document.importNode(list, true)
// insert it before the existing list
list.before(newList3)
// and remove to avoid conflicts
newList3.remove()
  • createAttribute(attr) — создает указанный атрибут

NodeIterator и TreeWalker interfaces

Интерфейсы NodeIterator и TreeWalker предназначены для обхода (обхода) деревьев узлов. Примеров их практического использования мне не попадалось, поэтому ограничусь парой примеров:

// createNodeIterator(root, referenceNode, pointerBeforeReferenceNode, whatToShow, filter)
const iterator = document.createNodeIterator(list)
log(iterator)
log(iterator.nextNode()) // #list
log(iterator.nextNode()) // #item_a
log(iterator.previousNode()) // #item_a
log(iterator.previousNode()) // #list
log(iterator.previousNode()) // null
// createTreeWalker(root, whatToShow, filter)
// apply filters - https://dom.spec.whatwg.org/#interface-nodefilter
const walker = document.createTreeWalker(list, '0x1', { acceptNode: () => 1 })
log(walker)
log(walker.parentNode()) // null
log(walker.firstChild()) // #item_a
log(walker.lastChild()) // null
log(walker.previousSibling()) // null
log(walker.nextSibling()) // #item_b
log(walker.nextNode()) // #item3
log(walker.previousNode()) // #item_b

Element interface

Этот интерфейс предназначен для обработки элементов.

  • localName и tagName — имя тега
log(list.localName) // ul
log(list.tagName) // UL
  • id — геттер/сеттер для идентификатора
  • className — геттер/сеттер для CSS-класса
log(list.id) // list
list.id = 'LIST'
log(LIST.className) // list
  • classList — все CSS-классы элемента(DOMTokenList object)
const button = createEl('button', 'Click me', 'my_button', 'btn btn-primary')
log(button.classList)
/*
DOMTokenList(2)
  0: "btn"
  1: "btn-primary"
  length: 2
  value: "btn btn-primary"
*/