JavaScript в браузере. Объектная модель документа

Объект window в браузере обеспечивает доступ:

Window как глобальный объект пользовательских скриптов мы рассматривали ранее.

Объектная модель браузера

К BOM относится все, что не относится к DOM:

Объектная модель документа (DOM)

Document Object Model («объектная модель документа») — это программный интерфейс доступа к содержимому XML-документов, а также изменять содержимое, структуру и оформление таких документов.

В JavaScript DOM используется для доступа и манипуляций с узлами HTML через свойство document.

Дерево DOM

Дерево DOM — это представление совокупности узлов HTML-страницы в виде древовидной иерархии с учетом вложенности узлов друг в друга.

Рассмотрим код простейшей страницы:

<!DOCTYPE html>
<html>
    <head>
        <title>Page</title>
    </head>
    <body><!-- myPage body -->
        <main>Hello, <span>word</span></main>
    </body>
</html>

DOM-дерево для такой страницы будет выглядеть так:

Похожую структуру можно увидеть, если открыть вкладку Elements инструментов разработчика Google Chrome.

Данное дерево состоит из:

Особенности построения дерева: пробелы перед <head> игнорируются, а все узлы после </body> попадают внутрь тела страницы. Также при построении дерева браузеры используют "исправление вестки": закрытие тегов или добавление недостающих.

Помимо наглядной визуализации структуры документа DOM-дерево дает возможность навигации по документу, используя отношения "родительский/дочерний узел" и "соседний узел".

Навигация по DOM-элементам

Прежде, чем применить какие-то изменения к элементам страницы, к ним нужно получить доступ.

Своими свойствами объект document обеспечивает доступ к следующим узлам:

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

Данные свойства работают только для чтения, т.е. напрямую переопределить узел (например, document.body.firstChild = null) нельзя.

Кроме того, навигация происходит по всем узлам независимо от их типа, а чаще всего интересна навигация и взаимодествие с узлами типа "элемент".

Узлы-элементы можно отфильтровать по типу, т.к. у каждого элемента есть собственные свойства:

Типы узлов

