Наша совместная команда Banwar.org

Связаться с нами

  • (097) ?601-88-87
    (067) ?493-44-27
    (096) ?830-00-01

Статьи

Document Object Model

  1. дерева
  2. стандарт
  3. Обхід дерева
  4. Пошук елементів
  5. міняємо документ
  6. створення вузлів
  7. атрибути
  8. Розташування елементів (layout)
  9. стилі
  10. каскадні таблиці стилів
  11. селектори запитів
  12. Розташування та анімація
  13. Використання синуса і косинуса для обчислення координат
  14. підсумок
  15. Вправи
  16. Елементи на ім'я тегів
  17. капелюх кота

Коли ви відкриваєте веб-сторінку в браузері, він отримує вихідний текст HTML і розбирає (парсит) його приблизно так, як наш парсер з голови 11 розбирав програму. Браузер будує модель структури документа і використовує її, щоб намалювати сторінку на екрані.

Це уявлення документа і є одна з іграшок, доступних в пісочниці JavaScript. Ви можете читати її і змінювати. Вона змінюється в реальному часі - як тільки ви її підправляти, сторінка на екрані оновлюється, відображаючи зміни.

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

Можна уявити HTML як набір вкладених коробок. Теги начебто <body> і </ body> включають в себе інші теги, які в свою чергу включають теги, або текст. Ось вам приклад документа з попередньої глави:

<Html> <head> <title> Моя домашня сторінка </ title> </ head> <body> <h1> Моя домашня сторінка </ h1> <p> Привіт, я марійних і це моя домашня сторінка. </ P> <p> А ще я книжку написав! Читайте її <a href = "http://eloquentjavascript.net"> тут </ a>. </ P> </ body> </ 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, "книг"));

Властивості текстового вузла nodeValue містить рядок тексту.

Пошук елементів

Часто буває корисним орієнтуватися по цих посиланнях між батьками, дітьми та родинними вузлами і проходити по всьому документу. Однак якщо нам потрібен конкретний вузол в документі, дуже незручно йти по ньому, починаючи з document.body і тупо перебираючи жорстко заданий в коді шлях. Поступаючи так, ми вносимо в програму допущення про точну структуру документа - а її ми пізніше можемо захотіти змінити. Інший ускладнює фактор - текстові вузли створюються навіть для пропусків між вузлами. У документі з прикладу у тега body не три дочірніх (h1 і два p), а цілих сім: ці три плюс прогалини до, після і між ними.

Так що якщо нам потрібен атрибут href із заслання, ми не повинні писати в програмі щось на кшталт: «друга дитина шосту дитину document.body». Краще б, якщо б ми могли сказати: «перша посилання в документі». І так можна зробити:

var link = document .body.getElementsByTagName ( "a") [0]; console .log (link.href);

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

Щоб знайти конкретний вузол, можна задати йому атрибут id і використовувати метод document.getElementById.

<P> Мій страус Гертруда: </ p> <p> <img id = "gertrude" src = "img / ostrich.png"> </ p> <script> var ostrich = document .getElementById ( "gertrude"); console .log (ostrich.src); </ Script>

Третій метод - getElementsByClassName, який, як і getElementsByTagName, шукає в вмісті вузла-елемента і повертає всі елементи, що містять в своєму класі задану рядок.

міняємо документ

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

<P> Один </ p> <p> Два </ p> <p> Три </ p> <script> var paragraphs = document .body.getElementsByTagName ( "p"); document .body.insertBefore (paragraphs [2], paragraphs [0]); </ Script>

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

Метод replaceChild використовується для заміни одного дочірнього вузла іншим. Він приймає два вузла: новий, і той, який треба замінити. Замінний вузол повинен бути дочірнім вузлом того елемента, чий метод ми викликаємо. Як replaceChild, так і insertBefore як перший аргумент очікують отримати новий вузол.

створення вузлів

