Статические и динамические таргеты

Stimulus — это легковесный JavaScript-фреймворк, ориентированный на добавление интерактивности к HTML без необходимости переписывать всю архитектуру приложения. Одной из ключевых концепций Stimulus являются таргеты. Они позволяют легко взаимодействовать с конкретными элементами DOM, обеспечивая читаемый и поддерживаемый код.


Основы таргетов

Таргет в Stimulus — это элемент DOM, который обозначен с помощью специального атрибута data-<controller>-target. Контроллер может затем ссылаться на эти элементы напрямую, не прибегая к стандартным методам поиска через querySelector.

Пример определения таргета:

<div data-controller="example">
  <input data-example-target="input" type="text">
  <button data-action="click->example#submit">Отправить</button>
</div>

В контроллере example можно обратиться к таргету так:

import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  static targets = ["input"]

  submit() {
    console.log(this.inputTarget.value);
  }
}

Ключевые моменты:

  • static targets — массив имен таргетов.
  • Каждый элемент с атрибутом data-<controller>-target автоматически создаёт свойство <name>Target в контроллере.
  • При наличии нескольких элементов с одинаковым таргетом можно использовать <name>Targets для получения коллекции.

Множественные таргеты

Stimulus позволяет иметь несколько элементов с одинаковым именем таргета. Это полезно для группировки элементов, например, списка кнопок или полей формы.

<div data-controller="tabs">
  <button data-tabs-target="tab">Вкладка 1</button>
  <button data-tabs-target="tab">Вкладка 2</button>
  <button data-tabs-target="tab">Вкладка 3</button>
</div>

В контроллере можно работать со всеми таргетами сразу:

export default class extends Controller {
  static targets = ["tab"]

  highlight(index) {
    this.tabTargets.forEach((tab, i) => {
      tab.classList.toggle("active", i === index);
    });
  }
}

Особенности:

  • this.tabTargets возвращает массив элементов.
  • Методы Target и Targets различаются по числу элементов: одиночный или коллекция.

Динамические таргеты

Статические таргеты определяются в HTML на этапе рендеринга страницы. Динамические таргеты позволяют работать с элементами, которые создаются или удаляются в процессе работы приложения.

Stimulus автоматически обновляет коллекцию таргетов при изменении DOM, если элементы создаются через innerHTML или другие методы, поддерживающие MutationObserver.

Пример динамического добавления:

<div data-controller="list">
  <button data-action="click->list#addItem">Добавить элемент</button>
  <ul data-list-target="items"></ul>
</div>
export default class extends Controller {
  static targets = ["items"]

  addItem() {
    const li = document.createElement("li");
    li.textContent = "Новый элемент";
    li.dataset.listTarget = "items";
    this.itemsTarget.appendChild(li);
  }
}

Особенности динамических таргетов:

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

Методы работы с таргетами

Stimulus предоставляет несколько удобных методов для работы с таргетами:

  • this.<targetName>Target — доступ к одиночному элементу.
  • this.<targetName>Targets — коллекция элементов.
  • this.has<TargetName>Target — проверка наличия хотя бы одного элемента.
  • this.<targetName>Target и this.<targetName>Targets всегда актуальны, даже при динамическом добавлении элементов.

Пример проверки существования таргета:

if (this.hasInputTarget) {
  this.inputTarget.focus();
}

Тонкости и лучшие практики

  1. Именование: Таргеты должны быть названы осмысленно, отражая их роль, например button, form, modalContent.
  2. Изоляция контроллера: Контроллер должен работать только со своими таргетами, избегая глобального поиска через querySelector.
  3. Динамическое добавление: Если элементы часто добавляются или удаляются, следует использовать MutationObserver или делегирование через события для корректного взаимодействия.
  4. Коллекции: Использование Targets удобно для циклических операций или группировки элементов с одинаковой функциональностью.

Связь с действиями

Таргеты тесно интегрированы с механизмом действий (data-action). Это позволяет легко привязывать события к конкретным элементам и манипулировать ими через контроллер:

<div data-controller="counter">
  <button data-counter-target="increment" data-action="click->counter#increase">+</button>
  <span data-counter-target="value">0</span>
</div>
export default class extends Controller {
  static targets = ["value"]

  increase() {
    this.valueTarget.textContent = parseInt(this.valueTarget.textContent) + 1;
  }
}

Каждый таргет получает свою функцию без необходимости вручную искать элементы.


Комбинация статических и динамических таргетов

Часто встречается сценарий, когда часть элементов статична, а часть создаётся динамически. Stimulus позволяет без проблем комбинировать оба подхода, сохраняя единообразный интерфейс к таргетам.

<div data-controller="gallery">
  <div data-gallery-target="container">
    <img src="img1.jpg" data-gallery-target="photo">
    <img src="img2.jpg" data-gallery-target="photo">
  </div>
  <button data-action="click->gallery#addPhoto">Добавить фото</button>
</div>
export default class extends Controller {
  static targets = ["container", "photo"]

  addPhoto() {
    const img = document.createElement("img");
    img.src = "new.jpg";
    img.dataset.galleryTarget = "photo";
    this.containerTarget.appendChild(img);
  }
}

Все новые элементы автоматически становятся частью photoTargets, позволяя работать с ними так же, как с первоначальными.


Закрепление знаний

Использование статических и динамических таргетов делает код Stimulus структурированным, минимизирует прямое взаимодействие с DOM и упрощает поддержку сложной интерактивной логики. Комбинирование таргетов с действиями позволяет создавать высоко модульные и читаемые контроллеры, адаптированные к динамическому изменению интерфейса.