Введение

В этой части серии мы применим принципы объектно-ориентированного программирования (oop), чтобы воссоздать виджет с рейтингом в звездочках, который мы видели в части 1, и сделать его многоразовым в процессе.

Это распространенный вопрос на собеседовании с инженером.

Часть 1 этой серии можно найти здесь.

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

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

Настраивать

Настройка будет такой же, как и в первой статье. Мы будем использовать шаблон JS из codesandbox.io. В этом шаблоне есть папка src, содержащая файлыindex.js, style.css и файл index.html за пределами src. На этот раз нам не нужно добавлять какие-либо стили в файл styles.css.

HTML

Часть HTML очень проста. Необходимо изменить только одну строку. Перейдите в файл index.html и замените существующий div следующей строкой.

<div class="stars-container1"></div>

Этот div будет служить контейнером для виджета звезд, который мы создадим позже.

JavaScript

Поскольку для создания виджета мы используем объектно-ориентированное программирование, давайте продолжим и создадим каркас нашего класса внутри файла index.js.

class Stars {
  constructor() {}
  
  init() {}
  
  setRating() {}
}

Этот класс назван Stars и в настоящее время имеет пустые методы constructor, init и setRating.

Конструктор

constructor поможет нам создавать уникальные экземпляры виджетов. Мы заставим его принимать параметры, которые определяют:

  • где будет размещен виджет
  • количество звезд, которое у него будет
  • стили для каждого элемента списка, который будет служить контейнером для отдельной звезды

Параметры className, numOfStars и styleOptions будут иметь пустую строку, 0 и литерал объекта с атрибутом margin в качестве значений по умолчанию соответственно.

constructor(
    className = "",
    numOfStars = 0,
    styleOptions = { margin: "5px" }
  ) {
    this.isValid = false;
    this.numOfStars = numOfStars;
    this.stars = [];    
    this.styleOptions = styleOptions;
    this.starsContainer = document.querySelector(className);
​
    try {
      if (this.starsContainer) {
        this.className = className;
        this.isValid = true;
      } else {
        this.isValid = false;
        throw new Error(`${className} does not exist.`);
      }
    } catch (e) {
      console.log(e.message);
    }
​
    if (this.isValid && this.numOfStars > 0) {
      this.init();
    }
  }
​

Выше видно, что мы добавляем несколько атрибутов к объекту this. Переменная this.isValid будет служить флагом, указывающим, что параметры, передаваемые в конструктор, являются допустимыми. На этом этапе важно выполнить проверку, чтобы убедиться, что правильные значения передаются в constructor во время создания виджета.

Нам также необходимо убедиться, что className, передаваемый в конструктор, является допустимым именем класса для существующего элемента в файле index.html. Это важно, потому что этот элемент будет тем элементом, на котором мы будем монтировать виджет. Кроме того, если пользователь передал 0 в качестве количества звездочек, виджета не будет. Следовательно, если у нас нет действительных значений для параметров className и numOfStars, значение this.isValid будет false, и это избавит нас от ненужного вызова функции init.

У нас также есть переменная this.stars, имеющая в качестве значения пустой массив, и this.starsContainer, значение которой является возвращенным значением querySelector. Оба вскоре сыграют важную роль.

Обработка исключений

Добавлен блок try-catch для перехвата исключений, которые могут возникнуть из-за недопустимого classNames. Внутри блока try мы проверяем, существует ли значение this.starsContainer. Если он существует, мы присваиваем className this.className и значение true this.isValid. Если это не так, мы присваиваем значение false this.isValid и выдаем ошибку, используя встроенный конструктор Javascript Error. Вы можете передать в этот Error конструктор любое сообщение, которое, по вашему мнению, будет правильно выражать проблему. Это хороший подход для создания общих ошибок.

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

Вне блока try-catch последний фрагмент кода, который мы пишем, - это условие, которое проверяет, истинно ли значение this.isValid, а значение this.numOfStars больше 0. И, если это так, мы можем вызвать метод theinit.

В этом

Метод init предназначен для того, чтобы абстрагироваться от логики конструктора, которая нам нужна:

  • создать пустой элемент неупорядоченного списка (ul)
  • генерировать элементы списка и элементы привязки
  • добавить звездочки HTML к элементам привязки
  • добавить прослушиватели событий щелчка к элементам привязки и реализовать логику, которая будет изменять цвета звездочек
  • добавить каждый элемент привязки к соответствующему элементу элемента списка
  • добавить каждый элемент списка к созданному нами элементу ul
  • добавьте элемент ul к элементу starsContainer
