On mobile Safari, when the virtual keyboard is open, the screen should render like this image:
where:
On mobile Safari, when the soft keyboard is open, dragging on the input or navbar can move the entire viewport up and down.
I have a screen capture video demonstrating the problem: https://youtu.be/GStBjRVpoGU
I've spent months on this problem, including trawling through many similar questions on Stack Overflow, but I've been unable to find a solution that works (or, at least, a solution that I've been able to make work).
This app is a hybrid mobile app: built as a web app with React.js but wrapped in a React Native app using the WebView component. Nevertheless, the same problem exists even if the web app is opened in a normal mobile Safari window.
Mobile Safari has a related problem where the soft keyboard pushes the whole viewport up when it is opened so that the top half of the viewport is pushed up and off the screen. This excellent blog provides both a description of that problem and a solution for it. I implemented that solution. It stops the viewport from moving up when the soft keyboard opens, but the viewport can still slide around after the soft keyboard opens.
Another problem is that mobile Safari doesn't update window.innerHeight
when the soft keyboard is opened/closed. To get around this, I used react-native-keyboard-spacer
in the React Native app, a bit like this:
render() {
return (
<React.Fragment>
<SafeAreaView >
<WebView/>
</SafeAreaView>
<KeyboardSpacer />
</React.Fragment>
);
}
This changes the height of the Webview whenever the soft keyboard opens/closes, and thus window.innerHeight
etc also changes.
It's also known that position: fixed;
on mobile Safari doesn't work so well when the soft keyboard is open, so I used position: absolute;
instead, thanks to the suggestions at this other very useful blog.
I created a code sandbox to demonstrate the problem. Open it in mobile Safari to see the screen slide after the virtual keyboard is opened. You can also the actual code sandbox code here, which represents the closest I've come to solving this problem.
One thing to note about the code sandbox, though. There’s no WebView or KeyboardSpacer: it’s just a web page. As such, I’ve had to hardcode some heights in. But if you open it in mobile Safari you’ll see the viewport sliding all over the place once the soft keyboard is open.
Has anybody seen this particular problem before? How did you fix it? Many thanks in advance.
With the help of JMathew, I found a solution for this problem.
See a working codesandbox example here.
const messageListContainerRef = useRef<HTMLDivElement>(null);
//...
return (
//...
<div ref={messageListContainerRef}>
<MessageList/>
</div>
//...
)
inputContainerRef
and navbarContainerRef
. Use that to preventDefault()
on the touch move event, which will prevent the User from being able to slide the input and navbar up and down. For example:if (inputContainerRef.current) {
inputContainerRef.current.addEventListener('touchmove', (e) => {
e.preventDefault();
});
}
preventDefault()
on it. Instead, use this solution (link now dead) to prevent the User from scrolling past the bottom (and top) of the page by setting the scrollTop
value:if (messageListContainerRef.current) {
messageListContainerRef.current.addEventListener('touchmove', (e: any) => {
if (!e.currentTarget) {
return;
}
if (e.currentTarget.scrollTop === 0) {
e.currentTarget.scrollTop = 1;
} else if (e.currentTarget.scrollHeight === e.currentTarget.scrollTop +
e.currentTarget.offsetHeight) {
e.currentTarget.scrollTop -= 1;
}
});
}
Depending on your want/need, you could set those touchmove
event listeners on component mount, rather than on input focus. You could even set them for the whole document.body, if that works for you.