×
Вы действительно хотите открыть доступ к тестированию по курсу JavaScript Базовый 2015 на 40 дней?
ВИДЕОУРОК №3. Работа с документами.
- Основные конструкторы – Object(), Array(), Function(), Date(), String(). Принцип работы конструкторов, назначение ключевого слова this в конструкторе.
- Создание пользовательских конструкторов.
- Что такое прототип, использование прототипов и добавление свойств и методов в прототип.
- Работа с конструктором Object
- Объектно-ориентированные техники в языке JavaScript. Реализация наследования в JavaScript.
- Способы подключения JavaScript сценариев к HTML документу.
- Создание сценариев (модулей), которые добавляют минимальное количество глобальных переменных.
- Использование свойств объекта document. Методы для получения объектов со страницы.
- DOM – Document Object Model, примеры создания новых узлов, манипулирование существующими узлами, удаление узлов.
- Создание и использование таймеров, использование функций setInterval и setTimeout в языке JavaScript
- Использование Location
- Использование объекта Navigator
- Создание всплывающих окон с помощью JavaScript кода.
- Работа с типом данных string. Методы для работы со строковыми значениями.
- Регулярные выражения в языке JavaScript. Синтаксис и методы, которые могут работать с регулярными выражениями.
- Основы работы с CSS. Создание CSS правил и подключение правил к HTML документам.
- Рассмотрение отдельных CSS свойств, которые часто используются при создании динамических страниц.
- Способы изменения CSS стилей через JavaScript код. Работа с вычисляемыми стилями (computed styles).
- Модель обработки события DOM Level 0. Варианты создания обработчиков, преимущества и недостатки.
- Модель обработки события DOM Level 2. Маршрутизация события, контроль распространения события по дереву разметки с помощью методов stopPropagation() и preventDefault()
- Модель обработки событий Internet Explorer.
- Интерфейс объекта события (Event)
- События мыши.
- Обработка событий клавиатуры.
- Примеры обработки событий.
- Элемент form, его назначение и способы получения к нему доступа с помощью JavaScript кода.
- Элемент input, свойства и типы элементов.
- Примеры проверки (валидации) данных введенных пользователем в форму.
- Пример использования объекта Date для работы с датой и временем.
- Назначение cookies рассмотрение принципов хранения данных на стороне клиента.
- Свойство cookie объекта document. Примеры создания, удаления и изменения значений.
- Другие механизмы хранения данных на стороне клиента - WebStorage, использование свойств localStorage и sessionStorage.
- Элемент img, способы получения объекта элемента с изображением и основные его свойства.
- Примеры предварительной загрузки изображений с сервера.
- Создание графики на стороне клиента с помощью CSS.
- Создание графики на стороне клиента с помощью SVG.
- Создание графики на стороне клиента с помощью Canvas(HTML5).
- Основы работы веб приложений. Разбор протокола HTTP. Использование приложения Fiddler для откладки HTTP запросов.
- AJAX – Asynchronous JavaScript And XML.
- Использование объекта XMLHttpRequest для создания синхронных и асинхронных HTTP запросов.
- Использование XMLHttpRequest для отправки данных с POST и GET запросами.
- Примеры простого AJAX приложения.
Добрый день! Я рад приветствовать вас на курсе JavaScript для профессионалов. Тема урока: «Работа с документами». В прошлом курсе JavaScript Базовый, мы больше времени потратили на то чтобы разобраться с синтаксисом языка JavaScript. Мы изучали циклы, условия и другие конструкции. В этом курсе мы больше времени потратим на то, чтобы разобраться с теми возможностями языка, которые предоставляют нам доступ к среде web-браузера. Мы с вами в этом курсе будем рассматривать такие компоненты среды программирования как объект window, иерархию объектов на стороне клиента и управляемую событийную модель. Объект Window – это глобальный объект, который представляет из себя контейнер, выполняющий весь JavaScript код. То есть, когда мы в своем коде создаем глобальную переменную, когда мы создаем функцию, в любом случае наша глобальная переменная или функция, она будет находится как свойство или метод объекта Window. То есть Window – это глобальный объект, в котором происходит выполнение всего кода. DOM – Document Object Model – это дерево, которое строит браузер, когда качает HTML разметку с стороны сервера. В этом уроке мы разберем основные свойства и методы, позволяющие манипулировать этим деревом. Мы научимся создавать новые элементы, удалять элементы и менять настройки отдельных элементов. И так же браузер предоставляет для нас событийную модель. Событийная модель позволяет нам реагировать на определенные действия пользователя. Например, мы можем определить, когда пользователь нажал по кнопке, когда пользователь навел мышку на определенный сегмент страницы, браузер, в зависимости от действий пользователя может запустить события, а JavaScript код это событие может на сработать. Событийную модель мы будем детальнее разбирать на следующих занятиях. В этом уроке мы обсудим несколько тем. Первая тема – это способы интеграции JavaScript кода в страницу. Мы рассмотрим как можно подключить сценарии. Различные варианты. Далее мы разберем что такое namespace. Как можно сделать контейнеры, которые смогут минимизировать количество глобальных переменных в вашем коде. А далее мы разберем основные свойства объекта document, посмотри для каких целей какое свойство можно применить. И в завершение урока разберем что такое DOM, разберем иерархию объектов, которые построит браузер на клиенте. Есть несколько способов подключения JavaScript кода к HTML документу. Первый способ мы не раз использовали в предыдущих занятиях – это использование элемента script. Когда мы создаем элемента script, то все содержимое этого элемента – это и будет JavaScript кодом, который браузер должен интерпретировать. При задании элемента script мы можем установить для него атрибут type, но это является не обязательным. Браузеры по умолчанию принимаю содержимое элемента script как текстовый контент, являющийся JavaScript кодом. Элемент script мы часто помещаем в тег
, но это не является обязательным. Элемент script мы можем размещать в любом месте кода. Очень часто при задании JavaScript кода в документ, мы используем элемент script не для того, чтобы в него поместить сам сценарий, а для того, чтобы с помощью этого элемента связать страницу с внешним JavaScript файлом. Это очень удобно, потому что один и тот же сценарий мы в итоге можем использовать на разных страницах, в разных HTML документах. Если мы хотим связать документ с внешним JavaScript файлом, мы должны для элемента script указать атрибут src, и в этом атрибуте определить месторасположение того файла, в котором находится необходимый нам код. Когда браузер дойдет до 14 строчки, он сделает дополнительный запрос на сервер, скачает с сервера JavaScript файл и выполнит его. При этом сам JavaScript файл должен в себе содержать только код. Элемент script добавлять в этот файл уже не нужно. Следующий способ использования JavaScript кода – это определение обработчиков на события. Что такое событие? Событие – это определенная ситуация, которая наступает в нашем документе. Определенная ситуация, которая наступает из-за действия пользователя или из-за других каких-то факторов. Например, у нас есть событие click. Это событие происходит в тот момент, когда пользователь нажимает мышкой в определенной части документа. Есть события onMouseMove. Это событие происходит, когда курсор движется по документу. Ну событий есть очень много, мы сейчас их не будем рассматривать, дальше у нас выделен целый урок на разбор событий и на способы обработки событий. Сейчас на 19 строке создается элемент input. Многие знают, что input – это элемент, который будет превращен в control – в элемент управления. С помощью атрибута type мы указываем в какой именно элемент управления превратится input. В нашем случае мы input превращаем в кнопку и пишем на input, на самой кнопке пишем текст «Кнопка». С помощью атрибута onclick мы указываем какой JavaScript код необходимо выполнить, когда пользователь произведет нажатие на кнопку. При нажатии мы запустим alert(“Hello”). Все атрибуты, которые в элементах начинаются на с префикса on – эти атрибуты являются атрибутами, с помощью которых устанавливаются обработчики, обработчики на событие. Давайте мы сейчас попробуем запустить этот пример и посмотрим, как будет срабатывать событие. В начале вы видите сообщение «Hello world!». Оно у нас отобразилось, потому что на строке мы подключали script. То есть тут браузер закачал этот script и выполнил его. Второй раз сообщение «Hello world!», отобразилась разметка документа и еще раз сообщение «Hello world!». Это произошло из-за того, что на строке у нас еще раз подключен элемент скрип. Мы сейчас дойдем до этой точки, и объясним почему она находится в середине элемента body. И теперь, когда мы находимся уже в документе. Если мы сейчас нажмем по кнопке, если мы производим клик, браузер берет атрибут onclick и выполняет то содержимое, которое в этом атрибуте заложено. В атрибут onclick мы можем устанавливать JavaScript код, который необходимо выполнить при нажатии. Кликнули по кнопке, появилось сообщение Hello!. Следующий способ установки обработчиков событий и интеграции JavaScript кода в документ – это использование псевдо-протокола, который называется JavaScript. На строке 24 создается ссылка, но в href атрибуте, вместо определения HTTP адреса, мы устанавливаем JavaScript адрес. Это означает, что клик по это ссылке не перенаправляет браузер на другую страницу, а запускает JavaScript код, определённый после двоеточия. После двоеточия мы вызывает alert, и соответственно нажатие посылки приводит к тому, что у нас выполняется функция, выводящая сообщения. Ну и теперь мы подошли к последней части этого примера, к использованию элемента script в конце документа перед закрывающим тегом body. Когда браузер видит элемент script, он ведет себя следующим образом. Вот на примере 14 строчки. Браузер интерпретирует все эти строчки, а доходя до 14 строки, браузер останавливается, делает запрос на сервер, качает с сервера JavaScript файл и выполняет его. Пока файл не выполнится, браузер не продолжит, как бы не скачивание, а интерпретацию остального документа. Браузер до тех пока не убедится, что JavaScript файл скачан и выполнен, браузер не будет делать дальнейших каких-либо операций. Представьте, что это JavaScript файл, как и в нашем случае, нам нет разницы когда он будет работать. В начале документа, в конце документа. Задача этого JavaScript файла, например, дополнительно оформить HTML документ. Допустим найти поле ввода в этом HTML документе и сделать, чтобы это поле ввода стало календариком. Чтобы при клике по полю ввода появлялся календарик. Нам не обязательно этот календарик добавлять в начале загрузки, мы можем календарик добавить уже и после того, как HTML разметка будет показана пользователю. Вот если мы сейчас убираем script на строке. Смотрите, мы запускаем пример, выводится сообщение Hello world, но при этом браузер ничего не показывает, потому что он еще не имеет права идти дальше, пока не закончит JavaScript код, идти дальше интерпретировать содержимое документа. Браузер вывел нам сообщение, как только мы нажали на кнопочку ок, браузер до конца прошелся по разметке и нарисовал нам все содержимое документа. Если же мы делаем наоборот, если мы убираем эту строчку на 14 строке и оставляем комментарий в конце. Видите, браузер нам показывает страницу, а уже после того, как страница была отображена браузер выводит сообщение, которое отобразилось JavaScript кодом. В чем преимущество именно вот такой последовательности. Этот JavaScript файл, в котором отображается сообщение может весить не несколько байт, как в нашем случае, а несколько сотен килобайт. И скачиваться он может не за доли секунды, а за там допустим пол минуты или за минуту. И получается, что если мы вот такой громоздкий JavaScript код подключим в начале документа, то пользователь ничего не увидит на странице до тех пор, пока браузер не скачает JavaScript код с сервера. А вот если мы script подключаем после всего документа перед закрывающим тегом body, то в таком случае браузер сможет показать пользователю разметку. Пользователь уже сможет читать текст на странице. А пока пользователь читает текст, пользователь докачает JavaScript файл, и выполнит какую-нибудь дополнительную настройку документа. Вот например, если задача JavaScript файла просто добавить календарик к textBox, эту задачу явно можно сделать в самый последний момент. Показать пользователю текст, чтобы он уже мог заинтересоваться страницей, а потом догрузить сценарий и видоизменить документ, дополнить документ. Если вы слышали о такой библиотеке как jQuerry UI, которая позволяет на страницу добавлять различные виджеты, те же календарики, выпадающие списки, аккордеоны. Вот jQuerry есть смысл подключить перед закрывающим тегом body, потому что пользователю будет отображен контент, а потом, когда догрузится jQuerry UI этот контент как-то изменится и оформится. Но пользователь сможет начать с ним работать до того, как JavaScript библиотека будет скачана с сервера.Во втором примере показан способ как можно сделать очень примитивные обработчики для событий и элементов управления. Этот способ нежелательно использовать, в следующих примерах мы разберем более правильные подходы в создании обработчиков, мы будем с вами разбирать ненавязчивый JavaScript код, но пока для простоты мы рассмотри такой вариант реакции на действие пользователя. На 16 строке создается кнопка, которой указывается атрибут onclick. В onclick мы помещаем вызов функции buttonClickHandler(). Видите, что у нас есть круглые скобочки, то есть при нажатии на кнопку будет запущена функция. Функция определена выше и в этой функции просто отображается alert(). Вот по сути этот вариант ничем не отличается от предыдущего, от 19 строчки, когда мы alert() определили непосредственно в атрибуте onclick. Но если в этом коде нам потребуется выполнять несколько реакций, одну и более, то есть несколько инструкций, то в таком случае нам придётся все эти инструкции помещать в одну строку. Редактирование такого кода будет очень неудобным. Вот если мы используем такой подход. Если мы создаем отдельную функцию, здесь в этой функции мы можем разработать полноценный сценарий, который будет выполнен при нажатии по кнопке. Но то, что вы сейчас видите, является навязчивым JavaScript кодом, потому что мы непосредственно в HTML разметку помещаем JavaScript операцию. Вот вызов функции. Это делать не очень хорошо, потому в дальнейшем, когда вы будете создавать сложные приложения, вы должны четко разделать, что есть отдельно интерфейс, который представлен HTML кодом, есть отдельно логика, которая представлена JavaScript кодом. Есть отдельно стили, которые представлены CSS кодом. И вот каждый документ HTML, JS и CSS они должны выполнять четко поставленную перед собой задачу. Не надо делать так, чтобы все находилось у вас в одной куче, в одном документе. В учебных примерах мы будем использовать иногда вот такой подход, потому что он очень простой, потому что так проще показать какие-то действия, какие-то операции выполнить на нажатие по кнопке. Но в реальных ситуациях, в реальных сложных приложениях такое делать конечно не желательно.
В языке JavaScript нету специальных ключевых слов или конструкций для создания пространств имен. То есть для создания неких контейнеров, в которых будет выполнятся ваш код, в которых будут находится имена переменных, имена функций. В многих языках программирования ест понятие пространства имен, позволяющие вам создать переменную, которая будет называться точно так же как и у другого разработчика. Который находится в другом пространстве имен. В языке JavaScript, если мы создадим глобальную переменную у себя в сценарии, а потом подключим сценарий другого разработчика, который использует точно такую же глобальную переменную, то мы можем получить очень нежелательное поведение. Т.к. пространств имен в языке JavaScript нет, то для реализации подобного поведения, поведения подобного использованию пространств имен мы можем использовать два похода. Первый подход – это создание объектов, как пространств имен, второй подход – это использование анонимных функций. Сейчас в примере мы разберем в начале проблему, которая возникает из-за отсутствия пространств имен, а потом посмотрим как эту проблему можно решить. Одним из способов, которые вы сейчас видите на слайде. В начале мы разберем проблему, которая возникает при работе с глобальными переменными. В HTML документе, который мы сейчас открыли есть два сценария: строка 5 и 6. Подключаются файлы BadModule1 и BadModule2. Давайте посмотрим, что в этих файлах находится. BadModule1 – на первый взгляд абсолютно нормальных JavaScript код. На первой строке создается переменная с именем name со значением MODULE1. На строке 3 определяется функция с именем startModule1() и задача этой функции очень простая. В тело документа отобразить значение переменной name. И переменная на первой строчке и функция на третьей строчке – это глобальные переменные. То есть переменная будет видна абсолютно всем JavaScript файлам, которые есть в HTML документа. И то же самое касается функций. Когда мы выполняем код в браузере, то весь этот код является содержимым глобального объекта Window. И получается, что на первой строке name – это свойство объекта Window. А на 3 строчке startModule1() – это метод глобального объекта Window. Давайте посмотрим что происходит во втором файле BadModule2. Во втором файле у нас фактически такая же картина. На первой строке переменная name, на третьей строке функция, но с другим именем «startModule2». Что происходит у нас в текущих документах. Когда браузер будет загружать вот этот первый документ, браузер на первой строке выполнит создание переменной name. Браузер для глобального объекта Window добавит свойство name, в котором будет находится значение MODULE1. И также для глобального объекта будет добавлено свойство startModule1(), точнее не свойство а метод. Дальше браузер загрузит второй JavaScript файл. На первой строке браузер увидит, что мы обращаемся к глобальному свойству name, и значение MODULE1 меняем на MODULE2. Браузер на первой строке не будет создавать никаких дополнительных переменных, потому что весь JavaScript код выполняется в одном глобальном объекте. В объекте Window. Поэтому первая строчка – это переопределение переменной name, которая была создана раньше, в предыдущем JavaScript файле. Ну а на 3 строке создается новый метод startModule2(), потому что имя его отличается от предыдущего метода. Если бы на 3 строке мы назвали метод startModule1(), то тогда бы мы перетёрли ту функциональность, которая была добавлена с помощью кода в Module1. И вот смотрите последовательность работы приложения. На 5 строке браузер скачивает BadModule1.js, создает переменную name и создает функцию startModule1(). На 6 строчке браузер скачивает второй файл, создает, уже не создает, а перетирает значение переменной name, которое создал предыдущий файл, и в эту переменную записывает новое значение MODULE2. Ну и добавляет функцию startModule2(). И теперь, когда на 9 и 10 строке мы начинаем запускать функции startModule1() и startModule2(), рассчитывая, что каждая функция выведет свое собственное имя. Первая функция выведет сообщение, что она работает с Module1, а вторая их Module2, то так код работать не будет, мы не увидим, то на что мы рассчитывали. Мы увидим, что обе функции выведут абсолютно одинаковое значение. Видите? MODULE2 и MODULE2. Хотя казалось, что мы работаем с двумя разными переменными, вот name здесь и name здесь. Мы должны понимать, что создавая глобальную переменную, вы эту переменную создаете сразу для всех сценариев. И это является проблемой, потому что если вы создали глобальную переменную в своём коде, а другой разработчик, скрипт которого вы подключили, скачали с интернета и подключили к своей странице, если другой разработчик тоже будет использовать такую же переменную у себя в коде, то тогда ваш код может начать работать не предсказуемо. По этому чтобы избавится от вот таких глобальных переменных мы можем применять несколько техник. Первая техника, которую мы рассмотрим – это использование объектов, как пространств имен.
Суть этой техники заключается в создании только одной глобальной переменной, в которой находится объект, а в этом объекте уже содержатся все функции, все переменные, которые необходимы нашему коду. На строке 9 создается глобальная переменная Module1. Этой переменной присваивается пустой объект. Далее на 11 строке объекту Module1 добавляется свойство name и значение Module1. В предыдущем примере мы вместо свойства использовали просто переменную, которая становилась глобальной переменной. Сейчас у нас только одна глобальная переменная и к этой переменной, к объекту, который хранится в ней, мы записываем, добавляем свойства. Так же на 13 строке для Module1 объекта мы добавляем метод startModule1() и в этом методе, в тело документа отображаем значение свойства name, которое создали на 11 строке. Точно такие же изменения происходят во втором файле. Мы создаем объект, но уже с другим именем Module2, на 8 строчке прибавляем свойство name и добавляем метод startModule2(), в котором отображаем Module2.name. Получается, что когда мы работаем вот с таким вот кодом у нас шансы на конфликты очень сильно уменьшаются. Раньше у нас было несколько глобальных переменных, теперь JavaScript файл создает только одну глобальную переменную. Соответственно шанс того, что имена переменных будут разные повышается и никаких конфликтов не произойдет. Также, дополнительно, чтобы обезопасить себя мы можем сделать так, что JavaScript файлы имели такое же имя как и имя глобальной переменной, которую файл добавляет. Если посмотреть сейчас на содержимое HTML документа, мы видим, что код в этом документе практически не отличается от кода в первом примере, который у нас работал не правильно. В начале мы подключаем два сценария, а потом на 10, 11 строчке используем ту функциональность, которая была добавлена сценариями. Первый сценарий добавил Module1, а второй добавил Module2. Две глобальные переменные. И у каждой переменной сейчас находится объект со своими свойствами и своими методами. И получается, что теперь мы можем спокойно внутри этих объектов использовать одинаковые имена переменных, одинаковые имена функций. Это не повлечет за собой никаких проблем, потому что объект теперь выступает в роли пространства имен и гарантирует, что если где-то в другом объекте будет находится такая-же переменная – это ни к чему плохому не приведет. Вот если мы запустим этот пример, то он уже у нас срабатывает корректно, выводятся сообщения MODULE1 и MODULE2. Именно те сообщения, которые мы и рассчитывали увидеть, вызывая функции startModule1() и startModule2().
Следующая техника – это использование анонимной функции. Если открыть документ Module3.js, то мы видим, что он кардинально отличается от предыдущих примеров. Во первых в этом документе у нас находится достаточно странных код. Определяется функция. Смотрите, как эту функцию мы определяем с самого начала. В начале мы выполняем создание двух таких операторов. В первых круглых скобках мы указываем функцию, которой не даем имя, и у которой есть определенное тело, определенные операции. Наша задача – свести к минимуму количество глобальных переменных. Если посмотреть сейчас на вот этот код. Функция, которую мы создали она никак не называется. Соответственно эта функция у нас не добавляет никаких свойств в глобальный объект Window. То есть эта функция безымянная и мало того, эта функция у нас сразу же запускается. Вот само определение функцию, а эти круглые скобочки заставляют ее работать. То есть мы создали функцию и сразу же ее запустили, при этом функция не создала никаких глобальных имен, и в свою очередь, вы помните, что каждая функция – это локальная область видимости. То есть те переменные, которые находятся в функции – они никому не мешают, они существуют только внутри этой функции. Вот получается у нас идеальных вариант. Функция безымянная, функция запустилась и все, что мы делаем в функции оно теперь никому не вредит, никто не видит тех переменных, которые мы внутри функции создали. То есть вот строка 7, когда мы создаем переменную message – эта переменная доступна только внутри этой функции. И 9 строчка – мы эту переменную выводим. Выводим «Hello from module3». Если зайти в module4, то здесь тоже создается переменная message, которую мы потом отображаем в сообщении и получается здесь переменная message, она не имеет ничего общего с переменной message, которая было создана здесь. Но в этом способе создания пространств имен также есть и недостаток. Мы не можем теперь достучатся к функциональности, которая здесь находится. То есть функциональность может просто запустится на определенное событие, на определенный момент времени, когда мы работаем с документом. Но мы не можем по своему желанию взять c JavaScript файла какой-то метод и выполнить его, потому что у нас просто нет никаких имен глобальных, за которые можно было бы зацепится. То есть если мы собираемся создавать какой-то набор инструментов, допустим набор функций, который позволяет выполнять некоторые математические операции. Например, искать синусы, косинусы, квадратные корни. В такой ситуации, когда мы создаем библиотеку, инструментарий нам удобнее использовать подход, использованный в первой технике, когда мы создаем объект. Для инструментария, для библиотеки с инструментами этот вариант будет более подходящим. Но когда мы создаем например сценарий, который начинает работать в момент загрузки формата и связан только с этим документом. Например, он находит на странице элемент управления и проверяет правильность введенных в эти элементы управления данных. Вот для такой ситуации нам подойдет этот шаблон, потому что смотрите как в итоге он используется в документе. Мы его просто подключаем. Мы не можем из анонимных функций его вытащить не имея глобальных переменных. Анонимные функции не предоставляют нам никаких идентификаторов, по которым мы можем обратится к поведению или функциональности, заложенных в этих JavaScript файлах. Вот две техники, позволяющие свести к минимуму глобальные переменные, либо сделать так, чтобы была одна, либо вообще убрать глобальные переменные.
Как вы помните, тема текущего урока – работа с документами. Вот мы с вами подошли к слайдам и примерам, которые покажут, как мы можем манипулировать содержимым, загруженным в документ. Перед тем, как мы перейдем к коду, давайте посмотри иерархию объектов клиентского JavaScript кода. На 4 слайде вы видите, что корневым элементом у нас является текущее окно, в котором загружается HTML разметка и JavaScript код. По сути весь код выполняется в контексте глобального объекта Window и любые переменные, любые глобальные функции, которые мы создаем у себя в коде – они превращаются в свойства этого глобального объекта. Глобальный объект окна доступен в JavaScript коде через свойство Window, но кроме этого глобальный объект Window содержит в себе много других полезных свойств, которые мы часто используем при написании клиентских сценариев. Перечень этих свойств вы видите на первом уровне этого дерева. Здесь свойства navigator, location, frames, history, screen, document. Этих свойств намного больше. Здесь перечисляются только основные. Вот например с помощью объекта navigator мы можем получить доступ к информации о текущем браузере, можем использовать геолокационное API, чтобы узнать текущее месторасположение клиента и много чего другого. С помощью объекта location мы можем узнать, где сейчас находится текущий пользователь, на какой он находится странице. С помощью frames мы можем определить количество фреймов, которые есть в текущем документе. History – это объект позволяющий контролировать историю посещения пользователя. Screen – это информация о текущем разрешении экрана пользователя. И document – это то свойство, с которым мы очень часто будем работать. С помощью document мы получаем доступ к текущей разметке, к тому дереву элементов, которые создал браузер, когда получал с сервера HTML разметку. По сути все, что представлено на этом слайде – это все свойства. Вот document, screen, history – это все свойства объекта Window. И каждое из этих свойств содержит в себе объект. Например свойство history содержит объект History, свойство screen – объект Screen. Но нас сейчас будет интересовать только свойство document, в котором находится объект document. Мы сейчас с вами потихоньку подходим к такому понятию как DOM. Document Object Model. И самые простые способы манипулировать с DOM – это пользоваться свойствами, которые находятся в документе. Например, у документа есть такие свойства как forms, links, images. То есть это коллекция всех элементов форм, коллекция всех ссылок на страницы. Коллекция всех картинок. Эти свойства можно продолжать, их намного больше чем те которые мы видим. Например, в коллекции forms находится коллекция elements – все input, которые есть в формочке. В отдельных элементах этого массива у нас находится коллекция select. Например, один из элементов в форме – это элемент select, в котором находятся option, и получается, что мы можем получить из select массив option, которые выводятся пользователю. То есть получается что весь наш документ – это массив, в котором находятся массивы и каждый элемент этого массива он по сути связан с каким-то конкретным элементом на странице. Допустим если мы берем нулевой элемент из массива forms – мы получаем доступ к нулевой по счету форме, которая была создана в HTML разметке. Вот с помощью этих свойств мы можем простым способом манипулировать элементами текущего документа. Давайте сейчас перейдем к примерам кода и несколько этих свойств рассмотрим в работе.
По сути сейчас наше внимание в третьей части урока будет сконцентрировано на свойстве document и на объекте Document, который представляет текущий документ, который загрузил браузер. Сейчас мы рассмотри два способа как можно отображать в тело документа информацию, как можно генерировать новый контент для страницы. Мы уже с вами не раз пользовались методом document.write() и вот многие не знают, что его можно помещать в любую часть документа. В предыдущих примерах мы с вами использовали его в элементе script, но на самом деле вы можете поместить эту инструкцию в любую часть страницы. Та точка, где вы поместили document.write(), то в этой части документа и будет сгенерирован новый контент. Вот если мы сейчас на 18 строчке видим параграф с оформлением, с рамочкой, с отступами, с какими-то настройками шрифта. Когда внутрь этого параграфа мы помещаем элемент script и в этом элементе указываем JavaScript инструкции, то вот document.write() на 20 и 21 строчке выведет содержимое именно внутрь параграфа, то есть эти строчки у нас появятся внутри параграфа с черной рамочкой нарисованной. Давайте запустим и проверим. Вот видите, параграф с черной рамкой и вот два наших сообщения, которые мы вывели с помощью document.write(). То есть где мы указываем эти инструкции, где мы вызываем этот код, там у нас и генерируется HTML разметка новая. То есть если мы эти элементы script выносим в head, то по сути у нас разметка генерируется внутри head, внутри заголовка страницы. Но я думаю, что многие из вас обратили внимание при работе с подсказкой Visual Studio, что она подсказывает не только метод write() но и еще и метод writeln(). Но в большинстве случаев, когда мы пользуемся методом writeln() – этот метод ничем нам не помогает. Вот сейчас мы переделали методы вызова на 20 и 21 строке, если мы обновим документ, вы видите, что вызов метода writeln() никакого эффекта не дал. То есть код на страницу выводится так же, как и с использованием предыдущего кода. В чем же тогда разница метода writeln(). Многие догадались, что writeln() – это write line, то есть написать линию. То есть метод writeln() – его задача вывести текст и сразу же после текста установить символы переноса на новую строку. Но, я думаю, что вы знаете, используя параграф, используя div, просто используя текст в HTML разметке, этот текст всегда форматируется по определенному правилу. У нас в HTML коде всегда удаляются лишние переносы, удаляются лишние пробелы. И соответственно если мы в параграфе генерируем с помощью document.write() какой-то текст, то этот текст форматируется по правилам HTML разметки, то есть все лишние переносы у нас удаляются. Но есть специальный элемент – элемент pre, в котором мы можем использовать текст с форматированием и браузер оставляет это форматирование. То есть если мы добавляем там табуляции, переносы на новую строчку, браузер выводит этот контент так, как мы его сгенерировали. Вот вы видите, что на 26 строке создается элемент pre и в этом элементе мы устанавливаем вызов метода writeln() два раза. Получается сейчас в этих строчках метод writeln() будет срабатывать корректно, будет перенос на новую строку. Сначала выведется Hello а потом world!. Вот вы видите две строчки вывелось. Если мы поменяем pre на p, то в таком случае у нас метод writeln() перестанет работать. Он будет добавлять в конец строки перенос, но этот перенос будет удалятся самим браузером. Вот для того, чтобы вы понимали разницу между методами writeln(), и методом write() вот мы рассмотрели этот пример. И также очень важно знать насчет того, что document.write() можно применять только тогда, когда мы строим документ. После того, как документ был загружен браузером и полностью сформирован, мы не имеем права вызывать document.write(), потому что это приведет к созданию абсолютно нового пустого документа в котором будет заново генерироваться контент. Если вы используете document.write(), то использование его в таком ключе, использование его внутри элемента script абсолютно нормально, Но когда вы используете document.write() в функциях обработчиках – это может повлечь за собой не очень желательное поведение. Вот на 33 строке у нас есть кнопка, при нажатии по которой вызывается функция ButtonHandler(). И в этой функции мы делаем безобидную операцию. На 10 строчке берем document.write(“Hello world!!”). Посмотрите, к чему приводит обращение к кнопке. Видите, какая у нас загруженная разметка, если мы нажимаем по кнопке – страница у нас полностью обнуляется и мы видим только фразу Hello world!! Что сейчас произошло? Когда браузер формирует разметку впервые, то он выполняет последовательно все инструкции, которые видит, создает в теле документа сообщение, которое мы выводили write() и writeln(). Когда браузер до конца анализирует эту разметку и превращает ее в видимый для пользователя контент, браузер закрывает документ. Так как документ полностью сформирован, документ считается закрытым, и в этот документ мы не можем добавить никаких символов. Если мы в коде после закрытия документа запустим еще раз document.write() это приведет к тому, что браузер создаст новый документ, в этот документ запишет новое сообщения, а старое визуальное оформление, которое было загружено, он просто выбросит, удалит. Поэтому когда на 10 строке мы вызывает document.write() мы вызываем это явно после того, как страница была отрисована перед пользователем, потому что пользователь увидел кнопку и нажал эту кнопку. Вот получается, когда уже страница готова, document.write() он просто удаляет все содержимое страницы и генерирует новый контент. Поэтому никогда не используйте document.write() для того, чтобы при нажатии по каким-то кнопкам при обработки событий менять содержимое документа. Вот это к чему приводит.
В следующем примере мы рассмотрим несколько свойств, которые есть у объекта document. Некоторые из свойств уже устарели и не используются, некоторые мы применяем достаточно часто. Самое первое свойство, которое мы используем на 9 строчке – это свойство document.bgColor. C его помощью мы можем получить информацию о цвете фона текущего документа. В этом примере на строке 6 мы используем для документа backgroung color. Вот это Hex значение определяет цвет. По сути вот этот цвет мы используем на 9 строке, пытаемся его отобразить. Давайте посмотрим будет ли отображать нам его браузер Chrome. Видите? Bgcolor в случае с браузером Chrome выводится не будет. Если мы переключимся, например на IE, то вот мы видим, что он отображает цвет текущего документа. Bgcolor – это свойство – которое дает доступ к фону документа, цвету фона документа. Следующее свойство на 12 строчке – свойство cookie. C его помощью мы можем обратится к cookie файлам, и узнать какие файлы были связаны с текущим ресурсом. Детальнее о том, как работать с cookie файлами мы с вами будет разбирать на следующий занятиях. Но вот доступ к этим файлам выполняется посредством document. C помощью свойства lastModified() мы можем получить информацию о дате последнего изменения документа. Вот если мы сейчас запускаем этот пример, то вот мы видим дату, когда этот документ был последний раз изменен. Далее, строка 18. С помощью document. Location мы можем получить объект location, который определяет текущее месторасположение в браузере, то есть мы видим адрес, на котором мы находимся. http://localhost и т.д. Вот посмотрите, что выводит Chrome. Вот он наш адрес, только часть символов, так как они русские символы, они заменились на значения URL encode. Далее следующее свойство referrer. C его помощью мы можем узнать адрес предыдущей страницы с которой пользователь сюда попал. Например, если это у нас страница – личный кабинет, а до этого пользователь находился на домашней странице вашего сайта. Вот когда пользователь перейдет с домашней страницы на эту, на страницу личного кабинета, то в свойстве referrer будет находится адрес домашней страницы. То есть предыдущей страницы, с которой мы произвели переход. С помощью свойства title мы можем получить содержимое заголовка страницы. То есть вот наш заголовок «Свойства объекта document». Вот мы этот заголовок считываем. Также, я думаю вы знаете, что в документах вы можете создавать якоря. Что такое якорь? На 27 строке мы создали ссылку с именем anchor, а ниже на строке 19 мы создали еще одну ссылку, которая в href использует адрес #anchor. То есть если мы кликнем по этой ссылке, go to anchor. То мы поднимемся выше и спозиционируем браузер на 27 строке. Вот как это у нас выглядит. Вот наша страница. В странице много переносов сделаны. Если мы кликаем по этой ссылке go to anchor, мы возвращаемся в начало страницы, там где был создан якорь. Я думаю вы не раз встречали такие якоря, например, на странице Википедия. Когда вы видите содержимое статьи, кликаете по отдельным пунктам содержимого, вы перенаправляетесь на текущий же документ, просто куда-то ниже, ближе к концу документа. Вот получается мы можем определить якоря, и также мы можем в коде этими якорями пользоваться с помощью свойства anchors мы можем получить доступ ко всем якорям, которые есть в текущем документе. Мы сейчас обращаемся к нулевому якорю и отображаем свойство name этого якоря, то есть выводим имя того якоря, который был создан в документе. В нашем случае вот как этот якорь называется. Он по сути так и называется. На 125 строке мы обращаемся к свойству forms и получаем коллекцию всех элементов форм текущего документа. В нашем документе сейчас форм нету, по этому браузер возвращает пустую коллекцию. Видите, объект HTMLCollection. Но в этой коллекции данные отсутствуют. С помощью document.images мы тоже получаем коллекцию, в которой находятся все картинки. Так как в документе сейчас нету картинок здесь тоже пусто. И последнее свойство – это свойство на 131 строчке, свойство links, которое возвращает все ссылки. В нашем случае здесь сейчас находится две ссылки. На 119 у нас есть ссылка, и выше в начале документа тоже определена ссылка. Вот получается с помощью этого свойство мы знаем какие ссылки есть у нас на текущей странице. Это не все естественно свойства. Мы не будем сейчас разбирать все, что есть в объекте document? Это просто такие самые основные свойства, так, чтобы вы просто имели представление, что документ – это объект, в котором с помощью свойств можно получить доступ к содержимому текущей страницы. Использовать эти свойства, для того, чтобы получать доступ к элементам документа, к HTML элементам не очень удобно, т.к. мы достаточно ограничены в поиске и должны опираться на порядковый номер элемента в документе. Например, если нам нужно взять первую ссылку на странице, нужно взять links по нулевому индексу. Там, если мы хотим взять ссылку, которая находится в конце документа, нам нужно посчитать количество ссылок, которое нас интересует и выбрать ту, которая необходима, выбрать именно по индексу. Соответственно, если мы поменяем содержимое документа, поменяем количество ссылок, то нам нужно будет пересчитывать индексы или переписывать свой код. По этому, когда мы строим сценарии, то в случае, если нам необходимо получать доступ к документу. Если нам необходимо, например найти какой-нибудь div, повесить на div обработчик, или поменять его стили, то мы используем несколько методов объекта document, позволяющих производить поиск по Id, по имени тега, по атрибуту name, по различным параметрам, по различным критериям. В следующих примерах мы разберем какие именно методы позволяют получать элементы DOM, получать те элементы, которые находятся в дерево объектов браузера.
Первый пример, который мы рассмотрим – это пример использования я думаю известного уже многим метода getElementById(). В этом примере на строке 7 и 8 создаются два элемента. Первый элемент – это параграф с id = output. И второй элемент – это ссылка с адресом страницы. Мы хотим сейчас сделать так, чтобы содержимое этих элементов изменилось у нас. То есть сам контент. После того, как мы определили эти элементы на 10 строке создается элемента script. Очень важно, чтобы этот script находился после определения этих Id-шников. И теперь, когда мы определяем логику этого скрипта. 13 строчка – мы создаем переменную параграф. И в эту переменную записываем document.getElementById(). То есть получить элемент по Id-шнику. Мы говорим, что хотим получить ElementID с output, и вот при вызове этого кода, браузер находит в своем дереве элемент id=’output’ и вот ссылку на этот элемент записывает в переменную paragraph. И теперь если мы будем в коде взаимодействовать с переменной paragraph, на самом деле мы будем работать с этим объектом в документе, менять сам этот параграф. Точно таким же способом мы получаем ссылку на гиперлинк. Берем document.getElementById(), указываем link, и браузер дает нам ссылку на этот документ. И далее строка 17 и 18. Используя переменные, настроенные выше, обращается к свойству innerHTML. Это свойство, дающее нам доступ к вот этому тексту. Тексту, который находится между открывающим и закрывающим элементами. И вот получается, что и для параграфа, и для innerHTML в гиперлинке мы задаем тот же самый текст, который здесь был, только в верхнем регистре и з знаками восклицания в конце. Давайте проверим, как будет работать данный пример. Видите, как мы изначально определили код, и что произошло с кодом. Запустили страницу. JavaScript код видоизменил это вот содержимое. Но обратите внимание, что произойдет, если элемент script, в котором мы обращаемся к id-шниками, перенесем выше. Видите, что ничего не произошло. У нас как в нижнем регистре были созданы элементы, так они и остались при выводе. Почему так произошло? Потому что браузер интерпретировал данный документ с верху в низ. И на строке 10 браузер начал искать в текущем документе, в той части документа, которую браузер уже знает, начал искать элемент с id-шником, но вы видите, что выше этой строки нигде у нас нет элемента output, браузер пока не знает, что такой элемент вообще существует. Браузер узнает о нем только на строке 18 задолго после того, как выполнит 10 строку. Поэтому эта строка приводит к ошибку. Мы не находим элемент и браузер прекращает выполнять дальнейший JavaScript код. Просто создаем элементы и отображаем эти элементы в документе. Поэтому, когда вы ищете элементы по идентификатору, по атрибуту, вы должны быть уверены, что браузер уже этот элемент скачал с сервера и что он существует. Если вы пишете JavaScript код до того, как элемент будет определен, то естественно браузер еще никаким образом не может узнать о существовании id-шника. По этому один из самых простых способов, как мы можем производить поиск элементов по id – это определение сценариев в конце документа. В конце документа браузер уже будет знать о существовании все элементов. Если же вы хотите сделать так, чтобы сценарий находился в элементе head, как и в большинстве наши примеров предыдущих, то вы должны использовать специальное событие, которое будет сигнализировать о том, что браузер полностью закачал HTML код с сервера, закачал все картинки и знает о существовании всего содержимого документа. Если событие, которое называется window.onLoad. Это событие указывает на то, что документ полностью готов к работе. Вот сейчас как мы изменили четвертый пример. Этот пример по сути работает так же как и третий. Смотрите, если мы его запускаем, то видим, что текст в документе вывелся в верхнем регистре, хотя изначально он был записан в нижнем регистре. Когда браузер будет анализировать эту разметку, на строке 10 браузер столкнется с этой конструкцией: window.onLoad. window – это глобальный объект. onLoad – это событие, которое браузер запускает в тот момент, когда полностью проанализирует HTML код, когда загрузит с сервера все картинки, все сценарии, все css файлы. То есть когда вся страница будет загружена и готова к работе, браузер запустит событие onLoad. И на 10 строке, когда мы будем выполнять эту строчку – браузер не запустит функцию, потому что мы указываем здесь, что на событие onLoad в качестве обработчика мы хотим установить функцию, вот эту анонимную функцию. То есть когда событие onLoad произойдет, эта функция должна выполнится. По сути браузер вот что сделал. Он увидел, что на onLoad есть функция, но еще не выполнял тот код, который в этой функции. Браузер к этому коду вернется только тогда, когда дойдет до последней строчки HTML кода и проанализирует весь документ полностью. Потом когда браузер скачает все ссылки, все картинки и все сценарии с css файлами, вот только после этого, когда он все докачает, запустится событие onLoad, и в этом событии мы начнем спокойно уже искать элементы с id output и id link. Мы можем быть уверены, что на событие onLoad эти элементы будут у нас в документе. Ну естественное если мы не допустим ошибки при именовании id здесь, либо в самой разметке. Поэтому у вас есть два варианта, как вы можете выполнить поиск по именам или по идентификаторам: либо писать код в конце JavaScript документа, либо делать так, чтобы код запускался на событие onLoad.
Кроме поиска по идентификаторам мы можем производить поиск еще по именам, по атрибуту name, и поиск по именам тегов. В пятом примере, в текущем документе размещено несколько input. Все эти input используют type radio – это будут radio button. И для всех input используется один и тот же атрибут name. Я думаю, вы знаете, что radio button в HTML группируются по атрибуту name. Что означает группируются? Если мы запускаем пример, то radio button, находящиеся в одной группе, только один из них может находится в выбранном состоянии. Вот если мы поставим здесь разные имена. Answer1 и answer2, то в таком случае у нас каждый radio button может быть выбран. Соответственно это совсем неправильное поведение radio button и мы должны использовать одинаковые имена, чтобы radio button работали правильно. Как мы можем получить доступ сразу ко всем этим radio button? Давать каждому radio button уникальный id будет не удобно. Код для работы с такими radio buttons будет слишком громоздкий. Поэтому есть очень простой способ получить сразу все radio buttons по атрибуту name. Для этого что мы делаем? На строке 7 мы указываем, что когда загрузится окно, мы хотим выполнить функцию. Когда браузер загрузит весь этот код, браузер сделает вот такую операцию, выполнит эту строчку кода. Браузер найдет по id answerButton – элемент в текущем документе. answerButton – это вот эта кнопка на 31 строке. Браузер ее нашел и далее браузер на событие onClick добавляет функцию обработчик. Вот эта функция должна будет запустится браузером в тот момент, когда пользователь кликнет по кнопке. Если пользователь нажимает по кнопке, запускается уже содержимое этой функции. Функция на 13 строчке производит поиск по атрибуту name. Document.getElementsByName(). Вот этот метод возвращает нам все элементы, у который атрибут name равен answer. Обратите внимание на имя “getElementsByName”. Буква s указывает на то, что этот метод возвращает массив элементов, а не один элемент. По этому, если в предыдущем примере мы создавали просто переменную, в которую записывался объект, то в этом примере radioButtons – это по сути массив, в который будут записаны отдельные объекты. И далее мы запускаем цикл на 16 строчке, перебираем циклом. Цикл у нас запускается на количество элементов в массиве. И на каждой итерации мы в переменную res? В пустую переменную, записываем radioButton[i].value, то есть записываем атрибут, который мы нашли, потом пробел и radioButton[i].checked. Указываем в каком состоянии находится отдельный input, выбрал его пользователь или нет. И потом по завершению этого цикла мы обрабатываем все элементы с атрибутом answer и выводим общие сведения в переменную res и отображаем ее alert(). Давайте проверим, что получится. Выбираем, да? Нажимаем «Получить ответ» и получаем вот результат, что Да в состоянии true, а Нет и Не знаю в состоянии false. Выбираем другой элемент, нажимаем «Получить ответ». Вот теперь Не знаю в состоянии true, а остальные элементы в состоянии false. Вот еще один из методов, который позволяет получить доступ ко всем элементам с одинаковым атрибутом name.
И последний стандартный метод, который может нам пригодится – это метод, позволяющий получить элементы по имени тега. В текущем документе у нас есть несколько параграфов. Первый параграф с помощью inline стилей получает настройку “color:green”. То есть параграф рисует текст зеленым цветом. Остальные параграфы отображаются черным цветом. Но на самом деле у нас произведутся изменения, дополнительные изменения, потому что кроме этих изменений мы добавим еще стили с помощью JavaScript кода. На 7 строке устанавливается обработчик на загрузку документа и когда документ будет полностью загружен, то строка 9 – создается переменная paragraphs, и в эту переменную записывается вызов метода document.getElementsByTagName(). То есть получить элементы по имени тега. Мы хотим получить все элементы с именем p. То есть все параграфы, которые есть в текущем документе. Так как параграфов много, мы получаем не один элемент, а массив. Даже если параграф в текущем документе будет один, всегда getElementsByTagName() возвращает массив. Если параграф на странице один, то в массиве будет находится один элемент параграфа. И когда мы получили этот параграф, на 11 строчке мы запускаем цикл на количество элементов в параграфе, а на строке 13 обращаемся к параграфу по индексу, обращаемся к свойству style, а свойство style – это объект, который хранит в себе все css стили, мы обращаемся к свойству color. Указываем, что все параграфы на странице должны быть красными. Вот как только страница загружается, выполняется наш код, который проходится по параграфам и меняет ихний цвет на красный. Вот меняет цвет на красный. Видите как запустилась страница. Изначально первый элемент был зеленый, теперь все элементы стали красными. Если мы закомментируем строку №13 и мы не будем ничего делать с параграфами, то вот что произойдет при запуске. Первый элемент зеленый, все остальные черные, потому что мы не устанавливали для них оформление. Запомните эти методы. С их помощью мы будем получать доступ к элементам на странице во всех следующих уроках. Если вы работали с HTML5 или с библиотекой jQuerry, то я думаю, что вы знаете дополнительные техники получения доступа к элементам странице с помощью css селекторов. Но в этом курсе мы не рассматриваем эти подходы. Теперь, когда мы знаем как получить доступ к отдельному элементу в документе, мы рассмотрим примеры работы с узлами в DOM дереве. С помощью getElementById() getElemensByTagName() мы просто получаем по имени, или по значению атрибута элементы в текущей разметке. Но с помощью функций, которые мы сейчас рассмотрим, мы можем делать более сложные манипуляции. Мы можем находить все дочерние элементы относительно выбранного. Можем переходить от одного элемента к другому, который находится на одном уровне. Можем находить родительские элементы, можем производить различные манипуляции и даже на лету производить новые элементы, которые будут появляться в документе. Значит сейчас мы разберем с вами примеры работы с DOM. Что такое DOM мы уже когда-то слышали. DOM – это Document Object Model. Это дерево, которое строит браузер на основе разметки, скачанной со стороны сервера. Каждый тег, который есть в HTML коде он превращается в соответствующий узел дерева. И с помощью JavaScript кода мы можем находить узлы, удалять их, менять их, добавлять какие-то отдельные свойства этим узлам. То есть по сути мы можем манипулировать содержимым всего документа с помощью JavaScript кода. В начале давайте рассмотрим HTML код текущего документа. Вот в нашем документе есть очень простой кусочек разметки. На 36 строке создается ul с id=”list” и в этом ul находится несколько list item. Немножко позже нам будет понятно почему эти list items выведены в одну строчку. Почему они отформатированы таким способом, а не привычным нам, когда каждый элемент находится на новой строке. Вот мы сейчас хотим написать JavaScript код, который найдет элемент с id = «list» и сделает так, что все дочерние элементы, которые находятся в list они буду перекрашены в красный цвет. Для того, чтобы понять что именно браузер делает с HTML разметкой в начале давайте посмотри на дерево, которое формируется браузером. Вот я привык пользоваться вот таким плагином в Firefox как DOM Inspector. Я думаю, что для хрома, для других браузеров вы можете найти эквиваленты. Ну вот для Firefox вы можете скачать бесплатное расширение. Получить доступ к нему вы можете через этот пункт меню. DOM Inspector представляет из себя инструмент, показывающий дерево, которое браузер сформировал, показывающий изменения в этом дереве по мере выполнения JavaScript кода. Так же DOM Inspector может показать отдельные свойства и атрибуты узлов дерева. Вот если мы, например, кликаем, слева мы видим дерево, если мы кликаем по элементу дерева, то мы увидим настройки этого элемента, его состояние. Вот давайте сейчас посмотри как выглядит HTML код и как сгенерировалось дерево. Вот посмотрите – корневой элемент, в котором находится все дерево – это document. То есть все что браузер скачал, вся HTML разметка она превращается в элементы, в узлы, которые заложены в один корневой узел, в document. Вот если мы раскрываем document, мы видим, что как и в Visual Studio, видите, все элементы, которые будут находится в нашем документе заложены внутрь элемента html. Вот нам браузер и рисует, что у нас есть один вложенный элемент, элемент HTML. В этом элементе есть два дочерни элемента – это head и body. Вот наш head и вот наш body – два дочерних элемента. Но так же инспектор показывает нам, что у нас есть еще и текст между head и body. Что это за текст? То есть это полноценный узел дерева. Этот узел вот здесь находится. После head у нас есть перенос на новую строку. Вот этот перенос вот он у нас и отображается. Если посмотреть в head – то в head у нас есть text, title, text, script, text. Вот сколько дочерних узлов. Хотя, если посмотреть в код, то узлов у нас поменьше. Мы вроде бы создали только title и script, но вы должны помнить, что вот здесь есть перенос, вот здесь есть перенос на новую строку, и здесь перенос на новую строку. Поэтому и получается, что в инспекторе у нас отображается вот столько узлов. Несколько текстовых узлов и title со script. В title у нас находится текст. Это действительно так. И в script у нас находится тоже текст, потому что JavaScript код тоже является обычным текстом. Если открыть body, то в body у нас есть элемент ul и внутри этого элемента находится много list items. Обратите внимание, что между list items нету переносов, поэтому у нас в браузере, в DOM Inspector точнее отображается ul с текстом перед и после, потому что есть переносы на 35 строчке и на строчке 36. Но если посмотреть в сам ul, то у нас там идут чистые list items без лишни текстовых узлов. Но уже в каждом list item находится по тексту, потому что фраза item1, item2, item3 – это все текстовое содержимое. Когда мы запустили пример, вот как мы увидели страницу. Все элементы были перекрашены в красный цвет, хотя нигде здесь стилевого оформления нету. Почему у нас сейчас все элементы стали красными? Потому что в сценарии у нас выполняется вот такая вот операция. На 25 строке при загрузке документа мы на 27 строчке создаем переменную е, записываем туда элемент с id list, то есть весь этот ul, а потом на строке 29 мы указываем, что на объекте e мы берем свойство childNodes, то есть дочерние узлы. Дочерние узлы для ul – это вот все вот эти вот list items, которые здесь у нас перечислены. Всем этим list items мы хотим задать красный цвет. На строке 30 мы берем e.childNodes[i], то есть берем первый list item и в свойство style color записываем значение red. И получаем первый узел красный, потом второй красный, третий красный, каждый узел меняет свой цвет на красный. Когда мы анализируем DOM дерево, то на самом деле типов-узлов может быть очень много. Вот мы сейчас с вами в DOM инспекторе увидели с вами по сути три типа-узла. Мы увидели document – это корневой узел. Мы увидели элементы – это узлы, которые являются HTML тегами, и мы увидели узлы типа текст. То есть это просто содержимое текстового типа. Это переносы на новую строчку, это JavaScript код, это текст, который выводится в элементах. Вот если мы хотим в коде узнать на какой узел мы наткнулись, что из себя представляет узел, мы можем обратится к свойству noteType. Вот например мы можем взять и на каждой итерации цикла мы alert отображаем childNodes[i].nodeType и посмотреть на то, какой тип узла мы сейчас используем. Мы сохранили страницу и здесь ее обновили. Видите? На первой итерации первый узел – это 1, видите он перекрасился в красный цвет. На следующей итерации второй узел перекрасился – это тип 1, и третий узел – тип 1. Мы увидели, что все узлы childNodes – они были типа 1. Что такое тип 1? У нас здесь есть маленькая табличка. 1 – это element, то есть мы находились с вами в обычном элементе разметки. Если бы мы увидели значение 3 – это означает, что узел, который мы достали – это узел текстового содержимого. Если бы мы увидели значение 8 – это комментарий, значение 9 – это корневой элемент document/ Если вы посмотрите спецификацию, то увидите, что возможных типов узлов 12 штук. Но мы сейчас не будем разбирать все эти узлы. Чаще всего используются те, которые вы видите сейчас на экране. Вот именно с этими узлами мы будем работать в следующих примерах. Во втором примере мы немного переписали код на более реалистичный. Обычно, когда мы создаем разметку, то форматирование, допустим, тех же списков должно выглядеть вот так вот. Каждый list item будет находится на новой строке. И получается, что в таком случае у нас вот на 27 строке, на 28 в конце, на 29, 30 здесь есть переносы на новую строчку, то есть в DOM древе здесь находятся текстовые узлы и когда мы работаем с документом нам эти текстовые узлы ни к чему. Мы по сути должны их отбрасывать и никаким образом не обрабатывать, не изменять. Вот как мы можем написать код, который будет менять только текстовые элементы. То есть не текстовые, а Элементы разметки, а не текстовые узлы. На строке 12 мы запускаем цикл, который будет выполнять количество итераций, совпадающее с количеством дочерних элементов. В нашем случае – это будет три итерации. На каждой итерации создается переменная temp. В эту переменную записывается узел, и-ый узел из дочерних узлов и далее проверяется условие: если тип узла не 1. Если мы наткнулись на текст, на комментарий, на еще какой-нибудь узел, то в таком случае мы делаем continue и просто переходим на следующий узел. Пропускаем текстовый узел берем следующий. Если этот узел является элементом, то мы на 20 строчке берем его и меняем его стиль. То есть мы сейчас красный цвет будем добавлять только к узлам с типом 1. Все остальные узлы мы будем игнорировать. И вот если этот пример мы запустим, он тоже у нас работает. Но теперь в JavaScript коде мы игнорируем все элементы и узлы, которые не являются элементами.
В следующем примере мы посмотрим как можно получить с помощью свойство доступ к родительскому элементу. Сейчас в разметке находится два тега. Это div и вложенный в этот div параграф. Получается, что для этого параграфа родительским элементом является элемент div. И вот ка мы можем с помощью свойств получить от этого элемента доступ к его родителю. При загрузке документа находим параграф по id = p1, а далее на 11 строчке с помощью свойства parentNode.style.border устанавливаем рамку, в которую вложен параграф. Получается, что рамка в один пиксел сплошная и красного цвета будет установлена для этого div. Запустим. Вот мы видим, что параграф, который мы нашли, мы поднялись выше к родительскому элементу и его сделали с красной рамочкой. Вот еще одно свойство, которое помогает производить навигацию по дереву. Подниматься к родительскому элементу. До этого мы посмотрели childNodes – свойство дающее возможность получить все дочерние элементы первого уровня. А вот свойство для получения родительского элемента.
В следующем примере показаны свойства, позволяющие получить первый дочерний элемент и последний дочерний элемент. На 20 строке ul. В нем вложены list items. Вот на строке 9мы нашли ul по id, далее нашли первый дочерний элемент, сделали его красным, а последний дочерний элемент сделали зеленым. Получается, что этот текст у нас будет красным, этот текст будет зеленым. Вот как выполнился наш пример. Но если мы сделаем переносы вот здесь, то смотрите что получится. Все элементы у нас остались стандартными черными, потому что по сути красный цвет мы попытались добавить к переносу, который у нас находится перед list item, а зеленый цвет к переносу, который находится после list item. Потому что firstChild – это просто первый узел. Не важно что это за узел. Текст, комментарий или элемент. Поэтому вот нам нужно помнить об этом и если мы пользуемся firstChild, если мы хотим найти именно элемент, нужно будет убедится в том, что первый дочерний узел является элементом. А в нашем случае сейчас первый дочерний узел- это перенос на новую строчку.
Следующее свойство, которое позволяет производить навигацию по дереву – это nextSibling и previousSibling. Sibling – это сестринский элемент. То есть это элемент, который находится в дереве на одном уровне. Элементы имеющие одного и того же родителя называются у нас Sibling, например, вот эти все ul друг для друга Sibling, потому что они находятся в одном и том же ul. Значит, для того, чтобы получить доступ к следующему элементу на одном уровне мы должны использовать свойство nextSibling. Сейчас мы на 9 строке нашли все list item по TagName, а потом вытащили второй list item. То есть вот это нулевой list item, это первый, а вот этот второй list item. Мы его нашли. Ссылку на него поместили в переменную i, а потом на 12 строке сказали, что туче Sibling.style.color = “red”. Сказали, что следующий узел, следующий узел одного уровня – это item 4, вот item 4 должен быть красным, а previousSibling.style.color = “green”. Мы сказали, что предыдущий узел должен быть зеленого цвета. Давайте проверим, сработало ли это у нас. И вот предыдущий узел стал зеленым цветом, следующий стал красным. Вот как с помощью этого свойства мы можем прыгать по элементам, по узлам одного уровня.
В примере номер 6 мы увидим, как можно динамически создавать элементы в DOM, и как мы можем добавлять в DOM. Значит сейчас на странице находится только один элемент div с id div1. Для этого элемента мы добавили рамку, для того, чтобы было его лучше видно в документе. Далее мы находим элемент с помощью getElementById, помещаем этот элемент в переменную div1. Это обязательно делаем после того, как страница будет загружена. И вот на 15 строке мы впервые с вами сталкиваемся с методом createElement. C помощью этого методы мы можем создать любой новый элемент разметки. То сеть создать новый любой тег. Мы вызывая createElement в параметр бросаем p. По сути это означает, что мы создаем элемент параграф. Этот новый параграф записан в переменную p1. Пока этот параграф нигде не будет отображаться, потому что мы не сказали в какую именно часть документа этот параграф мы помещаем. Далее для параграфа мы задаем свойство innerHTML, то есть указываем тот текст, который будет находится между открывающим и закрывающим тегом p. И последнее, что мы обязательно должны сделать – это указать куда именно параграф будет добавлен в документ. Мы берем переменную div1, берем ссылку на div, который лежит на странице и с помощью метода appendChild() указываем, что этот созданный и настроенный параграф должен превратится в дочерний элемент div. И получается, что этот участок кода он является эквивалентом написания вот такой разметки. Но эту разметку мы создаем не вручную, а создаем с помощью JavaScript кода. Вот этот блок кода, 19 строчка, когда мы создаем новый параграф, задаем ему текст и выполним вот эту операцию, этот участок кода является эквивалентом написания вот такой строчки. Почему параграф помещается у нас перед div? Потому что мы обращаемся к документу, берем body документа, то есть берем вот этот элемент, узел тело документа, и указываем, что хотим выполнить вставку элемента p2 перед элементом div1. То есть insert before – это вставить перед каким-то элементом. Первый параметр – это тот элемент, который мы добавляем в документ, а вторым параметром мы указываем перед чем мы его добавляем. Вот мы его добавляем на 27 строку перед div1. Вот, но на самом деле этот код мы сейчас удалим, эти параграфы у нас создаются динамически. Давайте проверим. Вот первый параграф в div поместился, а второй параграф создался перед div. Запомните метод createElement(), с его помощью вы можете динамически создать любой элемент разметки.
В 7 примере мы рассмотрим методы, которые показывают, как мы можем с помощью JavaScript кода взаимодействовать с атрибутами элементов. В документе размещены три ссылки. Каждая ссылка использует свой собственный elementId. У нас есть а1, а2, а3. Первая ссылка не использует других атрибутов, то есть эта ссылка не завершенная до конца, если по ней кликнуть, то никуда мы перейти не сможем, а вторая и третья ссылка использует атрибут href. После того, как документ загружается, с помощью JavaScript кода мы получаем все ссылки по идентификаторам и дальше с каждой ссылкой выполняем операцию связанную с атрибутом href. В начале мы устанавливаем для первой ссылки атрибут href. Для того, чтобы в JavaScript коде изменить атрибут мы вызываем в коде метод setAttribute() и передаем два параметра. Первый параметр указывает именно на то какой атрибут мы добавляем, а второй параметр определяет значение этого атрибута. Получается после выполнения 12 строки у элемента а, который создался на 23 строке, у нас появляется атрибут href со значением http://edu.cbsystematics.com. C помощью метода removeAttribute() мы можем удалить любой атрибут, который находится в элементе. Вот на строке с помощью removeAttribute мы удаляем вот данный атрибут и его значение. Вторая ссылка перестает работать, так как не знает на какую страницу она ведет. Ну и если нам нужно получить информацию о значении атрибутов мы можем вызвать getAttribute() передать в качестве параметра имя интересующего нас атрибута и в результате получить либо undefined, если атрибута нету, либо значение, которые находятся в атрибуте. В атрибуте а3 у нас есть данные, поэтому эта строчка она выведет адрес, на который ведет третья ссылка. Вот мы видим, что вывелось сообщение на строке 18. Мы видим, что первая ссылка начала работать, хотя изначально она не могла работать, потому что атрибут href не установлен. Но после загрузки на 12 строке код добавил атрибут href и код теперь рабочий, а вторая ссылка перестала работать, так как с помощью кода на 15 строке мы удалили атрибут href. Теперь браузер не знает куда эта ссылка направляется.
В восьмом примере мы увидим пример тех свойств и методов, которые были рассмотрены в предыдущих 7 файлах. Сначала давайте запустим и рассмотрим как работает этот пример. У нас на странице отображается список и есть несколько кнопок, которые манипулируют элементами этого списка. Первая кнопка – выбрать первый элемент. Кликнув по этой кнопке первый элемент становится красного цвета. Вы брать последний элемент – эта кнопка помечает последний элемент списка. Выбрать следующий элемент – видите все элементы стают черными и первый выделяется зеленым. Если мы еще раз нажимаем ее, то второй элемент становится зеленым. И так далее мы циклически перепрыгиваем через элементы. Выбрать предыдущий элемент – это функция, которая выполняет обратную операцию. То есть берет и переходит на предыдущий элемент, который был в списке. Добавить элемент – с помощью этой кнопки мы добавляем в конец элементы в конец списка. Удалить элемент – мы удаляем эти добавленные элементы. И добавить в начало – с помощью этой кнопки мы расширяем список добавляя элементы в начало списка. Вот мы сейчас посмотрим как можно будет динамически вот таким образом манипулировать элементами списка. В начале давайте посмотрим на разметку. Для того, чтобы проще было манипулировать элементами, элемент ul мы поместили в документ с таким форматированием: все list items находятся на одной строке, но естественно, что мы можем сделать, что каждый list item может быть на отдельной строке, просто в таком случае придется немного видоизменить код JavaScript сценария, находящегося выше, и сделать так, чтобы у нас производились дополнительные проверки типов узлов. Ну а ниже под этим ul у нас находится набор input. Для того, чтобы проще был этот пример у нас все обработчики установлены вот таким вот способом, с помощью непосредственно вызова JavaScript кода в атрибутах onClick. Мы уже с вами ранее обсуждали, что это не самая хорошая практика. Но для простоты примера мы оставим сейчас именно такую реализацию обработчиков. Давайте посмотрим как работает первый обработчик selectFirstChild, то есть выбрать первый дочерний элемент. Задача этого обработчика – найти элемент с id list и выдать первый дочерний элемент. Либо этот list item, либо первый элемент, который был сгенерирован кодом. Давайте поднимемся в начало файла и вот функция-обработчик – selectFirstChild(). Функция в начале получает объект ul с помощью getElementByID(), далее на следующей строке мы получаем firstChildElement, который доступен через свойство firstChild. Так как у нас может не быть первого дочернего элемента, на 10 строке мы проверяем была ли получена ссылка на дочерний элемент. Если нажимая на кнопку delete мы удалили все дочерние элементы, которые есть в ul. В таком случае обращение к этому свойству даст нам результат null. И вот если у нас результат не null, то тогда на 12 строке мы обращаемся к элементу child, который нашли, с помощью метода setAttribute() устанавливаем атрибут style с параметром color:red. По сути вот строчка 12 она программным образом делает то, что мы могли бы сделать вот таким вот способом: обратится к элементу, установить style=”color:red”. То есть то, что мы сделали в JavaScript коде эквивалентно вот такой записи в HTML коде. Но это тоже не самый подходящий вариант использования оформления элементов. Лучше использовать не атрибут style, а использовать то, что мы с вами видели в предыдущих примерах. Обратится к элементу child, мы можем взять свойство style и с помощью свойства color установить значение red. Просто для того, чтобы показать как мы можем работать с атрибутами мы именно используем код на 12 строчке. Но более адекватным решением для заданием стилей будет именно 12 строчка, которую мы сейчас написали. Мы ее пока уберем. Вот значит как работает код, который выбирает первый дочерний элемент. Если мы нажимаем на кнопку Выбрать последний дочерний элемент, то обработчик этой кнопки – это selectLastChild(). Вот как работает эта функция: selectLastChild() по сути делает все то же самое, единственное, что отличается – это на 19 строке идет обращение к свойству lastChild. Здесь был firstChild, здесь lastChild. Так как мы можем удалить все вложенные элементы списка, то lastChild может нам вернуть значение null. По этому мы на всякий случай проверяем на null переменную child, а потом уже меняем стиль. Следующая кнопка, которая есть в интерфейсе – Select Next Node. Эта кнопка у нас выбирает первый элемент, потом следующий элемент, следующий и т.д. циклически. То есть получается с помощью нажатия по этой кнопке мы можем перепрыгивать через дочерние узлы этого списка. Давайте смотреть как эта функция у нас реализована. Значит у нас есть selectNextNode() функция, которая в начале выполняет операцию resetColor(), то есть сбросить цвет. resetColor – это функция, которая определена у нас ниже на 57 строке, и в этой функции мы выполняем вот такую вот операцию. Мы на 58 строке находим все элементы по имени тега li, запускаем цикл, чтобы перебрать все эти элементы и каждому элементу устанавливаем атрибут color:black. То есть какого бы цвета не был элемент, мы его перекрашиваем в черный. Но потом, когда мы все эти элементы перекрасили в черный, мы уже запускаем непосредственно логику этого метода selectNextNode(), если переменная node == null. Переменная node вот она создана перед самой функцией. Вот если переменная пустая, то тогда мы находим список, находим первый дочерний элемент, перекрашиваем его в зеленый цвет и выполняем операцию return. То есть это значит, что по выполнению этого кода функция selectNextNode() она прекращает работу. Но когда мы еще раз нажимаем на кнопку показать следующий узел, строка 28, мы сбрасываем все цвета list items. В это условие мы уже не попадаем, потому что предыдущий раз, когда мы запускали эту функцию на 31 строке в переменную node был записан объект, по этому переменная node уже не null, мы переходим на 36 строку и указываем, что в переменную node мы записываем следующий дочерний узел, который находится на одном уровне, то есть следующий list item. Проверяем, смогли ли мы получить list item, потому что если мы дойдем до последнего list item в списке, то у него уже нету следующих сестринских узлов, и на 37 строке мы проверяем, если у нас нету следующего узла, то есть мы проверяем, что если мы не получили null, тогда мы устанавливаем значение. Если же мы получили null, то при следующем нажатии на кнопку, при следующем вызове функции selectNode, мы попадем в условие, потому что в переменной node будет null, мы обратно начнем заходить в данное условие. Получается, что с помощью вот данного блока кода, с помощью свойства nextSibling мы переходим от одного list item к другому list item. Дальше у нас есть функция selectPrevNode(). Эта функция работает точно так же как и предыдущая, здесь код можно было кстати оптимизировать, чтобы не было настолько ярко выражено дублирование, потому что вы видите, что код у нас практически одинаковый, отличается тут только firstChild а тут lastChild. Тут nextSibling, тут previousSibling(). По этому эти функции можно было бы как-нибудь отрефакторить и сделать так, чтобы они не так дублировались. Но суть здесь в том, что мы берем предыдущий list item из списка и получается, что при нажатиях по кнопке Выбрать предыдущий элемент мы двигаемся в обратную сторону. Зеленым становится предыдущий list item. Далее при нажатии на кнопку createNewChild, у нас при нажатии на кнопку Добавить новый элемент запускается функция createNewChild() и эта функция выполняет вот такую простую операцию. С помощью getElementById мы находим ul далее с помощью document.createElement создаем list item во вложенный текст list item записываем значение New Item, а потом на 70 строчке берем ul, который ранее получили указываем, что мы добавляем дочерний элемент для ul, и указываем, что дочерним элементом будет только что созданный list item. То есть вот создали item, указали в какую часть разметки этот item должен быть помещен. Если мы хотим удалять элементы, кстати мы не разбирали эту функцию в предыдущих примерах, мы можем использовать метод removeChild(). На 74 строчке нашли ul, нашли последний элемент из ul, и далее проверяем, если этот элемент не равен null, потому что возможно мы уже все list item удалили и child уже просто не существует в ul. Если элемент был найдет и переменная не null, то тогда мы вызываем метод removeChild(), то есть мы говорим, что в этом list нужно удалить дочерний элемент и какой именно дочерний элемент мы хотим удалять. Удалять вот последний. И получается, что нажимая по кнопке Удалить элемент, вот сейчас нажав на эту кнопку мы удалим последний дочерний элемент. Исчез элемент. И вот так мы будем удалять элементы пока список не станет пустым. Дальше при нажатии на Удалить элемент ничего не происходит, потому что условие у же не срабатывает. Нажимая на кнопку мы пытаемся получить lastChild, значение, которое сюда возвращается – null, потому что child нету ну и соответственно условие не срабатывает. Ну и ниже у нас остается функция createChildAtStart(). Эта функция срабатывает, когда мы нажимаем кнопки Добавить в начало. Нажав по этой кнопке мы в начало списка добавляем элемент. Мы с вами уже видели метод insertBefore(). Если мы используем appendChild() то мы помещаем дочерний элемент в конец, insertBefore() позволяет нам добавить элемент перед каким-нибудь другим элементом. Вот мы сейчас в коде находим ссылку на ul, потом мы создаем новый list item через createElement() указываем для него текст, потом проверяем, существует ли у нас первый дочерний элемент в списке, потому что вдруг мы опять все удалили и у нас нет дочерних элементов. Если первый дочерний элемент существует, то 88 строчка, мы используем list, вызываем на list insertBefore(), говорим, что в list хотим вставить значение, и вставить это значение хотим перед firstChild, перед первым элементом списка и указываем какое именно значение мы вставляем. То есть созданный item помещается перед первым дочерним элементом ul списка. Ну и вот как этот код у нас работает. В начале мы добавим несколько элементов, хотя, лучше просто обновим. Вот у нас есть list items, если мы нажимаем Добавить в начало, в начале появляются list items. Вот такой пример, демонстрирующий работу разных свойств, разных методов, позволяющих динамически создавать элементы DOM, динамически менять разметку, динамически удалять, редактировать содержимое дерева и переходить от одного элемента к другому через свойства, такие как firstChild, lastChild, next previousSibling и т.д.
В последнем примере нашего урока мы рассмотрим как с помощью JavaScript кода сортировать элементы в DOM дереве. Этот пример вы можете найти в книге Дэвида Фленэгана «JavaScript: подробное руководство». В HTML разметке этого документа создается элемент ul с id mylist. В этом элементе находится несколько list items и мы хотим сделать так, чтобы при нажатии по кнопке элементы, которые находятся внутри этого списка, у нас были отсортированы по алфавиту. Для того, чтобы сделать сортировку на строке 46 запускаем функцию sortchildren и в качестве параметра в эту функцию передаем id того элемента, дочерние узлы которого мы собираемся сортировать. Давайте проверим как будет работать этот пример. Нажав изначально на one two three four, нажав на кнопку Sort List мы видим, что элементы стали отсортированы по алфавиту. Давайте посмотрим как реализована эта функция sortChildren(). Первое, что делает эта функция, это проверяет правильность типа данных, который в нее был передан. Если мы передаем typeOf == string, то функция считает, что мы передаем ей идентификатор и поэтому, если условие выполняется, то в переменную е записывается document.getElementById(), записывается ссылка на тот элемент, который по id был найден в разметке. Если же это не строка, а что-нибудь другое, то функция воспринимает переменную как объект, как непосредственно узел DOM дерева, который необходимо как-то использовать. На строке 19, после того, как мы проверили переменную е и получили ссылку на узел в дереве, если е была строкой, мы создаем массив дочерних элементов. Изначально этот массив пустой. Дочерних элементов нет. Далее на 20 строке мы запускаем цикл for используя такую очень интересную технику. Мы привыкли, что в цикле for должен быть счетчик целочисленный, который на каждой итерации увеличивается, и мы делаем проверку, чтобы счетчик не превысил какое либо значение. Но если вы помните урок по циклам из предыдущего курса, то вы помните, что цикл for он на самом деле содержит три компонента, которые не обязательно должны быть счетчиками, целочисленными значениями. Первый компонент – это указание какую переменную мы будем использовать в качестве счетчика. Второй компонент – это определение условия, которое скажет когда цикл должен завершить работу. А третий компонент – это операция, которую нам нужно сделать в конце каждой итерации. Мы указываем, что нашим счетчиком должна быть переменная х. Эта переменная изначально будет содержать в себе значение firstChild. В переменную будет записан первых дочерний элемент вот этого объекта. Далее мы будем цикл крутить до тех пор, пока переменная х не будет равна null. То есть когда мы дойдем до последнего дочернего узла в списке. У нас больше не будет firstChild, не будет следующих элементов, по этому мы прекратим работу, как только х будет null. Вот ну и для того, чтобы на каждой итерации можно было получать следующий дочерний узел, мы выполняем вот такое действие в конце каждой итерации. В переменную х записываем nextSibling. Получается, что на первой итерации мы создали переменную и записали в нее первых дочерний элемент, проверили, чтобы это элемент не был null. Выполнили итерацию цикла. Потом перешли к этой операции, взяли следующий узел на одном уровне с firstChild, записали этот узел в переменную х, выполнили проверку условия и перешли дальше в тело цикла. Выполнили тело. Вернулись к началу, выполнили итерацию и изменили значение переменной х, сделали проверку и вот эти два действия у нас будут делаться до тех пор, пока условие х != null будет удовлетворять истинности. Получим последний уже элемент, дальше у нас нету уже элементов в списке, то тогда цикл прекратит работу. Что же делает этот цикл? Как он выполняет свое тело. В цикле мы проверяем, если x.nodeType == 1. А вы помните по табличке, что это элемент, что мы наткнулись именно на элемент разметки. Если мы нашли элемент разметки, то в массив children с помощью метода push() мы этот элемент записываем. То есть мы расширяем массив и в конец массива бросаем этот элемент. И в итоге, когда мы закончим цикл на 20 строке в массиве children будут находится только list items. То есть мы возьмём только узлы с типом 1 и добавим их в массив children. Теперь остается только этот массив сортировать, сортировать по возрастанию, убыванию, так как нам необходимо. Для того чтобы массив сортировать мы вызываем функцию sort(), стандартную функцию массива. Но нам необходимо задать свою логику сортировки, мы хотим сортировать узлы дерева, и хотим сортировать их по контенту, который находится внутри отдельного узла. То есть не по каким-то свойствам узла, а именно по вот тексту внутри узла. Поэтому мы при вызове функции sort() передаем в качестве параметра в эту функцию другую свою функцию. Задача вот этой передаваемой функции – указывать на то как сравнивается два соседних элемента массива. Какой из этих элементов больше, а какой меньше. Когда функция sort() начнет работать, когда функция sort() начнет сортировать элементы массива children, при каждой операции сравнении двух элементов, функция sort() возьмёт нашу функцию и в качестве первого параметра передаст первый сравниваемый элемент из массива children, в качестве второго – второй элемент, который берется из массива children. То есть вот эти две переменные – это то что функция sort() сравнивает в массиве children. Эта функция будет неоднократно вызываться, пока весь массив не будет отсортирован, и в эту функцию постоянно будут передаваться узлы DOM дерева. Вот как мы будем производить проверку. В начале мы создали две переменные s и t. В s записываем n.firstChild.data. То есть мы получили значение первого узла, текст, который находится непосредственно в list items. Этот текст мы записываем в переменную s и t, и далее делаем проверку: если s меньше чем t – возвращаем -1. Если s больше чем t – возвращаем 1, во всех остальных случаях возвращаем 0. То есть вот мы проверяем какая фраза находится в начале алфавита, какая фраза находится в конце. Сортируем все тексты, которые находятся в узлах дерева, сортируем их по алфавиту. И получается, что по завершению такого кода все содержимое массива children будет отсортировано по алфавиту. Но сортировка происходит вот в памяти, в нашем JavaScript коде. Нам теперь нужно эти сортированные элементы вывести обратно в документ, чтобы они опять стали частью разметки, чтобы пользователь их начал видеть. Для того, чтобы произвести отображение измененной информации на строке 34 запускается цикл, на количество элементов массива children и на каждой итерации мы берем объект е – ссылку на ul и с помощью appendChild добавляем из массива children i-ый элемент. То есть мы берем из массива и внутрь ul помещаем list items. Но операция выглядит немножко странно, потому что вот эти вот элементы они и так уже находятся в массиве children, по сути, если так подумать, то 34 строчка должна была расширить ul и сделать так, чтобы в ul после нажатия по кнопке Sort появилось еще четыре элемента. К существующим четырем добавляем еще четыре. Но дело в том, что в DOM дереве не может находится два идентичных узла. Если мы добавляем узел, который уже существует в дереве, то со старого места этот узел удаляется и появляется в новом месте. И получается, что когда мы отсортировали массив, то самым первым у нас будет элемент с текстом four. И получается что, когда мы этот элемент с текстом four еще раз appendChild() добавляем в ul, этот элемент удаляется со своего старого месторасположения, и появляется в новом месте, то есть в начале списка. И так происходит с каждым элементом. Каждый раз, когда мы узел дерева помещаем опять же в это дерево, со старого мета он удаляется, в новом – появляется. По этому нас не приходится чистить содержимое ul, нам достаточно просто еще раз отобразить, добавить элемент в разметку. Он просто удалится со своего старого месторасположения. Вот такой пример, в котором мы увидели интересное использование вот этих функций, которые были разобраны в последней части этого урока. На этом мы заканчиваем урок, посвященный работе с документами. В этом уроке мы увидели много полезных возможностей языка JavaScript. В начале мы посмотрели как можно добавлять JavaScript код к документу, потом мы посмотрели как можно реализовывать пространство имен и избавляться от лишних глобальных переменных, и во второй части урока мы рассмотрели как можно работать с документом. Мы увидели, что есть несколько функций getElementById() getElementByTagName() и прочие, которые позволяют нам получать доступ к элементам документа. Эти функции обязательно хорошо изучите, научитесь ими пользоваться, потому что вы будете постоянно из использовать, для того чтобы взаимодействовать с разметкой документа. И также мы увидели как с помощью нескольких свойств и методов мы можем видоизменять то, что есть в текущем документе. Мы увидели как можно добавлять новые узлы в дерево, как можно удалять узы из дерева, как можно делать навигацию с помощью свойств nextChild, previousChild, nextSibling, previousSibling и прочих. Эти свойства запомните и это достаточно важная функциональность, которая используется в написании JavaScript сценариев. На этом урок мы заканчиваем. Не забывайте делать дополнительное задание, которое находится в описании к урокам, там вы найдете полезные ссылки, там найдете краткое описание того, что мы на уроке просматриваем, ну и задание, которое вы должны будете самостоятельно выполнить. Спасибо за внимание, до новый встреч.