Цифровой элемент

Сайт использует файлы cookie для удобства работы, аналитики и рекламы. Нажимая «Принять» или продолжая пользоваться d-element.ru, вы соглашаетесь с нашей Политикой конфиденциальности и обработкой персональных данных (включая файлы cookie).

15 минут на чтение
4689
Отправь статью на почту?

Работа с динамическим контентом

Большинство современных сайтов работают с динамическим контентом, то есть с контентом, который загружается автоматически без перезагрузки страницы с помощью асинхронных (то есть “с задержкой”) запросов, выполняющихся при определенных условиях.

Содержание

Примеры ситуаций динамического контента в коде

  1. Страница товара, пользователь нажимает на кнопку "Купить", появляется модальное окно со множеством информации о текущем статусе корзины. Разметка этого модального окна изначально на странице отсутствует, чтобы страница загружалась быстрее. После нажатия пользователем на кнопку скрипт выполняет асинхронный запрос к серверу, который, в свою очередь, предоставит всю необходимую информацию о статусе корзины для вывода ее в модальном окне.
  2. Страница каталога, изначально отображено 20 карточек товаров. Пользователь пролистывает страницу вниз, подгружаются еще 20 новых элементов. При дальнейшей прокрутке подгрузятся еще +20 товаров и так далее. Если бы на странице каталога сразу отображались сотни карточек товаров, то страница грузилась бы значительно дольше, чем страница с двадцатью элементами.

Пример общей структуры динамического контента в коде:

<body>
<!--	Будет пустым во время загрузки страницы-->
<div id="container-for-rendering"></div>
<!-- В bundle.js происходит асинхронный запрос, запрашивающий информацию при определенном действии пользователя -->
<script src="bundle.js"></script>
</body> 

Особенности работы с динамическим контентом на примере классовых компонентов

При загрузке страницы скрипт ищет все соответствующие селектору DOM-элементы на странице и привязывает к ним определенный функционал:

<body>
	<div id="static">
		<label>
			<input type="text" placeholder="+7 (999) 999-99-99" data-js-mask-input>
		</label>
	
		<label>
			<input type="text" placeholder="+7 (999) 999-99-99" data-js-mask-input>
		</label>
	
		<label>
			<input type="text" placeholder="+7 (999) 999-99-99" data-js-mask-input>
		</label>
	</div>
	
	<script src="bundle.js"></script>
</body> 
 import IMask from 'imask'

export default class Masks {
  els = {
    instance: '[data-js-mask-input]'
  }
  
  constructor() {
    this.init()
  }
  
  init() {
    document.querySelectorAll(this.els.instance).forEach((el) => {
      new IMask(el)
    })
  }
}

Если изначально на странице отсутствуют DOM-элементы, к которым происходит обращение в скрипте, то после их динамического появления на странице необходимо запустить скрипт повторно для того, чтобы функционал компонента заработал:

 <!-- Будет пустым во время загрузки страницы -->
<div id="container-for-rendering">
 <!-- Подгрузится асинхронно --> 
<label> 
<input type="text" placeholder="+7 (999) 999-99-99" data-js-mask-input> 
</label> 
</div>

Общий алгоритм работы с динамическим контентом:

  1. Запросить динамическое содержимое посредством ajax/fetch;
  2. Вставить загруженный контент в необходимое место;
  3. Вызвать CustomEvent - всплывающее событие, сигнализирующее о том, что на странице появился новый контент и содержащее в себе его HTML nodes.
  4. Подхватить событие и вызвать реинициализацию метода класса.

Запрос и вставка динамического содержимого

export default class MyClass {
  els = {
    containerForRendering: '#container-for-rendering'
  }

  // ...

  render(markup) {
    document.querySelector(this.els.containerForRendering).insertAdjacentHTML('afterend', markup)
  }

  async getMarkup(url, cfg) {
    const resp = await fetch(url, cfg)
    return (resp.ok) ?
      await (cfg.type === 'json') ?
        resp.json() : resp.text() :
        Promise.reject(resp.statusText)
  }
  
  updateMarkup() {
    this.getMarkup.then((markup) => {
      this.render(markup)
    })
  }
}

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

Работа с CustomEvents

const el = document.querySelector('#my-element')
const event = new Event('myCustomEventName')

// Подписываемся на событие
el.addEventListener('myCustomEventName', (e) => {
  // ...
})

// Вызываем событие
el.dispatchEvent(event)

Помимо стандартных событий в JavaScript можно создавать свои события, подписывать на них элементы и вызывать их при необходимости.

Кастомные события с дополнительными данными

const el = document.querySelector('#my-element')

// Подписываемся на событие
el.addEventListener('myCustomEventName', (e) => {
  // ...
})

// Создаем событие с данными
const customEvent = new CustomEvent('myCustomEventName', {
  detail: {} // любые данные
})

// Вызываем событие
el.dispatchEvent(customEvent)

Любое кастомное событие можно дополнить абсолютно любыми данными, которые могут пригодиться после дальнейшего “отлова” события.

Функция для уведомления о появлении нового контента

// ./js/generic/evented.js

export const dispatchContentLoaded = (data) => {
  document.dispatchEvent(
    new CustomEvent('contentLoaded', {
      detail: data
    })
  )
}

Работа с событиями появления нового контента

import {dispatchContentLoaded} from './generic/eventing'

export default class MyClass {
  els = {
    containerForRendering: '#container-for-rendering'
  }

  // ...

  render(string) {
    const dynamicNode = document.querySelector(this.els.containerForRendering)
    
    dynamicNode.insertAdjacentHTML('afterend', string)
    dispatchContentLoaded({
      content: dynamicNode
    })
  }
}

Функция dispatchContentLoaded инкапсулирует (скрывает “под капот”) логику вызова кастомного события, что позволяет легко и просто уведомить другие скрипты о появлении динамического контента на странице, вызвав данную функцию после успешного выполнения асинхронного запроса и отрисовки новой разметки.

Функция подписки на обновление (на примере классового компонента)

export class AccordionCollection {
  // Коллекция экземпляров
  _collection = []

  constructor() {
    super(instance, Accordion)
    this.init()
    this.bindEvents()
  }

  // Ищет внутри коллекции по DOM-элементу. У экземпляров класса должен быть параметр instance, по нему идет проверка
  getByDOMElement(DOMElement) {
    return this.collection.find(item => item.instance === DOMElement)
  }

  // Добавляет экземпляр в коллекцию. По-умолчанию проверяет, существует ли экземпляр с таким instance.
  set collection(newCollectionItem) {
    const itemInCollection = this.getByDOMElement(newCollectionItem.instance)
    if (!itemInCollection) {
      this._collection = [...this._collection, newCollectionItem]
    }
  }

  // Публичная коллекция
  get collection() {
    return this._collection
  }

  init(context = document) {
    context.querySelectorAll(instance).forEach((el) => {
      this.collection = new Accordion(el)
    })
  }

  bindEvents() {
    onContentLoaded((e) => { // слушаем обновление контента
      this.init(e.detail.content) // e.detail.content - обновленное содержимое (html nodes)
    })
  }
}

В конструкторе класса, помимо инициализации (вызова метода init), происходит привязка события отслеживания появления динамического контента. И при появлении новых элементов произойдет повторная инициализация компонента с контекстом e.detail.content (поиск новых DOM-элементов произойдет в контексте конкретного элемента, полученного в e.detail.content).

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

Обработка событий при статическом контенте

export default class MyClass {
  els = {
    something: '[data-js-my-clickable-element]' 
  }

  constructor() {
    this.bindEvents()
  }
  
  handleClick(e) {
    // ...
  }
  
  bindEvents() {
    document.querySelectorAll(this.els.something).forEach((node) => {
      node.addEventListener('click', (e) => this.handleClick(e))
    })    
  }
}

Обработка событий при динамическом контенте

export default class MyClass {
  els = {
    something: '[data-js-my-clickable-element]'
  }

  constructor() {
    this.bindEvents()
  }

  handleClick(e) {
    if (e.target.matches(this.els.something) || e.target.closest(this.els.something)) {
      // обработка клика
    }
  }

  bindEvents() {
    document.addEventListener('click', (e) => this.handleClick(e))
  }
}

Подход к обработке событий статического и динамического контента немного отличается.

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

Для динамического контента такой подход не сработает, так как к новым элементам привязки событий уже не будет. Поэтому необходимо добавить обработчик события на элемент document (корневой элемент, то есть буквально на всю страницу), затем “отловить” цель события через e.target.matches (или через e.target.closest, что, правда, чуть менее эффективно, но в некоторых случаях необходимо) и далее обработать событие.

Заключение

Работа с динамическим контентом меняет парадигму мышления разработчика, ведь нельзя рассчитывать на то, что при запуске страницы будут все необходимые элементы для работы конкретного функционала компонента, ибо даже небольшая задержка до появления контента после асинхронного запроса “поломает” неподготовленный к подобному поведению компонент. В лучшем случае - не заработает конкретный функционал, привязанный к элементу. В худшем - “положит” намертво всю страницу целиком.

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

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


Мне не нравится
Россия, Челябинская область, Челябинск, ул. Энтузиастов, 2, оф. 200 Телефон: +7 (351) 220-45-35

Читайте в нашем блоге

Все статьи
Согласование документов в Битрикс24: как настроить маршруты, статусы и автоматизацию

Согласование документов в Битрикс24: как настроить маршруты, статусы и автоматизацию

Согласование договора, счета, коммерческого предложения или внутренней заявки часто начинается с простого сообщения: «Посмотрите документ и сог...

27.08.2026
36
Интеграция 1С и 1С-Битрикс: как правильно организовать обмен каталогом, остатками, ценами и заказами

Интеграция 1С и 1С-Битрикс: как правильно организовать обмен каталогом, остатками, ценами и заказами

Для интернет-магазина или B2B-портала недостаточно просто разместить каталог товаров на сайте. Информация о номенклатуре, ценах, остатках и зак...

26.08.2026
74
Профессиональные SEO-редиректы для 1С-Битрикс: управляем URL без .htaccess и лишних цепочек

Профессиональные SEO-редиректы для 1С-Битрикс: управляем URL без .htaccess и лишних цепочек

Редиректы — одна из тех вещей, которые на небольшом сайте кажутся простой технической мелочью. Нужно перенести страницу — добавили 301. Изменил...

24.08.2026
60
Копирование инфоблоков в 1С-Битрикс: как быстро создать полную копию структуры и данных

Копирование инфоблоков в 1С-Битрикс: как быстро создать полную копию структуры и данных

Инфоблоки — один из основных механизмов хранения структурированных данных в «1С-Битрикс: Управление сайтом». На них строятся каталоги, новости,...

20.08.2026
72
Yandex SmartCaptcha для 1С-Битрикс: современная защита форм без доработки компонентов

Yandex SmartCaptcha для 1С-Битрикс: современная защита форм без доработки компонентов

Спам через формы обратной связи, автоматические регистрации, массовые запросы на восстановление пароля, боты в комментариях и отзывах — стандар...

12.08.2026
236
Версия для слабовидящих на сайте 1С-Битрикс: доступность без отдельного шаблона и дублирования контента

Версия для слабовидящих на сайте 1С-Битрикс: доступность без отдельного шаблона и дублирования контента

Доступность сайта — это не просто дополнительная кнопка в шапке и не формальное изменение размера шрифта. Людям с нарушениями зрения могут быть...

06.08.2026
453
Cookie-баннер для 1С-Битрикс: как получать и учитывать согласия пользователей, а не просто показывать уведомление

Cookie-баннер для 1С-Битрикс: как получать и учитывать согласия пользователей, а не просто показывать уведомление

Большинство сайтов используют cookie-файлы. Они необходимы для авторизации пользователей, сохранения корзины, работы личного кабинета, веб-анал...

04.08.2026
243
Как проверить сайт на 1С-Битрикс на вирусы и трояны: возможности расширенного антивирусного модуля

Как проверить сайт на 1С-Битрикс на вирусы и трояны: возможности расширенного антивирусного модуля

Вредоносный код на сайте не всегда выглядит как отдельный файл с очевидным названием virus.php. Злоумышленники могут внедрить несколько строк в...

03.08.2026
164