Выразительный JavaScript: Document Object Model (объектная модель документа). Еще один блог веб разработчика Запрошенный элемент не подключен к модели dom

Справочник содержит описание всех свойств и методов стандартных встроенных объектов JavaScript.

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

Объектная модель документа (Document Object Model, DOM) – это интерфейс программирования приложений (Application Programming Interface, API) для XML, который был расширен также для работы с HTML.

В DOM всё содержимое страницы (элементы и текст) представляется как иерархия узлов. Рассмотрим следующий код:

Простая страница

Привет Мир!

Этот код можно изобразить с помощью DOM как иерархию узлов:

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

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

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

Справочник по DOM содержит описание объектов Document, Element, Event и NodeList, включая описание их методов и свойств:

Справочник BOM

BOM (Browser Object Model в переводе с анг. - Объектная Модель Браузера) обеспечивает доступ к окну браузера и позволяет манипулировать им и его элементами.

BOM-объекты обеспечивают доступ к функционалу браузера независимо от контента веб-страницы. Тема BOM интересна и одновременно сложна, потому что из-за длительного отсутствия спецификации производители браузеров свободно расширяли BOM по своему усмотрению. Многие элементы, схожие в разных браузерах, стали стандартами де-факто, которые соблюдаются и по сей день из соображений взаимной совместимости. Чтобы стандартизировать эти фундаментальные аспекты JavaScript, консорциум W3C определил основные BOM-элементы в спецификации HTML5.

Модель DOM часто называют деревом DOM, поскольку она состоит из дерева объектов, называемых узлами. В вы узнали, что такое Document Object Model (DOM), как получить доступ к объекту document и изменить его свойства с помощью консоли, также мы разницу между исходным кодом HTML и DOM.

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

Терминология HTML

Понимание терминов HTML и JavaScript критически необходимо для работы с DOM. Давайте вкратце рассмотрим основные термины.

Посмотрите на этот элемент HTML:

Home

В нем присутствует анкор, который является ссылкой на index.html.

  • a – тег
  • href – атрибут
  • html – значение атрибута
  • Home – текст.

Все, что находится между открывающимся и закрывающимся тегами, составляет HTML-элемент.

Вернемся к файлу index.html из предыдущего мануала:




Learning the DOM


Document Object Model

Самый простой способ доступа к элементу с JavaScript – это атрибут id. Давайте добавим указанную выше ссылку в файл index.html с id=»nav».

...

Document Object Model
Home

...

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

Затем используйте метод getElementById() для доступа ко всему элементу. В консоли введите следующее:

document.getElementById("nav");
Home

Метод getElementById() извлечет весь элемент. Теперь вместо того, чтобы вводить этот объект и метод каждый раз, когда вам нужно получить доступ к ссылке nav, вы можете поместить элемент в переменную, чтобы с ним было проще работать.

let navLink = document.getElementById("nav");

Переменная navLink содержит анкор. Здесь можно легко изменять атрибуты и значения. Например, чтобы изменить место ссылки, измените атрибут href:

navLink.href = "https://www.wikipedia.org";

Также можно изменить текст, переназначив свойство textContent:

navLink.textContent = "Navigate to Wikipedia";

Теперь, просмотрев этот элемент в консоли или проверив тег Elements, вы увидите, как он обновился.

navLink;
Navigate to Wikipedia

Изменения отобразятся и на фронт-энде.

Обновление страницы вернет все исходные значения.

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

