I\'m trying to mimic other mobile chatting apps where when you select the send-message
textbox and it opens the virtual keyboard, the bottom-most message is still i
My solution is the same as your proposed solution with an addition of conditional check. Here's a description of my solution:
scrollTop
and last clientHeight
of .messages
to oldScrollTop
and oldHeight
respectivelyoldScrollTop
and oldHeight
every time a resize
happens on window
and update oldScrollTop
every time a scroll
happens on .messages
window
is shrunk (when the virtual keyboard shows), the height of .messages
will automatically retract. The intended behaviour is to make the bottommost content of .messages
still visible even when .messages
' height retracts. This requires us to manually adjust the scroll position scrollTop
of .messages
.scrollTop
of .messages
to make sure that the bottommost part of .messages
before its height retraction happens is still visiblescrollTop
of .messages
to make sure that the bottommost part of .messages
remains the bottommost part of .messages
after height expansion (unless expansion cannot happen upwards; this happens when you're almost at the top of .messages
)My (initial possibly flawed) logical thinking is: resize
happens, .messages
' height changes, update on .messages
scrollTop
happens inside our resize
event handler. However, upon .messages
' height expansion, a scroll
event curiously happens before a resize
! And even more curious, the scroll
event only happens when we hide the keyboard when we have scrolled above the maximum scrollTop
value of when .messages
is not retracted. In my case, this means that when I scroll below 270.334px
(the maximum scrollTop
before .messages
is retracted) and hide the keyboard, that weird scroll
before resize
event happens and scrolls your .messages
to exactly 270.334px
. This obviously messes up our solution above.
Fortunately, we can work around this. My personal deduction of why this scroll
before the resize
event happens is because .messages
cannot maintain its scrollTop
position of above 270.334px
when it expands in height (this is why I mentioned that my initial logical thinking is flawed; simply because there's no way for .messages
to maintain its scrollTop
position above its maximum value). Therefore, it immediately sets its scrollTop
to the maximum value it can give (which is, unsurprisingly, 270.334px
).
Because we only update oldHeight
on resize, we can check if this forced scroll (or more correctly, resize
) happens and if it does, don't update oldScrollTop
(because we have already handled that in resize
!) We simply need to compare oldHeight
and the current height on scroll
to see if this forced scrolling happens. This works because the condition of oldHeight
not being equal to the current height on scroll
will only be true when resize
happens (which is coincidentally when that forced scrolling happens).
Here's the code (in JSFiddle) below:
window.onload = function(e) {
let messages = document.querySelector('.messages')
messages.scrollTop = messages.scrollHeight - messages.clientHeight
bottomScroller(messages);
}
function bottomScroller(scroller) {
let oldScrollTop = scroller.scrollTop
let oldHeight = scroller.clientHeight
scroller.addEventListener('scroll', e => {
console.log(`Scroll detected:
old scroll top = ${oldScrollTop},
old height = ${oldHeight},
new height = ${scroller.clientHeight},
new scroll top = ${scroller.scrollTop}`)
if (oldHeight === scroller.clientHeight)
oldScrollTop = scroller.scrollTop
});
window.addEventListener('resize', e => {
let newScrollTop = oldScrollTop + oldHeight - scroller.clientHeight
console.log(`Resize detected:
old scroll top = ${oldScrollTop},
old height = ${oldHeight},
new height = ${scroller.clientHeight},
new scroll top = ${newScrollTop}`)
scroller.scrollTop = newScrollTop
oldScrollTop = newScrollTop
oldHeight = scroller.clientHeight
});
}
.container {
width: 400px;
height: 87vh;
border: 1px solid #333;
display: flex;
flex-direction: column;
}
.messages {
overflow-y: auto;
height: 100%;
}
.send-message {
width: 100%;
display: flex;
flex-direction: column;
}
Tested on Firefox and Chrome for mobile and it works for both browsers.