У наступному прикладі нам треба зробити скрипт, який замінює всі картинки (тег <img>) в документі текстом, що містяться в їх атрибуті "alt", який задає альтернативне текстове представлення картинки.

Для цього треба не тільки видалити картинки, а й додати нові текстові вузли їм на заміну. Для цього ми використовуємо метод document.createTextNode.

<P> Це <img src = "img / cat.png" alt = "Кішка"> в <img src = "img / hat.png" alt = "чобітках">. </ P> <p> <button onclick = "replaceImages ()"> Замінити </ button> </ p> <script> 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); }}} </ Script>

Отримуючи рядок, 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, що створює вузол-елемент і використовує інші аргументи в якості його дітей. Ця функція потім використовується для додавання додаткової інформації до цитати.

<Blockquote id = "quote"> Ніяка книга не може бути закінчена. Під час роботи над нею ми дізнаємося досить для того, щоб знайти її незрілою відразу ж після того, як ми відволіклися від неї. </ Blockquote> <script> 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 ")); </ Script>

атрибути

До деяких елементів атрибутів, типу href у посилань, можна отримати доступ через однойменне властивість об'єкта. Це можливо для обмеженого числа часто використовуваних стандартних атрибутів.

Але HTML дозволяє призначати вузлам будь атрибути. Це корисно, тому що дозволяє вам зберігати додаткову інформацію в документі. Якщо ви придумаєте свої назви атрибутів, їх не буде серед властивостей вузла-елемента. Замість цього вам треба буде використовувати методи getAttribute і setAttribute для роботи з ними.

<P data-classified = "secret"> Код запуску 00000000. </ p> <p data-classified = "unclassified"> У кішки чотири ноги. </ P> <script> var paras = document .body.getElementsByTagName ( "p"); Array .prototype.forEach.call (paras, function (para) {if (para.getAttribute ( "data-classified") == "secret") para.parentNode.removeChild (para);}); </ Script>

Рекомендую перед іменами вигаданих атрибутів ставити "data-", щоб бути впевненим, що вони не конфліктують з будь-якими іншими. Як простий приклад ми напишемо підсвічування синтаксису, який шукає теги <pre> ( "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 [0])); node.appendChild (strong); pos = keywords.lastIndex; } Var after = text.slice (pos); node.appendChild (document .createTextNode (after)); }

Функція highlightCode приймає вузол <pre> і регулярку (з включеною налаштуванням global), збігається з ключовим словом мови програмування, яке містить елемент.

Властивість textContent використовується для отримання всього тексту вузла, а потім встановлюється в порожній рядок, що призводить до очищення вузла. Ми в циклі проходимо по всіх входженням вираження keyword, додаємо між ними текст у вигляді простих текстових вузлів, а співпав текст (ключові слова) додаємо, укладаючи їх в елементи <strong> (жирний шрифт).

Ми можемо автоматично підсвітити весь код сторінки, перебираючи в циклі всі елементи <pre>, у яких є атрибут data-language, і викликаючи на кожному highlightCode з правильною регулярки.

var languages ​​= {javascript:}; 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 ​​[lang]); }}

Ось приклад:

<Source lang = "html"> <p> А ось і вона, функція ідентифікації: </ p> <pre data-language = "javascript"> function id (x) {return x; }

Є один часто використовуваний атрибут, class, ім'я якого є ключовим словом в JavaScript. З історичних причин, коли старі реалізації JavaScript не вміли поводитися з іменами властивостей, збігалися з ключовими словами, цей атрибут доступний через властивість під назвою className. Ви також можете отримати до нього доступ за його справжнім іменем class через методи getAttribute і setAttribute.

Розташування елементів (layout)

Ви могли помітити, що різні типи елементів розташовуються по-різному. Деякі, типу параграфів <p> і заголовків <h1> розтягуються на всю ширину документа і з'являються на окремих рядках. Такі елементи називають блоковими. Інші, як посилання <a> або жирний текст <strong> з'являються на одній сходинці з навколишнім їх текстом. Вони називаються вбудованими (inline).

