Подтвердить что ты не робот

Прокручиваемый div, чтобы придерживаться снизу, когда внешний div изменяется в размере

Вот пример приложения чата →

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

Теперь рассмотрим этот случай:

  • Пользователь прокручивается в нижней части беседы
  • .text-input, динамически увеличивается

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

Один из способов исправить это, если мы используем реакцию, вычислить высоту ввода текста, и если что-нибудь изменится, сообщите .messages-container

componentDidUpdate() {
  window.setTimeout(_ => {
    const newHeight = this.calcHeight();
    if (newHeight !== this._oldHeight) {
      this.props.onResize();
    }
    this._oldHeight = newHeight;
  });
}

Но это вызывает видимые проблемы с производительностью, и печально, что такие сообщения передаются таким образом.

Есть ли лучший способ? Могу ли я использовать css таким образом, чтобы выразить, что когда .text-input-увеличивается, я хочу по существу shift up все .messages-container

4b9b3361

Ответ 1

2: повторная проверка этого ответа

Ваш друг здесь flex-direction: column-reverse;, который делает все, что вы просите, при выравнивании сообщений в нижней части контейнера сообщений, как например, Skype и многие другие приложения для чата.

.chat-window{
  display:flex;
  flex-direction:column;
  height:100%;
}
.chat-messages{
  flex: 1;
  height:100%;
  overflow: auto;
  display: flex;
  flex-direction: column-reverse;
}

