繁体   English   中英

当外部 div 的大小发生变化时,可滚动的 div 会粘在底部

[英]Scrollable div to stick to bottom, when outer div changes in size

这是一个示例聊天应用程序 ->

这里的想法是让.messages-container尽可能多地占据屏幕。 .messages-container.scroll保存消息列表,如果有更多消息然后屏幕的大小,滚动。

现在,考虑这个案例:

  1. 用户滚动到对话底部
  2. .text-input ,动态变大

现在,用户不再滚动到对话底部,而是文本输入增加,他们不再看到底部。

修复它的一种方法,如果我们使用 react,计算文本输入的高度,如果有任何变化,让 .messages-container 知道

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

但是,这会导致明显的性能问题,并且像这样传递消息令人难过。

有没有更好的办法? 我可以以这样的方式使用 css 来表达当 .text-input-increases 时,我想基本上shift up所有 .messages-container

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;的缺点flex-direction: column-reverse; 是 IE/Edge/Firefox 中的一个错误,滚动条不显示,您可以在此处阅读更多信息: Flexbox column-reverse and overflow in Firefox/IE

好处是您在移动设备/平板电脑上拥有约 90% 的浏览器支持,对台式机拥有约 65% 的浏览器支持,并且随着错误得到修复而计数,......并且有一个解决方法。

// 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>


旁注 1:检测方法没有经过全面测试,但它应该适用于较新的浏览器。

旁注 2:为聊天输入附加调整大小事件处理程序可能比调用 updateScroll 函数更有效。

注意:感谢HaZardouS重用他的 html 结构

你只需要一个 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>

请尝试以下小提琴 - https://jsfiddle.net/Hazardous/bypxg25c/ 尽管小提琴目前正在使用 jQuery 来增大/调整文本区域的大小,但关键在于用于消息容器和输入容器类的 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;
}

.messages-container 的 flex-shrink 值设置为 1,.input-container 的 flex-shrink 值设置为 0。 这可确保在重新分配大小时消息容器会缩小。

我已经移动了messages text-input ,绝对将其定位到容器的底部,并相应地为messages足够的底部填充空间。

运行一些代码将一个类添加到conversation ,它使用漂亮的 CSS 转换动画更改text-input的高度和messages底部填充。

JavaScript 在 CSS 过渡运行的同时运行“scrollTo”函数以保持滚动在底部。

当滚动再次离开底部时,我们从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>

你写;

 Now, consider this case: The user scrolls to the bottom of the conversation The .text-input, dynamically gets bigger

动态设置 .text-input 的方法不是触发 this.props.onResize() 的逻辑位置吗?

敬启者,

上面的答案不足以解决我的问题。

我找到的解决方案是使我的 innerWidth 和 innerHeight 变量保持不变 - 因为浏览器的 innerWidth 在滚动时会发生变化以适应滚动条。

var innerWidth = window.innerWidth
var innerHeight = window.innerHeight

OR FOR REACT

this.setState({width: window.innerWidth, height: window.innerHeight})

换句话说,要忽略它,您必须使所有内容保持不变,就好像它从不滚动一样。 请记住在调整大小/方向更改时更新这些!

奥斯卡

恕我直言,当前的答案不是正确的:1/ flex-direction: column-reverse; 颠倒消息的顺序 - 我不想那样。 2/ javascript 也有点老套和过时

如果您想让它像 PRO 一样,请使用具有以下属性的间隔框:

flex-grow: 1;
flex-basis: 0;

并位于消息上方。 它将他们推到聊天输入。 当用户输入新消息并且输入高度增加时,滚动条向上移动,但是当消息发送(输入被清除)时,滚动条回到底部。

检查我的片段:

 body { background: #ccc; } .chat { display: flex; flex-direction: column; width: 300px; max-height: 300px; max-width: 90%; background: #fff; } .spacer-box { flex-basis: 0; flex-grow: 1; } .messages { display: flex; flex-direction: column; overflow-y: auto; flex-grow: 1; padding: 24px 24px 4px; } .footer { padding: 4px 24px 24px; } #chat-input { width: 100%; max-height: 100px; overflow-y: auto; border: 1px solid pink; outline: none; user-select: text; white-space: pre-wrap; overflow-wrap: break-word; }
 <div class="chat"> <div class="messages"> <div class="spacer-box"></div> <div class="message">1</div> <div class="message">2</div> <div class="message">3</div> <div class="message">4</div> <div class="message">5</div> <div class="message">6</div> <div class="message">7</div> <div class="message">8</div> <div class="message">9</div> <div class="message">10</div> <div class="message">11</div> <div class="message">12</div> <div class="message">13</div> <div class="message">14</div> <div class="message">15</div> <div class="message">16</div> <div class="message">17</div> <div class="message">18</div> </div> <div class="footer"> <div contenteditable role="textbox" id="chat-input"></div> </div> <div>

希望我能帮上忙 :) 干杯

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM