Цифровой элемент
15 минут на чтение
2501
Отправь статью на почту?

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

Подписаться

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

Содержание

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

  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

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

Все статьи
Лучшие бесплатные и условно-бесплатные аналоги Figma в 2024 году

Лучшие бесплатные и условно-бесплатные аналоги Figma в 2024 году

Figma — один из наиболее востребованных инструментов для создания интерфейсов и прототипов в сфере веб-дизайна и разработки мобильных приложени...

10.12.2024
177
Интеграция Битрикс24 и Asterisk

Интеграция Битрикс24 и Asterisk

Современные компании всё чаще сталкиваются с необходимостью интеграции IP-телефонии и CRM-систем для улучшения управления клиентскими коммуника...

29.11.2024
288
Хостинг. Лучшие хостинг-провайдеры в России

Хостинг. Лучшие хостинг-провайдеры в России

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

19.11.2024
432
Региональное продвижение сайта: поддомены или подпапки

Региональное продвижение сайта: поддомены или подпапки

Запросы пользователей в поисковых системах делятся на две категории: геозависимые и геонезависимые. Геозависимые запросы — это...

29.10.2024
1164
Битрикс24 vs Microsoft SharePoint: Как выбрать оптимальную платформу для вашего бизнеса?

Битрикс24 vs Microsoft SharePoint: Как выбрать оптимальную платформу для вашего бизнеса?

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

10.09.2024
1605
Внедрение системы управления персоналом и автоматизация: обзор HRM-систем

Внедрение системы управления персоналом и автоматизация: обзор HRM-систем

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

06.09.2024
1678
ELMA365 CRM – система автоматизации и управления бизнес процессами

ELMA365 CRM – система автоматизации и управления бизнес процессами

ELMA365 CRM – это мощная и гибкая CRM-BPM система, разработанная российской компанией ELMA, специально для удовлетворения потре...

02.09.2024
1473
Что делать, если Google Документы перестанут работать? Топ-9 альтернатив для России

Что делать, если Google Документы перестанут работать? Топ-9 альтернатив для России

Периодически в интернете появляются слухи о возможной блокировке Google Docs в России. Хотя пока нет серьезных причин для паники, лучше заранее...

27.08.2024
3763