Ванильный JavaScript и HTML. Никаких фреймворков. Никаких библиотек. Никаких проблем

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!


Используете для создания приложений Vue, React, Angular или Svelte? Я использую, и если вы тоже, и уверен, что вам уже давно не приходилось писать приложение, которое выводит информацию без этих прекрасных инструментов.

Когда-то многие из нас писали веб-приложения только с помощью тех средств, что были встроены в браузер. И хотя современные инструменты помогают нам абстрагироваться от этого (и имеют много других преимуществ), всё ещё полезно знать, что происходит у них под капотом.

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

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

Давайте рассмотрим разные способы вывода информации. И держите под рукой эту документацию!

Образец приложения


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


Инструменты


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

Мы будем использовать только HTML, TypeScript/JavaScript, CSS и браузерную DOM (объектную модель документа). Давайте посмотрим, как можно отобразить HTML.

Здесь вы можете почитать о том, как писать на TypeScript с помощью VS Code.

Как вы могли заметить, я использую TypeScript. Особенности его набора прекрасно помогают избегать багов, и я пользуюсь его возможностью транспилирования в любой формат JavaScript. Если хотите, можете использовать чистый JavaScript. В будущем я напишу статью о том, как транспилировать TypeScript.

Подход


Наше приложение отображает индикатор выполнения и список персонажей. Начнём с самого простого — индикатора, — и рассмотрим разные способы его отображения. Затем перейдём к списку и разберёмся, как меняется ситуация, если нужно выводить больше информации.

Отображение индикатора выполнения


Индикатор должен отображаться, пока приложение решает, какие персонажи должны попасть в список. То есть сначала индикатор невидим, затем пользователь нажимает кнопку обновления, индикатор появляется, и снова исчезает после отображения списка.

Все нижеописанные методики требуют размещения элементов по мере их создания. Например, индикатор и список персонажей где-то должны располагаться. Одним из решений является ссылка на существующий элемент, уже содержащий их. Или можно ссылаться на элемент и заменять его новыми данными.

Отображение индикатора с помощью внутреннего HTML


Создать HTML и поместить его внутрь другого элемента — это самое старое решение, но оно работает! Выбираете элемент, в котором появится индикатор, а затем задаёте для новых данных свойство innerHtml. Обратите внимание, что мы также пытаемся сделать код удобочитаемым с помощью шаблонных строк, позволяющих охватывать несколько строк.

const heroPlaceholder = document.querySelector('.hero-list');
heroPlaceholder.innerHTML = `
  <progress
    class="hero-list progress is-medium is-info" max="100"
  ></progress>
`;

Просто. Легко. Почему так нельзя сказать про любой код? Ну посмотрите, как быстро этот фрагмент решает проблему!

Вероятно, вы уже заметили, насколько уязвим этот код. Одна ошибка в HTML — и всё! У нас проблема. И действительно ли он удобочитаем? Да, это так, но что будет, когда HTML станет слишком сложным? Когда у вас 20 строк кода с классами, атрибутами и значениями… ну, вы поняли. Да, решение не идеальное. Но при небольшом количестве HTML вполне работает, и я рекомендую присмотреться к нему любителям всё писать в одну строку.

Также обратите внимание, как встроенный контент может влиять на удобочитаемость и стабильность кода. Например, если вам нужно добавить информацию внутрь индикатора для изменяющегося сообщения о загрузке, то можно сделать это с помощью замены, например, ${message}. Это не хорошо и не плохо, просто добавляет удобство при создании больших шаблонных строк.

И последнее: innerHTML может влиять на скорость отображения. Я не увлекаюсь избыточной оптимизацией, но лучше проверяйте производительность, потому что innerHTML может быть причиной возникновения циклов отрисовки и вывода макетов в браузере. Имейте в виду.

Индикатор выполнения с помощью DOM API


Уменьшить громоздкость длинных строк можно с помощью DOM API. Создаём элемент, добавляем необходимые классы и атрибуты, задаём значения, а потом добавляем его в DOM.

const heroPlaceholder = document.querySelector('.hero-list');
const progressEl = document.createElement('progress');
progressEl.classList.add('hero-list', 'progress', 'is-medium', 'is-info');
const maxAttr = document.createAttribute('max');
maxAttr.value = '100';
progressEl.setAttributeNode(maxAttr);
heroPlaceholder.replaceWith(progressEl);

Преимущество в том, что нужно писать больше кода и полагаться на API. То есть по сравнению с innerHTML при опечатках повышается вероятность, что система сообщит об ошибке, которая поможет найти причину проблемы и придумать решение.

А недостаток в том, что требуется шесть строк кода для вывода информации, которая выводится одной строкой с помощью innerHTML.

Код DOM API для отображения индикатора более читабелен, чем код с innerHTML? Я бы поспорил. А не в том ли причина, что HTML-код индикатора очень короткий и простой? Возможно. Если бы было 20 строк, то innerHTML стал бы гораздо сложнее в чтении… впрочем, и код DOM API тоже.

Отрисовка индикатора с помощью шаблонов


Ещё один способ заключается в создании тега <tеmplate> и использовании его для упрощения вывода информации.

Создадим <tеmplate> и присвоим ему ID. Шаблон не будет отображён на HTML-странице, но позднее вы можете ссылаться на его содержимое и использовать его. Это очень полезно, вы можете писать HTML везде, где это целесообразно: на HTML-странице, с применением всех полезных возможностей HTML-редактора.

<template id="progress-template">
  <progress class="hero-list progress is-medium is-info" max="100"></progress>
</template>

Затем код может взять шаблон с помощью метода document.importNode() из DOM API. По мере необходимости код может манипулировать содержимым шаблона. Теперь добавьте это содержимое в DOM, чтобы отобразить индикатор.

const heroPlaceholder = document.querySelector('.hero-list');
const template = document.getElementById('progress-template') as HTMLTemplateElement;
const fetchingNode = document.importNode(template.content, true);
heroPlaceholder.replaceWith(fetchingNode);

Шаблоны — хороший способ сборки HTML; мне он нравится, потому что позволяет писать HTML там, где это имеет смысл, и можно меньше делать с помощью кода на TypeScript/JavaScript.

Можно ли импортировать шаблоны из других файлов? Да, но с помощью других библиотек. Однако сейчас мы от них сознательно отказались. Импорт HTML обсуждается годами, но, как видно из материалов сайта «Can I Use», даже не все современные браузеры поддерживают эту возможность.

Отображение списка персонажей


Теперь поговорим о том, как можно отображать список персонажей с помощью тех же трёх методик. Отличие от отрисовки одиночного HTML-элемента <prоgress> и списка персонажей заключается в том, что теперь мы:

  • отображаем несколько HTML-элементов;
  • добавляем несколько классов;
  • добавляем определённую последовательность из дочерних элементов;
  • отображаем для каждого персонажа много одинаковой информации;
  • динамически отображаем текст внутри элементов.

Отображение персонажей с помощью внутреннего HTML


При использовании innerHTML целесообразно начать с массива персонажей и итерировать его. Таким образом можно создавать по строке за раз. Строки будут отличаться только именами персонажей и описанием, которые можно вставлять с помощью шаблонных строк. Каждый персонаж в массиве создаёт тег li, который связывается со строчным массивом. Наконец, строчный массив преобразуется в чистый HTML, обёрнутый в ul и назначенный для innerHTML.

