Марейн Хавербеке - Выразительный JavaScript
console.log("offsetHeight:", para.offsetHeight);
</script>
Самый эффективный способ узнать точное расположение элемента на экране – метод getBoundingClientRect. Он возвращает объект со свойствами top, bottom, left, и right (сверху, снизу, слева и справа), которые содержат положение элемента относительно левого верхнего угла экрана в пикселях. Если вам надо получить эти данные относительно всего документа, вам надо прибавить текущую позицию прокрутки, которая содержится в глобальных переменных pageXOffset и pageYOffset.
Разбор документа – задача сложная. В целях быстродействия браузерные движки не перестраивают документ каждый раз после его изменения, а ждут так долго, как это возможно. Когда программа JavaScript, изменившая документ, заканчивает работу, браузеру надо будет просчитать новую раскладку страницы, чтобы вывести изменённый документ на экран. Когда программа запрашивает позицию или размер чего-либо, читая свойства типа offsetHeight или вызывая getBoundingClientRect, для предоставления корректной информации тоже необходимо рассчитывать раскладку.
Программа, которая периодически считывает раскладку DOM и изменяет DOM, заставляет браузер много раз пересчитывать раскладку, и в связи с этим будет работать медленно. В следующем примере есть две разные программы, которые строят линию из символов X шириной в 2000 пикселей, и измеряют время работы.
<span id="one"></span>
<span id="two"></span>
<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"));
});
// → тупо заняло 32 ms
time("умно", function() {
var target = document.getElementById("two");
target.appendChild(document.createTextNode("XXXXX"));
var total = Math.ceil(2000 / (target.offsetWidth / 5));
for (var i = 5; i < total; i++)
target.appendChild(document.createTextNode("X"));
});
// → умно заняло 1 ms
</script>
Стили
Мы видели, что разные элементы HTML ведут себя по-разному. Некоторые показываются в виде блоков, другие встроенные. Некоторые добавляют визуальный стиль – например, <strong> делает жирным текст и <a> делает текст подчёркнутым и синим.
Внешний вид картинки в теге <img> или то, что ссылка в теге <a> при клике открывает новую страницу, связано с типом элемента. Но основные стили, связанные с элементом, вроде цвета текста или подчёркивания, могут быть нами изменены. Вот пример использования свойства style (стиль):
<a href=".">Обычная ссылка</a>
<a href="." style="color: green">Зелёная ссылка</a>
Атрибут 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">
Красотень
<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>
Теперь <strong>текст тега strong</strong> наклонный и серый.
«Каскадные» означает, что несколько правил комбинируются для получения окончательного стиля документа. В примере на стиль по умолчанию для <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, а id задан как main */
p.a.b#main {
margin-bottom: 20px;
}
Приоритет самых поздних правил работает, когда у правил одинаковая детализация. Это мера того, насколько точно оно описывает подходящие элементы, определяемая числом и видом необходимых аспектов элементов. К примеру, правило для p.a более детально, чем правила для p или просто .a, и будет иметь приоритет.
Запись p > a {...} применима ко всем тегам <a>, находящимся внутри тега
и являющимся его прямыми потомками. p a {...} применимо также ко всем тегам <a> внутри
, при этом неважно, является ли <a> прямым потомком или нет.
Селекторы запросов
В этой книге мы не будем часто использовать таблицы стилей. Понимание их работы критично для программирования в браузере, но подробное разъяснение всех их свойств заняло бы 2-3 книги. Главная причина знакомства с ними и с синтаксисом селекторов (записей, определяющих, к каким элементам относятся правила) – мы можем использовать тот же эффективный мини-язык для поиска элементов DOM.
Метод querySelectorAll, существующий и у объекта document, и у элементов-узлов, принимает строку селектора и возвращает массивоподобный объект, содержащий все элементы, подходящие под него.
Люблю грозу в начале
<span>мая</span>
Когда весенний первый гром
Как бы <span>резвяся
<span>и играя</span></span>
Грохочет в небе голубом.
<script>
function count(selector) {
return document.querySelectorAll(selector).length;
}
console.log(count("p")); // Все элементы
// → 4
console.log(count(".animal")); // Класс animal
// → 2
console.log(count("p .animal")); // Класс animal внутри
// → 2
console.log(count("p > .animal")); // Прямой потомок
// → 1
</script>
В отличие от методов вроде getElementsByTagName, возвращаемый querySelectorAll объект не интерактивный. Он не изменится, если вы измените документ.
Метод querySelector (без All) работает сходным образом. Он нужен, если вам необходим один конкретный элемент. Он вернёт только первое совпадение, или null, если совпадений нет.
Расположение и анимация
Свойство стилей position сильно влияет на расположение элементов. По умолчанию оно равно static, что означает, что элемент находится на своём обычном месте в документе. Когда оно равно relative, элемент всё ещё занимает место, но теперь свойства top и left можно использовать для сдвига относительно его обычного расположения. Когда оно равно absolute, элемент удаляется из нормального «потока» документа – то есть, он не занимает место и может накладываться на другие. Кроме того, его свойства left и top можно использовать для абсолютного позиционирования относительно левого верхнего угла ближайшего включающего его элемента, у которого position не равно static. А если такого элемента нет, тогда он позиционируется относительно документа.