.chat-input { border-top: 1px solid #999; padding: 20px 5px }
.chat-input-text { width: 60%; min-height: 40px; max-width: 60%; }

Недостатком flex-direction: column-reverse; является ошибка в IE/Edge/Firefox, где полоса прокрутки не отображается, что вы можете прочитать здесь: Flexbox column- обратное и переполнение в Firefox/IE

Наверх. У вас есть поддержка 90% браузеров на мобильных устройствах и планшетах и ​​~ 65% для рабочего стола, и подсчет исправления ошибки... и есть обходной путь. p >

// scroll to bottom
function updateScroll(el){
  el.scrollTop = el.scrollHeight;
}
// only shift-up if at bottom
function scrollAtBottom(el){
  return (el.scrollTop + 5 >= (el.scrollHeight - el.offsetHeight));
}

В приведенном ниже фрагменте кода я добавил 2 функции сверху, чтобы IE/Edge/Firefox вел себя так же, как это делает flex-direction: column-reverse;.

function addContent () {
  var msgdiv = document.getElementById('messages');
  var msgtxt = document.getElementById('inputs');
  var atbottom = scrollAtBottom(msgdiv);

  if (msgtxt.value.length > 0) {
    msgdiv.innerHTML += msgtxt.value + '<br/>';
    msgtxt.value = "";
  } else {
    msgdiv.innerHTML += 'Long long content ' + (tempCounter++) + '!<br/>';
  }
  
  /* if at bottom and is IE/Edge/Firefox */
  if (atbottom && (!isWebkit || isEdge)) {
    updateScroll(msgdiv);
  }
}

function resizeInput () {
  var msgdiv = document.getElementById('messages');
  var msgtxt = document.getElementById('inputs');
  var atbottom = scrollAtBottom(msgdiv);

  if (msgtxt.style.height == '120px') {
    msgtxt.style.height = 'auto';
  } else {
    msgtxt.style.height = '120px';
  }
  
  /* if at bottom and is IE/Edge/Firefox */
  if (atbottom && (!isWebkit || isEdge)) {
    updateScroll(msgdiv);
  }
}


/* fix for IE/Edge/Firefox */
var isWebkit = ('WebkitAppearance' in document.documentElement.style);
var isEdge = ('-ms-accelerator' in document.documentElement.style);
var tempCounter = 6;

function updateScroll(el){
  el.scrollTop = el.scrollHeight;
}
function scrollAtBottom(el){
  return (el.scrollTop + 5 >= (el.scrollHeight - el.offsetHeight));
}
html, body { height:100%; margin:0; padding:0; }

.chat-window{
  display:flex;
  flex-direction:column;
  height:100%;
}
.chat-messages{
  flex: 1;
  height:100%;
  overflow: auto;
  display: flex;
  flex-direction: column-reverse;
}

.chat-input { border-top: 1px solid #999; padding: 20px 5px }
.chat-input-text { width: 60%; min-height: 40px; max-width: 60%; }


/* temp. buttons for demo */
button { width: 12%; height: 44px; margin-left: 5%; vertical-align: top; }

/* begin - fix for hidden scrollbar in IE/Edge/Firefox */
.chat-messages-text{ overflow: auto; }
@media screen and (-webkit-min-device-pixel-ratio:0) {
  .chat-messages-text{ overflow: visible; }
  /*  reset Edge as it identifies itself as webkit  */
  @supports (-ms-accelerator:true) { .chat-messages-text{ overflow: auto; } }
}
/* hide resize FF */
@-moz-document url-prefix() { .chat-input-text { resize: none } }
/* end - fix for hidden scrollbar in IE/Edge/Firefox */
<div class="chat-window">
  <div class="chat-messages">
    <div class="chat-messages-text" id="messages">
      Long long content 1!<br/>
      Long long content 2!<br/>
      Long long content 3!<br/>
      Long long content 4!<br/>
      Long long content 5!<br/>
    </div>
  </div>
  <div class="chat-input">
    <textarea class="chat-input-text" placeholder="Type your message here..." id="inputs"></textarea>
    <button onclick="addContent();">Add msg</button>
    <button onclick="resizeInput();">Resize input</button>
  </div>
</div>

Ответ 2

Вам просто нужен один набор правил CSS:

.messages-container, .scroll {transform: scale(1,-1);}

Вот и все, вы сделали!

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

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

Вот демо и скрипка для игры:

//Reverse wheel direction
document.querySelector('.messages-container').addEventListener('wheel', function(e) {
  if(e.deltaY) {
    e.preventDefault();
    e.currentTarget.scrollTop -= parseFloat(getComputedStyle(e.currentTarget).getPropertyValue('font-size')) * (e.deltaY < 0 ? -1 : 1) * 2;
  }
});

//The rest of the JS just handles the test buttons and is not part of the solution
send = function() {
  var inp = document.querySelector('.text-input');
  document.querySelector('.scroll').insertAdjacentHTML('beforeend', '<p>' + inp.value);
  inp.value = '';
  inp.focus();
}
resize = function() {
  var inp = document.querySelector('.text-input');
  inp.style.height = inp.style.height === '50%' ? null : '50%';
}
html,body {height: 100%;margin: 0;}
.conversation {
  display: flex;
  flex-direction: column;
  height: 100%;
}
.messages-container {
  flex-shrink: 10;
  height: 100%;
  overflow: auto;
}
.messages-container, .scroll {transform: scale(1,-1);}
.text-input {resize: vertical;}
<div class="conversation">
  <div class="messages-container">
    <div class="scroll">
      <p>Message 1<p>Message 2<p>Message 3<p>Message 4<p>Message 5
      <p>Message 6<p>Message 7<p>Message 8<p>Message 9<p>Message 10
    </div>
  </div>
  <textarea class="text-input" autofocus>Your message</textarea>
  <div>
    <button id="send" onclick="send();">Send input</button>
    <button id="resize" onclick="resize();">Resize input box</button>
  </div>
</div>

Ответ 3

Попробуйте следующую скрипту - https://jsfiddle.net/Hazardous/bypxg25c/. Хотя скрипта в настоящее время использует jQuery для увеличения/изменения размера текстовой области, crux находится в стилях, связанных с flex, используемых для классов сообщений-контейнеров и классов ввода-контейнера -

.messages-container{
  order:1;
  flex:0.9 1 auto;
  overflow-y:auto;
  display:flex;
  flex-direction:row;
  flex-wrap:nowrap;
  justify-content:flex-start;
  align-items:stretch;
  align-content:stretch;
}

.input-container{
  order:2;
  flex:0.1 0 auto;
}

Значение flex-shrink устанавливается равным 1 для .messages-container и 0 для контейнера. Это гарантирует, что контейнер сообщений сжимается при перераспределении размера.

Ответ 4

Я переместил text-input в пределах messages, абсолютно расположил его до нижней части контейнера и дал messages достаточное нижнее дополнение к пробелу соответственно.

Запустите некоторый код, чтобы добавить класс в conversation, который изменяет высоту text-input и нижнее дополнение messages, используя приятную анимацию перехода CSS.

JavaScript запускает функцию "scrollTo" одновременно с переходом CSS-перехода, чтобы сохранить прокрутку внизу.

Когда свиток снова опустится, мы удалим класс из conversation

Надеюсь, что это поможет.

https://jsfiddle.net/cnvzLfso/5/

var doScollCheck = true;
var objConv = document.querySelector('.conversation');
var objMessages = document.querySelector('.messages');
var objInput = document.querySelector('.text-input');

function scrollTo(element, to, duration) {
  if (duration <= 0) {
    doScollCheck = true;
    return;
  }
  var difference = to - element.scrollTop;
  var perTick = difference / duration * 10;

  setTimeout(function() {
    element.scrollTop = element.scrollTop + perTick;
    if (element.scrollTop === to) {
      doScollCheck = true;
      return;
    }
    scrollTo(element, to, duration - 10);
  }, 10);
}

function resizeInput(atBottom) {
  var className = 'bigger',
    hasClass;
  if (objConv.classList) {
    hasClass = objConv.classList.contains(className);
  } else {
    hasClass = new RegExp('(^| )' + className + '( |$)', 'gi').test(objConv.className);
  }
  if (atBottom) {
    if (!hasClass) {
      doScollCheck = false;
      if (objConv.classList) {
        objConv.classList.add(className);
      } else {
        objConv.className += ' ' + className;
      }
      scrollTo(objMessages, (objMessages.scrollHeight - objMessages.offsetHeight) + 50, 500);
    }
  } else {
    if (hasClass) {
      if (objConv.classList) {
        objConv.classList.remove(className);
      } else {
        objConv.className = objConv.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
      }
    }
  }
}

objMessages.addEventListener('scroll', function() {
  if (doScollCheck) {
    var isBottom = ((this.scrollHeight - this.offsetHeight) === this.scrollTop);
    resizeInput(isBottom);
  }
});
html,
body {
  height: 100%;
  width: 100%;
  background: white;
}
body {
  margin: 0;
  padding: 0;
}
.conversation {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  height: 100%;
  position: relative;
}
.messages {
  overflow-y: scroll;
  padding: 10px 10px 60px 10px;
  -webkit-transition: padding .5s;
  -moz-transition: padding .5s;
  transition: padding .5s;
}
.text-input {
  padding: 10px;
  -webkit-transition: height .5s;
  -moz-transition: height .5s;
  transition: height .5s;
  position: absolute;
  bottom: 0;
  height: 50px;
  background: white;
}
.conversation.bigger .messages {
  padding-bottom: 110px;
}
.conversation.bigger .text-input {
  height: 100px;
}
.text-input input {
  height: 100%;
}
<div class="conversation">
  <div class="messages">
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is a message content
    </p>
    <p>
      This is the last message
    </p>
    <div class="text-input">
      <input type="text" />
    </div>
  </div>
</div>

Ответ 5

Вы пишете:

Now, consider this case:

    The user scrolls to the bottom of the conversation
    The .text-input, dynamically gets bigger