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 .messageswindow 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.