Как предотвратить прокрутку липкого элемента по всему окну внутри более широкого блока? - PullRequest
8 голосов
/ 25 апреля 2019

Я разрабатываю веб-интерфейс, который включает в себя много прокрутки, используя липкое позиционирование, которое прекрасно работает, за исключением одной маленькой детали. Ниже приведен минималистский пример. Вы можете попробовать его на CodePen , чтобы увидеть, как работают стикеры при горизонтальной и вертикальной прокрутке.

<header>
 <nav>
  <ul id=menu class=flex><li><a href="#">Menu bar item</a></li></ul>
 </nav>
 <div class=flex>
  <div id=button>Button</div>
  <div id=bar>Bar wider than the window</div>
 </div>
</header>
<main class=flex>
 <div id=leftbar></div>
 <div id=content></div>
</main>
*           {line-height: 2em; background-position: center center}
body        {margin: 0; padding: 0}
body, a     {color: #fff}
ul,li       {margin: 0; padding: 0; list-style-type: none}
.flex       {display: flex}
.flex > *   {flex: 0 0 auto}
header      {display: block; position: sticky; top: 0; left: 0; z-index: 1;
             width: min-content; min-width: 100%; background: #000066}
#menu       {flex-wrap: wrap; position: sticky; left: 0; width: 100vw;
             background: #006600}
#button     {position: sticky; left: 0; width: 4em; background: #000066}
#bar        {width: 150vw}
main        {min-height: 100vh} /* Ensure scrollbar */
#leftbar    {position: sticky; left: 0; width: 4em; background: #444444}
#content    {width: 150vw}
/* Background grid to visualize scrolling parts */
#bar, #leftbar, #content {
 background-image: url(data:image/gif;base64,R0lGODlhEAAQAIABAO7u7v///yH5BAEKAAEA
 LAAAAAAQABAAAAIdhI9pwe0PnnRxzmphlniz7oGbmJGWeXmU2qAcyxQAOw==)}

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

Об фиксированном позиционировании не может быть и речи, потому что высота строки меню будет меняться, так что потребуется куча очень уродливых JS, читающих offsetHeight и перемещающих элементы ниже после каждого изменения DOM - гадость!

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

Редактировать: Я вижу, что решения JS начинают добавляться, поэтому давайте положим конец этому с этими 3 линиями JS, которые легко решают проблему. Я бы просто предпочел решение CSS / HTML, чтобы избежать проблем с поддержкой кода в будущем.

var f=function() {document.getElementById('menu').style.width=document.body.clientWidth+'px';};
window.addEventListener('DOMContentLoaded',f);
window.addEventListener('resize',f);

Ответы [ 3 ]

0 голосов
/ 25 апреля 2019

Обновление

Установка ширины полосы прокрутки на 15px и установка ширины зеленой строки меню на calc(100vw - 15px) может сделать это.

Я не знаю, как это сделать с помощью чистого CSS.Однако вы можете добиться того, что вам нужно, с помощью vanilla JS.

Я использовал функцию, предоставленную @lostsource в этом SO вопросе, чтобы получить ширину полосы прокрутки.

Затем я создал функцию - resizeMenuBar() -, которая устанавливает ширину строки меню равной ширине window.innerWidth минус ширина полосы прокрутки.

Я добавил двух прослушивателей событий -один для window.load и другой для window.resize.Обработчик событий - это функция resizeMenuBar.

* {
  line-height: 2em;
  background-position: center center;
}

body {
  margin: 0;
  padding: 0;
}

body,
a {
  color: #fff;
}

ul,
li {
  margin: 0;
  padding: 0;
  list-style-type: none;
}

.flex {
  display: flex;
}

.flex>* {
  flex: 0 0 auto;
}

header {
  display: block;
  position: sticky;
  top: 0;
  left: 0;
  width: min-content;
  min-width: 100%;
  z-index: 1;
  background: #000066;
}

#menu {
  flex-wrap: wrap;
  position: sticky;
  left: 0;
  width: calc(100vw - 15px);
  background: #006600;
}

#button {
  position: sticky;
  left: 0;
  width: 4em;
  background: #000066;
}

#bar {
  width: 150vw;
}

main {
  min-height: 100vh;
}


/* Ensure scrollbar */

#leftbar {
  position: sticky;
  left: 0;
  width: 4em;
  background: #444444;
}

#content {
  width: 150vw;
}


/* Background grid to visualize scrolling parts */

#bar,
#leftbar,
#content {
  background-image: url(data:image/gif;base64,R0lGODlhEAAQAIABAO7u7v///yH5BAEKAAEALAAAAAAQABAAAAIdhI9pwe0PnnRxzmphlniz7oGbmJGWeXmU2qAcyxQAOw==);
}

body::-webkit-scrollbar {
  width: 15px;
}

body::-webkit-scrollbar-track {
  -webkit-box-shadow: inset 0 0 6px #ccc;
}

body::-webkit-scrollbar-thumb {
  background-color: grey;
  outline: 1px solid grey;
}
<header>
  <nav>
    <ul id=menu class=flex>
      <li><a href="#">Menu bar item</a></li>
    </ul>
  </nav>
  <div class=flex>
    <div id=button>Button</div>
    <div id=bar>Bar wider than the window</div>
  </div>
