Предотвратить прокрутку div, когда сталкивается с div под ним?

10

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

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

Вот скрипка, которая демонстрирует проблему в полном объеме

Как вы можете видеть, когда вы нажимаете кнопку «Добавить поле», div сообщений также прокручивается вверх. Я бы хотел, чтобы он остался там, где был. Точно так же, если вы слегка прокручиваетесь, чтобы видеть только второе-последнее сообщение, нажатие кнопки должно аналогичным образом сохранить эту позицию при нажатии.

Интересно то, что это поведение «иногда» сохраняется. Например, в некоторых обстоятельствах (которые я не могу точно вывести) позиция прокрутки сохраняется. Я просто хотел бы, чтобы он постоянно вел себя как таковой.

window.onload = function(e) {
  document.querySelector(".messages").scrollTop = 10000;
};

function test() {
  document.querySelector(".send-message").classList.toggle("some-margin");
}
.container {
     width: 400px;
     height: 300px;
     border: 1px solid #333;
     display: flex;
     flex-direction: column;
}
 .messages {
     overflow-y: auto;
     height: 100%;
}
 .send-message {
     width: 100%;
     display: flex;
     flex-direction: column;
}
 .some-margin {
     margin-bottom: 100px;
}
<div class="container">
   <div class="messages">
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
   </div>
   <div class="send-message">
      <input />
   </div>
</div>
<button onclick="test()">add margin</button>

введите описание изображения здесь

Райан Пешел
источник
1
Здравствуйте еще раз, я тот, кто ответил на ваш вопрос, используя JS. Я хотел бы сообщить вам о вещи, которую вы, возможно, неправильно истолковали. Вы упомянули: прокрутка сообщений также прокручивается вверх . На самом деле, не divпрокручивается вверх, а уменьшается высота div. Так почему это важно? Ну, это означает, что он scrollbarне корректирует свою позицию. Полоса прокрутки запоминает только свое положение относительно верхней части контейнера. Когда высота div уменьшается, кажется, что полоса прокрутки прокручивается вверх, но на самом деле это не так.
Ричард
1
Ваш пост имеет смысл (в том смысле, что люди знают, какого результата вы хотите), но я просто делюсь чем-то, если эти знания вам не очевидны (в случае, если они могут помочь вам придумать идею для решения сказал проблему) ;-)
Ричард
Да, очень верно, спасибо за разъяснения. Он только «выглядит» так, как будто прокручивается вверх, потому что вы не можете видеть нижнее содержимое, которое когда-то могли. На самом деле положение прокрутки не меняется. Немного иллюзии. Да, это может помочь найти решение.
Райан Пешел

Ответы:

5

Это забавное решение, которое вам может понравиться.

Что мы знаем о div, так это то, что он сохраняет только верхнюю позицию полосы прокрутки, поэтому, если высота изменилась по какой-либо причине, полоса прокрутки останется прежней, и это является причиной вашей проблемы.

В качестве обходного пути вы можете перевернуть .messages180 градусов с помощью transform: rotate(180deg) scaleX(-1);и откинуть назад, .messageчтобы отменить перелистывание контента, тогда div будет поддерживать нижнюю полосу прокрутки (которая является верхней) автоматически.

function test() {
  document.querySelector(".send-message").classList.toggle("some-margin")
}
.container {
  width: 400px;
  height: 300px;
  border: 1px solid #333;
  display: flex;
  flex-direction: column;
}

.messages {
  overflow-y: auto;
  height: 100%;
  transform: rotate(180deg) scaleX(-1);
}

.message
{
  transform: rotate(180deg) scaleX(-1);
}
.send-message {
  width: 100%;
  display: flex;
  flex-direction: column;
}

.some-margin {
  margin-bottom: 100px;
}
<div class="container">
  <div class="messages">
  <div class="message">hello1</div>
  <div class="message">hello2</div>
  <div class="message">hello3</div>
  <div class="message">hello4</div>
  <div class="message">hello5</div>
  <div class="message">hello6</div>
  <div class="message">hello7</div>
  <div class="message">hello8</div>
  <div class="message">hello9</div>
  <div class="message">hello10</div>
  <div class="message">hello11</div>
  <div class="message">hello12</div>
  <div class="message">hello13</div>
  <div class="message">hello14</div>
  <div class="message">hello15</div>
  <div class="message">hello16</div>
  <div class="message">hello17</div>
  <div class="message">hello18</div>
  <div class="message">hello19</div>
  <div class="message">hello20</div>
  </div>
  <div class="send-message">
  <input />
  </div>
</div>

<button onclick="test()">add margin</button>

