可滚动的div粘到底部,当外部div变化的大小

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

这里的想法是让.messages-container尽可能多的占用屏幕。 在.messages-container.scroll保存消息列表,如果有更多的消息,那么屏幕的大小会滚动。

现在,考虑这种情况:

  1. 用户滚动到会话的底部
  2. .text-inputdynamic变大

现在,不是用户保持滚动到对话底部,文本input增加,他们不再看到底部。

一种方法来解决它,如果我们正在使用的反应,计算文本input的高度,如果有任何改变,让.messages容器知道

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

但是,这会导致可见的性能问题,并且像这样传递消息很可悲。

有没有更好的办法? 我可以用这样的方式使用css来expression:.text-input-increase时,我想实质上shift up所有的.messages-container

2:修改这个答案

你的朋友在这里是flex-direction: column-reverse; 你可以在消息容器底部alignment消息,就像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列 – 在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)); } 

在下面的代码片段中,我添加了上面的两个函数,使IE / Edge / Firefox的行为方式与flex-direction: column-reverse;相同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> 

你只需要一个CSS规则集:

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

就是这样,你完成了! 这将保持对话滚动到底部,最后一个消息在视图中,无论任何resize的容器,input框,或其他任何东西。 它适用于所有现代浏览器。

它是如何工作的:首先,它垂直翻转外部的messages-container元素(处理滚动),使顶部成为底部。 然后翻转内scroll元素,使消息不会颠倒。

这种方法确实有一个奇怪的副作用:当您在消息框中使用鼠标滚轮(或模拟其中的一个)时,滚动方向将反转。 如果这让你感到困扰,你可以用JavaScript修复它(如下所示)。

这里有一个演示和一个jsfiddle来玩:

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

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

运行一些代码来添加一个类到conversation ,它使用一个漂亮的CSS过渡animation来改变text-input和底部填充的高度。

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> 

请尝试下面的小提琴 – https://jsfiddle.net/Hazardous/bypxg25c/ 。 虽然小提琴目前正在使用jQuery来增大/调整文本区域的大小,但症结在于用于消息容器和input容器类的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。 这确保消息容器在重新分配大小时收缩。

你写;

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