当外部 div 的大小发生变化时,可滚动的 div 会粘在底部 [英] Scrollable div to stick to bottom, when outer div changes in size
问题描述
这是一个示例聊天应用程序 ->
这里的想法是让 .messages-container
尽可能多地占据屏幕.在 .messages-container
中,.scroll
保存消息列表,如果有更多消息,则滚动屏幕大小.
现在,考虑这个案例:
- 用户滚动到对话底部
.text-input
,动态变大
现在,用户不再滚动到对话底部,而是文本输入增加,他们不再看到底部.
一种解决方法,如果我们使用 react,计算 text-input 的高度,如果有任何变化,让 .messages-container 知道
componentDidUpdate() {window.setTimeout(_ => {const newHeight = this.calcHeight();如果(新高度!== this._oldHeight){this.props.onResize();}this._oldHeight = newHeight;});}
但是,这会导致明显的性能问题,而且像这样传递消息令人难过.
有没有更好的办法?我可以以这种方式使用 css 来表示当 .text-input-increases 时,我想基本上 shift up
所有 .messages-container
第二次修订本答案
你在这里的朋友是 flex-direction: column-reverse;
它会在将消息对齐在消息容器底部的同时完成你的所有请求,就像 Skype 和许多其他聊天应用程序所做的那样.
.chat-window{显示:弹性;弹性方向:列;高度:100%;}.聊天消息{弹性:1;高度:100%;溢出:自动;显示:弹性;弹性方向:反列;}.chat-input { border-top: 1px solid #999;填充:20px 5px }.chat-input-text { 宽度:60%;最小高度:40px;最大宽度:60%;}
flex-direction: column-reverse;
的缺点是 IE/Edge/Firefox 中的一个错误,滚动条不显示,您可以在此处阅读更多信息:Firefox/IE 中的 Flexbox 列反转和溢出
好处是移动设备/平板电脑的浏览器支持率约为 90%,台式机的浏览器支持率约为 65%,并且随着错误得到修复而计算在内,......还有一个解决方法.>
//滚动到底部函数更新滚动(el){el.scrollTop = el.scrollHeight;}//只有在底部时向上移动函数 scrollAtBottom(el){返回 (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);如果(msgtxt.value.length > 0){msgdiv.innerHTML += msgtxt.value + '
';msgtxt.value = "";} 别的 {msgdiv.innerHTML += '长长的内容' + (tempCounter++) + '!<br/>';}/* 如果在底部并且是 IE/Edge/Firefox */if (atbottom && (!isWebkit || isEdge)) {更新滚动(msgdiv);}}函数调整大小输入(){var msgdiv = document.getElementById('messages');var msgtxt = document.getElementById('inputs');var atbottom = scrollAtBottom(msgdiv);如果(msgtxt.style.height == '120px'){msgtxt.style.height = '自动';} 别的 {msgtxt.style.height = '120px';}/* 如果在底部并且是 IE/Edge/Firefox */if (atbottom && (!isWebkit || isEdge)) {更新滚动(msgdiv);}}/* 修复 IE/Edge/Firefox */var isWebkit = ('WebkitAppearance' in document.documentElement.style);var isEdge = ('-ms-accelerator' in document.documentElement.style);var tempCounter = 6;函数更新滚动(el){el.scrollTop = el.scrollHeight;}函数 scrollAtBottom(el){返回 (el.scrollTop + 5 >= (el.scrollHeight - el.offsetHeight));}
html, body { height:100%;保证金:0;填充:0;}.聊天窗口{显示:弹性;弹性方向:列;高度:100%;}.聊天消息{弹性:1;高度:100%;溢出:自动;显示:弹性;弹性方向:反列;}.chat-input { border-top: 1px solid #999;填充:20px 5px }.chat-input-text { 宽度:60%;最小高度:40px;最大宽度:60%;}/* 温度.演示按钮 */按钮 { 宽度:12%;高度:44px;左边距:5%;垂直对齐:顶部;}/* 开始 - 修复 IE/Edge/Firefox 中隐藏的滚动条 */.chat-messages-text{ 溢出:自动;}@media 屏幕和 (-webkit-min-device-pixel-ratio:0) {.chat-messages-text{ 溢出:可见;}/* 重置 Edge,因为它将自己标识为 webkit */@supports (-ms-accelerator:true) { .chat-messages-text{ 溢出:自动;} }}/* 隐藏调整大小 FF */@-moz-document url-prefix() { .chat-input-text { resize: none } }/* 结束 - 修复 IE/Edge/Firefox 中隐藏的滚动条 */
<div class="chat-messages"><div class="chat-messages-text" id="messages">长内容 1!<br/>长长内容2!<br/>长长内容3!<br/>长长内容4!<br/>长长内容 5!<br/>
<div class="chat-input"><textarea class="chat-input-text" placeholder="在此处输入您的消息..." id="inputs"></textarea><button onclick="addContent();">添加消息</button><button onclick="resizeInput();">调整输入大小</button>
旁注 1:检测方法未经过全面测试,但应该适用于较新的浏览器.
附注 2:为聊天输入附加调整大小事件处理程序可能比调用 updateScroll 函数更有效.
注意:感谢 HaZardouS 重用他的 html 结构
Here is an example chat app ->
The idea here is to have the .messages-container
take up as much of the screen as it can. Within .messages-container
, .scroll
holds the list of messages, and in case there are more messages then the size of the screen, scrolls.
Now, consider this case:
- The user scrolls to the bottom of the conversation
- The
.text-input
, dynamically gets bigger
Now, instead of the user staying scrolled to the bottom of the conversation, the text-input increases, and they no longer see the bottom.
One way to fix it, if we are using react, calculate the height of text-input, and if anything changes, let .messages-container know
componentDidUpdate() {
window.setTimeout(_ => {
const newHeight = this.calcHeight();
if (newHeight !== this._oldHeight) {
this.props.onResize();
}
this._oldHeight = newHeight;
});
}
But, this causes visible performance issues, and it's sad to be passing messages around like this.
Is there a better way? Could I use css in such a way, to express that when .text-input-increases, I want to essentially shift up
all of .messages-container
2:nd revision of this answer
Your friend here is flex-direction: column-reverse;
which does all you ask while align the messages at the bottom of the message container, just like for example Skype and many other chat apps do.
.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%; }
The downside with flex-direction: column-reverse;
is a bug in IE/Edge/Firefox, where the scrollbar doesn't show, which your can read more about here: Flexbox column-reverse and overflow in Firefox/IE
The upside is you have ~ 90% browser support on mobile/tablets and ~ 65% for desktop, and counting as the bug gets fixed, ...and there is a workaround.
// 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));
}
In the below code snippet I've added the 2 functions from above, to make IE/Edge/Firefox behave in the same way flex-direction: column-reverse;
does.
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>
Side note 1: The detection method is not fully tested, but it should work on newer browsers.
Side note 2: Attach a resize event handler for the chat-input might be more efficient then calling the updateScroll function.
Note: Credits to HaZardouS for reusing his html structure
这篇关于当外部 div 的大小发生变化时,可滚动的 div 会粘在底部的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!