Бэзил
источник
В этом проблема прокрутки колесика мыши назад (прокрутка вверх идет вниз, прокрутка вниз идет вверх)
Райан Пешел
Хорошо, я потратил много времени на это, но до сих пор у других разработчиков есть 3 ответа, которые вы не ищете :) Я надеялся помочь вам пожелать удачи. но как советник, который тратит время, пытаясь ответить на ваш вопрос, вы не будете платить за это.
Василий
2
Конечно, вещь. В любом случае, спасибо
Райан Пешел
Это очень умно ;-) К ​​сожалению, обратная прокрутка немного отталкивает.
Ричард
3

Нормальное поведение полосы прокрутки - она ​​должна быть наверху, поэтому, когда вы устанавливаете ее на нижнюю часть при загрузке страницы, вы должны поддерживать ее самостоятельно, потому что когда когда-либо нижний контент нажимает ее, div scroll перемещается наверх.

Итак, у меня есть два решения для вас:

  1. Переверните сообщения внутри div сообщений, чтобы последнее сообщение было первым, чтобы прокрутка всегда была сверху.

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

function scrollbottom(e)
    {
      e.scrollTop = e.clientHeight;
    }

Проверьте фрагмент

var elem = document.querySelector(".messages");

window.onload = function(e){ 
  scrollbottom(elem);
}

function test() {
  document.querySelector(".send-message").classList.toggle("some-margin");
  scrollbottom(elem);
}

function scrollbottom(e)
{
  e.scrollTop = e.clientHeight;
}
.container {
  width: 400px;
  height: 300px;
  border: 1px solid #333;
  display: flex;
  flex-direction: column;
}

.messages {
  overflow-y: auto;
  height: 100%;
}

.send-message {
  width: 100%;
  display: flex;
  flex-direction: column;
}

.some-margin {
  margin-bottom: 100px;
}
<div class="container">
  <div class="messages">
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  </div>
  <div class="send-message">
  <input />
  </div>
</div>

<button onclick="test()">add margin</button>

Бэзил
источник
К сожалению, я не хочу, чтобы просто прокрутить вниз. Я хочу сохранить свою позицию. Кроме того, вполне вероятно, что это можно сделать без Javascript. Из ОП: «Точно так же, если вы слегка прокручиваетесь, чтобы видеть только второе-последнее сообщение, нажатие кнопки должно аналогичным образом сохранить эту позицию при нажатии».
Райан Пешел
3

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

Проверьте этот фрагмент

function test() {
  document.querySelector(".send-message").classList.toggle("some-margin");
}
.container {
  width: 400px;
  border: 1px solid #333;
  display: flex;
  flex-direction: column;
}

.messages {
  height: 300px;
  overflow-y: auto;
}

.send-message {
  width: 100%;
  display: flex;
  flex-direction: column;
}

.some-margin {
  margin-bottom: 50px;
}
<div class="container">
  <div class="messages">
  <div class="message">hello1</div>
  <div class="message">hello2</div>
  <div class="message">hello3</div>
  <div class="message">hello4</div>
  <div class="message">hello5</div>
  <div class="message">hello6</div>
  <div class="message">hello7</div>
  <div class="message">hello8</div>
  <div class="message">hello9</div>
  <div class="message">hello10</div>
  <div class="message">hello11</div>
  <div class="message">hello12</div>
  <div class="message">hello13</div>
  <div class="message">hello14</div>
  <div class="message">hello15</div>
  <div class="message">hello16</div>
  <div class="message">hello17</div>
  <div class="message">hello18</div>
  <div class="message">hello19</div>
  <div class="message">hello20</div>
  </div>
  <div class="send-message">
  <input />
  </div>
</div>

<button onclick="test()">add margin</button>

Бэзил
источник
Это не работает, потому что поле должно быть применено .send-message. Если вы посмотрите на мой предыдущий вопрос, то это сделано потому, что это похоже на удар по марже от открытия виртуальной клавиатуры на мобильных устройствах (нижняя часть поля - всего лишь замена 1: 1 для отладки решения)
Райан Пешел
3

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

window.onload = function(e) {
  document.querySelector(".messages").scrollTop = 10000;
}

function test() {
  let state = document.querySelector(".send-message").classList.toggle("some-margin")
  let div = document.querySelector(".messages");

  if (state) {
    div.dataset.top = div.scrollTop;
    div.scrollTop += 100 // same as margin-bottom of .some-margin
  } else {
    div.scrollTop = div.dataset.top;
  }
}
.container {
  width: 400px;
  height: 300px;
  border: 1px solid #333;
  display: flex;
  flex-direction: column;
}

.messages {
  overflow-y: auto;
  height: 100%;
}

.send-message {
  width: 100%;
  display: flex;
  flex-direction: column;
}

.some-margin {
  margin-bottom: 100px;
}
<div class="container">
  <div class="messages">
    <div class="message">hello1</div>
    <div class="message">hello2</div>
    <div class="message">hello3</div>
    <div class="message">hello4</div>
    <div class="message">hello5</div>
    <div class="message">hello6</div>
    <div class="message">hello7</div>
    <div class="message">hello8</div>
    <div class="message">hello9</div>
    <div class="message">hello10</div>
    <div class="message">hello11</div>
    <div class="message">hello12</div>
    <div class="message">hello13</div>
    <div class="message">hello14</div>
    <div class="message">hello15</div>
    <div class="message">hello16</div>
    <div class="message">hello17</div>
    <div class="message">hello18</div>
    <div class="message">hello19</div>
    <div class="message">hello20</div>
  </div>
  <div class="send-message">
    <input />
  </div>
</div>

<button onclick="test()">add margin</button>

User863
источник
0

Я имею в виду следующее - это решение только для CSS, которое решает именно ваш пример:

.some-margin{margin-bottom:100px;}

.messages{margin-top:-100px;}

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

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

Сохранение своей позиции, чтобы последний прочитанный контент был виден.

За исключением полосы прокрутки решает, что вы хотите видеть верхнюю часть видимого в данный момент контента, а не нижнюю. (Это проще увидеть, если вы используете цифры: https://jsfiddle.net/1co3x48n/ )

Если вы хотите использовать javascript, есть ответы на все вопросы: https://www.google.com/search?q=scrollbar+always+on+bottom+css+site:stackoverflow.com

Честно говоря, тот факт, что вы можете просмотреть ~ 3+ страниц этого и найти только ответы Javascript, говорит мне, что вы не найдете ответ только для CSS, конечно же, не тот, который широко совместим с браузером.

Элиезер Берлин
источник
0

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

componentDidMount() {
  this.screenHeight = window.innerHeight;

  let chatElem = document.querySelector(".conversations-chat");

  window.addEventListener('resize', () => {
    let diff = this.screenHeight - window.innerHeight;

    chatElem.scrollTop += diff;

    this.screenHeight = window.innerHeight;      
  });
}

Тем не менее, это только кажется, иногда работает. Он работает 100% времени при нажатии на текстовое поле, но по какой-то причине при закрытии виртуальной клавиатуры он работает только в том случае, если он был прокручен достаточно. В противном случае он каждый раз сбрасывается в одну и ту же позицию (около дна, но не совсем внизу).

Не уверен, что это вызывает. Завтра мне придется расследовать больше. Пока это самый близкий.

Райан Пешел
источник
Как мы все говорили в наших ответах, кажется, что это невозможно сделать только с помощью одного CSS. (Существует проблема с вашим JS-кодом, которая заключается в том, что chatElem.scrollTop не может быть меньше 0 , поэтому, если он слишком близко к вершине, он пытается стать отрицательным числом, вместо этого становится 0, затем, когда вы увеличиваете экран снова появляется свиток там, где его раньше не было.)
Eliezer Berlin
Кроме того, chatElem.scrollTop не может быть больше, чем chatElem.scrollHeight - chatElem.offsetHeight , поэтому он создаст дополнительную прокрутку там, где ее раньше не было, если вы попытаетесь это сделать.
Eliezer Berlin
0

Создайте контейнер, как в приведенных выше примерах, но затем просто измените CSS, когда начнете печатать.

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

Вы должны быть в состоянии настроить CSS для ваших нужд. Вы даже можете обойтись без JS, если будете использовать: фокус или немного другую настройку CSS, будьте креативны :)

function activate(){
    document.querySelector("#container").classList.toggle("active");
  }
#container{
    position:relative;
    width:300px;
    height:100vh;
    max-height:230px;
    overflow:hidden;
  }
  #messages{
    position:absolute;
    bottom:30px;
    overflow:auto;
    height:200px;
    width:100%;
    background:#eee;
  }
  #container.active #messages {
    bottom:100px;
  }
  #send-message{
    position:absolute;
    border:1px solid #ccc;
    bottom:0;
    height:28px;
  }
  #container.active #send-message{
    height:100px;
  }
<div id="container">
  <div id="messages">
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  <div class="message">hello</div>
  </div>
  <div id="send-message">
    <input id="newmessage" onclick="activate()" type="text" />
  </div>
</div>

Rmaxx
источник
0

Вы должны добавить document.querySelector(".messages").scrollTop = 10000;в testфункцию, когда добавляете маржу, затем scrollTopзаканчиваете загрузку, которую вы установили, scrolltopи когда вы добавляете маржу или удаляете маржу scrolltop, не установленную снова, измените ваш скрипт так, как это

