Еще один блог веб разработчика. Манипуляции с DOM на чистом JavaScript Dom объектная модель документа

Этот раздел представляет краткое знакомство с Объектной Моделью Документа (DOM) - что такое DOM, каким образом предоставляются структуры HTML и XML документов, и как взаимодействовать с ними. Данный раздел содержит справочную информацию и примеры.

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

Объектная Модель Документа (DOM) – это программный интерфейс (API) для HTML и XML документов. DOM предоставляет структурированное представление документа и определяет то, как эта структура может быть доступна из программ, которые могут изменять содержимое, стиль и структуру документа. Представление DOM состоит из структурированной группы узлов и объектов, которые имеют свойства и методы. По существу, DOM соединяет веб-страницу с языками описания сценариев либо языками программирования.

Веб-страница – это документ. Документ может быть представлен как в окне браузера, так и в самом HTML-коде. В любом случае, это один и тот же документ. DOM предоставляет другой способ представления, хранения и управления этого документа. DOM полностью поддерживает объектно-ориентированнное представление веб-страницы, делая возможным её изменение при помощи языка описания сценариев наподобие JavaScript.

Каким образом доступен DOM?

Вы не должны делать ничего особенного для работы с DOM. Различные браузеры имеют различную реализацию DOM, эти реализации показывают различную степень соответсвия с действительным стандартом DOM (это тема, которую мы пытались не затрагивать в данной документации), но каждый браузер использует свой DOM, чтобы сделать веб страницы доступными для взаимодествия с языками сценариев.

При создании сценария с использованием элемента , либо включая в веб страницу инструкцию для загрузки скрипта, вы можете немедленно приступить к использованию программного интерфейса (API), используя элементы или для взаимодействия с самим документом, либо для получения потомков этого документа, т.е. различных элементов на странице. Ваше программирование DOM может быть чем-то простым, например, вывод сообщения с использованием функции объекта , или использовать более сложные методы DOM, которые создают новое содержимое, как показанно в следующем примере:

В следующем примере внутри элемента определен код JavaScript, данный код устанавливает функцию при загрузке документа (когда весь DOM доступен для использования). Эта функция создает новый элемент H1, добавляет текст в данный элемент, а затем добавляет H1 в дерево документа:

< html > < head > < script > // запуск данной функции при загрузке документа window. onload = function () { // создание нескольких элементов // в пустой HTML странице heading = document. createElement( "h1" ) ; heading_text = document. createTextNode( "Big Head!" ) ; heading. appendChild( heading_text) ; document. body. appendChild( heading) ; } < body > Важные типы данных

Данный раздел предназначен для краткого описания различных типов и объектов в простой и доступной манере. Существует некоторое количество различных типов данных, которые используются в API, на которые вы должны обратить внимание. Для простоты, синтаксис примеров в данном разделе обычно ссылается на узлы как на element s, на массивы узлов как на nodeList s (либо просто element s) и на атрибуты узла, просто как на attribute s.

Ниже таблица с кратким описанием этих типов данных.

document Когда член возвращает объект типа document (например, свойство элемента ownerDocument возвращает документ к которому он относится), этот обьект document является собственным корневым обьектом. В DOM document Reference разделе описан объект document.
element
element обозначает элемент или узел типа element, возвращаемый членом DOM API. Вместо того, чтобы говорить, что метод document.createElement() возвращает ссылку на node, мы просто скажем, что этот элемент возвращает element, который просто был создан в DOM. Объекты element реализуют DOM element интерфейс и также более общий Node интерфейс. Оба интерфейса включены в эту справку.
nodeList
NodeList

массив элементов, как тот, что возвращается методом Document.getElementsByTagName(). Конкретные элементы в массиве доступны по индексу двумя способами:

  • list.item(1)

Эти способы эквивалентны. В первом способе item() - единственный метод объекта NodeList. Последний использует обычный синтаксис массивов, чтобы получить второе значение в списке.

attribute Когда attribute возвращается членом API (например, метод createAttribute()) - это будет ссылка на объект, который предоставляет специальный (хоть и небольшой) интерфейс для атрибутов. Атрибуты - это узлы в DOM, как и элементы, хотя вы можете редко использовать их в таком виде.
namedNodeMap namedNodeMap подобна массиву, но элементы доступны по имени или индексу. Доступ по индексу - это лишь для удобства перечисления, т.к. элементы не имеют определенног порядка в списке. Этот тип данных имеет метод item() для этих целей и вы можете также добавлять и удалять элементы из namedNodeMap
DOM-интерфейсы (DOM interfaces)

Это руководство об объектах и реальных вещах, которые вы можете использовать для управления DOM-иерархией. Есть много моментов, где понимание того, как это работает, может удивлять. Например, объект, представляющий HTML form элемент, берет своё свойство name из интерфейса HTMLFormElement, а свойство className - из интерфейса HTMLElement. В обоих случаях свойство, которое вы хотите, находится в этом объекте формы.

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

Интерфейсы и объекты (Interfaces and objects)

Многие объекты реализуют действия из нескольких интерфейсов. Объект таблицы, например, реализует специальный , который включает такие методы как createCaption и insertRow . Но так как это таблица - это ещё и HTML-элемент, table реализует интерфейс Element , описанный в разделе . Наконец, так как HTML-элемент (в смысле DOM) - это узел (node) в дереве, которое составляет объектную модель для HTML- или XML-страницы, табличный элемент также реализует более общий интерфейс Node , из которого происходит Element .

Var table = document.getElementById("table"); var tableAttrs = table.attributes; // Node/Element interface for (var i = 0; i < tableAttrs.length; i++) { // HTMLTableElement interface: border attribute if(tableAttrs[i].nodeName.toLowerCase() == "border") table.border = "1"; } // HTMLTableElement interface: summary attribute table.summary = "note: increased border";

Основные интерфейсы в DOM (Core interfaces in the DOM)

Этот раздел перечисляет несколько самых распространенных интерфейсов в DOM. Идея не в том чтобы описать, что делают эти методы API, но в том чтобы дать вам несколько мыслей насчет видов методов и свойств, которые вы будете часто видеть, используя DOM. Эти распространенные части API использованы в большинстве примеров раздела в конце этой справки.

Document, window - это объекты, чьи интерфейсы вы, как правило, очень часто используете в программировании DOM. Говоря простыми словами, объект window представляет что-то вроде браузера, а объект document - корень самого документа. Element наследуется от общего интерфейса Node , и эти интерфейсы вместе предоставляют много методов и свойств, которые можно применять у отдельных элементов. Эти элементы также могут иметь отдельные интерфейсы для работы с типами данных, которые эти элементы содержат, как в примере с объектом table в предыдущем случае.

Ниже представлен краткий список распространненых членов API, используемых в программировании веб- и XML-страниц с использованием DOM:

  • parentNode.appendChild (node)
Тестирование DOM API

Этот документ содержит примеры для каждого интерфейса, который вы можете использовать в своей разработке. В некоторых случаях примеры - полноценные веб-страницы с доступом к DOM в элементе , также перечислены элементы, необходимые чтобы запустить скрипт в форме, и HTML-элементы, над которыми будут производиться операции DOM. Когда встречается такой случай, можно просто копировать и вставить пример в новый HTML-документ, сохранить и запустить его в браузере.

Есть случаи, однако, где примеры более лаконичные. Чтобы запустить примеры, которые лишь демонстрируют основы взаимодействия интерфейсов с HTML-элементами, вы можете подготовить тестовую страницу, в которую будете помещать функции внутрь скриптов. Следующая очень простая веб-страница содержит элемент в заголовке, в который вы можете поместить функции, чтобы протестировать интерфейс. Страница содержит несколько элементов с атрибутами, которые можно возвращать, устанавливать или, другими словами, манипулировать и содержит пользовательский интерфейс, необходимый, чтобы вызывать нужные функции из браузера.

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