</header>
<main class=flex>
  <div id=leftbar></div>
  <div id=content></div>
</main>
0 голосов
/ 26 апреля 2019

Кажется, что хак ниже работает нормально только на chrome

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

* {
  line-height: 2em;
  background-position: center center;
}
body {
  margin: 0;
  padding: 0;
}
body,
a {
  color: #fff;
}
ul,
li {
  margin: 0;
  padding: 0;
  list-style-type: none;
}
.flex {
  display: flex;
}
.flex > * {
  flex: 0 0 auto;
}
header {
  display: block;
  position: sticky;
  top: 0;
  left: 0;
  width: min-content;
  min-width: 100%;
  z-index: 1;
  background: #000066;
}
/*addedd*/
nav {
  width: calc(100% + 60px);
  transform: translateX(-60px);
}
/**/
#menu {
  flex-wrap: wrap;
  position: sticky;
  left: 0;
  width: 100vw;
  background: #006600;
  transform: translateX(60px); /*added*/
}
#button {
  position: sticky;
  left: 0;
  width: 4em;
  background: #000066;
}
#bar {
  width: 150vw;
}
main {
  min-height: 100vh;
} /* Ensure scrollbar */
#leftbar {
  position: sticky;
  left: 0;
  width: 4em;
  background: #444444;
}
#content {
  width: 150vw;
}
/* Background grid to visualize scrolling parts */
#bar,
#leftbar,
#content {
  background-image: url(data:image/gif;base64,R0lGODlhEAAQAIABAO7u7v///yH5BAEKAAEALAAAAAAQABAAAAIdhI9pwe0PnnRxzmphlniz7oGbmJGWeXmU2qAcyxQAOw==);
}
<header>
 <nav>
  <ul id=menu class=flex>
   <li><a href="#">Menu bar item</a></li>
  </ul>
 </nav>
 <div class=flex>
  <div id=button>Button</div>
  <div id=bar>Bar wider than the window</div>
 </div>
</header>
<main class=flex>
 <div id=leftbar></div>
 <div id=content></div>
</main>

60px - это случайное значение, которое должно быть просто больше ширины прокрутки.

Получить трюк не просто, но есливы удалите перевод у вас будет следующее:

* {
  line-height: 2em;
  background-position: center center;
}
body {
  margin: 0;
  padding: 0;
}
body,
a {
  color: #fff;
}
ul,
li {
  margin: 0;
  padding: 0;
  list-style-type: none;
}
.flex {
  display: flex;
}
.flex > * {
  flex: 0 0 auto;
}
header {
  display: block;
  position: sticky;
  top: 0;
  left: 0;
  width: min-content;
  min-width: 100%;
  z-index: 1;
  background: #000066;
}
/*addedd*/
nav {
  width: calc(100% + 60px);
}
/**/
#menu {
  flex-wrap: wrap;
  position: sticky;
  left: 0;
  width: 100vw;
  background: #006600;
}
#button {
  position: sticky;
  left: 0;
  width: 4em;
  background: #000066;
}
#bar {
  width: 150vw;
}
main {
  min-height: 100vh;
} /* Ensure scrollbar */
#leftbar {
  position: sticky;
  left: 0;
  width: 4em;
  background: #444444;
}
#content {
  width: 150vw;
}
/* Background grid to visualize scrolling parts */
#bar,
#leftbar,
#content {
  background-image: url(data:image/gif;base64,R0lGODlhEAAQAIABAO7u7v///yH5BAEKAAEALAAAAAAQABAAAAIdhI9pwe0PnnRxzmphlniz7oGbmJGWeXmU2qAcyxQAOw==);
}
<header>
 <nav>
  <ul id=menu class=flex>
   <li><a href="#">Menu bar item</a></li>
  </ul>
 </nav>
 <div class=flex>
  <div id=button>Button</div>
  <div id=bar>Bar wider than the window</div>
 </div>
</header>
<main class=flex>
 <div id=leftbar></div>
 <div id=content></div>
</main>

Переполнение nav приведет к логическому увеличению прокрутки.Теперь представьте, что вы будете прокручивать только без учета дополнительных 60px.Это будет работать нормально, но, конечно, пользователь не остановит прокрутку до 60px, поэтому хитрость заключается в том, чтобы переместить поток в левую сторону с помощью translate.

0 голосов
/ 25 апреля 2019

Как уже упоминалось в комментарии, проблема в том, что 100vw включает ширину полосы прокрутки.На самом деле не существует элегантного решения, но обходной путь - уменьшить ширину #menu до 99vw, а затем заполнить пустое пространство псевдоэлементом того же цвета фона.

#menu {
  width: 99vw;
  background: #006600;
}
#menu:before {
  content: '';
  width: 5vw;
  left: 100%;
  top: 0;
  bottom: 0;
  position: absolute;
  background: inherit;
}

Помните, что это работает, только если на вашем #menu есть плоский фоновый цвет (нет изображения, нет прозрачности), поэтому я не уверен, поможет ли это вашему делу, но это лучшее решение для кроссбраузера, которое я мог бы использоватьс.

См .: https://codepen.io/anon/pen/WWLedm

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...