<script>
    window.onload = function(e){ 
    document.querySelector(".messages").scrollTop = 10000;
}

function test() {
    document.querySelector(".send-message").classList.toggle("some-margin")
            document.querySelector(".messages").scrollTop = 10000;
}
    </script>
MBadrian
источник
0

Кажется, есть проблема с функцией переключения js. Я изменил это на простой прятки. Также я добавил ширину тега ввода. Вам также нужно добавить позицию: абсолютное в div-сообщения send и bottom: 0, чтобы оно оставалось внизу. затем поместите position: относительный в контейнер так, чтобы div отправляющего сообщения оставался в контейнере. Это означает, что контейнер сообщений не будет влиять на сами сообщения и поднимать их.

window.onload = function(e) {
  document.querySelector(".messages").scrollTop = 10000;
};

function test() {
  var x = document.querySelector(".send-message");

  if (x.style.display === "none") {
    x.style.display = "block";
  } else {
    x.style.display = "none";
  }
}
.container {
     width: 400px;
     height: 300px;
     border: 1px solid #333;
     display: flex;
     flex-direction: column;
     position: relative;
}
 .messages {
     overflow-y: auto;
     height: 100%;
}
 .send-message {
     width: 100%;
     display: flex;
     flex-direction: column;
     position: absolute;
     bottom: 0;
}
 .send-message input {
     width:100%;
}
 .some-margin {
     margin-bottom: 100px;
}

 
 
<div class="container">
   <div class="messages">
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">test</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
      <div class="message">hello</div>
   </div>
   <div class="send-message">
      <input />
   </div>
</div>
<button onclick="test()">add margin</button>

Shannaki
источник
Я не понимаю это решение. Кажется, это просто скрывает текстовое поле отправки сообщения, когда вы нажимаете добавить поле?
Райан Пешел
-1

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

window.onload = function(e){ 
  document.querySelector(".messages").scrollTop = 10000;
}

document.querySelector('button').addEventListener('click', test)

function test() {
  document.querySelector(".send-message").classList.toggle("some-margin")
  document.querySelector('.wrapper').classList.toggle('wrapper--transformed')
}
.container {
  width: 400px;
  height: 300px;
  border: 1px solid #333;
  display: flex;
  flex-direction: column;
}

.messages {
  position: relative;
  overflow-y: auto;
  height: 100%;
}

.wrapper {
  display: block;
}

.wrapper--transformed {
  transform: translateY(-100px);
  margin-bottom: -100px;
}

.send-message {
  width: 100%;
  display: flex;
  flex-direction: column;
}

.some-margin {
  margin-bottom: 100px;
}
<div class="container">
  <div class="messages">
    <div class = "wrapper">
      <div class="message">hello1</div>
      <div class="message">hello2</div>
      <div class="message">hello3</div>
      <div class="message">hello4</div>
      <div class="message">hello5</div>
      <div class="message">hello6</div>
      <div class="message">hello7</div>
      <div class="message">hello8</div>
      <div class="message">hello9</div>
      <div class="message">hello1</div>
      <div class="message">hello2</div>
      <div class="message">hello3</div>
      <div class="message">hello4</div>
      <div class="message">hello5</div>
      <div class="message">hello6</div>
      <div class="message">hello7</div>
      <div class="message">hello8</div>
      <div class="message">hello9</div>
      <div class="message">hello1</div>
      <div class="message">hello2</div>
    </div>
  </div>
  <div class="send-message">
    <input />
  </div>
</div>
<button>add margin</button>

Верхний раздел имеет небольшую проблему, когда есть поле. Хитрость здесь в том, чтобы использовать отрицательное поле и использовать translate для «прокрутки вверх» (на самом деле не прокрутки, а иллюзии) wrapperdiv.

Ричард
источник
Да, проблема самого верхнего раздела - это проблема. Кроме того, подразумевается ли это, зная точный размер мобильной клавиатуры, чтобы знать, сколько компенсировать? Я не уверен, что у меня есть доступ к этой информации.
Райан Пешел
Чтобы ответить на ваш вопрос: да. Вышеуказанная техника должна знать точную высоту клавиатуры. Однако существуют способы использования JS для определения высоты элемента и изменения CSS.
Ричард
Как бы вы определили высоту мобильной виртуальной клавиатуры?
Райан Пешел
@RyanPeschel Является ли эта виртуальная клавиатура элементом HTML (например div)?
Ричард
Я не уверен, как это закодировано.
Райан Пешел
-2

К нему можно очень просто обратиться с помощью имен якоря. Посмотрите на скрипку JS на https://jsfiddle.net/gvbz93sx/

HTML Change <a name="bottom"></a>

JS Change document.location.hash = "#bottom";

Animesh
источник