Код типа (nodeType) Тип узла Описание
1 ELEMENT_NODE Узел элемента
3 TEXT_NODE Текстовый узел (#text)
7 PROCESSING_INSTRUCTION_NODE Узел инструкции обработки
8 COMMENT_NODE Узел комментария (#comment)
9 DOCUMENT_NODE Узел документа (#document)
10 DOCUMENT_TYPE_NODE Узел типа документа
11 DOCUMENT_FRAGMENT_NODE Узел фрагмента документа

Для получения только узлов-элементов можно воспользоваться код:

var els = document.documentElement.childNodes;
for(var i = 0; i < els.length; i++) {
    if(els[i].nodeType == 1) {
        console.log( els[i].nodeName );
    }
}

Но есть другой способ получать только узлы-элементы — использовать свойства, привязанные к элементам:

Из особенностей: IE8 и ниже поддерживает только children и в него, вопреки стандарту, попадают узлы-комментарии.

Document так же имеет дополнительные навигационные ссылки:

Элемент формы также имеет ссылку на коллекцию элементов формы через elements

Поисковые методы DOM

Для получения конкретного элемента неудобно проходить по всему дереву. Верстка может меняться, из-за чего скрипты, привязанные к конкретному расположению элемента в дереве, могут перестать работать. Потому для на практике для обращения к элементам-потомкам используют поисковые методы DOM:

* — современные методы, перед использованием необходимо проверять поддержку браузерами

Для изменения элементов есть следующие свойства:

Атрибуты и DOM-свойства

При интерпретации HTML браузер создает DOM-модель. При этом многие стандартные HTML-атрибуты становятся свойствами элементов.

Для манипуляций с атрибутами используют методы:

Все атрибуты элемента можно получить через свойство attributes в виде коллекции.

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

Синхронизация между свойствами и атрибутами происходит только для стандартных свойств, но и она не всегда точна. Из типичных примеров можно выделить:

Атрибут class

Атрибуту class соответствую два объекта: className (строка) и classList (коллекция). ClassList поддерживается IE10 и выше.

ClassList удобен своими методами для проверки и изменения классов: contains, add/remove, toggle.

Нестандартные атрибуты

В html элементы могут иметь универсальные атрибуты, такие как id, style, class, т.е. атрибуты, общие для всех элементов. Также элементы могут иметь специфические атрибуты, например, for у <label>, href у <a> и т.д. Если элементу добавить нестандартный атрибут, то соответствующее свойство не будет создано. Для управления такими атрибутами нужно использовать только методы *Attribute

Для пользовательских атрибутов используются атрибуты с именем, начинающимся на data-. Доступ к таким атрибутам есть через свойство dataset.

Стили элеменета, свойство style

Изменение стилевого оформления элемента доступно через его свойство style. Названия стилевых свойств соответствуют названиям в CSS, но вместо дефиса используется запись свойств в camelCase-формате (кроме float, для которого в style другое свойство).

var body = document.body;
body.style.borderRadius = "20px"; // border-radius: 20px;
body.style.fontSize = "3em"; // font-size: 3em;
body.style.cssFloat = "none"; // float: none; исключение!

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

Для добавления свойств с вендорными префиксами префикс добавляеся с большой буквы:

body.style.WebkitTransform = "rotate(30deg)"; // -webkit-transform: rotate(30deg);
body.style.MsTransform = "rotate(30deg)"; // -ms-transform: rotate(30deg);
body.style.transform = "rotate(30deg)"; // transform: rotate(30deg);

Присвоеннное значение стилевого свойства можно сбросить, присвоив пустую строку. Это бывает удобно, например, в случае, если для какого-то элемента на странице мы указали el.style.display = "none";, а затем надо вернуть элементу его исходное состояние. Достаточно написать el.style.display = ""; чтобы удалить назначенное ранее через JavaScript значение.

Следует помнить, что самый более простым способом поменять внешний вид элемента является добавление ему класса. Таким образ код короче, к тому же мы перекладываем всю ответственность за отображение элемента на CSS-стили. Редактирование стилевых свойств через JavaScript целесообразно в случаях, когда необходимо задать параметры элементы исходяиз каких-то других параметров, значения которых неизвесны либо завистят размеров окна браузера, времени суток и т.д.

cssText

Все присвоенные стили можно прочитать из специального свойства cssText в виде одной строки. С помощью него же можно переопределить эти стили.

var body = document.body;
body.style.backgroundColor = "#F00";
body.style.fontSize = "3em";
console.log(body.style.cssText); // 'background-color: rgb(255, 0, 0); font-size: 3em;'
body.style.cssText = "background-color: #0F0; font-size: 16px;";

При назначении стилевых свойств (не только через cssText) внутреннее представление значений может меняться. В частности, при указании цвета в любом формате в свойстве он все равно будет храниться в формате rgb(rgba).

getComputedStyle

При получении свойств из style отобразятся только свойства, заданные через JavaScript. Для получения стилевых свойств элемента с учетом CSS и каскада используется функция getComputedStyle:

<style>
    div { color: #F00; }
</style>

<div></div>

<script>
    var computed = getComputedStyle(document.querySelector("div"));
    console.log(computed.color); // rgb(255, 0, 0)
</script>
bI

Добавление элементов на страницу

Для добавления элементов на страницу используется два метода: append и insertBefore. Оба метода вызываются для родительского элемента, append вставляет самым последним дочерним элементом, а insertBefore — перед элементом, указанным в качестве второго параметра.

Добавлять можно как уже существующий элемент, в этом случае он просто переместится, так и вновь созданный. За создание элементов отвечает метод createElement, который запускается с одним аргументом - типом создаваемого элемента.

var body = document.body;
var newDiv = document.createElement("div"); // создали новый div
newDiv.innerText = "I`m the last div";
body.appendChild(newDiv); // и добавили его в конец body

var oldDiv = body.querySelector("div"); // нашли самый первый div на странице
body.insertBefore(oldDiv, newDiv); // и вставили его перед вновь созданным

При передаче в качестве второго аргумента null метод insertBefore сработает аналогично вызову appendChild.

Удаление элементов

В случае необходимости удалить элемент со страницы используют метод removeChild:

body.removeChild( oldDiv );

Удалить элемент можно также через замену его новым элементом:

// удаляем oldDiv и вставляем на его место newDiv
body.replaceChild( newDiv, oldDiv );

При использований операций вставки над уже созданными элементами не нужно предварительно удалять их со "старого" места, они просто переместятся по дереву DOM в указанную позицию.