Для будь-якого документа браузери можуть побудувати розташування елементів, розклад, в якому у кожного буде розмір і положення на основі його типу і вмісту. Потім цей розклад використовується для створення зовнішнього вигляду документа.

Розмір і положення елемента можна дізнатися через JavaScript. Властивості offsetWidth і offsetHeight видають розмір в пікселях, яку він обіймав елементом. Піксель - основна одиниця вимірювання в браузерах, і зазвичай відповідає розміру мінімальної точки екрану. Подібним чином, clientWidth і clientHeight дають розмір внутрішньої частини елемента, що не влючая ширину його кордонів (border).

<P style = "border: 3px solid red"> Я в коробочці </ p> <script> var para = document .body.getElementsByTagName ( "p") [0]; console .log ( "clientHeight:", para.clientHeight); console .log ( "offsetHeight:", para.offsetHeight); </ Script>

Найефективніший спосіб дізнатися точне розташування елемента на екрані - метод getBoundingClientRect. Він повертає об'єкт з властивостями top, bottom, left, і right (зверху, знизу, зліва і справа), які містять положення елемента щодо лівого верхнього кута екрану в пікселях. Якщо Ви бажаєте отримати ці дані щодо всього документа, вам треба додати поточну позицію прокрутки, яка міститься в глобальних змінних pageXOffset і pageYOffset.

Розбір документа - завдання складне. З метою швидкодії браузерні движки НЕ перебудовують документ кожного разу після його зміни, а чекають так довго, як це можливо. Коли програма JavaScript, змінила документ, закінчує роботу, браузеру треба буде прорахувати нову розкладку сторінки, щоб вивести змінений документ на екран. Коли програма запитує позицію або розмір чого-небудь, читаючи властивості типу offsetHeight або викликаючи getBoundingClientRect, для надання коректної інформації теж необхідно розраховувати розкладку.

Програма, яка періодично зчитує розкладку DOM і змінює DOM, змушує браузер багато разів перераховувати розкладку, і в зв'язку з цим буде працювати повільно. У наступному прикладі є дві різні програми, які будують лінію з символів X шириною у 2000 пікс, і вимірюють час роботи.

<P> <span id = "one"> </ span> </ p> <p> <span id = "two"> </ span> </ p> <script> 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"));}); 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"));}); </ Script>

стилі

Ми бачили, що різні елементи HTML поводяться по-різному. Деякі показуються у вигляді блоків, інші вбудовані. Деякі додають візуальний стиль - наприклад, <strong> робить жирним текст і <a> робить текст підкресленим і синім.

Зовнішній вигляд картинки в тезі <img> або ті, что ПОСИЛАННЯ в тезі <a> при кліці відкріває нову сторінку, пов'язане з типом елемента. Але основні стилі, пов'язані з елементом, на кшталт кольору тексту або підкреслення, ми можемо змінити. Ось приклад використання властивості style (стиль):

<P> <a href = "." > Звичайна посилання </ a> </ p> <p> <a href = "." style = "color: green"> Зелена посилання </ a> </ p>

Атрибут style може містити одне або кілька оголошень властивостей (color), за яким слідує двокрапка і значення. У разі декількох оголошень вони розділяються крапкою з комою: "color: red; border: none ".

Багато всякого можна змінити за допомогою стилів. Наприклад, властивість display контролює, чи показується елемент в блоковому або вбудованому вигляді.

Текст показаний <strong> вбудованим </ strong>, <strong style = "display: block"> в вигляді блоку </ strong>, і <strong style = "display: none"> взагалі не видно </ strong>.

Блоковий елемент виводиться окремим блоком, а останній взагалі не видно - display: none відключає показ елементів. Таким чином можна ховати елементи. Зазвичай це переважно повного видалення їх з документа, тому що їх легше потім при необхідності знову показати.

