Динамический макет CSS для мегаменю, которое читается строками или столбцами в зависимости от количества пунктов меню

Я создаю прототип мегаменю, в котором пункты меню должны отображаться «умно», где, в зависимости от количества пунктов меню, они будут читаться либо в виде строки, либо в виде столбцов.

У пунктов меню будут свои дочерние элементы, поэтому на самом деле они такие:

 mega menu left text | menu item 1   menu item 2   menu item 3
                     | sublink1 A    sublink2 A    sublink3 A
                     | sublink1 B    sublink2 B
                     | sublink1 C

Я не включаю приведенные ниже для простоты.

Отображение будет зависеть от количества пунктов меню. Например, если бы в меню было 1–3 пункта, оно выглядело бы как строка:

mega menu left text | menu item 1   menu item 2   menu item 3

Если бы в меню было более трех пунктов, оно читалось бы как столбцы: сверху вниз, затем вправо, но все равно нужно быть «умным», поскольку пункт меню 4 находится в отдельном столбце, а не под пунктом меню. 3 (всегда необходимо поддерживать 3 столбца с правой стороны):

mega menu left text | menu item 1   menu item 3   menu item 4
                    | menu item 2

5 пунктов меню:

mega menu left text | menu item 1   menu item 3   menu item 5
                    | menu item 2   menu item 4

6 пунктов меню:

mega menu left text | menu item 1   menu item 3   menu item 5
                    | menu item 2   menu item 4   menu item 6

7 пунктов меню:

mega menu left text | menu item 1   menu item 4   menu item 7
                    | menu item 2   menu item 5
                    | menu item 3   menu item 6

8 пунктов меню:

mega menu left text | menu item 1   menu item 4   menu item 7
                    | menu item 2   menu item 5   menu item 8
                    | menu item 3   menu item 6

9 пунктов меню:

mega menu left text | menu item 1   menu item 4   menu item 7
                    | menu item 2   menu item 5   menu item 8
                    | menu item 3   menu item 6   menu item 9

10 пунктов меню:

mega menu left text | menu item 1   menu item 5   menu item 9
                    | menu item 2   menu item 6   menu item 10
                    | menu item 3   menu item 7   
                    | menu item 4   menu item 8

Есть ли способ сделать это элегантно в CSS? Я думал, что столбцы CSS могут сделать это, но это не совсем работает. Вот скрипт/демо: если вы переместите ползунок слева, вы увидите, куда он упадет:

https://jsfiddle.net/vsdpjwn9/
Здесь используется break-inside: avoid-column; на <li>

https://jsfiddle.net/3x8scuwt/
Здесь используется display: inline-block; на <li>

Я также попробовал отдельный прототип с флексбоксом, но он, похоже, развалился быстрее, поэтому я отказался от него. Похоже, что CSS-сетка будет такой же. Дело в том, что эти пункты меню будут динамическими, их будет разное количество, каждый с разным количеством подссылок. Вероятно, будет не более 3 строк, но я не хочу от этого зависеть.

Любое решение css или потребует, чтобы javascript установил собственный макет в зависимости от количества отдельных пунктов меню?

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

Вот один из кодов скрипки:

HTML:

