Метод 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")); // Все элементы <p>
// → 4
console.log(count(".animal")); // Класс animal
// → 2
console.log(count("p .animal")); // Класс animal внутри <p>
// → 2
console.log(count("p > .animal")); // Прямой потомок <p>
// → 1
</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.