Имейте липкую панель навигации поверх содержащих блоков

Я долго ломал голову над тем, как сделать липкую панель навигации поверх содержимого страницы.

У меня есть веб-страница, содержащая заголовок, основной блок и нижний колонтитул.

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

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

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

Как бы мне это сделать?

.header,
.footer {
  background-color: silver;
  height: 40vh;
}

.main {
  display: flex;
}

.nav > .navcontent {
  position: sticky;
  top: 0;
}

.content > .block {
  height: 80vh;
  padding-left: 20%;
  padding-right: 20%;
}

.content > .block.-b1 {
  background-color: aqua;
}

.content > .block.-b2 {
  background-color: teal;
}

.content > .block.-b3 {
  background-color: blue;
}
<header class = "header">
  <h1>Header</h1>
</header>
<main class = "main">
  <div class = "content">
    <div class = "block -b1">
      <h1>My content</h1>
      <p>Integer interdum varius diam. Nam aliquam  velit a pede. Vivamus dictum nulla et wisi. Vestibulum a massa.  Donec vulputate nibh vitae risus dictum varius. Nunc suscipit, nunc  nec facilisis convallis, lacus ligula bibendum nulla, ac  sollicitudin sapien nisl fermentum velit. Lorem ipsum dolor sit  amet, consectetuer adipiscing elit. Nullam commodo dui ut augue  molestie scelerisque. Sed aliquet rhoncus tortor. Fusce laoreet,  turpis a facilisis tristique, leo mauris accumsan tellus, vitae  ornare lacus pede sit amet purus. Sed dignissim velit vitae ligula.  Sed sit amet diam sit amet arcu luctus ullamcorper.</p>
    </div>
    <div class = "block -b2">
      <p>Integer interdum varius diam. Nam aliquam  velit a pede. Vivamus dictum nulla et wisi. Vestibulum a massa.  Donec vulputate nibh vitae risus dictum varius. Nunc suscipit, nunc  nec facilisis convallis, lacus ligula bibendum nulla, ac  sollicitudin sapien nisl fermentum velit. Lorem ipsum dolor sit  amet, consectetuer adipiscing elit. Nullam commodo dui ut augue  molestie scelerisque. Sed aliquet rhoncus tortor. Fusce laoreet,  turpis a facilisis tristique, leo mauris accumsan tellus, vitae  ornare lacus pede sit amet purus. Sed dignissim velit vitae ligula.  Sed sit amet diam sit amet arcu luctus ullamcorper.</p>
    </div>
    <div class = "block -b3">
      <p>Integer interdum varius diam. Nam aliquam  velit a pede. Vivamus dictum nulla et wisi. Vestibulum a massa.  Donec vulputate nibh vitae risus dictum varius. Nunc suscipit, nunc  nec facilisis convallis, lacus ligula bibendum nulla, ac  sollicitudin sapien nisl fermentum velit. Lorem ipsum dolor sit  amet, consectetuer adipiscing elit. Nullam commodo dui ut augue  molestie scelerisque. Sed aliquet rhoncus tortor. Fusce laoreet,  turpis a facilisis tristique, leo mauris accumsan tellus, vitae  ornare lacus pede sit amet purus. Sed dignissim velit vitae ligula.  Sed sit amet diam sit amet arcu luctus ullamcorper.</p>
    </div>
  </div>
  <nav class = "nav">
    <ul class = "navcontent">
      <li>Link 1
      <li>Link 2
      <li>Link 3
    </ul>
  </nav>
</main>
<footer class = "footer">
  <h1>Footer</h1>
</footer>

Должна ли ваша навигация иметь фиксированную ширину или быть адаптивной?

t.niese 02.07.2024 14:49

Я стремился к фиксированной ширине (это зависело от экрана пользователя).

Mat 02.07.2024 14:54
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Введение в 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. Это простой сайт, ничего вычурного. Основная цель -...
Toor - Ангулярный шаблон для бронирования путешествий
Toor - Ангулярный шаблон для бронирования путешествий
Toor - Travel Booking Angular Template один из лучших Travel & Tour booking template in the world. 30+ валидированных HTML5 страниц, которые помогут...
2
2
82
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Один из способов сделать это — заставить .content проходить по всей ширине (flex: 0 0 100%;) и переместить навигацию, которая «оттесняется» этим, снова влево на ее собственную ширину, используя transform: translateX(-100%);

Этот подход оставил пробелы справа, поэтому слегка изменился:

.navcontent получает фиксированную ширину (которую вы все равно хотели использовать), через flex: 0 0 3em;

А полноширинный .content получает дополнительный margin-right: -3em; для противодействия ширине навигации.

.header,
.footer {
  background-color: silver;
  height: 40vh;
}

.main {
  display: flex;
}

.nav > .navcontent {
  position: sticky;
  top: 0;
  flex: 0 0 3em;
  padding: 0;
}

.content {
  flex: 0 0 100%;
  margin-right: -3em;
}

.content > .block {
  height: 80vh;
  padding-left: 20%;
  padding-right: 20%;
}

.content > .block.-b1 {
  background-color: aqua;
}

.content > .block.-b2 {
  background-color: teal;
}

.content > .block.-b3 {
  background-color: blue;
}
<header class = "header">
  <h1>Header</h1>
</header>
<main class = "main">
  <div class = "content">
    <div class = "block -b1">
      <h1>My content</h1>
      <p>Integer interdum varius diam. Nam aliquam  velit a pede. Vivamus dictum nulla et wisi. Vestibulum a massa.  Donec vulputate nibh vitae risus dictum varius. Nunc suscipit, nunc  nec facilisis convallis, lacus ligula bibendum nulla, ac  sollicitudin sapien nisl fermentum velit. Lorem ipsum dolor sit  amet, consectetuer adipiscing elit. Nullam commodo dui ut augue  molestie scelerisque. Sed aliquet rhoncus tortor. Fusce laoreet,  turpis a facilisis tristique, leo mauris accumsan tellus, vitae  ornare lacus pede sit amet purus. Sed dignissim velit vitae ligula.  Sed sit amet diam sit amet arcu luctus ullamcorper.</p>
    </div>
    <div class = "block -b2">
      <p>Integer interdum varius diam. Nam aliquam  velit a pede. Vivamus dictum nulla et wisi. Vestibulum a massa.  Donec vulputate nibh vitae risus dictum varius. Nunc suscipit, nunc  nec facilisis convallis, lacus ligula bibendum nulla, ac  sollicitudin sapien nisl fermentum velit. Lorem ipsum dolor sit  amet, consectetuer adipiscing elit. Nullam commodo dui ut augue  molestie scelerisque. Sed aliquet rhoncus tortor. Fusce laoreet,  turpis a facilisis tristique, leo mauris accumsan tellus, vitae  ornare lacus pede sit amet purus. Sed dignissim velit vitae ligula.  Sed sit amet diam sit amet arcu luctus ullamcorper.</p>
    </div>
    <div class = "block -b3">
      <p>Integer interdum varius diam. Nam aliquam  velit a pede. Vivamus dictum nulla et wisi. Vestibulum a massa.  Donec vulputate nibh vitae risus dictum varius. Nunc suscipit, nunc  nec facilisis convallis, lacus ligula bibendum nulla, ac  sollicitudin sapien nisl fermentum velit. Lorem ipsum dolor sit  amet, consectetuer adipiscing elit. Nullam commodo dui ut augue  molestie scelerisque. Sed aliquet rhoncus tortor. Fusce laoreet,  turpis a facilisis tristique, leo mauris accumsan tellus, vitae  ornare lacus pede sit amet purus. Sed dignissim velit vitae ligula.  Sed sit amet diam sit amet arcu luctus ullamcorper.</p>
    </div>
  </div>
  <nav class = "nav">
    <ul class = "navcontent">
      <li>Link 1
      <li>Link 2
      <li>Link 3
    </ul>
  </nav>
</main>
<footer class = "footer">
  <h1>Footer</h1>
</footer>

Проблема с этим подходом — пустое пространство, создаваемое в правой части страницы. Добавление overflow: hidden к элементу main решает проблему, но нарушает позиционирование sticky

Mat 02.07.2024 14:58

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

CBroe 02.07.2024 15:20

Это работает. Это не так практично, как система сеток, так как она может заставить вас настраивать для каждого разрешения отступы .block, чтобы они не попадали под панель навигации и не затрудняли центрирование панели навигации и содержимого block или невозможный. Это немного сложнее поддерживать. Тем не менее, это правильный ответ на вопрос и, вероятно, лучшая альтернатива использованию системы позиционирования grid.

Mat 03.07.2024 14:47
Ответ принят как подходящий

Чтобы решить эту проблему, я бы, вероятно, использовал display: grid и определил перекрывающиеся области для .content и .nav с помощью grid-template-columns.

Вероятно, это наиболее гибкое решение относительно структуры и стиля HTML.

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

Затем эту переменную можно было изменить с помощью медиа-запроса в зависимости от размера экрана.

:root {
  --nav-width : 150px;
}


.header,
.footer {
  background-color: silver;
  height: 40vh;
}

.main {
  display: grid;
  grid-template-columns: [content-start] 1fr [nav-start] var(--nav-width) [content-end nav-end];
}

.nav {
  grid-area: nav;
}

.content {
  grid-area: content;
}

.nav>.navcontent {
  position: sticky;
  top: 0;
}

.content>.block {
  min-height: 80vh;
  padding-left: 20%;
  padding-right: calc( 20% + var(--nav-width));
}

.content>.block.-b1 {
  background-color: aqua;
}

.content>.block.-b2 {
  background-color: teal;
}

.content>.block.-b3 {
  background-color: blue;
}
<header class = "header">
  <h1>Header</h1>
</header>
<main class = "main">
  <nav class = "nav">
    <ul class = "navcontent">
      <li>Link 1
      <li>Link 2
      <li>Link 3
    </ul>
  </nav>
  <div class = "content">
    <div class = "block -b1">
      <h1>My content</h1>
      <p>Integer interdum varius diam. Nam aliquam velit a pede. Vivamus dictum nulla et wisi. Vestibulum a massa. Donec vulputate nibh vitae risus dictum varius. Nunc suscipit, nunc nec facilisis convallis, lacus ligula bibendum nulla, ac sollicitudin
        sapien nisl fermentum velit. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nullam commodo dui ut augue molestie scelerisque. Sed aliquet rhoncus tortor. Fusce laoreet, turpis a facilisis tristique, leo mauris accumsan tellus, vitae
        ornare lacus pede sit amet purus. Sed dignissim velit vitae ligula. Sed sit amet diam sit amet arcu luctus ullamcorper.</p>
    </div>
    <div class = "block -b2">
      <p>Integer interdum varius diam. Nam aliquam velit a pede. Vivamus dictum nulla et wisi. Vestibulum a massa. Donec vulputate nibh vitae risus dictum varius. Nunc suscipit, nunc nec facilisis convallis, lacus ligula bibendum nulla, ac sollicitudin
        sapien nisl fermentum velit. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nullam commodo dui ut augue molestie scelerisque. Sed aliquet rhoncus tortor. Fusce laoreet, turpis a facilisis tristique, leo mauris accumsan tellus, vitae
        ornare lacus pede sit amet purus. Sed dignissim velit vitae ligula. Sed sit amet diam sit amet arcu luctus ullamcorper.</p>
    </div>
    <div class = "block -b3">
      <p>Integer interdum varius diam. Nam aliquam velit a pede. Vivamus dictum nulla et wisi. Vestibulum a massa. Donec vulputate nibh vitae risus dictum varius. Nunc suscipit, nunc nec facilisis convallis, lacus ligula bibendum nulla, ac sollicitudin
        sapien nisl fermentum velit. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nullam commodo dui ut augue molestie scelerisque. Sed aliquet rhoncus tortor. Fusce laoreet, turpis a facilisis tristique, leo mauris accumsan tellus, vitae
        ornare lacus pede sit amet purus. Sed dignissim velit vitae ligula. Sed sit amet diam sit amet arcu luctus ullamcorper.</p>
    </div>
  </div>
</main>
<footer class = "footer">
  <h1>Footer</h1>
</footer>

Использование системы позиционирования по сетке определенно было лучшим вариантом. В итоге я определил .content и каждый .block как subgrid, позволив им использовать дорожки своих родительских (main) столбцов и использовать их для правильного позиционирования текста. Это означало, что мне нужно было определить ширину навигационной панели только один раз и мне не нужна была никакая переменная. Если неясно, могу предоставить код.

Mat 03.07.2024 14:33

@Mat, да, подсетки определенно позволяют лучше настроить отзывчивость. А в наши дни можно использовать подсетку — в зависимости от целевой аудитории. Кстати, всякий раз, когда вы находите лучшее решение, чем опубликованные - даже если оно основано на одном из показанных решений - не стесняйтесь создавать свой собственный ответ и принимать его как решение. Итак, создайте ответ, который покажет, как использовать подсетку для решения вашей проблемы.

t.niese 03.07.2024 14:40

Под целевой аудиторией вы имеете в виду таргетинг на старые/редкие браузеры?

Mat 03.07.2024 14:49

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

Mat 03.07.2024 14:52

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

t.niese 03.07.2024 15:52

Хорошо, я только что опубликовал это.

Mat 03.07.2024 17:52

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

.main — блок, содержащий:

  • панель навигации (.nav),
  • и .content.

Задав .main сетку, мы можем легко расположить элемент .nav (родительский элемент .navbar) где угодно, а затем растянуть .content по всей ширине .main.

.main {
  display: grid;
  grid-template-columns: 1fr 50% 5% 15% 1fr;
}

.nav {
  /** Puts the navigation bar on the first row, fourth column, over the .content */
  grid-row: 1;
  grid-column: 4;
  z-index: 1;
}

.content {
  /** Puts the .content on the same row as the navigation bar, and
  across all columns of the grid to make it take all the width. */
  grid-row: 1;
  grid-column-start: 1;
  grid-column-end: 6;
}

1fr 50% 5% 15% 1fr — это чисто произвольные значения. Здесь мы определяем, как мы хотим, чтобы наш контент, наш текст и наша панель навигации располагались, а также пространство, которое они занимают. Здесь я планирую поместить текст в столбец размера 50%, но это может быть любой другой размер, в процентах или в любой другой единице измерения. 5% — это промежуток, который я хочу между моим текстом и панелью навигации, 15% — это ширина, которую я хочу для панели навигации, а 1fr с обеих сторон будет центрировать текст и панель навигации.

Теперь у каждого .block есть желаемая полная ширина, но мы хотим, чтобы их текстовое содержимое располагалось только во втором столбце (тот, который охватывает 50% здесь). Это гарантирует, что текст не будет скрыт панелью навигации. (Он также правильно центрирует текст и обеспечивает зазор 5% между текстом и панелью навигации.)

Таким образом, каждый .block также должен использовать сетку. Мы можем повторно использовать сетку блока .main, используя subgrid на .content, а затем на каждом .block, не забывая также сделать .block на полную ширину.

.content,
.content > .block {
  display: grid;
  grid-template-columns: subgrid;
 }

/** Make each .block span all the width of the grid. */
.content > .block {
  grid-column-start: 1;
  grid-column-end: 6;
}

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

.content > .block > .blockcontent {
  grid-column: 2;
}

/**
  Setting up a basic example layout for the page.
*/
* {
margin: 0;
}

.header,
.footer {
  background-color: silver;
  height: 40vh;
}

.content > .block {
  min-height: 80vh;
}

.content > .block.-b1 {
  background-color: aqua;
}

.content>.block.-b2 {
  background-color: teal;
}

.content>.block.-b3 {
  background-color: blue;
}

/**
 * Code required for a sticky navbar
 */

.main {
  display: grid;
  
  /** Dimensions and positions need only be specified here and can be any value and unit.*/
  grid-template-columns: 1fr 50% 5% 15% 1fr;
}

/**Navbar*/

.nav {
  grid-row: 1;
  grid-column: 4;
  z-index: 1;
}

.nav > .navcontent {
  position: sticky;
  top: 0;
}


/** Content */

.content {
  grid-row: 1;
  grid-column-start: 1;
  grid-column-end: 6;
}

.content,
.content > .block {
  display: grid;
  grid-template-columns: subgrid;
 }



.content > .block {
  grid-column-start: 1;
  grid-column-end: 6;
  
  display: grid;
  grid-template-columns: subgrid;
}

.content > .block > .blockcontent {
  grid-column: 2;
}
<header class = "header">
  <h1>Header</h1>
</header>
<main class = "main">
  <nav class = "nav">
    <ul class = "navcontent">
      <li>Link 1
      <li>Link 2
      <li>Link 3
    </ul>
  </nav>
  <div class = "content">
    <div class = "block -b1">
      <div class = "blockcontent">
        <h1>My content</h1>
        <p>Integer interdum varius diam. Nam aliquam velit a pede. Vivamus dictum nulla et wisi. Vestibulum a massa. Donec vulputate nibh vitae risus dictum varius. Nunc suscipit, nunc nec facilisis convallis, lacus ligula bibendum nulla, ac sollicitudin
          sapien nisl fermentum velit. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nullam commodo dui ut augue molestie scelerisque. Sed aliquet rhoncus tortor. Fusce laoreet, turpis a facilisis tristique, leo mauris accumsan tellus, vitae
          ornare lacus pede sit amet purus. Sed dignissim velit vitae ligula. Sed sit amet diam sit amet arcu luctus ullamcorper.</p>
      </div>
    </div>
    <div class = "block -b2">
      <div class = "blockcontent">
        <p>Integer interdum varius diam. Nam aliquam velit a pede. Vivamus dictum nulla et wisi. Vestibulum a massa. Donec vulputate nibh vitae risus dictum varius. Nunc suscipit, nunc nec facilisis convallis, lacus ligula bibendum nulla, ac sollicitudin
          sapien nisl fermentum velit. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nullam commodo dui ut augue molestie scelerisque. Sed aliquet rhoncus tortor. Fusce laoreet, turpis a facilisis tristique, leo mauris accumsan tellus, vitae
          ornare lacus pede sit amet purus. Sed dignissim velit vitae ligula. Sed sit amet diam sit amet arcu luctus ullamcorper.</p>
      </div>
    </div>
    <div class = "block -b3">
      <div class = "blockcontent">
        <p>Integer interdum varius diam. Nam aliquam velit a pede. Vivamus dictum nulla et wisi. Vestibulum a massa. Donec vulputate nibh vitae risus dictum varius. Nunc suscipit, nunc nec facilisis convallis, lacus ligula bibendum nulla, ac sollicitudin
          sapien nisl fermentum velit. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nullam commodo dui ut augue molestie scelerisque. Sed aliquet rhoncus tortor. Fusce laoreet, turpis a facilisis tristique, leo mauris accumsan tellus, vitae
          ornare lacus pede sit amet purus. Sed dignissim velit vitae ligula. Sed sit amet diam sit amet arcu luctus ullamcorper.</p>
      </div>
    </div>
  </div>
</main>
<footer class = "footer">
  <h1>Footer</h1>
</footer>

Действительно хорошее решение.

t.niese 03.07.2024 18:06

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