С помощью стандартной прокрутки и пары улучшений.

Для начала сверстаем горизонтальное меню:

<!-- index.html -->
<menu class="menu">
  <li class="menu-item">
    <a href="#">Новости</a>
  </li>
  <li class="menu-item">
    <a href="#">Проекты</a>
  </li>
  <li class="menu-item">
    <a href="#">Книги</a>
  </li>
  <!-- ... -->
</menu>
/* style.css */
.menu {
  display: flex;
  font: 24px/32px Bureausans, Arial, sans-serif;
}

.menu-item {
  margin-right: 12px;
}

.menu-item:last-child {
  margin-right: 0;
}

Теперь поместим меню в блок с ограниченной шириной и горизонтальной прокруткой:

Для прокрутки потащите меню мышью
<!-- index.html -->
<div class="menuLimiter">
  <menu class="menu">
    <li class="menu-item">
      <a href="#">Новости</a>
    </li>
    <li class="menu-item">
      <a href="#">Проекты</a>
    </li>
    <li class="menu-item">
      <a href="#">Книги</a>
    </li>
    <!-- ... -->
  </menu>
</div>
/* style.css */
.menuLimiter {
  width: 350px;
  padding: 0 10px;

  /* Зададим возможность горизонтальной прокрутки */
  overflow-x: scroll;

  /* Для Сафари дополнительно зададим инерцию при прокрутке.
  С инерцией меню будет «оттягиваться» при попытке прокрутить
  его за его пределы. */
  -webkit-overflow-scrolling: touch;
}

/* ... */

При прокрутке появляется горизонтальный скроллбар. В коротком меню он нам ни к чему, спрячем его:

<!-- index.html -->
<div class="menuWrapper">
  <div class="menuLimiter">
    <menu class="menu">
      <li class="menu-item">
        <a href="#">Новости</a>
      </li>
      <li class="menu-item">
        <a href="#">Проекты</a>
      </li>
      <li class="menu-item">
        <a href="#">Книги</a>
      </li>
      <!-- ... -->
    </menu>
  </div>
</div>
/* style.css */

.menuLimiter {
  /* ... */

  /* Добавим дополнительный нижнее поле
  элементу, в котором прокручивается меню */
  padding-bottom: 20px;

  /* С помощью отрицательного отступа
  компенсируем «нарощенное» поле  */
  margin-bottom: -20px;
}

.menuWrapper {
  /* Спрячем всё, что выходит за границы родителя */
  overflow: hidden;
}

Добавим немного косметики в виде «забеления» по краям с помощью маски:

/* style.css */

.menuLimiter {
  /* ... */
  mask-image: linear-gradient(
      90deg,
      transparent 0, rgba(0, 0, 0, .25) 9px,
      #000 18px,
      #000 calc(100% - 18px),
      rgba(0, 0, 0, .25) calc(100% - 9px),
      transparent
    );
}

Осталась одна проблема. При некоторых ширинах экрана меню обрезается так, что кажется, что всё поместилось и прокручивать нечего:

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

/* style.css */

@media only screen and (max-width: 400px) {
  .menu-item { margin-right: 18px; }
}

@media only screen and (max-width: 426px) {
  .menu-item { margin-right: 8px; }
}

@media only screen and (max-width: 450px) {
  .menu-item { margin-right: 9px; }
}

@media only screen and (max-width: 470px) {
  .menu-item { margin-right: 18px; }
}

@media only screen and (max-width: 510px) {
  .menu-item { margin-right: 18px; }
}

P. S. Это был совет о веб‑разработке. Хотите знать всё о коде, тестах, фронтенд‑разработке, цеэсэсе, яваскрипте, рельсах и джейде? Присылайте вопросы.

Веб‑разработка
Отправить
Поделиться
Запинить

Комментарии

Вместо:
.menu‑item { margin‑right: 15px; }
.menu‑item:last‑child { margin‑right: 0; }

Можно написать:
.menu‑item:not(:last‑child) { margin‑right: 15px; }

Строчек вдвое меньше, нет переопределения.

15 июня 2018

Руст, почему у вас menu → li ? Разве это допустимо, чтобы лист‑айтем был где‑то кроме листа?

15 июня 2018

У PostCSS есть плагин для автоматического включения ‑webkit‑overflow‑scrolling: touch

18 июня 2018

Андрей, menu ведёт себя как неупорядоченный список (ul), когда внутри него содержится хотя бы один элемент li:
https://html.spec.whatwg.org/…‑content.html#the‑menu‑element
https://developer.mozilla.org/…‑US/docs/Web/HTML/Element/menu

В остальных случая это обычный контейнер.

18 июня 2018

Руст, я обыскался весь, обгуглился, но не могу понять, как у вас сделано так, что вот это мобильное меню в примерах таскается мышью на десктопе. Это по сути не особо надо, но просто любопытно. Просветите, что за хак?

17 авг 2018

Андрей, за это отвечает микро‑библиотека: https://github.com/asvd/dragscroll

22 авг 2018

Заходим с телефона на главную. Там меню в виде:

Новости Проекты Книги Школа …

От ссылки "Школа" видна первая только буква. Жмем на нее.
Открывается соответствующая страница и "Школа" отображается по центру, хотя порядок элементов меню прежний.

Вопрос: каким образом ?

26 сен 2018

Плавность прокрутки (инерция) чем осуществляется?
Что‑то гуглю и не могу найти.

19 окт 2018

Александр, инерция для прокручивающегося блока задаётся с помощью свойства ‑webkit‑overflow‑scrolling: touch

13 ноя 2018

Алексей, Вася Половнёв ответил вам в отдельном совете:
https://bureau.ru/soviet/20181018/

13 ноя 2018
Александр Краснов

Как написать скрипт, который будет анализировать ширину элементов и расставлять их так, чтобы один из элементов всегда обрезался?

16 сен 2021

А как при этом сделать дропдаун меню? Оно будет обрезаться oveflow: hidden и в целом, контейнером.

8 июня 2024

Евгений, для этого придётся рендерить выпадайку снаружи контейнера. Чтобы сделать это самому, нужно будет определять координаты и размеры активного элемента с помощью JS, а потом двигать выпадайку в нужное место. Или можно воспользоваться одной из современных библиотек, поддерживающих такое поведение. Например: https://floating-ui.com/

19 июня 2024

Рекомендуем другие советы