Preventing scroll with 100% body height when the virtual keyboard's open?

2k views Asked by At

I have a chat app with sticky header and footer elements. When the mobile virtual keyboard is open, I set the document's height to window.visualViewport.height. E.g. if the browser's height is 1000px and the virtual keyboard is 400px, then window.visualViewport.height is 600px and both <html> and <body> are set to 600px. The header and footer would both appear correctly within the 600px viewport.

However, users can still scroll the whole page up by 400px, meaning they'll see 400px of empty space at the bottom. How can I prevent this empty space from showing up while still having the sticky header/footer?

I tried:

  1. Detecting scroll events, but they aren't fired
  2. Using an IntersectionObserver on an element below the 600px viewport, but it never triggers
  3. Using position: fixed on the footer, but it doesn't stick to the bottom when scrolling
  4. The document's scroll Y position is always 0
  5. Setting navigator.virtualKeyboard.overlaysContent doesn't do anything

Mostly tested in Android + Chrome, but same issue occurs in iOS + Safari.

Video demo, the initial bottom white space is the keyboard: https://i.imgur.com/OMSXAAt.mp4

Edit

I found a hacky solution using window.visualViewport.addEventListener('scroll', ...). On scroll, I'd add some padding to the top of the page equal to window.visualViewport.offsetTop. However, there's some lag, so users can scroll a little, then the scroll handler runs. On iOS Safari, the lag can be more than 1 second. Is there a better solution?

2

There are 2 answers

0
mpmcintyre On

If the position is okay while the keyboard comes up I would suggest using the virtualkeyboard event and making the parent div hide scrolling when it is active, and make it scrolable when it goes away again:

Code snippet:

if ("virtualKeyboard" in navigator) {
  navigator.virtualKeyboard.addEventListener("geometrychange", (event) => {
    const { x, y, width, height } = event.target.boundingRect;
    // Test if the keyboard is open, you will have to write this yourself
    // But just for an example:
    let keyboardOpen = height > 0;
    if (keyboardOpen) {
        // Make the parent element stop scrolling by making the overflow hidden
        document.getElementById("parent-div").style.overflow = "hidden";
    } else {
        // Make the parent element scrollable again
        document.getElementById("parent-div").style.overflow = "scroll";
    }

  });
}

Just be sure to insert the correct div ID into the document query and give the div an ID in its HTML tag.

References:

Mozzila virtual keyboard API

virtual keyboard api

0
Eliezer Berlin On

Here you are. A combination of techniques seems to be most effective.

Source: Get viewport height when soft keyboard is on

JSBin code: https://jsbin.com/nayigetido/1/edit?html,css,js,output

JSBin Output: https://jsbin.com/nayigetido/1/edit?output

HTML:

<head>
  <!-- Dont forget the meta tag. -->
  <meta name="viewport" content="width=device-width, initial-scale=1.0, interactive-widget=resizes-content">
</head>
<body>
  <div id="chat">
    <div class="messages">Messages</div>
    <input type="text"/>
  </div>
</body>

CSS:

#chat{
  height:100%;
  width:100%;
  display:flex;
  flex-direction:column;
  padding:3em;
  box-sizing:border-box;
}
.messages{
  height:100%;
  flex:1 1 auto;
}
input{
  width:100%;
}

html,body{
  margin:0;
  height:100%;
}

JS:

window.addEventListener('resize', () => {
  const chat = document.getElementById('chat');
  chat.style.height = window.visualViewport.height + 'px';
});