Памятка по операциям с DOM-элементами

Выборки по селектору:

//get all elements by tagName
document.getElementsByTagName("div");
//get all elements by className
document.getElementsByClassName("my-class"); //more fast

//querySelectorAll accepts full CSS selector groups, which lets you specify more than one unrelated selector. For instance
document.querySelectorAll("#myElement .my-class li:first-child");
document.querySelectorAll('textarea, input[type=text]');
document.querySelector('textarea, input[type=text]'); //first Element

document.getElementById('myElement') // IE 5.5+
// or
document.querySelector('#myElement') // IE 8+

document.getElementsByTagName('div')
// or
document.querySelectorAll('div')

document.getElementsByClassName('some-class')
// or
document.querySelectorAll('.some-class')

Все методы в примерах выше начинают свой поиск с вершины DOM дерева, которые не могут быть эффективны при работе с тонной DOM узлов. Однако мы можем вызывать методы непосредственно на узле DOM дерева.

Метод querySelectorAll будет возвращать статический NodeList, тогда как getElementsByTagName и getElementsByClassName будут возвращать живой NodeList.

Хорошо, так в чем отличие?
Подумайте о статическом NodeList как снимок DOM дерева, когда метод querySelectorAll был вызван. Все что вы делаете с DOM после вызова метода querySelectorAll не будет отображено в NodeList при возврате. В то время как живой NodeList вернувший getElementsByTagName и будет getElementsByClassName.

Конечно, это относиться только если DOM дерево меняется после того как вызывается метод, если вы уверены, что никаких изменений в будущем не произойдет, вы в безопасности что бы использовать метод querySelectorAll.


Также отметим, что querySelector возвращает первый подходящий Element узла, в то время как querySelectorAll вернет NodeList.

Получить первый элемент

document.getElementsByTagName('div')[0]
// or
document.querySelectorAll('div')[0]
// or
document.querySelector('div')

Получить дочерние узлы элементов

document.getElementsByTagName('div').childNodes // IE <9
// or
document.getElementsByTagName('div').children // IE 9+

Не используйте метод childNodes если не знаете что делаете, или вы должны поддерживать IE <9, так как он может вернуть не являющиеся узлы элемента как текстовые узлы.

Получить первый дочерний элемент

var myList = document.getElementById('myList');
myList.children[0] // IE 9+
// or
myList.firstChild // IE <9
// or
myList.firstElementChild // IE 9+

Метод firstChild имеет аналогичные подводные камни как childNodes.

Получить последний дочерний элемент

var myList = document.getElementById('myList');
myList.lastChild // IE <9
// or
myList.children[myList.length - 1] // IE 9+
// or simply
myList.lastElementChild // IE 9+

Как и c firstChild и childNodes, lastChild следует использовать только если вы собираетесь поддерживать IE <9.

Получить родителя элемента

myElement.nextSibling // IE <9
// or
myElement.nextElementSibling // IE 9+

Нативные методы будут возвращать null, если MyElement является последним потомком его родителя.

Получить предыдущий элемент

myElement.previousSibling // IE <9
// or
myElement.previousElementSibling // IE 9+

Нативные методы будут возвращать null, если MyElement является первым потомком его родителя.

nextSibling и previousSibling методы должны рассматриваться так же, как и childNodes, firstChild и lastChild, для их возврата узлов за исключением HTMLElements.

Если вам нужно использовать любой из этих методов для поддержки старых браузеров, то вы можете проверить nodeType для безопасности. HTMLElement nodeType равен 1, если другое значение отличное от 1, то у вас не HTMLElement.

Перемещение по элементам

//get parent element
myElement.parentElement;
//get next
myElement.nextElementSibling;
//get previous
myElement.previousElementSibling;

//children elements/ Read-only
myElement.children;

//first and last children element
myElement.firstElementChild;
myElement.lastElementChild;

Нативные методы будут возвращать null, если myElement является последним (первым) потомком его родителя.


Обход DOM дерева может быть непростой задачей (особенно для новичков) и к счастью jQuery имеет много полезных методов что бы помочь. Однако не все jQuery методы имеют родной эквивалент.

Получить ближайший элемент вверх по DOM дереву (предка) соответствующий селектору

 Element.closest()

Манипуляции с HTML кодом элементов

// get
var html = elem.innerHTML;