Код JavaScript може безпосередньо діяти на стиль елемента через властивість вузла style. У ньому міститься об'єкт, що має властивості для всіх властивостей стилів. Їх значення - рядки, в які ми можемо писати для зміни якогось аспекту стилю елемента.

<P id = "para" style = "color: purple"> Красотень </ p> <script> var para = document .getElementById ( "para"); console .log (para.style.color); para.style.color = "magenta"; </ Script>

Деякі імена властивостей стилів містять дефіси, наприклад font-family. Так як з ними незручно було б працювати в JavaScript (довелося б писати style [ «font-family»]), назви властивостей в об'єкті стилів пишуться без дефіса, а замість цього в них з'являються великі літери: style.fontFamily

каскадні таблиці стилів

Система стилів в HTML називається CSS (Cascading Style Sheets, каскадні таблиці стилів). Таблиця стилів - набір стилів в документі. Його можна писати всередині тега <style>:

<Style> strong {font-style: italic; color: gray; } </ Style> <p> Тепер <strong> текст тега strong </ strong> похилий і сірий. </ P>

«Каскадні» означає, що кілька правил комбінуються для отримання остаточного стилю документа. У прикладі на стиль за замовчуванням для <strong>, який робить текст жирним, накладається правило з тега <style>, за яким додається font-style і колір.

Коли значення властивості визначається декількома правилами, пріоритет залишається у більш пізніх. Якби стиль тексту в <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 #main {margin-bottom: 20px; }

Пріоритет найпізніших правил працює, коли у правил однакова деталізація. Це міра того, наскільки точно воно описує відповідні елементи, що визначається числом і видом необхідних аспектів елементів. Наприклад, правило для pa більш детально, ніж правила для p або просто .a, і буде мати пріоритет.

Запис p> a {...} може бути застосована до всіх тегам <a>, що знаходяться всередині тега <p> і є його прямими нащадками. pa {...} може бути застосовано також до всіх тегам <a> всередині <p>, при цьому неважливо, чи є <a> прямим нащадком чи ні.

селектори запитів

У цій книзі ми не будемо часто використовувати таблиці стилів. Розуміння їх роботи критично для програмування в браузері, але докладне роз'яснення всіх їх властивостей зайняло б 2-3 книги. Головна причина знайомства з ними і з синтаксисом селектор (записів, що визначають, до яких елементів відносяться правила) - ми можемо використовувати той же ефективний міні-мову для пошуку елементів DOM.

Метод querySelectorAll, що стоїть і у об'єкту document, і у елементів-вузлів, приймає рядок селектора і повертає массівоподобний об'єкт, що містить всі елементи, які підходять під нього.

<P> Люблю грозу на початку <span> травня </ span> </ p> <p> Коли весняний перший грім </ p> <p> Як би <span> резвяся <span> і граючи </ span> </ span> </ p> <p> Гуркотить в небі блакитному. </ P> <script> function count (selector) {return document .querySelectorAll (selector) .length; } Console .log (count ( "p")); console .log (count ( ".animal")); console .log (count ( "p .animal")); console .log (count ( "p> .animal")); </ Script>

На відміну від методів на кшталт getElementsByTagName, що повертається querySelectorAll об'єкт не інтерактивний. Він не зміниться, якщо ви зміните документ.

Метод querySelector (без All) працює подібним чином. Він потрібен, якщо вам необхідний один конкретний елемент. Він поверне тільки перший збіг, або null, якщо збігів немає.

Розташування та анімація

Властивість стилів position сильно впливає на розташування елементів. За замовчуванням воно дорівнює static, що означає, що елемент знаходиться на своєму звичайному місці в документі. Коли воно дорівнює relative, елемент все ще займає місце, але тепер властивості top і left можна використовувати для зсуву щодо його звичайного розташування. Коли воно дорівнює absolute, об'єкт був видалений з нормального «потоку» документа - тобто, він не займає місце і може накладатися на інші. Крім того, його властивості left і top можна використовувати для абсолютного позиціонування щодо лівого верхнього кута найближчого включає його елемента, у якого position не дорівнює static. А якщо такого елемента немає, тоді він позиціонується щодо документа.

