Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to keep fixed html element visible on bottom of screen when the soft keyboard is open on iOS Safari?

In a web page I have an input field and a div that is fixed to the bottom of the window (with these CSS properties: position:fixed; and bottom:0;

I made a Codepen to show what I'm talking about: https://codepen.io/anon/pen/xpQWbb/

Chrome on Android keeps the div visible even when the soft keyboard is open:

enter image description here

However, Safari on iOS seems to draw the soft keyboard over the fixed element:

enter image description here

(I should mention I'm testing on the iOS simulator on my Macbook, because I don't have a working iPhone)

Is there a way to make iOS Safari keep the element visible even when the soft keyboard is open, like how Chrome does it?

like image 960
Andreyu Avatar asked Jan 18 '18 11:01

Andreyu


1 Answers

I recently ran in to this problem when creating a chat input that should stay fixed at the bottom of the page. Naturally the iOS keyboard displayed on top of the chat input. Knowing the exact keyboard height seems more or less impossible. I embarked on a quest to find a solid value to base my calculations on so i can manually position the chat input container above the keyboard. I wanted to find the actual "innerHeight" value, in other words the currently visible area of the webpage. Due to how the iOS keyboard works, the only way to get that value with the keyboard open seems to be to scroll to the very bottom of the page, and then take a sample of "window.innerHeight".

So, i set up an event listener on my input field on 'click' (since on 'focus' caused a lot of issues for me). This opens the keyboard, which takes a while, so after i set a timeout for 1000ms to make sure (hopefully) that my keyboard is fully open. After 1000ms i quickly scroll to the bottom of the page with javascript, save the value of "window.innerHeight" in this state, and scroll back to where i was. This gives me the actual height of the visible area on the screen.

It seems like the browser window is placed behind the keyboard until you scroll to the very bottom, in which case the whole window 'scrolls up' and the bottom is placed at the top of the keyboard view.

Once i have this value i use currently scrolled value (window.scrollY) plus the value i saved minus the height of my absolute positioned element to determine where to place it. I opted to also hide the input while scrolling since it's flicking around quite a bit. Another downside to this is that you get a quick flick of the page when it does the measurement at the bottom.

Another thing i couldn't solve was the variable height of the address bar. I just made the input a bit higher than i needed so it would have some "padding" at the bottom.

var correctInnerHeight = window.innerHeight;
var isFocused = false;
var docHeight = $(document).height();
var input = $('.myInput');
input.click(function(e){
  isFocused = true;
  input.css('position', 'absolute');
  // Wait for the keyboard to open
  setTimeout(function(){
    docHeight = $(document).height();
    var lastScrollPos = $(document).scrollTop();
    // Scroll to the bottom
    window.scroll(0, $(document).height());
    // Give it a millisecond to get there
    setTimeout(function(){
      // Save the innerHeight in this state
      correctInnerHeight = window.innerHeight;
      console.log(correctInnerHeight);
      // Now scroll back to where you were, or wish to be.
      window.scroll(0, lastScrollPos);
      fixInputPosition();
      // Make sure the input is focused
      input.focus();
    }, 1);
  }, 1000);
});

input.on('blur', function(){
  input.css('position', 'fixed');
  input.css('top', 'auto');
  input.css('bottom', '');
  isFocused = false;
});

$(window).scroll(function(){
  fixInputPosition();
});

function fixInputPosition(){
  if(isFocused){
    var offsetTop = ($(window).scrollTop() + correctInnerHeight) - input.height();
    offsetTop = Math.min(docHeight, offsetTop);
    input.css('top', offsetTop);
    input.css('bottom', 'auto');
  }
};
body, html{
  margin: 0;
}
html{
  width: 100%;
  height: 2000px;
}
.myInput{
  position: fixed;
  height: 30px;
  bottom: 0;
  width: 100%;
  border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type='text' class='myInput'>
like image 195
Tim Palander Avatar answered Nov 11 '22 00:11

Tim Palander