// set
elem.innerHTML = '<div>New html</div>';

Манипуляции с текстом элементов

// get
var text = elem.textContent;

// set
elem.textContent = 'New text';

Создание новых элементов

let frag = document.createDocumentFragment();
let myDiv = document.createElement("div");
let a = document.createElement("a");
a.setAttribute('href','https://google.com');
a.textContent = 'Click Me!';
myDiv.appendChild(a);
frag.appendChild(myDiv);
document.body.appendChild(frag);

Создание элемента

document.createElement('div');


Добавляет содержимое в конец элементов

parentNode.appendChild(newNode);


Добавляет содержимое в начало элементов

referenceNode.insertBefore(newNode, referenceNode.firstElementChild);
// or
referenceNode.insertAdjacentElement('afterbegin', newNode); // FF 48.0+, IE8+


Вставить непосредственно перед элементом Insert directly before an Element

referenceNode.parentNode.insertBefore(newNode, referenceNode);
// or
referenceNode.insertAdjacentElement('beforebegin', newNode); // FF 48.0+, IE8+


Вставить непосредственно после элемента Insert directly after an Element

referenceNode.parentNode.insertBefore(newNode, referenceNode.nextElementChild);
// or
referenceNode.insertAdjacentElement('afterend', newNode); // FF 48.0+, IE8+


Примечание: ‘beforebegin’ и ‘afterend’ будут работать только тогда, когда referenceNode находиться в DOM дереве и имеет родительский элемент.

Многократное добавление элементов
Стоит так же отметить, что добавление элемента к узлу, который находиться в DOM дереве приведет к перерисовке. Это не очень хорошо, потому что браузер должен пересчитать размеры и положение нового элемента, что так же приведет к изменениям потомков элемента, предков и элементов, которые появятся после него в DOM. Если вы добавляете много элементов в DOM, то это может занять некоторое время.

Что бы этого избежать, вы можете сделать добавление с DocumentFragment. Фрагмент документа является объектом document который существует только в памяти, поэтому добавление к нему не будет вызывать никаких перекомпоновок.

Позиционирование элементов:

//By default new Element will be insert at the end of nodeList
parentNode.appendChild(newNode);

document.body.insertBefore(newNode, referenceNode);

//or
referenceNode.insertAdjacentElement(position, node);

//delete
referenceNode.parentNode.removeChild(referenceNode);
// or
referenceNode.remove(); //No IE support

//delete all
while (parentNode.firstChild) {
    parentNode.removeChild(parentNode.firstChild);
}

Примечание: ‘beforebegin’ и ‘afterend’ будут работать только тогда, когда referenceNode находиться в DOM дереве и имеет родительский элемент.
Возможны варианты:
        'beforebegin': Before the element itself.
        'afterbegin': Just inside the element, before its first child.
        'beforeend': Just inside the element, after its last child.
        'afterend': After the element itself.

 Удаление:

referenceNode.parentNode.removeChild(referenceNode);
// or
referenceNode.remove(); // FF 23.0+, 23.0+, Edge (No IE support)

Работа с атрибутами:

let valueOfAttribute = elem.attributeName;// get
elem.attributeName = 'newValue';// set

//or
let valueOfAttribute = elem[attributeName];
elem[attributeName] = 'newValue';

//WebAPI style
elem.getAttribute(attributeName);
elem.setAttribute(attributeName, value);
elem.removeAttribute(attributeName);
elem.hasAttribute(attributeName); // returns a boolean

//add CSS style
elem.style.backgroundImage = 'url(/image.jpg)';

Data атрибуты (data-*)

// get
elem.getAttribute('data-something')

// set
elem.setAttribute('data-something')

dataset:

// get
elem.dataset.myCustomAttr

// set
elem.dataset.myCustomAttr = 'new-value';

// check
if ( 'myCustomAttr' in elem.dataset ) {
// elem has the attribute 'data-my-custom-attr'
}

Работа с именами классов:

// add a specified class
elem.classList.add('my-class')
// remove a specified class
elem.classList.remove('my-class')
// toggle a specified class
elem.classList.toggle('my-class')
// check the element has the specified class
elem.classList.contains('my-class') // returns a boolean

События

//add
target.addEventListener(type, handler);
//remove
target.removeEventListener(type, handler); //handler must be a named function