July 19th, 2021

JavaScript: карусель картинок, строим каркас 1

Решил задачу «Карусель» к подразделу 2.1 «Введение в браузерные события» второй части учебника по JavaScript.

Задача состоит в том, чтобы на HTML-странице создать карусель картинок. Что такое «карусель картинок»? Это механизм, с помощью которого пользователь может листать имеющиеся картинки кнопками влево и вправо. В таком виде, бывает, оформляют фотоальбом на сайте или фотоальбомы в соцсетях.

В задаче дана HTML-страница, на которой частично уже построена карусель картинок. Требуется достроить каркас карусели, созданный на языках HTML и CSS, а затем придать карусели интерактивность, написав функционал кнопок на языке JavaScript. Код заданной HTML-страницы можно посмотреть в песочнице.

Мне было интересно сначала понять, как создали каркас карусели на языках HTML и CSS.

Для постройки тестовой карусели в задаче дано десять картинок:
https://ru.js.cx/carousel/1.png
https://ru.js.cx/carousel/2.png
...
https://ru.js.cx/carousel/10.png

На этих картинках изображено десять разных смайликов.

Задача изначально упрощена: как я понимаю, предполагается, что все картинки представляют собой одинаковые квадраты (размеры разных картинок равны друг другу, ширина и высота каждой картинки тоже равны друг другу). Для хранения этого размера в будущем скрипте будем использовать константу PIC_SIZE, равную 130px (это размер оригиналов заданных картинок).

В теле HTML-страницы картинки помещены в пункты HTML-списка:
<ul>
  <li><img src="https://ru.js.cx/carousel/1.png"></li>
  <li><img src="https://ru.js.cx/carousel/2.png"></li>
  <!-- ... -->
  <li><img src="https://ru.js.cx/carousel/10.png"></li>
</ul>

У меня в браузере («Microsoft Edge» на движке «Chromium») это выглядит так (картинка):



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

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

По умолчанию тип отображения (CSS-свойство display) пунктов HTML-списка равен значению list-item. В нашем случае он ведет себя как блочный элемент, то есть занимает всю ширину области просмотра. Кроме этого, этот тип отображения генерирует перед содержимым пункта псевдо-элемент ::marker (значок пункта списка). Поменяв тип отображения на значение inline-block, мы добьемся исчезновения псевдо-элементов ::marker у пунктов списка, а также того, что пункты списка смогут располагаться горизонтально, друг за другом:
ul li {
  display: inline-block; /* разместить пункты списка горизонтально */
}

У меня в браузере это выглядит так (картинка):



У HTML-списка браузеры по умолчанию обычно устанавливают внутренние (padding) и внешние (margin) отступы. В нашей карусели картинок эти отступы не нужны (могут усложнить расчёты в скрипте на языке JavaScript), поэтому уберем их:
ul {
  margin: 0;             /* уберем внешние и внутренние */
  padding: 0;            /* умолчательные отступы от браузера */
}

ul li {
  display: inline-block; /* разместим пункты списка горизонтально */
}

Для картинок (это уже объяснялось в нескольких предыдущих постах) в стиле картинок укажем их ширину и высоту, чтобы браузер в любой ситуации знал размер содержимого пунктов списка и мог быстро отобразить картинки и пункты списка, не роясь в кэше в поисках размеров картинок. (Также таким образом исключаем ситуацию, когда при первой загрузке HTML-страницы браузер не знает размеров картинок и расчеты в скрипте на языке JavaScript, отталкивающиеся от размеров картинок, могут в этом случае дать неверные результаты.) Меняем код стилей:
ul {
  margin: 0;             /* уберем внешние и внутренние */
  padding: 0;            /* умолчательные отступы от браузера */
}

ul img {
  width: 130px;          /* размеры картинок */
  height: 130px;
}

ul li {
  display: inline-block; /* разместим пункты списка горизонтально */
}

Также в нашей карусели картинок нам не нужны и промежутки между картинками (тоже могут усложнить расчёты в скрипте на языке JavaScript). Есть ли они? Чтобы проверить это, временно сделаем красную рамку вокруг каждого пункта HTML-списка и серый фон для каждой картинки (оригиналы картинок имеют прозрачный фон):
ul {
  margin: 0;             /* уберем внешние и внутренние */
  padding: 0;            /* умолчательные отступы от браузера */
}

ul img {
  width: 130px;          /* размеры картинок */
  height: 130px;
  background-color: silver; /* временно, серый фон */
}

ul li {
  display: inline-block; /* разместим пункты списка горизонтально */
  border: 1px solid red;    /* временно, красная рамка */
}

У меня в браузере получилось следующее (картинка):


Во-первых, обратим внимание на то, что между нашим HTML-списком и границей области просмотра браузера всё еще есть промежуток (у меня в браузере он равен 8px). Это внешний отступ (margin), устанавливаемый браузером по умолчанию для тела (body) HTML-страницы. Нам он не помешает, потому что в будущем мы вставим наш HTML-список внутрь другого контейнера, HTML-элемента div.

Во-вторых, на иллюстрации видно, что между отдельными пунктами нашего HTML-списка (напомню, пункты временно обведены красной границей-рамкой для наглядности) есть промежуток. Откуда он взялся? На самом деле этот промежуток есть в исходном коде нашей HTML-страницы, это пробельные символы между HTML-элементами li (напомню, в состав пробельных символов входят не только пробелы, но и символы новой строки, и символы табуляции).

