Шпаргалка по методам 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 elementconst newItem = createEl('item0', 0) //
and add it to the top of the listlist.prepend(newItem)
//
Create another itemconst newItem2 = createEl('item4', 4) //
and add it to the end of the listlist.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 elementslist.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 existingconst newList = list.cloneNode(false) //
remove the `ID` in the avoidance of collisionsnewList.removeAttribute('id') //
change its text contentnewList.textContent = 'new list' //
and insert it after an existing listlist.after(newList)
//
create another listconst 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 existingconst newList3 = document.importNode(list, true) // insert it before the existing list list.before(newList3) //
and remove to avoid conflictsnewList3.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"
*/