Дерево и узлы DOM

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

  • Узел элемента
  • Текстовый узел
  • Узел комментария
  • Когда элемент HTML является элементом в DOM, он называется узлом элемента. Любой одиночный текст вне элемента является текстовым узлом, а комментарий HTML – узлом комментария. Помимо этих трех типов узлов, сам объект document – это узел документа, который является корневым узлом всех остальных узлов.

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

    Для примера создайте файл nodes.html. добавьте в него текстовый узел, а также узлы комментария и элемента.




    Learning About Nodes


    An element node

    A text node.

    Узел элемента html является родительским. head и body – дочерние узлы html. body содержит три дочерних узла, и все они находятся на одном уровне – тип узла не влияет на уровень вложения.

    Примечание : При работе с DOM, сгенерированным HTML, отступы исходного кода HTML создают много пустых текстовых узлов, которые не будут видны во вкладке DevTools Elements. Больше об этом по ссылке .

    Определение типа узла

    Каждый узел в документе имеет тип, доступ к которому осуществляется через свойство nodeType. В Mozilla Developer Network имеется обновленный список всех констант типов узлов. Ниже приведена таблица наиболее распространенных типов узлов.

    Во вкладке Elements в Developer Tools вы можете заметить, что всякий раз, когда вы нажимаете и выделяете любую строку в DOM, рядом с ней появляется значение == $0. Это очень удобный способ получить доступ к текущему активному элементу.

    В консоли node.html нажмите на первый элемент в body (h1).

    С помощью консоли узнайте тип выбранного узла с помощью свойства nodeType.

    $0.nodeType;
    1

    Выбрав элемент h1, вы увидите 1 как вывод, который относится к ELEMENT_NODE. Сделайте то же самое с другими узлами, и они вернут 3 и 8 соответственно.

    Зная, как получить доступ к элементу, вы можете увидеть тип узла, не выделяя элементы в DOM.

    document.body.nodeType;
    1

    В дополнение к nodeType вы также можете использовать свойство nodeValue, чтобы узнать значение текстового узла или узла комментария, а nodeName – для получения тега элемента.

    Изменение DOM с помощью событий

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

    Вернитесь в файл index.html и добавьте элемент button с id. Также нужно добавить ссылку на новый файл в новый js-каталог js/scripts.js.




    Learning the DOM


    Document Object Model
    Change Background Color


    Событие в JavaScript – это действие, которое выполняет пользователь. Пользователь наводит указатель мыши на элемент, нажимает на него или на определенную клавишу на клавиатуре – это все события. В этом конкретном случае кнопка должна выполнить действие, когда пользователь нажмет на нее. Для этого нужно добавить слушателя событий. Создайте файл scripts.js и сохраните его в новом каталоге js. В файле нужно определить элемент button и присвоить его переменной.

    Используя метод addEventListener(), кнопка будет прослушивать клики и выполнять свою функцию после клика.

    ...
    button.addEventListener("click", () => {
    // action will go here
    });

    Внутри функции нужно поместить код из предыдущего мануала, чтобы изменить цвет фона на fuchsia.

    ...

    Так выглядит скрипт:

    let button = document.getElementById("changeBackground");
    button.addEventListener("click", () => {
    document.body.style.backgroundColor = "fuchsia";
    });

    Сохраните и закройте файл. Обновите страницу index.html в браузере. Нажмите на новую кнопку, и цвет фона страницы изменится.

    Tags: ,

    На этом уроке мы рассмотрим, что такое DOM, зачем он нужен, а также то, как он строится.

    Что такое DOM?

    Браузер, когда запрашивает страницу и получает в ответе от сервера её исходный HTML-код, должен сначала его разобрать. В процессе анализа и разбора HTML-кода браузер строит на основе него DOM-дерево .

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

    DOM – это объектная модель документа, которую браузер создаёт в памяти компьютера на основании HTML-кода, полученного им от сервера.

    Если сказать по-простому, то HTML-код – это текст страницы, а DOM – это набор связанных объектов, созданных браузером при парсинге её текста.

    В Chrome исходный код страницы, который получает браузер, можно посмотреть во вкладке «Source» на панели «Инструменты веб-разработчика».


    В Chrome инструмента, с помощью которого можно было бы посмотреть созданное им DOM-дерево нет. Но есть представление этого DOM-дерева в виде HTML-кода, оно доступно на вкладке «Elements». С таким представлением DOM веб-разработчику, конечно, намного удобнее работать. Поэтому инструмента, который DOM представлял бы в виде древовидной структуры нет.


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

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

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

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

    Зачем нам нужен DOM API? Он нам нужен для того, чтобы мы могли с помощью JavaScript изменять страницу на «лету», т.е. делать её динамической и интерактивной.

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

    Сейчас в вебе практически нет сайтов в сценариях которых отсутствовала бы работа с DOM.

    Из чего состоит HTML-код страницы?

    Перед тем, как перейти к изучению объектной модели документа необходимо сначала вспомнить, что из себя представляет исходный код веб-страницы (HTML-документа).

    Исходный код веб-страницы состоит из тегов, атрибутов, комментариев и текста. Теги - это базовая синтаксическая конструкция HTML. Большинство из них являются парными. В этом случае один из них является открывающим, а другой – закрывающим. Одна такая пара тегов образует HTML-элемент. HTML-элементы могут иметь дополнительные параметры – атрибуты.

    В документе для создания определённой разметки одни элементы находятся внутри других. В результате HTML-документ можно представить как множество вложенных друг в друга HTML-элементов.

    В качестве примера рассмотрим следующий HTML код:

    Заголовок страницы Название статьи Раздел статьи

    Содержимое статьи

    В этом коде корневым элементом является html . В него вложены элементы head и body . Элемент head содержит title , а body – h1 и div . Элемент div в свою очередь содержит h2 и p .

    Теперь рассмотрим, как браузер на основании HTML-кода строит DOM-дерево.

    Как строится DOM-дерево документа?

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

    В результате браузер полученное DOM-дерево использует не только в своей работе, но также предоставляет нам API для удобной работы с ним через JavaScript.

    При строительстве DOM браузер создаёт из HTML-элементов, текста, комментариев и других сущностей этого языка объекты (узлы DOM-дерева).

    В большинстве случаев веб-разработчиков интересуют только объекты (узлы), образованные из HTML-элементов.

    При этом браузер не просто создаёт объекты из HTML-элементов, а также связывает их между собой определёнными связями в зависимости от того, как каждый из них относится к другому в коде.

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

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

    Чтобы получить DOM-дерево так как его строит браузер, необходимо просто «выстроить» все элементы в зависимости от их отношения друг к другу.

    Создание DOM-дерева выполняется сверху вниз.

    При этом корнем DOM-дерева всегда является сам документ (узел document). Далее дерево строится в зависимости от структуры HTML кода.

    Например, HTML-код, который мы рассматривали выше будет иметь следующее DOM-дерево:


    В самом верху этого дерева находится узел document . Данный узел связан с html , он является его ребёнком. Узел html образован элементом html (...). Узлы head (...) и body (...) имеют родительскую связь с html . По отношению друг ту другу они являются сиблингами, т.к. имеют одного родителя. Узел head связан с title (lt;title>...), он является его ребёнком. Узлы h1 и div связаны с body , для них он является родителем. Узел div связан с h2 (...) и p (), они являются его детьми.

    Начинается дерево как было уже отмечено выше с объекта (узла) document . Он в свою очередь имеет один дочерний узел, образованный элементом html (...). Элементы head (...) и body (...) находятся в html и, следовательно, являются его детьми. Далее узел head является родительским для title (lt;title>...). Элементы h1 и div вложены в body , значит они являются его детьми. В div непосредственно расположены элементы h2 (...) и p (). Это значит, что узел div для каждого из них является родительским.

    Вот так просто строится DOM-дерево в браузере на основании HTML-кода.

    Зачем нужно знать, как строится DOM дерево? Во-первых, это понимание той среды, в которой вы хотите что-то изменять. Во-вторых, большинство действий при работе с DOM сводится к поиску (выбору) нужных элементов. Не зная как устроено DOM-дерево и связи между узлами найти какой-то определенный элемент в нём будет достаточно затруднительно.

    Задание

    На основе DOM-дерева, представленного на рисунке, создайте HTML-код.



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

    Можно представить 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.

    Данная публикация предваряет ряд статей об альтернативных способах работы с 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 расходуется оперативная память и процессорные мощности.

    Понравилась статья? Поделиться с друзьями: