Я долго ломал голову над тем, как сделать липкую панель навигации поверх содержимого страницы.
У меня есть веб-страница, содержащая заголовок, основной блок и нижний колонтитул.
Я хочу, чтобы в главном блоке была панель навигации, позволяющая легко перемещаться между различными разделами его содержимого. Эта панель навигации должна быть всегда доступна пользователю, пока он перемещается по основному блоку, поэтому я хочу сделать ее 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>
Я стремился к фиксированной ширине (это зависело от экрана пользователя).
Один из способов сделать это — заставить .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
…
Вы сказали, что в любом случае хотите придать навигации фиксированную ширину, поэтому выше приведена модифицированная версия, которая не переводит элемент, но дает содержимому отрицательное поле справа, соответствующее ширине навигации.
Это работает. Это не так практично, как система сеток, так как она может заставить вас настраивать для каждого разрешения отступы .block
, чтобы они не попадали под панель навигации и не затрудняли центрирование панели навигации и содержимого block
или невозможный. Это немного сложнее поддерживать. Тем не менее, это правильный ответ на вопрос и, вероятно, лучшая альтернатива использованию системы позиционирования grid
.
Чтобы решить эту проблему, я бы, вероятно, использовал 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, да, подсетки определенно позволяют лучше настроить отзывчивость. А в наши дни можно использовать подсетку — в зависимости от целевой аудитории. Кстати, всякий раз, когда вы находите лучшее решение, чем опубликованные - даже если оно основано на одном из показанных решений - не стесняйтесь создавать свой собственный ответ и принимать его как решение. Итак, создайте ответ, который покажет, как использовать подсетку для решения вашей проблемы.
Под целевой аудиторией вы имеете в виду таргетинг на старые/редкие браузеры?
Спасибо за совет. То, как я это сделал, лучше соответствует моим конкретным потребностям и предпочтениям, но оба метода в равной степени применимы к этому конкретному вопросу, который я намеренно хотел сделать как можно более узким.
@ Конечно, но ты все равно должен показать это как ответ. Ваш вопрос должен помочь не только вам, но и всем, кто столкнулся с подобной проблемой. А когда ваше решение присутствует, у любого, кто его читает, появляется дополнительная возможность. И ваше решение будет лучшим для различных ситуаций.
Хорошо, я только что опубликовал это.
Вот альтернативный способ сделать это, используя макет сетки 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>
Действительно хорошее решение.
Должна ли ваша навигация иметь фиксированную ширину или быть адаптивной?