От автора: липкие таблицы прайсов идеально подходят для представления товаров и услуг с помощью длинных списков сопоставляемых функций. В этом руководстве мы собираемся создать таблицу с липким заголовком; попутно мы узнаем, как закреплять и откреплять элементы после определенной точки прокрутки.
Существует множество плагинов JavaScript, предлагающих такой функционал, например ScrollMagic.js.
Однако сегодня давайте бросим себе вызов и напишем собственный код.
Таблица прайсов, которую мы создаем
Для этого практического примера мы разработаем страницу прайсов, которая будет включать в столбцах различные планы подписки. По мере прокрутки вниз заголовок становится липким, чтобы оставаться на виду, а затем будет откреплен позднее. Таблица прайсов с комбинированным заголовком — это популярный шаблон пользовательского интерфейса, который вы, возможно, видели на таких сайтах, как monday.com.
Имейте в виду, что липкий функционал нашей демо запускается на окнах просмотра шире, чем 779 пикселей. Возможно, вам придется посмотреть полноэкранное демо, чтобы полностью оценить его!
Когда следует использовать таблицу прайсов?
Таблицы прайсов полезны, когда набор товаров, услуг или вариантов слишком широк, что затрудняет потребителю выбор. Если потребителю трудно решить, какой вариант выбрать, он, скорее всего, не выберет ничего. Информационная перегрузка = ошибка конверсии.
Потеря времени на принятие решений сводит на нет все преимущества, связанные с выбором!
«Исследования […] показывают, что избыток вариантов выбора часто приводит к тому, что мы становимся менее, а не более удовлетворенными, когда принимаем само решение». — Алина Тугенд, Нью-Йорк Таймс
Роль таблицы прайсов заключается в том, чтобы помочь пользователю визуально интерпретировать доступные ему варианты, возможно, иногда даже побуждая сосредоточиться на наиболее привлекательном выборе.
Плагины таблицы прайсов для WordPress
Прежде чем углубиться в рассмотрение темы руководства, вы можете взглянуть на набор плагинов таблицы прайсов для WordPress, доступных на Envato Market.
Один из многих примеров из ARPrice — плагин таблиц прайсов WordPress
С учетом вышесказанного, давайте теперь поможем нашим пользователям, создав для них супер понятную таблицу прайсов!
1. Начинаем с разметки страницы
Начнем с трех разделов:
<section>...</section> <section>...</section> <section>...</section>
Первый и третий разделы не будут играть важную роль; они будут содержать демо-контент и стили, чтобы гарантировать, что страница будет достаточно длинной для запуска желаемого эффекта прокрутки. Во втором разделе мы разместим таблицу. Мы обернем ее в контейнер, который будет адаптироваться к малым экранам:
<div class="container"> <div class="table-wrapper"> <table> <thead> <tr> <th>...</th> <th>...</th> <th>...</th> <th>...</th> </tr> </thead> <tbody> <tr> <td>...</td> <td>...</td> <td>...</td> <td>...</td> </tr> <!-- more rows here --> </tbody> </table> </div> </div>
Сама таблица будет содержать четыре столбца и представляет тарифные планы на товар или услугу. В нашем случае у нас будет три тарифных плана: начальный, базовый и профессиональный.
Заголовки таблицы
Липкий заголовок таблицы четко идентифицирует эти планы. Каждый план также будет включать кнопку призыва к действию.
Вот разметка для заголовков таблицы:
<tr> <th> <div> Select your plan <div class="svg-wrapper"> <svg viewBox="0 0 24 24"><path d="M12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm1 17v-4h-8v-2h8v-4l6 5-6 5z"/></svg> </div> </div> </th> <th> <div class="heading">...</div> <div class="info"> <div class="amount">...</div> <div class="billing-msg">...</div> <button type="button">...</button> </div> </th> <th> <div class="heading">...</div> <div class="info"> <div class="popular">...</div> <div class="amount">...</div> <div class="billing-msg">...</div> <button type="button">...</button> </div> </th> <th> <div class="heading">...</div> <div class="info"> <div class="amount">...</div> <div class="billing-msg">...</div> <button type="button">...</button> </div> </th> </tr>
Строки таблицы
Каждая строка таблицы будет описывать доступность функции во всех планах. Если план включает эту функцию, отображается SVG-иконка галочки. В противном случае отображается иконка серого крестика.
Вот разметка для функции, которая поддерживается всеми планами:
<tr> <td>...</td> <td> <svg class="starter" viewBox="0 0 24 24"><path d="M12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm-1.959 17l-4.5-4.319 1.395-1.435 3.08 2.937 7.021-7.183 1.422 1.409-8.418 8.591z"/></svg> </td> <td> <svg class="essential" viewBox="0 0 24 24"><path d="M12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm-1.959 17l-4.5-4.319 1.395-1.435 3.08 2.937 7.021-7.183 1.422 1.409-8.418 8.591z"/></svg> </td> <td> <svg class="professional" viewBox="0 0 24 24"><path d="M12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm-1.959 17l-4.5-4.319 1.395-1.435 3.08 2.937 7.021-7.183 1.422 1.409-8.418 8.591z"/></svg> </td> </tr>
А вот разметка для функции, которая доступна только в «Профессиональном» плане:
<tr> <td>...</td> <td> <svg class="not-included" viewBox="0 0 24 24"><path d="M12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm4.151 17.943l-4.143-4.102-4.117 4.159-1.833-1.833 4.104-4.157-4.162-4.119 1.833-1.833 4.155 4.102 4.106-4.16 1.849 1.849-4.1 4.141 4.157 4.104-1.849 1.849z"/></svg> </td> <td> <svg class="not-included" viewBox="0 0 24 24"><path d="M12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm4.151 17.943l-4.143-4.102-4.117 4.159-1.833-1.833 4.104-4.157-4.162-4.119 1.833-1.833 4.155 4.102 4.106-4.16 1.849 1.849-4.1 4.141 4.157 4.104-1.849 1.849z"/></svg> </td> <td> <svg class="professional" viewBox="0 0 24 24"><path d="M12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm-1.959 17l-4.5-4.319 1.395-1.435 3.08 2.937 7.021-7.183 1.422 1.409-8.418 8.591z"/></svg> </td> </tr>
2. Определение основных стилей
Когда разметка готова, мы перейдем к CSS. Нашим первым шагом является настройка некоторых переменных CSS и общих стилей сброса.
Для каждого плана мы определим соответствующую ему переменную. Это даст нам возможность легко изменить при необходимости его внешний вид.
Вот стили сброса:
:root { --white: white; --gray: #999; --lightgray: whitesmoke; --popular: #ffdd40; --starter: #f73859; --essential: #00AEEF; --professional: #FF7F45; } * { padding: 0; margin: 0; box-sizing: border-box; } button { background: none; border: none; cursor: pointer; } table { border-collapse: collapse; } body { font: 18px/1.5 'Noto Sans', sans-serif; background: var(--lightgray); }
Примечание: для простоты я не буду пояснять в этом руководстве все правила CSS. Это почти 290 строк CSS. Я буду рассматривать только самые важные из них. Не забудьте посмотреть их все, нажав на вкладку CSS демонстрационного проекта.
3. Стиль таблицы прайсов
Таблица прайсов будет иметь максимальную ширину и будет горизонтально центрирована на странице:
.container { max-width: 850px; padding: 0 10px; margin: 0 auto; } table { width: 100%; }
Строки будут вести себя как flex-контейнеры:
table tr { display: flex; }
Все ячейки будут иметь одинаковую ширину:
table th, table td { width: 25%; min-width: 150px; }
Чтобы разделить планы и дать четкое представление о том, какая ячейка принадлежит каждому плану, мы добавим к целевым элементам светло-серую рамку:
--lightgray: whitesmoke; table th .info, table td:not(:first-child) { border-left: 1px solid var(--lightgray); }
Делаем таблицу прайсов адаптивной
Как мы увидим в следующем разделе, эффект закрепления работает на окнах просмотра размером более 779 пикселей (вы можете изменить это значение). В маленьких окнах просмотра мы покажем стандартную таблицу, которую можно прокручивать по горизонтали.
Bootstrap также использует аналогичные функции для адаптивных таблиц. Существуют и другие (лучшие?) подходы для обработки адаптивных таблиц, но этого метода, безусловно, для нас достаточно.
4. Закрепление / открепление заголовка таблицы
Имея HTML и CSS, мы сосредоточимся на эффекте прокрутки, используя немного JavaScript. Это позволит нам сделать заголовок липким.
Переменные
Для начала мы возьмем копию желаемых элементов. Мы сохраним в переменных два класса, которые будем использовать позже:
const body = document.body; const firstSection = document.querySelector("section:nth-child(1)"); const lastSection = document.querySelector("section:nth-child(3)"); const table = document.querySelector("table"); const thead = document.querySelector("table thead"); const mq = window.matchMedia("(min-width: 780px)"); const stickyClass = "sticky-table"; const sticky2Class = "sticky2-table";
Следующим шагом является выполнение некоторых расчетов. В частности, мы хотим рассчитать следующие вещи:
Ширина таблицы.
Верхняя позиция таблицы относительно области просмотра.
Высота thead. Этот элемент будет закрепляться / открепляться.
Вот необходимый JavaScript для этого:
let tableWidth = table.offsetWidth; let tableOffsetTop = table.getBoundingClientRect().top; let theadHeight = thead.offsetHeight;
Обратите внимание, что мы храним вышеупомянутые значения в переменных let. Мы сделали это намеренно. Когда размер страницы изменяется, мы должны пересчитать вещи, описанные выше, и, таким образом, переназначить эти новые значения переменным.
При прокрутке
Каждый раз, когда мы прокручиваем страницу вверх или вниз, будет выполняться функция scrollHandler. Внутри этой функции мы выполним следующие действия, которые будут выполняться, только если ширина окна не менее 780 пикселей:
Получить количество пикселей, на которые пользователь прокрутил страницу от верхней части области просмотра.
Получить верхнюю позицию последнего раздела относительно области просмотра.
Проверить, прокрутил ли пользователь больше или столько же пикселей от начальной верхней позиции таблицы.
Если это произойдет, мы устанавливаем ширину thead, равную начальной ширине таблицы.
Затем мы проверяем, больше ли значение, полученное в шаге 2, чем высота thead.
Если это так, мы закрепляем элемент thead, добавляя класс sticky-table для body и удаляя класс sticky-table2 из того же элемента. В этот момент элемент thead становится фиксированным. Затем мы размещаем его в верхней части окна просмотра. Мы также задаем для body верхний отступ, равный высоте thead.
Если этого не так, мы прекращаем закрепление thead, добавляя sticky-table2 класс для body и удаляя класс sticky-table для того же элемента. В этот момент элемент thead открепляется и становится позиционированным абсолютно. Затем мы размещаем его в нижней части таблицы.
В случае , если пользователь прокрутил меньше начальной верхней позиции таблицы (thead еще не откреплен), мы удаляем из body классы sticky-table и stick-table2. Кроме того, мы устанавливаем его верхний отступ равным 0. В этот момент у thead нет позиции (статический элемент), поэтому мы сбрасываем его положение top на значение по умолчанию. Наконец, мы устанавливаем его ширину равной 100% (мы можем пропустить это).
Код, который реализует все это поведение, выглядит следующим образом:
window.addEventListener("scroll", scrollHandler); function scrollHandler() { if (mq.matches) { // 1 const scrollY = window.pageYOffset; // 2 const lastSectionOffsetTop = lastSection.getBoundingClientRect().top; // 3 if (scrollY >= tableOffsetTop) { // 4 thead.style.width = `${tableWidth}px`; // 5 if (lastSectionOffsetTop > theadHeight) { // 6 body.classList.remove(sticky2Class); body.classList.add(stickyClass); thead.style.top = 0; body.style.paddingTop = `${theadHeight}px`; } else { // 7 body.classList.remove(stickyClass); body.classList.add(sticky2Class); thead.style.top = `calc(100% - ${theadHeight}px)`; } } else { // 8 body.classList.remove(stickyClass, sticky2Class); body.style.paddingTop = 0; thead.style.width = "100%"; thead.style.top = "auto"; } } }
И связанные стили для каждого класса:
table thead { transition: box-shadow 0.2s; } .sticky-table table thead { position: fixed; left: 50%; transform: translateX(-50%); } .sticky-table table thead { box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.12); } .sticky2-table table thead { position: absolute; left: 0; }
5. Изменение размера страницы
Мы почти закончили! Пока что эффект липкого заголовка будет хорошо работать при загрузке страницы. Но что происходит, когда размер страницы изменяется? Ну, было бы здорово, если бы мы могли заставить эту демонстрацию работать и при изменении размера, верно? Давай сделаем это!
Поэтому каждый раз, когда мы изменяем размер страницы, будет выполняться функция resizeHandler.
Внутри этой функции мы сначала проверяем ширину окна, а затем либо обновляем значения вышеупомянутых переменных let, либо сбрасываем стили для thead и body. Обратите внимание, что мы получаем верхнюю позицию таблицы, беря высоту первой секции. Вы можете быть удивлены, почему мы не использовали свойство таблицы offsetTop? Во время моих тестов я заметил, что это не всегда дает точные результаты при изменении размера. Кроме того, getBoundingClientRect() не будет работать, потому что он также дает неправильные (даже отрицательные) значения.
Требуемый код JavaScript:
window.addEventListener("resize", resizeHandler); function resizeHandler() { if (mq.matches) { tableWidth = firstSection.offsetHeight; tableOffsetTop = table.offsetTop; theadHeight = thead.offsetHeight; } else { body.classList.remove(stickyClass, sticky2Class); body.style.paddingTop = 0; thead.style.width = "100%"; thead.style.top = "auto"; } }
Заключение
Вот и все, ребята! В этом руководстве нам удалось создать эффект прокрутки без использования какой-либо внешней библиотеки. Мы не только узнали, как создавать липкие элементы, но также и как откреплять (отпускать) их после определенного значения прокрутки.
Все это вместе дало нам действительно полезную таблицу прайсов с липким заголовком. Я надеюсь, что это упражнение помогло вам узнать что-то новое и вдохновило использовать его в будущем проекте. Прежде чем закончить, я хотел бы выделить две вещи:
Я использовал для создания этого эффекта элемент table. Это не всегда может быть идеальным методом, учитывая тот факт, что сегодня существуют более гибкие и мощные решения для разметки, такие как CSS Grid. Однако таблица часто является лучшим элементом для отображения данных, поэтому я воспользовался этим.
Представлять табличные данные на мобильных устройствах всегда сложно. В данном случае я применил простое решение прокрутки. Другой подход может состоять в том, чтобы полностью отказаться от табличного метода (оставить его только для > 779 пикселей) и применить решение карусели с тремя слайдами, где каждый слайд будет представлять тарифный план. Возможно, у вас есть идея получше, которую мы могли бы обсудить в комментариях ниже.
Как всегда, большое спасибо за прочтение!
Автор: George Martsoukos
Источник: https://webdesign.tutsplus.com
Редакция: Команда webformyself.