Избавиться от этого промежутка можно несколькими способами. Например, можно удалить пробельные символы между HTML-элементами li в исходном коде нашей HTML-страницы. Но тогда исходный код нашей HTML-страницы станет плохо читаемым (все пункты HTML-списка будут слеплены в одну строку). Как я видел, из исходного кода HTML-страниц многих сайтов, сделанных профессионалами, убраны все лишние пробельные символы. Там это оправдано, потому что пользователь исходный код HTML-страницы не читает (ему достаточно отображения HTML-страницы в браузере), а кодеры пользуются специальными программами, которые убирают лишние пробельные символы только в самом конце написания HTML-страницы, перед ее загрузкой на сайт.

В нашем же случае я не хотел бы убирать пробельные символы в коде HTML-списка, чтобы не ухудшать читаемость кода заданной HTML-страницы. Тогда можно применить другой способ: сделать размер шрифта равным нулю с помощью указания font-size: 0; в стиле нашего HTML-списка.

В-третьих, на иллюстрации видно, что внутри каждого пункта нашего HTML-списка, между нижней границей картинки со смайликом и нижней границей самого пункта тоже присутствует некий промежуток (у меня в браузере он составил 4px). Откуда он взялся?

Дело в том, что HTML-элемент img по умолчанию является строчным HTML-элементом, то есть его тип отображения — inline. А браузер старается расположить все строчные HTML-элементы в строке так, чтобы все они находились на базовом уровне строки. Что это за уровень? Это линия, на которой «стоят» все буквы в строке. У некоторых букв элементы этих букв «свисают» ниже базовой линии строки. В разных шрифтах по-разному, но зачастую, к примеру, у русской строчной буквы «у» или у английской буквы «g» нижняя закорючка «свисает» ниже базовой линии. И таких букв не так уж и мало в алфавитах и шрифтах. На этот случай браузер резервирует для строчных HTML-элементов определенное пространство ниже базового уровня (базовой линии) строки (в данном случае — 4px).

Чтобы проиллюстрировать описанное, добавим к нашему HTML-списку временный пункт с текстом:
<ul>
  <li><img src="https://ru.js.cx/carousel/1.png"></li>
  <li><img src="https://ru.js.cx/carousel/2.png"></li>
  <!-- ... -->
  <li><img src="https://ru.js.cx/carousel/10.png"></li>
  <li>Буквы: ф, р, g.</li> <!-- временный пункт -->
</ul>

Вот как это выглядит у меня в браузере (картинка):



Увеличим (я увеличил кусочек предыдущей картинки в графическом редакторе в 4 раза):



На этой иллюстрации я обозначил базовую линию тонкой красной линией. Текст четвертого пункта нашего HTML-списка отображен шрифтом по умолчанию (в моем браузере в настройках шрифтом по умолчанию установлен «Times New Roman»), размер шрифта тоже — по умолчанию (в моем браузере это 16px). В этом шрифте при этом размере строчные русские буквы «у», «ф», «р» и строчная английская буква «g», как видно на иллюстрации, «свисают» ниже базовой линии строки на 3 пикселя. Запятая «свисает» ниже базовой линии строки на 2 пикселя.

Теперь стало понятно, откуда взялся рассматриваемый промежуток. А как его убрать? Тут тоже есть несколько способов. Например, если поменять тип отображения HTML-элемента img с умолчательного inline на block с помощью указания display: block; в стиле HTML-элемента img, то HTML-элемент img станет не строчным, а блочным, браузер не станет резервировать место под «свисающие» буквы и рассматриваемый промежуток исчезнет.

Другой вариант — это вышеупомянутое указание font-size: 0; в стиле нашего HTML-списка. Если размер шрифта будет равен нулю, то браузеру не будет нужды резервировать место под «свисающие буквы» и рассматриваемый промежуток опять же исчезнет. Поэтому, думаю, стоит остановиться на этом варианте, потому что он устраняет сразу две проблемы (два вида промежутков).

Итак, мы организовали наш HTML-список картинок в горизонтальную очередь и убрали все ненужные отступы и промежутки между картинками. Однако, все вышеприведенные тесты проводились только для трех картинок. Если теперь добавить в HTML-список все 10 заданных изначально картинок, то те картинки, которые не поместятся в строке по ширине области просмотра браузера, перейдут на следующую строку. А нам нужно, чтобы все заданные картинки представляли очередь только в одну строку, даже если она выйдет за пределы области просмотра браузера. Для этого в стиль HTML-списка можно добавить указание width: 9999px;. Не обязательно, конечно, было брать такую большую ширину, но а вдруг в будущем нам захочется добавить картинок в карусель? Запас не помешает.

В итоге мы пока получили вот такой файл со стилями:
ul {
  margin: 0;             /* уберем внешние и внутренние умолчательные */
  padding: 0;            /* отступы, назначенные браузером */
  font-size: 0px;        /* уберем промежутки между картинками */
                         /* и промежутки для «свисающих» букв */
  width: 9999px;         /* разместим пункты списка в одну строку */
}

ul img {
  width: 130px;          /* размеры картинок */
  height: 130px;
}

ul li {
  display: inline-block; /* разместим пункты списка горизонтально */
                         /* и уберем маркеры списка */
}

Продолжение тут.