Ми можемо використовувати це для створення анімації. Наступний документ показує картинку з котом, яка рухається по еліпсу.

<P style = "text-align: center"> <img src = "img / cat.png" style = "position: relative"> </ p> <script> 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); </ Script>

Картинка отцентрирована на сторінці і їй задана 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 будується за допомогою ключових слів:

<Table> <tr> <th> name </ th> <th> height </ th> <th> country </ th> </ tr> <tr> <td> Kilimanjaro </ td> <td> 5895 < / td> <td> Tanzania </ td> </ tr> </ table>

Для кожного рядка в тезі <table> міститься тег <tr>. Усередині нього ми можемо розміщувати осередки: або осередки заголовків <th>, або звичайні комірки <td>.

Ті ж дані, що ми використовували в главі 6, знову доступні в змінної MOUNTAINS.

Напишіть функцію buildTable, яка, приймаючи масив об'єктів з однаковими властивостями, будує структуру DOM, що представляє таблицю. У таблиці повинна бути рядок з заголовками, де імена властивостей обгорнуті в елементи <th>, і має бути по одному рядку на об'єкт з масиву, де його властивості обгорнуті в елементи <td>. Тут стане в нагоді функція Object.keys, що повертає масив, що містить імена властивостей об'єкта.

Коли ви розберетеся з основами, вирівняйте осередки з числами по правому краю, змінивши їх властивість style.textAlign на «right».

<Style> table {border-collapse: collapse; } Td, th {border: 1px solid black; padding: 3px 8px; } Th {text-align: left; } </ Style> <script> function buildTable (data) {} document .body.appendChild (buildTable (MOUNTAINS)); </ Script>

Елементи на ім'я тегів

Метод getElementsByTagName повертає всі дочірні елементи з заданим ім'ям тега. Зробіть свою версію цього методу у вигляді звичайної функції, яка приймає вузол і рядок (ім'я тега) і повертає масив, що містить всі, хто сходить вузли з заданим ім'ям тега.

Щоб з'ясувати ім'я тега елемента, використовуйте властивість tagName. Зауважте, що воно поверне ім'я тега в верхньому регістрі. Використовуйте методи рядків toLowerCase або toUpperCase.

<H1> Заголовок з елементом <span> span </ span> всередині. </ H1> <p> Параграф з <span> раз </ span>, <span> два </ span> елементами spans. </ P> <script> function byTagName (node, tagName) {} console .log (byTagName (document .body, "h1") .length); console .log (byTagName (document .body, "span") .length); var para = document .querySelector ( "p"); console .log (byTagName (para, "span") .length); </ Script>

капелюх кота

Розширте анімацію кота, щоб і кіт і його капелюх <img src = "img / hat.png"> літали по протилежних сторонах еліпса.

Або нехай капелюх літає навколо кота. Або ще що-небудь цікаве придумайте.

Щоб спростити розташування безлічі об'єктів, непогано буде переключитися на абсолютне позиціонування. Тоді top і left будуть вважатися щодо лівого верхнього кута документа. Щоб не використовувати негативні координати, ви можете додати вказану кількість пікселів до значень position.

<Img src = "img / cat.png" id = "cat" style = "position: absolute"> <img src = "img / hat.png" id = "hat" style = "position: absolute"> <script > var cat = document .querySelector ( "#cat"); var hat = document .querySelector ( "#hat"); </ Script>

Новости

Banwar.org
Наша совместная команда Banwar.org. Сайт казино "Пари Матч" теперь доступен для всех желающих, жаждущих волнения и азартных приключений.