Селекторы и выбор элементов

Работа с выбором элементов в Motion One строится вокруг прямого использования DOM-селекции и гибкого принятия различных типов целей: строк CSS-селекторов, отдельных узлов DOM, коллекций элементов и массивов. Такой подход снимает необходимость в промежуточных абстракциях и позволяет анимации работать с уже существующей моделью документа.


Функции анимации принимают универсальный аргумент target, который определяет набор элементов для применения эффекта. Поддерживаются несколько основных форматов:

  • CSS-селектор (строка)
  • Element
  • NodeList
  • HTMLCollection
  • массив элементов

Каждый из вариантов приводит к одному результату — формированию набора DOM-узлов, к которым применяется анимация.


CSS-селекторы как основной механизм выбора

Наиболее распространённый способ — передача строки CSS-селектора. В этом случае происходит внутренний вызов document.querySelectorAll, и анимация применяется ко всем совпавшим элементам.

import { animate } from "motion";

animate(".box", {
  opacity: [0, 1],
  transform: ["translateY(20px)", "translateY(0px)"]
});

Селекторы могут быть любыми валидными CSS-выражениями:

animate("section .item", { x: 100 });
animate(".card.active", { scale: 1.1 });
animate("#header nav a", { color: "#ffcc00" });

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


Работа с NodeList и HTMLCollection

Результаты стандартных DOM-методов также напрямую поддерживаются:

const items = document.querySelectorAll(".item");

animate(items, {
  opacity: [0, 1],
  y: [30, 0]
});

NodeList автоматически интерпретируется как набор целей без необходимости преобразования в массив.

Аналогично работает HTMLCollection:

const children = document.getElementsByClassName("box");

animate(children, {
  scale: 0.9
});

Внутренне такие коллекции нормализуются в единый массив элементов, что обеспечивает единообразное применение анимации.


Прямая передача DOM-элементов

Для точечного управления используется передача конкретного DOM-узла:

const button = document.querySelector("button");

animate(button, {
  scale: 0.95
});

Такой способ предпочтителен, когда элемент уже заранее найден или хранится в переменной состояния приложения.


Массивы элементов

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

const cards = [
  document.querySelector(".card-1"),
  document.querySelector(".card-2"),
  document.querySelector(".card-3")
];

animate(cards, {
  rotate: 5
});

Массивы обрабатываются так же, как и DOM-коллекции, без дополнительной трансформации.


Комбинированные выборки

Часто требуется объединить несколько источников элементов. В таких случаях применяется ручное объединение:

const primary = document.querySelectorAll(".primary");
const secondary = document.querySelectorAll(".secondary");

animate([...primary, ...secondary], {
  opacity: 0.6
});

Spread-оператор обеспечивает объединение в единый массив целей.


Повторный выбор элементов при каждом запуске

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

animate(".notification", {
  x: 50
});

Если новые элементы с классом .notification появляются после первого вызова, следующий вызов автоматически их захватит.


Кэширование выборки

При необходимости фиксированного набора элементов используется предварительное сохранение:

const notifications = document.querySelectorAll(".notification");

function show() {
  animate(notifications, { opacity: 1 });
}

Такой подход исключает повторные DOM-запросы и стабилизирует набор целей.


Контекстный выбор элементов

В случаях, когда требуется ограничить область поиска, используется метод querySelector/querySelectorAll от конкретного контейнера:

const modal = document.querySelector(".modal");

animate(modal.querySelectorAll(".item"), {
  y: -10
});

Таким образом формируется локальная область анимации без влияния на глобальный DOM.


Вложенные структуры и сложные селекторы

CSS-селекторы позволяют описывать иерархии и состояния элементов:

animate(".list > li:first-child", { x: 20 });
animate(".grid .cell:nth-child(3)", { scale: 1.2 });

Поддержка стандартного CSS-синтаксиса делает выборку предсказуемой и совместимой с привычной моделью браузера.


Псевдоклассы и состояния

Селекторы с псевдоклассами позволяют реагировать на состояние элементов:

animate("button:hover", {
  scale: 1.05
});
animate("input:focus", {
  borderColor: "#4a90e2"
});

Такие селекторы полезны при построении декларативных эффектов, завязанных на пользовательское взаимодействие.


Динамическая фильтрация набора элементов

Иногда требуется дополнительная логика после получения NodeList:

const items = [...document.querySelectorAll(".item")];

const activeItems = items.filter(el => el.classList.contains("active"));

animate(activeItems, {
  opacity: 1
});

Фильтрация позволяет отделять визуально активные или логически значимые элементы перед анимацией.


Производительность при выборе элементов

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

Практика оптимизации:

  • сохранять результат querySelectorAll при повторном использовании
  • избегать чрезмерно глубоких селекторов
  • минимизировать универсальные селекторы (*, body *)
  • ограничивать область поиска контейнером

Типичные ошибки при выборе целей

Часто встречаются ситуации, когда набор элементов оказывается неожиданным:

  • использование слишком общего селектора приводит к анимации лишних узлов
  • передача null из querySelector вызывает отсутствие эффекта
  • работа с живыми коллекциями (HTMLCollection) может давать изменяющийся результат при мутациях DOM

Нормализация входных типов

Внутри механизма анимации все входные значения приводятся к единому виду — массиву DOM-элементов. Это обеспечивает одинаковое поведение независимо от источника:

  • строка → querySelectorAll
  • NodeList → массив
  • HTMLCollection → массив
  • Element → массив из одного элемента
  • массив → без изменений

Такая унификация исключает необходимость ручной обработки типов перед запуском анимации.


Использование селекторов в больших интерфейсах

В сложных приложениях селекторы становятся частью архитектуры UI. Они позволяют связывать структуру DOM с анимационной логикой без дополнительных слоёв абстракции:

animate(".sidebar .menu-item", {
  x: [-10, 0],
  opacity: [0, 1]
});
animate(".content article", {
  y: 20
});

Чем более структурирован DOM, тем точнее становится управление анимациями через селекторы.


Делегирование и повторное использование селекторов

Селекторы могут использоваться как единый источник истины для повторяющихся анимаций:

const ITEM_SELECTOR = ".timeline-item";

function animateIn() {
  animate(ITEM_SELECTOR, { opacity: 1, y: 0 });
}

function animateOut() {
  animate(ITEM_SELECTOR, { opacity: 0, y: 20 });
}

Такой подход снижает дублирование строк и упрощает поддержку интерфейса.