greed
greed

Reputation: 442

iOS 15 minimized address bar issue with fixed position, full screen content

Using iOS 15.0.2 Safari, when the on-screen keyboard is dismissed, the address bar can become minimized without updating the viewport height. This results a bottom gap that cannot be removed without pressing the address bar. Are there any known workarounds to this? It's possible to get to this state without any device rotations by using nested containers, but the simplest reproducible example that I could come up with is as follows:

  1. Rotate to landscape orientation
  2. Focus on the text input to bring up the on-screen keyboard
  3. Rotate to portraite orientation
  4. Press "Done" on the on-screen keyboard

body {
  padding: 0;
  margin: 0;
  overflow: hidden;
  position: fixed;
  inset: 0;
  -webkit-text-size-adjust: 100%;
}
input {
  font-size: 16px;
}
<head>
  <meta name="viewport" content="width=device-width,initial-scale=1, viewport-fit=cover">
  <meta name="theme-color" content="#000000">
</head>
<body>
  <input type="text" />
</body>

enter image description here

Upvotes: 16

Views: 17786

Answers (1)

zZeepo
zZeepo

Reputation: 376

I have recently faced the same problem, when setting the body position to fixed to block scrolling while an overlay is displayed. As soon as the scrolling is disabled, Safari stops rendering the lower area which would be covered by the expanded iOS UI, leaving behind the same black bar as you're showing in your screenshot.

Setting html.style.height to 100vh while the body is set to position: fixed seems to fix the issue.

I have build a simple example page where you can see the difference. After you open the page, you have to scroll down a little towards the buttons to collapse the Safari UI. Then you can toggle position: fixed or the code-to-fix-error class (to fix the error) by clicking the buttons.

In my example of the scroll blocking: Setting the height to calc(100vh - 1px) would also prevent the safari native rubber band / elastic scrolling effect while the the popup is active. This change should only be applied to iOS devices tho.

const btnError = document.querySelector('button.error')
btnError
  .addEventListener('click', () => {
    btnError.innerHTML = document.documentElement.classList.toggle('code-to-fix-error')
      ? 'Fix active'
      : 'Fix inactive'
  })

let y = 0
const btnToggle = document.querySelector('button.toggle')
btnToggle
  .addEventListener('click', () => {
    const _doc = document.documentElement
    if (_doc.classList.contains('active')) {
      _doc.style.setProperty('--scroll-y', `0`)
      _doc.classList.remove('active')
      window.scroll(0, y)
      btnToggle.innerHTML = 'position: static'
    } else {
      y = window.scrollY
      _doc.style.setProperty('--scroll-y', `-${y}px`)
      _doc.classList.add('active')
      btnToggle.innerHTML = 'position: fixed'
    }
  })
body {
  padding: 400px 0 0 0;
  margin: 0;
  inset: 0;
  -webkit-text-size-adjust: 100%;
  background-color: #435283;
  text-align: center;
  height: 8000px;
  width: 100%;
}

html.code-to-fix-error.active  {
  height: 100vh;
}

html.active body {
  top: var(--scroll-y);
  overflow: hidden;
  position: fixed;
}

button {
  appearance: none;
  line-height: 30px;
  padding: 0 10px;
  background-color: #FFF;
  color: #435283;
  border-radius: 3px;
}
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1, viewport-fit=cover">
    <meta name="theme-color" content="#000000">

    <style>/* styles here */</style>
  </head>
  <body>
    <button class="error">Fix inactive</button>
    <button class="toggle">position: static</button>
    
    <style>/* script here */</style>
  </body>
</html>

Upvotes: 17

Related Questions