0

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 for bounty: 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?

Leo Jiang
  • 24,497
  • 49
  • 154
  • 284
  • Maybe using [MDN: Relative length units based on viewport](https://developer.mozilla.org/en-US/docs/Web/CSS/length#relative_length_units_based_on_viewport) is the simple solution (has proper browser support: [caniuse.com](https://caniuse.com/?search=svh%2C%20lvh%2Cdvh)). Without a proper [reprex] there's little more to say. – Rene van der Lende Jul 19 '23 at 10:56
  • Might be a stupid question, but is the footer element relative to some parent/ancestor? And did you set the `bottom: 0;` property when position was fixed? I had a similar issue where using the calc() function instead of fixed heights, e.g. `height: calc(100vh - keyboardHeightInPixels)` which eliminated the scroll. Not sure if it's going to help for this one, but I think it's also worth mentioning that I didn't touch the or tag. The height was only set for the root container—in your case it's the
    element.
    – Ludolfyn Jul 25 '23 at 22:35
  • My case here: https://ditsem.io/app?q=asseblief. If you open devtools, switch on responsive and set the viewport to be 410px wide. Then you'll see the popup thing coming up from below. I wanted the content underneath to always show the searchbar at the top no matter the viewport size, so if you play with the height, you'll see the size of the popup shift around. It's not the same thing, but had a similar issue where if you scroll it will scroll up and reveal the cut off bottom of the popup. Maybe my CSS can be of some help – Ludolfyn Jul 25 '23 at 22:39

2 Answers2

0

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

mpmcintyre
  • 614
  • 5
  • 16
0

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';
});
Eliezer Berlin
  • 3,170
  • 1
  • 14
  • 27