< html > < head > < title > DOM Tests < script type = " application/javascript" > function setBodyAttr (attr, value) { if (document. body) eval ("document.body." + attr+ "="" + value+ """ ) ; else notSupported () ; } < body > < div style =" margin : .5 in; height : 400 ; " > < p > < b > < tt > text < form > < select onChange = " setBodyAttr(" text" , this.options.value);" > < option value = " black" > black < option value = " darkblue" > darkblue < p > < b > < tt > bgColor < select onChange = " setBodyAttr(" bgColor" , this.options.value);" > < option value = " white" > white < option value = " lightgrey" > gray < p > < b > < tt > link < select onChange = " setBodyAttr(" link" , this.options.value);" > < option value = " blue" > blue < option value = " green" > green < small > < a href = " http://www.brownhen.com/dom_api_top.html" id = " sample" > (sample link) < br > < form > < input type = " button" value = " version" onclick = " ver()" />

Чтобы протестировать много интерфейсов на одной странице, набор свойств, которые изменяют цвета веб-страницы, можно создать похожую веб-страницу с целой "консолью" кнопок, текстовых полей и других элементов. Следующий скриншот даёт идею, как интерфейсы могут быть сгруппированы вместе для тестирования

В этом примере выпадающее меню динамически обновляет доступные из DOM части веб-страницы (например, фоновый цвет, цвет ссылок и цвет текста). Однако при разработке тестовых страниц, тестирование интерфейсов, как вы об этом прочитали, важная часть изучения эффективной работы с DOM.

Данная публикация предваряет ряд статей об альтернативных способах работы с XML. "Альтернативных", потому что как правило работа с XML в 1С ограничивается разбором xml при помощи последовательного чтения - построчного разбора текстового содержимого. А ведь есть еще и другие способы.

Например, использование языка запросов к XML xPath или шаблонов трансформации XSL . Об этих вариантах будет рассказано в следующих статьях. Но все они опираются на базовое представление документов XML в виде DOM . О том, что такое DOM (document object model или объектная модель документа) и пойдет речь в публикации.

DOM базируется на представлении документа любой структуры в виде дерева узлов, каждый узел (нода) которого представляет собой элемент, атрибут элемента, текстовое значение элемента и т.п.. Связь между узлами построена по принципу "родитель - подчиненные". У корня документа (дерева DOM) родителя нет. У тупикового элемента нет подчиненного (такие элементы абстрактно называются листьями дерева). Таким образом модель DOM может создаваться не только для XML, но фактически для любого структурированного документа (HTML, XHTML). Так, например, браузер пользователя, получая HTML код веб-страницы из интернета, строит дерево DOM этой страницы в оперативной памяти компьютера пользователя.

Модель DOM открывает широкие возможности по манипуляции данными документа. Можно создавать новые узлы, вставлять их на разных уровнях дерева, копировать узлы, удалять узлы, искать узлы по разным параметрам и многое другое.

Модель DOM документа XML наглядно представлена на рисунке ниже.

Любой современный язык программирования имеет в своем составе средства (парсеры) для работы с таким деревом. Получая на вход строковое содержимое XML-парсер выстраивает в оперативной памяти дерево узлов и выполняет манипуляции с данными дерева. Преимущество такого подхода перед построчным разбором очевидно: одним запросом к дереву можно выбрать необходимы данные, не перебирая построчно весь документ, ведь в оперативной памяти находится полное представление элементов со всеми взимосвязями.

В платформе 1С модель DOM представлена специальным объектом ДокументDOM , который в свою очередь строится при помощи объекта ПостроительDOM и его метода Прочитать . На вход этому методу, как правило, подается либо объект ЧтениеXML , либо ЧтениеHTML , при помощи которых осуществляется непосредственное считывание из файлов или загрузка из строки текстового содержимого XML или HTML. Ну и далее есть ряд конструкций, позволяющих извлекать данные из объектоной модели прочитанного документа.

Из всех вариантов самым интересным с моей точки зрения является вариант №1 с использованием метода ВычислитьВыражениеXPath . Ему будет посвящена следующая статья.

Плюсы построчного разбора: потребность в ресурсах меньше. Минусы: долго по времени, чтобы получить данные нужно построчно прочитать весь файл, сложность программного кода при разборе XML-документов со сложной структурой.

Преимущество выборки через DOM: скорость выборки данных, простота программного кода. Минусы: требовательность к ресурсам, на построение и запросы к DOM расходуется оперативная память и процессорные мощности.

Основным инструментом работы и динамических изменений на странице является DOM (Document Object Model) - объектная модель, используемая для XML/HTML-документов.

Согласно DOM-модели, документ является иерархией.
Каждый HTML-тег образует отдельный элемент-узел, каждый фрагмент текста - текстовый элемент, и т.п.

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

Простейший DOM

Построим, для начала, дерево DOM для следующего документа.

Заголовок Прекрасный документ

Самый внешний тег - , поэтому дерево начинает расти от него.

Внутри находятся два узла: и - они становятся дочерними узлами для .

Теги образуют узлы-элементы (element node). Текст представлен текстовыми узлами (text node). И то и другое - равноправные узлы дерева DOM.

Пример посложнее

Рассмотрим теперь более жизненную страничку:

О лосях Правда о лосях.

  • Лось - животное хитрое
  • .. И коварное
  • Корневым элементом иерархии является html . У него есть два потомка. Первый - head , второй - body . И так далее, каждый вложенный тег является потомком тега выше:

    На этом рисунке синим цветом обозначены элементы-узлы, черным - текстовые элементы.

    Дерево образовано за счет синих элементов-узлов - тегов HTML.

    А вот так выглядит дерево, если изобразить его прямо на HTML-страничке:

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

    Рассмотрим чуть более сложный документ.

    Документ Data

    • Осторожно
    • Информация
    Made in Russia

    Верхний тег - html , у него дети head и body , и так далее. Получается дерево тегов:

    Атрибуты

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

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

    Вообще-то это секрет, но DOCTYPE тоже является DOM-узлом, и находится в дереве DOM слева от HTML (на рисунке этот факт скрыт).

    P.S. Насчет секрета - конечно, шутка, но об этом и правда далеко не все знают. Сложно придумать, где такое знание может пригодиться...

    Нормализация в различных браузерах

    При разборе HTML Internet Explorer сразу создает нормализованный DOM, в котором не создаются узлы из пустого текста.

    Firefox - другого мнения, он создает DOM-элемент из каждого текстового фрагмента.
    Поэтому в Firefox дерево этого документа выглядит так:

    На рисунке для краткости текстовые узлы обозначены просто решеткой. У body вместо 3 появилось 7 детей.

    Opera тоже имеет чем похвастаться. Она может добавить лишний пустой элемент "просто от себя".

    Чтобы это увидеть - откройте документ . Он выдает число дочерних узлов document.body , включая текстовые узлы.

    У меня получается 3 для IE, 7 для Firefox и 8 (!?) для Opera.

    На практике эта несовместимость не создает больших проблем, но нужно о ней помнить. Например, разница может проявить себя в случае перебора узлов дерева.

    Возможности, которые дает DOM

    Зачем, кроме красивых рисунков, нужна иерархическая модель DOM?

    Очень просто:

    Каждый DOM-элемент является объектом и предоставляет свойства для манипуляции своим содержимым, для доступа к родителям и потомкам.

    Для манипуляций с DOM используется объект document .
    Используя document , можно получать нужный элемент дерева и менять его содержание.

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

    Var ol = document.getElementsByTagName("ol") var hiter = ol.removeChild(ol.firstChild) var kovaren = ol.removeChild(ol.firstChild) ol.appendChild(kovaren) ol.appendChild(hiter)

    Для примера работы такого скрипта - кликните на тексте на лосиной cтраничке

    В старых руководствах и скриптах можно встретить модификацию HTML-кода страницы напрямую вызовом document.write .

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

    Избегайте document.write.. Кроме случаев, когда вы действительно знаете, что делаете (а зачем тогда читаете самоучитель - вы и так гуру)

    Разберем подробнее способы доступа и свойства элементов DOM.

    Доступ к элементам

    Любой доступ и изменения DOM берут свое начало от объекта document .

    Начнем с вершины дерева.

    document.documentElement

    Самый верхний тег. В случае корректной HTML-страницы, это будет .

    document.body

    Тег , если есть в документе (обязан быть).

    Следующий пример при нажатии на кнопку выдаст текстовое представление объектов document.documentElement и document.body . Сама строка зависит от браузера, хотя объекты везде одни и те же.

    function go() { alert(document.documentElement) alert(document.body) }

    Типы DOM-элементов

    У каждого элемента в DOM-модели есть тип. Его номер хранится в атрибуте elem.nodeType

    Всего в DOM различают 12 типов элементов.

    Обычно используется только один: Node.ELEMENT_NODE , номер которого равен 1. Элементам этого типа соответствуют HTML-теги.

    Иногда полезен еще тип Node.TEXT_NODE , который равен 3. Это текстовые элементы.

    Остальные типы в javascript программировании не используются.

    Следующий пример при нажатии на кнопку выведет типы document.documentElement , а затем тип последнего потомка узла document.body . Им является текстовый узел.

    function go() { alert(document.documentElement.nodeType) alert(document.body.lastChild.nodeType) } Текст

    Пример

    Например, вот так выглядел бы в браузере документ из примера выше, если каждый видимый элемент обвести рамкой с цифрой nodeType в правом верхнем углу.

    ... Data

    • Осторожно
    • Информация
    Made in Russia

    Здесь показаны только элементы внутри body , т.к только они отображаются на странице. Для элементов типа 1 (теги) в скобочках указан соответствующий тег, для текстовых элементов (тип 3) - стоит просто цифра.

    Дочерние элементы
  • Все дочерние элементы, включая текстовые находятся в массиве childNodes .

    В следующем примере цикл перебирает всех детей document.body .

    For(var i=0; i BODY

    style

    Это свойство управляет стилем. Оно аналогично установке стиля в CSS.

    Например, можно установить element.style.width:

    Исходный код этой кнопки:

    Есть общее правило замены - если CSS-атрибут имеет дефисы, то для установки style нужно заменить их на верхний регистр букв.

    Например, для установки свойства z-index в 1000, нужно поставить:

    Element.style.zIndex = 1000

    innerHTML

    Когда-то это свойство поддерживалось только в IE. Теперь его поддерживают все современные браузеры.

    Оно содержит весь HTML-код внутри узла, и его можно менять.

    Свойство innerHTML применяется, в основном, для динамического изменения содержания страницы, например:

    Document.getElementById("footer").innerHTML = "Bye! "

    Пожалуй, innerHTML - одно из наиболее часто используемых свойств DOM-элемента.

    className

    Это свойство задает класс элемента. Оно полностью аналогично html-атрибуту "class".

    Elem.className = "newclass"

    onclick , onkeypress, onfocus ...

    И другие свойства, начинающиеся на "on...", хранят функции-обработчики соответствующих событий. Например, можно присвоить обработчик события onclick .

    Подробнее об этих свойствах и обработчиках событий - см.

    Работа с DOM-моделью

    Каждый объект Window имеет свойство document , ссылающееся на объект Document. Этот объект Document не является автономным объектом. Он является центральным объектом обширного API, известного как объектная модель документа (DOM), который определяет порядок доступа к содержимому документа.

    Обзор модели DOM

    Объектная модель документа (Document Object Model, DOM) - это фундаментальный прикладной программный интерфейс, обеспечивающий возможность работы с содержимым HTML и XML-документов. Прикладной программный интерфейс (API) модели DOM не особенно сложен, но в нем существует множество архитектурных особенностей, которые вы должны знать.

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

    И узлы, представляющие строки текста. HTML-документ также может содержать узлы, представляющие HTML-комментарии. Рассмотрим следующий простой HTML-документ:

    Пример документа Это HTML-документ

    Пример простого текста.

    DOM-представление этого документа приводится на следующей диаграмме:

    Тем, кто еще не знаком с древовидными структурами в компьютерном программировании, полезно узнать, что терминология для их описания была заимствована у генеалогических деревьев. Узел, расположенный непосредственно над данным узлом, называется родительским по отношению к данному узлу. Узлы, расположенные на один уровень ниже другого узла, являются дочерними по отношению к данному узлу. Узлы, находящиеся на том же уровне и имеющие того же родителя, называются сестринскими . Узлы, расположенные на любое число уровней ниже другого узла, являются его потомками. Родительские, прародительские и любые другие узлы, расположенные выше данного узла, являются его предками.

    Каждый прямоугольник на этой диаграмме является узлом документа, который представлен объектом Node . Обратите внимание, что на рисунке изображено три различных типа узлов. Корнем дерева является узел Document, который представляет документ целиком. Узлы, представляющие HTML-элементы, являются узлами типа Element, а узлы, представляющие текст, - узлами типа Text. Document, Element и Text - это подклассы класса Node. Document и Element являются двумя самыми важными классами в модели DOM.

    Тип Node и его подтипы образуют иерархию типов, изображенную на диаграмме ниже. Обратите внимание на формальные отличия между обобщенными типами Document и Element, и типами HTMLDocument и HTMLElement. Тип Document представляет HTML и XML-документ, а класс Element представляет элемент этого документа. Подклассы HTMLDocument и HTMLElement представляют конкретно HTML-документ и его элементы:

    На этой диаграмме следует также отметить наличие большого количества подтипов класса HTMLElement, представляющих конкретные типы HTML-элементов. Каждый из них определяет JavaScript-свойства, отражающие HTML-атрибуты конкретного элемента или группы элементов. Некоторые из этих специфических классов определяют дополнительные свойства или методы, которые не являются отражением синтаксиса языка разметки HTML.

    Выбор элементов документа

    Работа большинства клиентских программ на языке JavaScript так или иначе связана с манипулированием элементами документа. В ходе выполнения эти программы могут использовать глобальную переменную document, ссылающуюся на объект Document. Однако, чтобы выполнить какие-либо манипуляции с элементами документа, программа должна каким-то образом получить, или выбрать, объекты Element, ссылающиеся на эти элементы документа. Модель DOM определяет несколько способов выборки элементов. Выбрать элемент или элементы документа можно:

      по значению атрибута id;

      по значению атрибута name;

      по имени тега;

      по имени класса или классов CSS;

      по совпадению с определенным селектором CSS.

    Все эти приемы выборки элементов описываются в следующих подразделах.

    Выбор элементов по значению атрибута id

    Все HTML-элементы имеют атрибуты id. Значение этого атрибута должно быть уникальным в пределах документа - никакие два элемента в одном и том же документе не должны иметь одинаковые значения атрибута id. Выбрать элемент по уникальному значению атрибута id можно с помощью метода getElementById() объекта Document:

    Var section1 = document.getElementById("section1");

    Это самый простой и самый распространенный способ выборки элементов. Если сценарию необходимо иметь возможность манипулировать каким-то определенным множеством элементов документа, присвойте значения атрибутам id этих элементов и используйте возможность их поиска по этим значениям.

    В версиях Internet Explorer ниже IE8 метод getElementById() выполняет поиск значений атрибутов id без учета регистра символов и, кроме того, возвращает элементы, в которых будет найдено совпадение со значением атрибута name.

    Выбор элементов по значению атрибута name

    HTML-атрибут name первоначально предназначался для присваивания имен элементам форм, и значение этого атрибута использовалось, когда выполнялась отправка данных формы на сервер. Подобно атрибуту id, атрибут name присваивает имя элементу. Однако, в отличие от id, значение атрибута name не обязано быть уникальным: одно и то же имя могут иметь сразу несколько элементов, что вполне обычно при использовании в формах радиокнопок и флажков. Кроме того, в отличие от id, атрибут name допускается указывать лишь в некоторых HTML-элементах, включая формы, элементы форм и элементы и .

    Выбрать HTML-элементы, опираясь на значения их атрибутов name, можно с помощью метода getElementsByName() объекта Document:

    Var radiobuttons = document.getElementsByName("favorite_color");

    Метод getElementsByName() определяется не классом Document, а классом HTMLDocument, поэтому он доступен только в HTML-документах и не доступен в XML-документах. Он возвращает объект NodeList , который ведет себя, как доступный только для чтения массив объектов Element.

    В IE метод getElementsByName() возвращает также элементы, значения атрибутов id которых совпадает с указанным значением. Чтобы обеспечить совместимость с разными версиями браузеров, необходимо внимательно подходить к выбору значений атрибутов и не использовать одни и те же строки в качестве значений атрибутов name и id.

    Выбор элементов по типу

    Метод getElementsByTagName() объекта Document позволяет выбрать все HTML или XML-элементы указанного типа (или по имени тега). Например, получить подобный массиву объект, доступный только для чтения, содержащий объекты Element всех элементов в документе, можно следующим образом:

    Var spans = document.getElementsByTagName("span");

    Подобно методу getElementsByName(), getElementsByTagName() возвращает объект NodeList. Элементы документа включаются в массив NodeList в том же порядке, в каком они следуют в документе, т.е. первый элемент

    В документе можно выбрать так:

    Var firstParagraph = document.getElementsByTagName("p");

    Имена HTML-тегов не чувствительны к регистру символов, и когда getElementsByTagName() применяется к HTML-документу, он выполняет сравнение с именем тега без учета регистра символов. Переменная spans, созданная выше, например, будет включать также все элементы , которые записаны как .

    Можно получить NodeList, содержащий все элементы документа, если передать методу getElementsByTagName() шаблонный символ «*».

    Кроме того, классом Element также определяет метод getElementsByTagName(). Он действует точно так же, как и версия метода в классе Document, но выбирает только элементы, являющиеся потомками для элемента, относительно которого вызывается метод. То есть отыскать все элементы внутри первого элемента

    Можно следующим образом:

    Var firstParagraph = document.getElementsByTagName("p"); var firstParagraphSpans = firstParagraph.getElementsByTagName("span");

    По историческим причинам класс HTMLDocument определяет специальные свойства для доступа к узлам определенных типов. Свойства images , forms и links , например, ссылаются на объекты, которые ведут себя как массивы, доступные только для чтения, содержащие элементы , и (но только те теги , которые имеют атрибут href). Эти свойства ссылаются на объекты HTMLCollection, которые во многом похожи на объекты NodeList, но дополнительно могут индексироваться значениями атрибутов id и name.

    Объект HTMLDocument также определяет свойства-синонимы embeds и plugins , являющиеся коллекциями HTMLCollection элементов . Свойство anchors является нестандартным, но с его помощью можно получить доступ к элементам , имеющим атрибут name, но не имеющим атрибут href. Свойство scripts определено стандартом HTML5 и является коллекцией HTMLCollection элементов .

    Кроме того, объект HTMLDocument определяет два свойства, каждое из которых ссылается не на коллекцию, а на единственный элемент. Свойство document.body представляет элемент HTML-документа, а свойство document.head - элемент . Эти свойства всегда определены в документе: даже если в исходном документе отсутствуют элементы и , браузер создаст их неявно. Свойство documentElement объекта Document ссылается на корневой элемент документа. В HTML-документах он всегда представляет элемент .

    Выбор элементов по классу CSS

    Значением HTML-атрибута class является список из нуля или более идентификаторов, разделенных пробелами. Он дает возможность определять множества связанных элементов документа: любые элементы, имеющие в атрибуте class один и тот же идентификатор, являются частью одного множества. Слово class зарезервировано в языке JavaScript, поэтому для хранения значения HTML-атрибута class в клиентском JavaScript используется свойство className.

    Обычно атрибут class используется вместе с каскадными таблицами стилей CSS, с целью применить общий стиль отображения ко всем членам множества. Однако кроме этого, стандарт HTML5 определяет метод getElementsByClassName() , позволяющий выбирать множества элементов документа на основе идентификаторов в их атрибутах class.

    Подобно методу getElementsByTagName(), метод getElementsByClassName() может вызываться и для HTML-документов, и для HTML-элементов, и возвращает «живой» объект NodeList, содержащий все потомки документа или элемента, соответствующие критерию поиска.

    Метод getElementsByClassName() принимает единственный строковый аргумент, но в самой строке может быть указано несколько идентификаторов, разделенных пробелами. Соответствующими будут считаться все элементы, атрибуты class которых содержат все указанные идентификаторы. Порядок следования идентификаторов не имеет значения. Обратите внимание, что и в атрибуте class, и в аргументе метода getElementsByClassName() идентификаторы классов разделяются пробелами, а не запятыми.

    Ниже приводится несколько примеров использования метода getElementsByClassName():

    // Отыскать все элементы с классом "warning" var warnings = document.getElementsByClassName("warning"); // Отыскать всех потомков элемента с идентификаторам "log" // с классами "error" и "fatal" var log = document.getElementById("log"); var fatal = log.getElementsByClassName("fatal error");

    Выбор элементов с использованием селекторов CSS

    Каскадные таблицы стилей CSS имеют очень мощные синтаксические конструкции, известные как селекторы, позволяющие описывать элементы или множества элементов документа. Наряду со стандартизацией селекторов CSS3 , другой стандарт консорциума W3C, известный как Selectors API , определяет методы JavaScript для получения элементов, соответствующих указанному селектору.

    Ключевым в этом API является метод querySelectorAll() объекта Document. Он принимает единственный строковый аргумент с селектором CSS и возвращает объект NodeList, представляющий все элементы документа, соответствующие селектору.

    В дополнение к методу querySelectorAll() объект документа также определяет метод querySelector() , подобный методу querySelectorAll(), - с тем отличием, что он возвращает только первый (в порядке следования в документе) соответствующий элемент или null, в случае отсутствия соответствующих элементов.

    Эти два метода также определяются классом Elements. Когда они вызываются относительно элемента, поиск соответствия заданному селектору выполняется во всем документе, а затем результат фильтруется так, чтобы в нем остались только потомки использованного элемента. Такой подход может показаться противоречащим здравому смыслу, так как он означает, что строка селектора может включать предков элемента, для которого выполняется сопоставление.

    Структура документа и навигация по документу

    После выбора элемента документа иногда бывает необходимо отыскать структурно связанные части документа (родитель, братья, дочерний элемент). Объект Document можно представить как дерево объектов Node. Тип Node определяет свойства, позволяющие перемещаться по такому дереву. Существует еще один прикладной интерфейс навигации по документу, как дерева объектов Element.

    Документы как деревья узлов

    Объект Document, его объекты Element и объекты Text, представляющие текстовые фрагменты в документе - все они являются объектами Node. Класс Node определяет следующие важные свойства:

    parentNode

    Родительский узел данного узла или null для узлов, не имеющих родителя, таких как Document.

    childNodes

    Доступный для чтения объект, подобный массиву (NodeList), обеспечивающий представление дочерних узлов.

    firstChild, lastChild

    Первый и последний дочерние узлы или null, если данный узел не имеет дочерних узлов.

    nextSibling, previousSibling

    Следующий и предыдущий братские узлы. Братскими называются два узла, имеющие одного и того же родителя. Порядок их следования соответствует порядку следования в документе. Эти свойства связывают узлы в двусвязный список.

    nodeType

    Тип данного узла. Узлы типа Document имеют значение 9 в этом свойстве. Узлы типа Element - значение 1. Текстовые узлы типа Text - значение 3. Узлы типа Comments - значение 8 и узлы типа DocumentFragment - значение 11.

    nodeValue

    Текстовое содержимое узлов Text и Comment.

    nodeName

    Имя тега элемента Element, в котором все символы преобразованы в верхний регистр.

    С помощью этих свойств класса Node можно сослаться на второй дочерний узел первого дочернего узла объекта Document, как показано ниже:

    Document.childNodes.childNodes == document.firstChild.firstChild.nextSibling

    Допустим, что рассматриваемый документ имеет следующий вид:

    TestHello World!

    Тогда вторым дочерним узлом первого дочернего узла будет элемент . В свойстве nodeType он содержит значение 1 и в свойстве nodeName - значение «BODY».

    Однако, обратите внимание, что этот прикладной интерфейс чрезвычайно чувствителен к изменениям в тексте документа. Например, если в этот документ добавить единственный перевод строки между тегами и , этот символ перевода строки станет первым дочерним узлом (текстовым узлом Text) первого дочернего узла, а вторым дочерним узлом станет элемент , а не .

    Документы как деревья элементов

    Когда основной интерес представляют сами элементы документа, а не текст в них (и пробельные символы между ними), гораздо удобнее использовать прикладной интерфейс, позволяющий интерпретировать документ как дерево объектов Element, игнорируя узлы Text и Comment, которые также являются частью документа.

    Первой частью этого прикладного интерфейса является свойство children объектов Element. Подобно свойству childNodes, его значением является объект NodeList. Однако, в отличие от свойства childNodes, список children содержит только объекты Element.

    Обратите внимание, что узлы Text и Comment не имеют дочерних узлов. Это означает, что описанное выше свойство Node.parentNode никогда не возвращает узлы типа Text или Comment. Значением свойства parentNode любого объекта Element всегда будет другой объект Element или корень дерева - объект Document или DocumentFragment.

    Второй частью прикладного интерфейса навигации по элементам документа являются свойства объекта Element, аналогичные свойствам доступа к дочерним и братским узлам объекта Node:

    firstElementChild, lastElementChild

    Похожи на свойства firstChild и lastChild, но возвращают дочерние элементы.

    nextElementSibling, previousElementSibling

    Похожи на свойства nextSibling и previousSibling, но возвращают братские элементы.

    childElementCount

    Количество дочерних элементов. Возвращает то же значение, что и свойство children.length.

    Эти свойства доступа к дочерним и братским элементам стандартизованы и реализованы во всех текущих браузерах, кроме IE.


    Структура документа

    Можно представить HTML как набор вложенных коробок. Теги вроде и включают в себя другие теги, которые в свою очередь включают теги, или текст. Вот вам пример документа из предыдущей главы:

    Моя домашняя страничка Моя домашняя страничка

    Привет, я Марийн и это моя домашняя страничка.

    А ещё я книжку написал! Читайте её здесь.

    У этой страницы следующая структура:

    Структура данных, использующаяся браузером для представления документа, отражает его форму. Для каждой коробки есть объект, с которым мы можем взаимодействовать и узнавать про него разные данные – какой тег он представляет, какие коробки и текст содержит. Это представление называется Document Object Model (объектная модель документа), или сокращённо DOM.

    Мы можем получить доступ к этим объектам через глобальную переменную document. Её свойство documentElement ссылается на объект, представляющий тег. Он также предоставляет свойства head и body, в которых содержатся объекты для соответствующих элементов.

    Деревья Вспомните синтаксические деревья из главы 11. Их структура удивительно похожа на структуру документа браузера. Каждый узел может ссылаться на другие узлы, у каждого из ответвлений может быть своё ответвление. Эта структура – типичный пример вложенных структур, где элементы содержат подэлементы, похожие на них самих.

    Мы зовём структуру данных деревом, когда она разветвляется, не имеет циклов (узел не может содержать сам себя), и имеет единственный ярко выраженный «корень». В случае DOM в качестве корня выступает document.documentElement.

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

    У типичного дерева есть разные узлы. У синтаксического дерева языка Egg были переменные, значения и приложения. У приложений всегда были дочерние ветви, а переменные и значения были «листьями», то есть узлами без дочерних ответвлений.

    То же и у DOM. Узлы для обычных элементов, представляющих теги HTML, определяют структуру документа. У них могут быть дочерние узлы. Пример такого узла - document.body. Некоторые из этих дочерних узлов могут оказаться листьями – например, текст или комментарии (в HTML комментарии записываются между символами).

    У каждого узлового объекта DOM есть свойство nodeType, содержащее цифровой код, определяющий тип узла. У обычных элементов он равен 1, что также определено в виде свойства-константы document.ELEMENT_NODE. У текстовых узлов, представляющих отрывки текста, он равен 3 (document.TEXT_NODE). У комментариев - 8 (document.COMMENT_NODE).

    То есть, вот ещё один способ графически представить дерево документа:

    Листья – текстовые узлы, а стрелки показывают взаимоотношения отец-ребёнок между узлами.

    Стандарт Использовать загадочные цифры для представления типа узла – это подход не в стиле JavaScript. Позже мы встретимся с другими частями интерфейса DOM, которые тоже кажутся чуждыми и нескладными. Причина в том, что DOM разрабатывался не только для JavaScript. Он пытается определить интерфейс, не зависящий от языка, который можно использовать и в других системах – не только в HTML, но и в XML, который представляет из себя формат данных общего назначения с синтаксисом, напоминающим HTML.

    Получается неудобно. Хотя стандарты – и весьма полезная штука, в нашем случае преимущество независимости от языка не такое уж и полезное. Лучше иметь интерфейс, хорошо приспособленный к языку, который вы используете, чем интерфейс, который будет знаком при использовании разных языков.

    Чтобы показать неудобную интеграцию с языком, рассмотрим свойство childNodes, которое есть у узлов DOM. В нём содержится объект, похожий на массив, со свойством length, и пронумерованные свойства для доступа к дочерним узлам. Но это – экземпляр типа NodeList, не настоящий массив, поэтому у него нет методов вроде forEach.

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

    Но эти проблемы не фатальные. JavaScript позволяет создавать абстракции. Легко написать вспомогательные функции, позволяющие выражать операции более понятно и коротко. Вообще, такого рода инструменты предоставляют много библиотек, направленных на программирование для браузера.

    Обход дерева Узлы DOM содержат много ссылок на соседние. Это показано на диаграмме:

    Хотя тут показано только по одной ссылке каждого типа, у каждого узла есть свойство parentNode, указывающего на его родительский узел. Также у каждого узла-элемента (тип 1) есть свойство childNodes, указывающее на массивоподобный объект, содержащий его дочерние узлы.

    В теории можно пройти в любую часть дерева, используя только эти ссылки. Но JavaScript предоставляет нам много дополнительных вспомогательных ссылок. Свойства firstChild и lastChild показывают на первый и последний дочерний элементы, или содержат null у тех узлов, у которых нет дочерних. previousSibling и nextSibling указывают на соседние узлы – узлы того же родителя, что и текущего узла, но находящиеся в списке сразу до или после текущей. У первого узла свойство previousSibling будет null, а у последнего nextSibling будет null.

    При работе с такими вложенными структурами пригождаются рекурсивные функции. Следующая ищет в документе текстовые узлы, содержащие заданную строку, и возвращает true, когда находит:

    Function talksAbout(node, string) { if (node.nodeType == document.ELEMENT_NODE) { for (var i = 0; i < node.childNodes.length; i++) { if (talksAbout(node.childNodes[i], string)) return true; } return false; } else if (node.nodeType == document.TEXT_NODE) { return node.nodeValue.indexOf(string) > -1; } } console.log(talksAbout(document.body, "книг")); // → true

    Свойства текстового узла nodeValue содержит строчку текста.

    Поиск элементов Часто бывает полезным ориентироваться по этим ссылкам между родителями, детьми и родственными узлами и проходить по всему документу. Однако если нам нужен конкретный узел в документе, очень неудобно идти по нему, начиная с document.body и тупо перебирая жёстко заданный в коде путь. Поступая так, мы вносим в программу допущения о точной структуре документа – а её мы позже можем захотеть поменять. Другой усложняющий фактор – текстовые узлы создаются даже для пробелов между узлами. В документе из примера у тега body не три дочерних (h1 и два p), а целых семь: эти три плюс пробелы до, после и между ними.

    Var link = document.body.getElementsByTagName("a"); console.log(link.href);

    У всех узлов-элементов есть метод getElementsByTagName, собирающий все элементы с данным тэгом, которые происходят (прямые или не прямые потомки) от этого узла, и возвращает его в виде массивоподобного объекта.

    Чтобы найти конкретный узел, можно задать ему атрибут id и использовать метод document.getElementById.

    Мой страус Гертруда:

    var ostrich = document.getElementById("gertrude"); console.log(ostrich.src);

    Третий метод – getElementsByClassName, который, как и getElementsByTagName, ищет в содержимом узла-элемента и возвращает все элементы, содержащие в своём классе заданную строчку.

    Меняем документ Почти всё в структуре DOM можно менять. У узлов-элементов есть набор методов, которые используются для их изменения. Метод removeChild удаляет заданную дочерний узел. Для добавления узла можно использовать appendChild, который добавляет узел в конец списка, либо insertBefore, добавляющий узел, переданную первым аргументом, перед узлом, переданным вторым аргументом.

    var paragraphs = document.body.getElementsByTagName("p"); document.body.insertBefore(paragraphs, paragraphs);

    Узел может существовать в документе только в одном месте. Поэтому вставляя параграф «Три» перед параграфом «Один» мы фактически удаляем его из конца списка и вставляем в начало, и получаем «Три/Один/Два». Все операции по вставке узла приведут к его исчезновению с текущей позиции (если у него таковая была).

    Метод replaceChild используется для замены одного дочернего узла другим. Он принимает два узла: новый, и тот, который надо заменить. Заменяемый узел должен быть дочерним узлом того элемента, чей метод мы вызываем. Как replaceChild, так и insertBefore в качестве первого аргумента ожидают получить новый узел.

    Создание узлов В следующем примере нам надо сделать скрипт, заменяющий все картинки (тег) в документе текстом, содержащимся в их атрибуте “alt”, который задаёт альтернативное текстовое представление картинки.

    Для этого надо не только удалить картинки, но и добавить новые текстовые узлы им на замену. Для этого мы используем метод document.createTextNode.

    Это в .

    Заменить

    function replaceImages() { var images = document.body.getElementsByTagName("img"); for (var i = images.length - 1; i >= 0; i--) { var image = images[i]; if (image.alt) { var text = document.createTextNode(image.alt); image.parentNode.replaceChild(text, image); } } }

    Получая строку, createTextNode даёт нам тип 3 узла DOM (текстовый), который мы можем вставить в документ, чтобы он был показан на экране.

    Цикл по картинкам начинается в конце списка узлов. Это сделано потому, что список узлов, возвращаемый методом getElementsByTagName (или свойством childNodes) постоянно обновляется при изменениях документа. Если б мы начали с начала, удаление первой картинки привело бы к потере списком первого элемента, и во время второго прохода цикла, когда i равно 1, он бы остановился, потому что длина списка стала бы также равняться 1.

    Если вам нужно работать с фиксированным списком узлов вместо «живого», можно преобразовать его в настоящий массив при помощи метода slice.

    Var arrayish = {0: "один", 1: "два", length: 2}; var real = Array.prototype.slice.call(arrayish, 0); real.forEach(function(elt) { console.log(elt); }); // → один // два

    Для создания узлов-элементов (тип 1) можно использовать document.createElement. Метод принимает имя тега и возвращает новый пустой узел заданного типа. Следующий пример определяет инструмент elt, создающий узел-элемент и использующий остальные аргументы в качестве его детей. Эта функция потом используется для добавления дополнительной информации к цитате.

    Никакая книга не может быть закончена. Во время работы над ней мы узнаём достаточно для того, чтобы найти её незрелой сразу же после того, как мы отвлеклись от неё. function elt(type) { var node = document.createElement(type); for (var i = 1; i < arguments.length; i++) { var child = arguments[i]; if (typeof child == "string") child = document.createTextNode(child); node.appendChild(child); } return node; } document.getElementById("quote").appendChild(elt("footer", "-", elt("strong", "Карл Поппер"), ", предисловие ко второму изданию ", elt("em", "Открытое общество и его враги "), ", 1950"));

    Атрибуты К некоторым элементам атрибутов, типа href у ссылок, можно получить доступ через одноимённое свойство объекта. Это возможно для ограниченного числа часто используемых стандартных атрибутов.

    Но HTML позволяет назначать узлам любые атрибуты. Это полезно, т.к. позволяет вам хранить дополнительную информацию в документе. Если вы придумаете свои названия атрибутов, их не будет среди свойств узла-элемента. Вместо этого вам надо будет использовать методы getAttribute и setAttribute для работы с ними.

    Код запуска 00000000.

    У кошки четыре ноги.

    var paras = document.body.getElementsByTagName("p"); Array.prototype.forEach.call(paras, function(para) { if (para.getAttribute("data-classified") == "secret") para.parentNode.removeChild(para); });

    Рекомендую перед именами придуманных атрибутов ставить “data-“, чтобы быть уверенным, что они не конфликтуют с любыми другими. В качестве простого примера мы напишем подсветку синтаксиса, который ищет теги (“preformatted”, предварительно отформатированный – используется для кода и простого текста) с атрибутом data-language (язык) и довольно грубо пытается подсветить ключевые слова в языке.

    Function highlightCode(node, keywords) { var text = node.textContent; node.textContent = ""; // Очистим узел var match, pos = 0; while (match = keywords.exec(text)) { var before = text.slice(pos, match.index); node.appendChild(document.createTextNode(before)); var strong = document.createElement("strong"); strong.appendChild(document.createTextNode(match)); node.appendChild(strong); pos = keywords.lastIndex; } var after = text.slice(pos); node.appendChild(document.createTextNode(after)); }

    Функция highlightCode принимает узел И регулярку (с включённой настройкой global), совпадающую с ключевым словом языка программирования, которое содержит элемент.

    Свойство textContent используется для получения всего текста узла, а затем устанавливается в пустую строку, что приводит к очищению узла. Мы в цикле проходим по всем вхождениям выражения keyword, добавляем между ними текст в виде простых текстовых узлов, а совпавший текст (ключевые слова) добавляем, заключая их в элементы (жирный шрифт).

    Мы можем автоматически подсветить весь код страницы, перебирая в цикле все элементы У которых есть атрибут data-language, и вызывая на каждом highlightCodeс правильной регуляркой.

    Var languages = { javascript: /\b(function|return|var)\b/g /* … etc */ }; function highlightAllCode() { var pres = document.body.getElementsByTagName("pre"); for (var i = 0; i < pres.length; i++) { var pre = pres[i]; var lang = pre.getAttribute("data-language"); if (languages.hasOwnProperty(lang)) highlightCode(pre, languages); } }

    Вот пример:

    А вот и она, функция идентификации:

    Function id(x) { return x; } highlightAllCode();

    Есть один часто используемый атрибут, class, имя которого является ключевым словом в JavaScript. По историческим причинам, когда старые реализации JavaScript не умели обращаться с именами свойств, совпадавшими с ключевыми словами, этот атрибут доступен через свойство под названием className. Вы также можете получить к нему доступ по его настоящему имени “class” через методы getAttribute и setAttribute.

    Расположение элементов (layout) Вы могли заметить, что разные типы элементов располагаются по-разному. Некоторые, типа параграфов

    И заголовков растягиваются на всю ширину документа и появляются на отдельных строках. Такие элементы называют блочными. Другие, как ссылки или жирный текст появляются на одной строчке с окружающим их текстом. Они называются встроенными (inline).

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

    Размер и положение элемента можно узнать через JavaScript. Свойства offsetWidth и offsetHeight выдают размер в пикселях, занимаемый элементом. Пиксель – основная единица измерений в браузерах, и обычно соответствует размеру минимальной точки экрана. Сходным образом, clientWidth и clientHeight дают размер внутренней части элемента, не считая бордюра (или, как говорят некоторые, поребрика).

    Я в коробочке

    var para = document.body.getElementsByTagName("p"); console.log("clientHeight:", para.clientHeight); console.log("offsetHeight:", para.offsetHeight);

    Самый эффективный способ узнать точное расположение элемента на экране – метод getBoundingClientRect. Он возвращает объект со свойствами top, bottom, left, и right (сверху, снизу, слева и справа), которые содержат положение элемента относительно левого верхнего угла экрана в пикселях. Если вам надо получить эти данные относительно всего документа, вам надо прибавить текущую позицию прокрутки, которая содержится в глобальных переменных pageXOffset и pageYOffset.

    Разбор документа – задача сложная. В целях быстродействия браузерные движки не перестраивают документ каждый раз после его изменения, а ждут так долго. как это возможно. Когда программа JavaScript, изменившая документ, заканчивает работу, браузеру надо будет просчитать новую раскладку страницы, чтобы вывести изменённый документ на экран. Когда программа запрашивает позицию или размер чего-либо, читая свойства типа offsetHeight или вызывая getBoundingClientRect, для предоставления корректной информации тоже необходимо рассчитывать раскладку.

    Программа, которая периодически считывает раскладку DOM и изменяет DOM, заставляет браузер много раз пересчитывать раскладку, и в связи с этим будет работать медленно. В следующем примере есть две разные программы, которые строят линию из символов X шириной в 2000 пикс, и измеряют время работы.

    function time(name, action) { var start = Date.now(); // Текущее время в миллисекундах action(); console.log(name, "заняло", Date.now() - start, "ms"); } time("тупо", function() { var target = document.getElementById("one"); while (target.offsetWidth < 2000) target.appendChild(document.createTextNode("X")); }); // → тупо заняло 32 ms time("умно", function() { var target = document.getElementById("two"); target.appendChild(document.createTextNode("XXXXX")); var total = Math.ceil(2000 / (target.offsetWidth / 5)); for (var i = 5; i < total; i++) target.appendChild(document.createTextNode("X")); }); // → умно заняло 1 ms

    Стили Мы видели, что разные элементы HTML ведут себя по-разному. Некоторые показываются в виде блоков, другие встроенные. Некоторые добавляют визуальный стиль – например, делает жирным текст и делает текст подчёркнутым и синим.

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

    Зелёная ссылка

    Атрибут style может содержать одно или несколько объявлений свойств (color), за которым следует двоеточие и значение. В случае нескольких объявлений они разделяются точкой с запятой: “color: red; border: none”.

    Много всякого можно изменить при помощи стилей. Например, свойство display контролирует, показывается ли элемент в блочном или встроенном виде.

    Текст показан встроенным, в виде блока, и вообще не виден.

    Блочный элемент выводится отдельным блоком, а последний вообще не виден – display: none отключает показ элементов. Таким образом можно прятать элементы. Обычно это предпочтительно полному удалению их из документа, потому что их легче потом при необходимости снова показать.

    Код JavaScript может напрямую действовать на стиль элемента через свойство узла style. В нём содержится объект, имеющий свойства для всех свойств стилей. Их значения – строки, в которые мы можем писать для смены какого-то аспекта стиля элемента.

    Красотень

    var para = document.getElementById("para"); console.log(para.style.color); para.style.color = "magenta";

    Некоторые имена свойств стилей содержат дефисы, например font-family. Так как с ними неудобно было бы работать в JavaScript (пришлось бы писать style["font-family"]), названия свойств в объекте стилей пишутся без дефиса, а вместо этого в них появляются прописные буквы: style.fontFamily

    Каскадные стили Система стилей в HTML называется CSS (Cascading Style Sheets, каскадные таблицы стилей). Таблица стилей – набор стилей в документе. Его можно писать внутри тега:
    strong { font-style: italic; color: gray; }

    Теперь текст тега strong наклонный и серый.

    «Каскадные» означает, что несколько правил комбинируются для получения окончательного стиля документа. В примере на стиль по умолчанию для, который делает текст жирным, накладывается правило из тега, по которому добавляется font-style и цвет.

    Когда значение свойства определяется несколькими правилами, приоритет остаётся у более поздних. Если бы стиль текста в включал правило font-weight: normal, конфликтующее со стилем по умолчанию, то текст был бы обычный, а не жирный. Стили, которые применяются к узлу через атрибут style, имеют наивысший приоритет.

    В CSS возможно задавать не только название тегов. Правило для.abc применяется ко всем элементам, у которых указан класс “abc”. Правило для #xyz применяется к элементу с атрибутом id равным “xyz” (атрибуты id необходимо делать уникальными для документа).

    Subtle { color: gray; font-size: 80%; } #header { background: blue; color: white; } /* Элементы p, у которых указаны классы a и b, а id задан как main */ p.a.b#main { margin-bottom: 20px; }

    Приоритет самых поздних правил работает, когда у правил одинаковая детализация. Это мера того, насколько точно оно описывает подходящие элементы, определяемая числом и видом необходимых аспектов элементов. К примеру, правило для p.a более детально, чем правила для p или просто.a, и будет иметь приоритет.

    Запись p > a {…} применима ко всем тегам, находящимся внутри тега и являющимся его прямыми потомками.
    p a {…} применимо также ко всем тегам внутри, при этом неважно, является ли прямым потомком или нет.

    Селекторы запросов В этой книге мы не будем часто использовать таблицы стилей. Понимание их работы критично для программирования в браузере, но подробное разъяснение всех их свойств заняло бы 2-3 книги. Главная причина знакомства с ними и с синтаксисом селекторов (записей, определяющих, к каким элементам относятся правила) – мы можем использовать тот же эффективный мини-язык для поиска элементов DOM.

    Метод querySelectorAll, существующий и у объекта document, и у элементов-узлов, принимает строку селектора и возвращает массивоподобный объект, содержащий все элементы, подходящие под него.

    Люблю грозу в начале мая

    Когда весенний первый гром

    Как бы резвяся и играя

    Грохочет в небе голубом.

    function count(selector) { return document.querySelectorAll(selector).length; } console.log(count("p")); // Все элементы

    // → 4 console.log(count(".animal")); // Класс animal // → 2 console.log(count("p .animal")); // Класс animal внутри

    // → 2 console.log(count("p > .animal")); // Прямой потомок

    // → 1

    В отличие от методов вроде getElementsByTagName, возвращаемый querySelectorAll объект не интерактивный. Он не изменится, если вы измените документ.

    Метод querySelector (без All) работает сходным образом. Он нужен, если вам необходим один конкретный элемент. Он вернёт только первое совпадение, или null, если совпадений нет.

    Расположение и анимация Свойство стилей position сильно влияет на расположение элементов. По умолчанию оно равно static, что означает, что элемент находится на своём обычном месте в документе. Когда оно равно relative, элемент всё ещё занимает место, но теперь свойства top и left можно использовать для сдвига относительно его обычного расположения. Когда оно равно absolute, элемент удаляется из нормального «потока» документа – то есть, он не занимает место и может накладываться на другие. Кроме того, его свойства left и top можно использовать для абсолютного позиционирования относительно левого верхнего угла ближайшего включающего его элемента, у которого position не равно static. А если такого элемента нет, тогда он позиционируется относительно документа.

    Мы можем использовать это для создания анимации. Следующий документ показывает картинку с котом, которая двигается по эллипсу.

    var cat = document.querySelector("img"); var angle = 0, lastTime = null; function animate(time) { if (lastTime != null) angle += (time - lastTime) * 0.001; lastTime = time; cat.style.top = (Math.sin(angle) * 20) + "px"; cat.style.left = (Math.cos(angle) * 200) + "px"; requestAnimationFrame(animate); } requestAnimationFrame(animate);

    Картинка отцентрирована на странице и ей задана position: relative. Мы постоянно обновляем свойства top и left картинки, чтобы она двигалась.

    Скрипт использует requestAnimationFrame для вызова функции animate каждый раз, когда браузер готов перерисовывать экран. Функция animate сама опять вызывает requestAnimationFrame, чтобы запланировать следующее обновление. Когда окно браузера (или закладка) активна, это приведёт к обновлениям со скорость примерно 60 раз в секунду, что позволяет добиться хорошо выглядящей анимации.

    Если бы мы просто обновляли DOM в цикле, страница бы зависла и ничего не было бы видно. Браузеры не обновляют страницу во время работы JavaScript, и не допускают в это время работы со страницей. Поэтому нам нужна requestAnimationFrame – она сообщает браузеру, что мы пока закончили, и он может заниматься своими браузерными вещами, например обновлять экран и отвечать на запросы пользователя.

    Наша функция анимации передаётся текущее время через аргументы, которое оно сравнивает с предыдущим (переменная lastTime), чтобы движение кота было однородным, и анимация работала плавно. Если бы мы просто передвигали её на заданный промежуток на каждом шаге, движение бы запиналось если бы, например, другая задача загрузила бы компьютер.

    Движение по кругу осуществляется с применением тригонометрических функций Math.cos и Math.sin. Я кратко опишу их для тех, кто с ними незнаком, так как они понадобятся нам в дальнейшем.

    Math.cos и Math.sin полезны тогда, когда надо найти точки на круге с центром в точке (0, 0) и радиусом в единицу. Обе функции интерпретируют свой аргумент как позицию на круге, где 0 обозначает точку с правого края круга, затем нужно против часовой стрелки, пока путь диной в 2π (около 6.28) не проведёт нас по кругу. Math.cos считает координату по оси x той точки, которая является нашей текущей позицией на круге, а Math.sin выдаёт координату y. Позиции (или углы) больше, чем 2π или меньше чем 0, тоже допустимы – повороты повторяются так, что a+2π означает тот же самый угол, что и a.

    Использование синуса и косинуса для вычисления координат Анимация кота хранит счётчик angle для текущего угла поворота анимации, и увеличивает его пропорционально прошедшему времени каждый раз при вызове функции animation. Этот угол используется для подсчёта текущей позиции элемента image. Стиль top подсчитывается через Math.sin и умножается на 20 – это вертикальный радиус нашего эллипса. Стиль left считается через Math.cos и умножается на 200, так что ширина эллипса сильно больше высоты.

    Стилям обычно требуются единицы измерения. В нашем случае приходится добавлять px к числу, чтобы объяснить браузеру, что мы считаем в пикселях (а не в сантиметрах, ems или других единицах). Это легко забыть. Использование чисел без единиц измерения приведёт к игнорированию стиля – если только число не равно 0, что не зависит от единиц измерения.

    Итог Программы JavaScript могут изучать и изменять текущий отображаемый браузером документ через структуру под названием DOM. Эта структура данных представляет модель документа браузера, а программа JavaScript может изменять её для изменения видимого документа. DOM организован в виде дерева, в котором элементы расположены иерархически в соответствии со структурой документа. У объектов элементов есть свойства типа parentNode и childNodes, которы используются для ориентирования на дереве.

    Внешний вид документа можно изменять через стили, либо добавляя стили к узлам напрямую, либо определяя правила для каких-либо узлов. У стилей есть очень много свойств, таких, как color или display. JavaScript может влиять на стиль элемента напрямую через его свойство style.

    УпражненияСтроим таблицу Мы строили таблицы из простого текста в главе 6. HTML упрощает построение таблиц. Таблица в HTML строится при помощи следующих тегов:

    name height country
    Kilimanjaro 5895 Tanzania

    Для каждой строки в теге содержится тег. Внутри него мы можем размещать ячейки: либо ячейки заголовков, либо обычные ячейки.

    Те же данные, что мы использовали в главе 6, снова доступны в переменной MOUNTAINS.

    Напишите функцию buildTable, которая, принимая массив объектов с одинаковыми свойствами, строит структуру DOM, представляющую таблицу. У таблицы должна быть строка с заголовками, где имена свойств обёрнуты в элементы, и должно быть по одной строчке на объект из массива, где его свойства обёрнуты в элементы. Здесь пригодится функция Object.keys, возвращающая массив, содержащий имена свойств объекта.

    Когда вы разберётесь с основами, выровняйте ячейки с числами по правому краю, изменив их свойство style.textAlign на "right".

    /* Определяет стили для красивых таблиц */ table { border-collapse: collapse; } td, th { border: 1px solid black; padding: 3px 8px; } th { text-align: left; } function buildTable(data) { // Ваш код } document.body.appendChild(buildTable(MOUNTAINS));

    Элементы по имени тегов Метод getElementsByTagName возвращает все дочерние элементы с заданным именем тега. Сделайте свою версию этого метода в виде обычной функции, которая принимает узел и строчку (имя тега) и возвращает массив, содержащий все нисходящие узлы с заданным именем тега.

    Чтобы выяснить имя тега элемента, используйте свойство tagName. Заметьте, что оно возвратит имя тега в верхнем регистре. Используйте методы строк toLowerCase или toUpperCase.

    Заголовок с элементом span внутри.

    Параграф с раз, два элементами spans.

    function byTagName(node, tagName) { // Ваш код } console.log(byTagName(document.body, "h1").length); // → 1 console.log(byTagName(document.body, "span").length); // → 3 var para = document.querySelector("p"); console.log(byTagName(para, "span").length); // → 2

    Шляпа кота Расширьте анимацию кота, чтобы и кот и его шляпа летали по противоположным сторонам эллипса.

    Или пусть шляпа летает вокруг кота. Или ещё что-нибудь интересное придумайте.

    Чтобы упростить расположение множества объектов, неплохо будет переключиться на абсолютное позиционирование. Тогда top и left будут считаться относительно левого верхнего угла документа. Чтобы не использовать отрицательные координаты, вы можете добавить заданное число пикселей к значениям position.

    var cat = document.querySelector("#cat"); var hat = document.querySelector("#hat"); // Your code here.

  • Понравилось? Лайкни нас на Facebook