<div id = "container">
  <div class = "left">
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.

    <div style = "margin-top:1rem;">
      <input id = "slider" type = "range" min = "1" max = "8" value = "8">
      <p id = "slider-output" style = "margin-top:0;"></p>
    </div>

  </div>

  <div class = "right">
    <ul>
      <li class = "1">
          <h4>1 Secondary Content</h4>
          <a href = "#a1">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
          <a href = "#a2">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
          <a href = "#a3">Lorem ipsum dolora</a><br>
          <a href = "#a4">Lorem ipsum dolora</a><br>
          <a href = "#a5">Lorem ipsum dolora</a>
      </li>

      <li class = "2">
          <h4>2 Secondary Content</h4>
          <a href = "#b1">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
          <a href = "#b2">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
          <a href = "#b3">Lorem ipsum dolora</a><br>
          <a href = "#b4">Lorem ipsum dolora</a><br>
          <a href = "#b5">Lorem ipsum dolora</a><br>
          <a href = "#b6">Lorem ipsum dolora</a>
      </li>

      <li class = "3">
          <h4>3 Secondary Content</h4>
          <a href = "#c1">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
          <a href = "#c2">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
          <a href = "#c3">Lorem ipsum dolora</a><br>
          <a href = "#c4">Lorem ipsum dolora</a>
      </li>

      <li class = "4">
          <h4>4 Secondary Content</h4>
          <a href = "#d1">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
          <a href = "#d4">Lorem ipsum dolora</a>
      </li>

      <li class = "5">
          <h4>5 Secondary Content</h4>
          <a href = "#e1">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
          <a href = "#e2">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
          <a href = "#e3">Lorem ipsum dolora</a><br>
          <a href = "#e4">Lorem ipsum dolora</a><br>
          <a href = "#e5">Lorem ipsum dolora</a>
      </li>

      <li class = "6">
          <h4>6 Secondary Content</h4>
          <a href = "#f1">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
          <a href = "#f2">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
          <a href = "#f3">Lorem ipsum dolora</a><br>
          <a href = "#f4">Lorem ipsum dolora</a><br>
          <a href = "#f5">Lorem ipsum dolora</a><br>
          <a href = "#f6">Lorem ipsum dolora</a>
      </li>

      <li class = "7">
          <h4>7 Secondary Content</h4>
          <a href = "#g1">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
          <a href = "#g2">Lorem ipsum dolora</a>
      </li>
      
      <li class = "8">
          <h4>8 Secondary Content</h4>
          <a href = "#h1">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
          <a href = "#h2">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
          <a href = "#h3">Lorem ipsum dolora</a><br>
          <a href = "#h4">Lorem ipsum dolora</a><br>
      </li>
    </ul>
  </div>
</div>

CSS:

    #container {
    width: 900px;
    min-height: 400px;
    display: flex;
    gap: 40px;
    padding: 1em;
    border: 1px solid #ccc;
}
.left {
    width: 25%;
    padding-right: 40px;
    border-right: 1px solid #ccc;
}
.right {
    width: 100%;
    margin-left: auto;
}
/*
ul {
  border: 1px solid red;
  height: 400px;
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;
  margin: 0;
  padding: 0;
}
*/

ul {
  list-style: none;
  columns: 3;
  column-gap: 25px;
  margin: 0;
  padding: 0;
  width: 98%;
  border: 1px solid blue;
}

li {
  border: 1px solid red;
  list-style-type: none;
  margin: 0 0 15px 0;
  
  /*display: inline-block;*/
  break-inside: avoid-column;
  
}
li:first-child {
  margin-top: 0;
}

li a {
  display: inline-block;
  margin: .125rem 0;
}

h4 {
  margin: 0;
}

[hidden] {
  display: none !important;
}

ЯС:

    const slider = document.getElementById('slider');
const output = document.getElementById('slider-output');
const LIs = document.querySelectorAll('li');

output.innerHTML = slider.value;

slider.oninput = function() {
  output.innerHTML = this.value;

  LIs.forEach(li => {
    if (+li.classList.value > slider.value) {
      li.setAttribute('hidden', '');
    } else {
      li.removeAttribute('hidden');
    }
  });
}

Помогите мне понять вашу идею. Зачем переключаться на столбцы, а не заполнять строки, когда места больше нет, как это сделала бы простая сетка? Кроме того, готовы ли вы написать для него какой-нибудь сложный и потенциально хрупкий JS или вам нужно чистое CSS-решение (но, возможно, более хрупкое)?

Mr. Hedgehog 25.04.2024 19:35

Таковы требования.

magenta placenta 25.04.2024 20:14

@magentaplacenta Требуется ли чистый CSS? Если да, то для этого я знаю только хакерское и довольно многословное решение на чистом CSS, которое плохо масштабируется.

SyndRain 29.04.2024 20:12
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Введение в CSS
Введение в CSS
CSS является неотъемлемой частью трех основных составляющих front-end веб-разработки.
Как выровнять Div по центру?
Как выровнять Div по центру?
Чтобы выровнять элемент <div>по горизонтали и вертикали с помощью CSS, можно использовать комбинацию свойств и значений CSS. Вот несколько методов,...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
CSS: FlexBox
CSS: FlexBox
Ранее разработчики использовали макеты с помощью Position и Float. После появления flexbox сценарий полностью изменился.
2
3
107
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

Однако это можно сделать довольно легко с помощью сетки CSS и небольшого количества JavaScript, изменяющего количество строк.

Единственная дополнительная сложность — это случай, когда элементов всего 4, по умолчанию последний li будет помещен во второй столбец, поэтому его необходимо указать в третьем столбце. Это можно сделать с помощью CSS или JavaScript.

ul {
    display: grid;
    --row-number: 3;
    grid-template-rows: repeat(var(--row-number), 1fr);
    grid-template-columns: repeat(3, 1fr);
    grid-auto-flow: column;
}

/*Use CSS for the special case*/
li:nth-child(4 of :not([hidden])):nth-last-child(-n + 1 of :not([hidden])) {
    grid-column: 3;
}
function updateColumnNumber() {
    ul.style.setProperty('--row-number', Math.ceil(slider.value / 3));
}

const slider = document.getElementById('slider');
const output = document.getElementById('slider-output');
const LIs = document.querySelectorAll('li');
const ul = document.getElementById('right-grid');
output.innerHTML = slider.value;

slider.oninput = function () {
    output.innerHTML = this.value;

    LIs.forEach(li => {
        if (+li.classList.value > slider.value) {
            li.setAttribute('hidden', '');
        } else {
            li.removeAttribute('hidden');
        }
    });
    updateColumnNumber();
}

let li4 = null;
function updateColumnNumber() {
    /*Or use JS for the special case*/
    /*li4?.style.removeProperty('grid-column');
    if (slider.value == 4) {
        li4 = ul.querySelectorAll("li:not([hidden])")[3];
        li4?.style.setProperty('grid-column', 3)
    }*/

    ul.style.setProperty('--row-number', Math.ceil(slider.value / 3));
}

updateColumnNumber();
#container {
  width: 900px;
  min-height: 400px;
  display: flex;
  gap: 40px;
  padding: 1em;
  border: 1px solid #ccc;
}

.left {
  width: 25%;
  padding-right: 40px;
  border-right: 1px solid #ccc;
}

.right {
  width: 100%;
  margin-left: auto;
}

ul {
  list-style: none;
  display: grid;
  --row-number: 3;
  grid-template-rows: repeat(var(--row-number), 1fr);
  grid-template-columns: 1fr 1fr 1fr;
  grid-auto-flow: column;
  gap: 25px;
  margin: 0;
  padding: 0;
  width: 98%;
  border: 1px solid blue;
}

li:nth-child(4 of :not([hidden])):nth-last-child(-n + 1 of :not([hidden])) {
    grid-column: 3;
}

li {
  box-sizing: border-box;
  border: 1px solid red;
  list-style-type: none;
  margin: 0 0 15px 0;
  /*display: inline-block;*/
  break-inside: avoid-column;
}

li:first-child {
  margin-top: 0;
}

li a {
  display: inline-block;
  margin: .125rem 0;
}

h4 {
  margin: 0;
}

[hidden] {
  display: none !important;
}
<div id = "container">
  <div class = "left">
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.

    <div style = "margin-top:1rem;">
      <input id = "slider" type = "range" min = "1" max = "10" value = "10">
      <p id = "slider-output" style = "margin-top:0;"></p>
    </div>

  </div>

  <div class = "right">
    <ul id = "right-grid">
      <li class = "1">
        <h4>1 Secondary Content</h4>
        <a href = "#a1">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
        <a href = "#a2">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
        <a href = "#a3">Lorem ipsum dolora</a><br>
        <a href = "#a4">Lorem ipsum dolora</a><br>
        <a href = "#a5">Lorem ipsum dolora</a>
      </li>

      <li class = "2">
        <h4>2 Secondary Content</h4>
        <a href = "#b1">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
        <a href = "#b2">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
        <a href = "#b3">Lorem ipsum dolora</a><br>
        <a href = "#b4">Lorem ipsum dolora</a><br>
        <a href = "#b5">Lorem ipsum dolora</a><br>
        <a href = "#b6">Lorem ipsum dolora</a>
      </li>

      <li class = "3">
        <h4>3 Secondary Content</h4>
        <a href = "#c1">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
        <a href = "#c2">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
        <a href = "#c3">Lorem ipsum dolora</a><br>
        <a href = "#c4">Lorem ipsum dolora</a>
      </li>

      <li class = "4">
        <h4>4 Secondary Content</h4>
        <a href = "#d1">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
        <a href = "#d4">Lorem ipsum dolora</a>
      </li>

      <li class = "5">
        <h4>5 Secondary Content</h4>
        <a href = "#e1">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
        <a href = "#e2">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
        <a href = "#e3">Lorem ipsum dolora</a><br>
        <a href = "#e4">Lorem ipsum dolora</a><br>
        <a href = "#e5">Lorem ipsum dolora</a>
      </li>

      <li class = "6">
        <h4>6 Secondary Content</h4>
        <a href = "#f1">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
        <a href = "#f2">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
        <a href = "#f3">Lorem ipsum dolora</a><br>
        <a href = "#f4">Lorem ipsum dolora</a><br>
        <a href = "#f5">Lorem ipsum dolora</a><br>
        <a href = "#f6">Lorem ipsum dolora</a>
      </li>

      <li class = "7">
        <h4>7 Secondary Content</h4>
        <a href = "#g1">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
        <a href = "#g2">Lorem ipsum dolora</a>
      </li>

      <li class = "8">
        <h4>8 Secondary Content</h4>
        <a href = "#h1">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
        <a href = "#h2">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
        <a href = "#h3">Lorem ipsum dolora</a><br>
        <a href = "#h4">Lorem ipsum dolora</a><br>
      </li>

      <li class = "9">
        <h4>9 Secondary Content</h4>
        <a href = "#h1">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
        <a href = "#h2">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
        <a href = "#h3">Lorem ipsum dolora</a><br>
        <a href = "#h4">Lorem ipsum dolora</a><br>
      </li>

      <li class = "10">
        <h4>10 Secondary Content</h4>
        <a href = "#h1">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
        <a href = "#h2">Lorem ipsum dalora itmet vistafa alotma pastruma</a><br>
        <a href = "#h3">Lorem ipsum dolora</a><br>
        <a href = "#h4">Lorem ipsum dolora</a><br>
      </li>
    </ul>
  </div>
</div>

Эй, спасибо! Это работает хорошо, если у вас нет только 1 или 2 предметов. В таких случаях будет лучше, если вы включите Grid-template-columns: 200px (размер для этого прототипа). Таким образом, все сохраняет ощущение «трех столбцов», независимо от количества элементов навигации. Видите ли вы какие-либо потенциальные проблемы при использовании такого решения, как ваше?

magenta placenta 29.04.2024 20:48

Пожалуйста! Если у вас есть только 1 или 2 элемента, и вы все равно хотите сохранить 3 столбца, вы можете просто установить grid-template-columns: repeat(3, 1fr), если жесткое кодирование ширины нежелательно. Других потенциальных проблем, если --row-number обновлено правильно, я не вижу.

SyndRain 29.04.2024 21:10

Просто помните, что вам нужно запускать метод обновления после инициализации количества статей, а также каждый раз, когда это число обновляется. Это проблема для JS-решений, хотя в более широком масштабе, возможно, вы можете рассмотреть возможность использования Прокси, чтобы упростить синхронизацию номера с --row-number.

SyndRain 29.04.2024 21:12

Другие вопросы по теме