function createListWithInnerHTML(heroes: Hero[]) {
  const rows = heroes.map(hero => {
    return `<li>
        <div class="card">
          <div class="card-content">
            <div class="content">
              <div class="name">${hero.name}</div>
              <div class="description">${hero.description}</div>
            </div>
          </div>
        </div>
      </li>`;
  });
  const html = `<ul>${rows.join()}</ul>`;
  heroPlaceholder.innerHTML = html;

Это работает. И возможно, это более удобочитаемо. Что насчёт производительности? Трудно ли выявить (или вообще возможно?) опечатки при наборе кода? Судить вам. Но давайте не будем делать выводов, пока не разберёмся с другими методиками.

Отображение персонажей с помощью DOM API


function createListWithDOMAPI(heroes: Hero[]) {
  const ul = document.createElement('ul');
  ul.classList.add('list', 'hero-list');
  heroes.forEach(hero => {
    const li = document.createElement('li');
    const card = document.createElement('div');
    card.classList.add('card');
    li.appendChild(card);
    const cardContent = document.createElement('div');
    cardContent.classList.add('card-content');
    card.appendChild(cardContent);
    const content = document.createElement('div');
    content.classList.add('content');
    cardContent.appendChild(content);
    const name = document.createElement('div');
    name.classList.add('name');
    name.textContent = hero.name;
    cardContent.appendChild(name);
    const description = document.createElement('div');
    description.classList.add('description');
    description.textContent = hero.description;
    cardContent.appendChild(description);
    ul.appendChild(li);
  });
  heroPlaceholder.replaceWith(ul);
}

Отображение персонажей с помощью шаблонов


Список персонажей можно отобразить с помощью шаблонов. Для этого применяется та же методика, что и для отображения элемента <prоgress>. Сначала на HTML-странице создаём шаблон. Этот HTML-код будет чуть сложнее, чем в случае с <prоgress>. Но это не проблема. Это просто HTML внутри HTML-страницы, так что мы можем легко исправлять ошибки и форматирование с помощью прекрасного редактора VS Code.

<template id="hero-template">
  <li>
    <div class="card">
      <div class="card-content">
        <div class="content">
          <div class="name"></div>
          <div class="description"></div>
        </div>
      </div>
    </div>
  </li>
</template>

Теперь можете написать код для создания списка персонажей. Сначала создаём ul для обёртывания строк с персонажами li. Теперь можно проходить по массиву и с помощью того же метода document.importNode() применять один и тот же шаблон для каждого персонажа. Обратите внимание, что шаблон можно многократно использовать для создания каждой строки. Он превращается в чертёж-исходник, по которому создаётся необходимое количество строк.

Список персонажей должен содержать имена и описания. То есть нужно будет заменять конкретные значения внутри шаблона. Для этого целесообразно каким-то образом определять и ссылаться на места, куда вы будете вставлять эти значения. В нашем примере для получения ссылки на каждого персонажа используется метод querySelector('your-selector'), после чего задаётся имя и описание.

function createListWithTemplate(heroes: Hero[]) {
  const ul = document.createElement('ul');
  ul.classList.add('list', 'hero-list');
  const template = document.getElementById('hero-template') as HTMLTemplateElement;
  heroes.forEach((hero: Hero) => {
    const heroCard = document.importNode(template.content, true);
    heroCard.querySelector('.description').textContent = hero.description;
    heroCard.querySelector('.name').textContent = hero.name;
    ul.appendChild(heroCard);
  });
  heroPlaceholder.replaceWith(ul);
}

Этот шаблон проще других методик? Я считаю, что относительно проще. С моей точки зрения, код построен по шаблону, который упрощает воспроизведение и читабельность, а также лучше защищает от ошибок.

Итоги


Я не упомянул о том, как отображают информацию популярные фреймворки и библиотеки. Vue, React, Angular и Svelte существенно облегчают задачу и требуют писать меньше кода. Также у них есть и другие преимущества. В этой статье мы рассмотрели лишь относительно простые методики вывода информации с помощью DOM и чистого HTML, а также TypeScript/JavaScript.

К чему мы пришли?

Надеюсь, вы теперь представляете, как выводить информацию без использования библиотек. Есть ли другие способы? Конечно. Можно написать функции, которые упростят код и позволят использовать его многократно? Конечно. Но однажды вам захочется поэкспериментировать с одним из таких прекрасных инструментов, как Vue, React, Angular или Svelte.

Эти фреймворки делают за нас много работы. Вы можете помнить, как выводить информацию с помощью чистого DOM-кода с JavaScript или jQuery. Или как делать это с помощью инструментов вроде handlebars или mustache. А может быть, вы никогда не выводили информацию в DOM без помощи фреймворка. Можете сами представить, какие процессы протекают под капотом современных библиотек и фреймворков, когда они выводят для вас информацию на экран.

Полезно знать, что можно сделать с помощью чистого HTML, TypeScript/JavaScript и CSS, даже если вы используете фреймворк, который избавляет вас от всего этого.

Вне зависимости от вашего опыта, я надеюсь, что вам был полезен этот краткий экскурс в некоторые методики отображения информации.
Источник: https://habr.com/ru/company/mailru/blog/483726/


Интересные статьи

Интересные статьи

Привет, Хабр! Меня зовут, Денис Сёмин, я руководитель отдела реализации клиентских и сетевых решений Orange Business Services в Самаре. В прошлый раз мой коллега Антон Кисляков р...
Существует очень тонкая грань между чистым, эффективным кодом и кодом, который может понять только его автор. А хуже всего то, что чётко определить эту грань невозможно. Некоторые про...
Для создания интерфейсов React рекомендует использовать композицию и библиотеки по управлению состоянием (state management libraries) для построения иерархий компонентов. Однако при сложных патте...
На сегодняшний день у сервиса «Битрикс24» нет сотен гигабит трафика, нет огромного парка серверов (хотя и существующих, конечно, немало). Но для многих клиентов он является основным инструментом ...
Тема статьи навеяна результатами наблюдений за методикой создания шаблонов различными разработчиками, чьи проекты попадали мне на поддержку. Порой разобраться в, казалось бы, такой простой сущности ка...