init() {
    const ul = document.createElement("ul");
    for (let i = 0; i < this.numOfStars; i++) {
      this.stars.push({ id: i + 1 });
    }
    ul.style.listStyleType = "none";
    ul.style.display = "flex";
    const stars = this.stars.map((star) => {
      const li = document.createElement("li");
      const a = document.createElement("a");
      
      li.style.margin = this.styleOptions.margin;   
      a.style.cursor = "pointer";
      
      a.innerHTML = "&#9733";
      a.id = star.id;
      
      a.addEventListener("click", (e) => {
        this.setRating(ul, e);
      });
    li.appendChild(a);
      return li;
    });
    const fragment = document.createDocumentFragment();
    for (const star of stars) {
      fragment.appendChild(star);
    }
    ul.appendChild(fragment);
    this.starsContainer.appendChild(ul);
 }

Вы можете видеть, что элемент ul создан с использованием метода createElement объекта document. У нас также есть цикл for, который позволяет нам вставить в массив this.stars литерал объекта с атрибутом id и значением, равным текущей переменной итерации для цикла. Как только это будет сделано, мы применим стили для удаления маркеров пунктов списка и горизонтального выравнивания элементов списка, которые мы собираемся создать в ближайшее время.

Теперь у нас есть массив объектов, хранящийся внутри переменной this.stars. Мы будем использовать этот массив для генерации li элементов, количество которых будет эквивалентно значению, хранящемуся в this.numOfElements. Для этого нам нужно вызвать карту в массиве this.stars и преобразовать каждый объект, который у нас есть в массиве, в элемент a li, который обертывает элемент привязки звездочкой HTML entity.

Сразу же внутри функции обратного вызова мы создаем два элемента li и a и применяем к ним некоторые стили. Кроме того, мы устанавливаем HTML entity, который будет отображаться в виде звезды при отображении в браузере, для атрибута innerHTML элемента привязки.

Мы также вызываем метод addEventListener для элемента привязки. Это поможет нам прослушивать click события при каждом щелчке по элементу. Логика, которую мы применяем внутри обратного вызова, поможет нам изменить цвета звезд. Эта логика будет реализована в методе setRating. Для выполнения этой работы методу потребуются ul и объект события в качестве параметров.

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

Это будет массив элементов списка, хранящихся в константе stars. Для повышения производительности мы создаем document fragment и добавляем к нему каждый элемент элемента списка, который находится в массиве звезд. Затем мы добавляем фрагмент к ul element, который, в свою очередь, добавляется к элементу, хранящемуся в this.starsContainer.

Реализовать setRating

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

setRating(ul, e) {
    const listItems = ul.querySelectorAll("li");
    const currentId = Number(e.target.id);
    for (const item of listItems) {
      const a = item.querySelector("a");
      a.style.color = "";
      if (a.id <= currentId) {
        a.style.color = "#ccac00";
      } else {
        a.style.color = "";
      }
    }
 }

Первое, что мы делаем внутри этой функции, - это используем метод querySelectorAll, чтобы получить все элементы элемента списка, которые находятся внутри элемента ul. Этот метод возвращает массив Javascript, содержащий элементы li. Теперь мы можем перебрать элементы массива и получить доступ к элементу привязки, который находится внутри каждого элемента.

После получения элемента привязки текущего элемента с помощью querySelector мы устанавливаем пустую строку в атрибут стиля color. Это операция сброса.

Затем мы проверяем, является ли id текущего элемента привязки, который мы перебираем, меньше или равно id элемента привязки, на который мы только что нажали. Мы можем получить этот идентификатор, используя целевой атрибут объекта события. Если это условие истинно, то мы устанавливаем шестнадцатеричное значение золотого цвета в color attribute стиля элемента привязки. В противном случае цвет будет иметь в качестве значения пустую строку.

использование

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

const stars1 = new Stars(".stars-container1", 5);

Вы можете найти исходный код здесь.

Резюме

Мы смогли создать многоразовый виджет с применением принципов объектно-ориентированного программирования (ООП).

Ознакомьтесь с этими ТОП-курсами JAVASCRIPT на Udemy:

1 - Полный курс JavaScript 2021: от нуля до эксперта!

2 - JavaScript: понимание странностей

3 - JavaScript - Полное руководство 2021 (Начальный + Продвинутый)

4 - Современный JavaScript с самого начала

5 - Современный учебный курс JavaScript

Посетите мой канал на YouTube, где есть курсы и руководства по программированию:
https://www.youtube.com/channel/UCA5ZfHC6koHsukCLzCzxuGA

Если вы ищете работу по разработке программного обеспечения, зайдите в НАЕМНЫЙ.