Skip to content
This repository has been archived by the owner on Oct 8, 2021. It is now read-only.

Fixed header jumps around when iOS keyboard disappears #5532

Open
jhogervorst opened this issue Jan 25, 2013 · 35 comments
Open

Fixed header jumps around when iOS keyboard disappears #5532

jhogervorst opened this issue Jan 25, 2013 · 35 comments

Comments

@jhogervorst
Copy link
Contributor

Issue description

We have a page with some input elements, a link or button, and a fixed header. Let's say you tap an input element and the iOS keyboard appears. Two things could happen:

  • When you scroll the page and dismiss the keyboard while the page still scrolls, the header does not return to its fixed position.
  • If you tap a link or button, the header shows up somewhere in the (vertical) middle of the screen and only returns to its fixed position after manually scrolling.

Especially the latest issue causes trouble, because people will often tap a link or button after entering something in an input field.

Test page

Here's a test page.

Open the page and tap one of the textareas. Now scroll the page and quickly tap the keyboard dismiss button. The fixed header will remain hidden.

Tap one of the textareas again and scroll to the link and button down on the page. Tap one of them. The header will end up in the middle of the page. Scroll a bit, and it'll jump back to the top.

Platforms/browsers and devices tested

  • Safari on iOS 6.0.2 (iPad mini).

jQuery Mobile and jQuery core version used

  • JS Bin test page: jQuery Mobile latest and jQuery 1.9.0.

Other relevant information

I know there have been more issues regarding the iOS keyboard together with fixed headers, but I'm not sure what's relevant so I just opened a new issue. Hope you don't mind.

If it helps I can make a short video showing the two problems. Let me know if it'd help.

@jaspermdegroot
Copy link
Contributor

I can reproduce the issue on iPad4 iOS 6.0.1. It has been addressed before https://github.com/jquery/jquery-mobile/blob/master/js/widgets/fixedToolbar.js#L240

@arschmitz
Copy link
Contributor

I'll look into this since I did original fix. This was pre ios6 though so will have to see what's changed

@arschmitz
Copy link
Contributor

this has been fixed by commit 96a4f42

@jhogervorst
Copy link
Contributor Author

This issue is not completely resolved!

The bug regarding dismissing the keyboard while the page is being scrolled is fixed. However, the bug when tapping a link/button when the keyboard is visible, does still occur.

Open up my test page (it references the latest version). Tap one of the textareas again and scroll to the link and button down on the page. Tap one of them. The header will end up in the middle of the page. Scroll a bit, and it'll jump back to the top.

I've made a video (MOV; 33MB) in which I demonstrate the issue in Safari on my iPad mini running iOS 6.1.

@jaspermdegroot jaspermdegroot reopened this Feb 7, 2013
@jhogervorst
Copy link
Contributor Author

I see the issue is currently planned for milestone 1.4.0. I understand it's not a critical bug. However, when using jQuery Mobile for a project which contains many forms (an interactive web application), this bug is really annoying.

Could you maybe reconsider the milestone for this bug?

If you have an idea where (which component/file) this should be fixed, I would be glad to take a look at the source code and see whether I can fix it (or provide a simple workaround). However, I'm not enough into the jQuery Mobile source to easily find this by myself.

@jaspermdegroot
Copy link
Contributor

@jhogervorst

Since we planned to fix this in 1.3 but couldn't fix the whole issue it makes sense to set the milestone to 1.3.1. I think the 1.4 was my mistake.
If we find a way to fix this issue you will find a link to the commit here so you can see for yourself if you already want to apply them.

@jaspermdegroot
Copy link
Contributor

Closing this ticket in favor of re-opening #4113

@jaspermdegroot
Copy link
Contributor

@jhogervorst

This should be fixed in lastest code.

@jhogervorst
Copy link
Contributor Author

This issue is not completely resolved!

The bug regarding dismissing the keyboard while the page is being scrolled is fixed. However, the bug when tapping a link/button when the keyboard is visible, does still occur.

Open up my updated test page (it references the real latest version). Tap one of the textareas and scroll to the link and button down on the page. Tap one of them. The header will end up in the middle of the page. Scroll a bit, and it'll jump back to the top.

I've made a new video (MOV; 32MB) in which I demonstrate the issue in Safari on my iPad mini running iOS 6.1.3.

@jhogervorst
Copy link
Contributor Author

I've made a temporary workaround for this issue.

After some testing it turned out that the fixed positioning of the header element is somehow not right in Safari on iPad. Even when setting values for top or margin-top, these changes were calculated relative to the (wrong) current position. This makes me think that the underlying cause might be a bug in the browser.

As shown in the videso, the toolbar pops back to the top when scrolling the page. It turns out that programmatically scrolling the page causes the browser to reposition the element. I've used this in the workaround.

Here's the the workaround (just place it somewhere in your JavaScript code):

$(document).on('blur', 'input, textarea', function() {
    setTimeout(function() {
        window.scrollTo(document.body.scrollLeft, document.body.scrollTop);
    }, 0);
});

The script watched all blur events on inputs and textareas. Then it scrolls the page (to the current location – you don't see any scrolling happen).

I hope this is useful to someone.

@IHNEL
Copy link

IHNEL commented Apr 5, 2013

I have the same issue when focus to an text input. The footer and footer jump around the screen. But you workaround helps me. Here is how I fixed it:

$(document).on('focus', 'input, textarea', function() {
setTimeout(function() {
window.scrollTo(document.body.scrollLeft, document.body.scrollTop);
}, 0);
});

@jaxtheking
Copy link

After a lot of fiddling, I got to the same solution as @jhogervorst - I wish I found it here before wasting so much time.
I confirm this solution works for forms on both long scrolling and non-scrolling pages.
@IHNEL I think you meant on blur, not on focus. I quickly tested the fix on focus and did not work.

I have validation on the page (jquery.validation), and even with the above fix, when one clicks the submit button on empty fields, the first field of the form get focused. This causes the header, although positioned correctly, to appear and lay over the focused field. I therefore came up with my own twist, not sure if it helps anyone or it'll be still applicable for jQM 1.3.1.

Before jQM is loaded:

$.mobile.fixedtoolbar.prototype.options.hideDuringFocus = '';

Then, in my template's js, i call this on pageshow (only for ios):

// Workaround for buggy header/footer fixed position when virtual keyboard is on/off
$('input, textarea')
.on('focus', function (e) {
    $('header, footer').css('position', 'absolute');
})
.on('blur', function (e) {
    $('header, footer').css('position', 'fixed');
    //force page redraw to fix incorrectly positioned fixed elements
    setTimeout( function() {
        window.scrollTo( $.mobile.window.scrollLeft(), $.mobile.window.scrollTop() );
    }, 20 );
});

The above "unfixes" the header/footer while an input field is focused. I don't really like for the header/footer to disappear in case the user scrolls when the keyboard is up. It looks particularly odd on short non-scrolling pages like a login screen.

@IHNEL
Copy link

IHNEL commented Apr 10, 2013

Sorry. When I post above comment, I thought the issue was fixed after a sanity test. But It wasn't. I also tried Jaxtheking's recommend, but nothing changes.
By some googlings, I see that this issue came from jQM v.1.0. Why was it not fixed after along time?

@anitsirc
Copy link

anitsirc commented May 1, 2013

I made this workaround based on @jhogervorst 's one... since the one he gave wasn't working for me.

$(document).on('focus', 'input, textarea', function() {
$.mobile.silentScroll($('div[data-role="header"]').offset().top);
});

@xavisavvy
Copy link

Here is a workaround I found worked for me in my case based on the response @anitsirc gave, this way I had the focused input in view. Thank you again for the suggestions everyone. scrolling when an input is focused was causing a big annoyance with the fixed jQM header floating wherever it pleases on iOS.

$(document).on('focus', 'input, textarea', function() {
$('div[data-role="header"]').css('position', 'absolute');
$.mobile.silentScroll($('input:focus').offset().top - 100);
});
$(document).on('blur', 'input, textarea', function() {
$('div[data-role="header"]').css('position', 'fixed');
});

@amol-c
Copy link

amol-c commented May 10, 2013

Amazing piece of code, i spent 3 hrs trying to figure this out !!!

@benwwest
Copy link

Hi,

not sure if the bug is completely the same but the title of this bug describes the issue i'm experiencing so thought i would post it here.

We have a fixed header with a search bar in it and I have noticed some erratic behavior when you click into the search bar.

On short pages where there is no scrolling or when you are at the top of a long page the header acts as expected. However if you are halfway down a long page and tap into the search box the header detaches itself and appears half way down the screen when the keyboard appears.

I have created a demo here: http://jsbin.com/edacuh/4/edit

Experienced on an iPhone 5, running 6.1.3 on both Safari and Chrome

Thanks in advance.

@triwav
Copy link

triwav commented Jun 13, 2013

I've got the same issue as benwwest. I have a search bar in the header and it causes the same issue. Within safari, if I change the header's padding to 0 and back to 1px after a short timeout it causes it to reposition correctly to the top. However in phonegap this does not work and the only way I can think to do it is manually try and calculate the correct spot for the header.

@brendajin
Copy link

I believe this is a browser issue. I am experiencing the same issues with form elements that are position:fixed, even without using jquery-mobile. This occurs on the iOS browser when focusing on text input. It also occurs for other types of elements on Nexus 7's Chrome browser.

@triwav
Copy link

triwav commented Jun 14, 2013

You're probably right. The question is, what can be done about it if anything? Obviously removing the header or footer when the keyboard comes up works but it definitely takes a way the native app feel though.

@markgmilner
Copy link

What exactly should the default behvior be? The fix I used to make sure the header stays fixed to the top of the page on blur is the following:

$(document).on('blur', 'input, textarea', function() {
            $.mobile.silentScroll($('div[data-role="header"]').offset().top);
        });

This is a variation of the fixes posted above. Using this, upon focusing on an input/textarea the fixed header is hidden, and upon finishing (on blur of the input/textarea) the keyboard is hidden and the header no longer is pushed down to the middle of the page. This fix keeps the toolbar fixed, yet hides it to allow for more room to see the input. Is this the behavior that is expected?

@triwav
Copy link

triwav commented Jun 19, 2013

I don't have any issues with after the keyboard goes away (blur). If you read benwwest post, it's when the keyboard comes up that's the issue. If you're at the top of the page it doesn't mess up but if you scroll down the page and select an input that brings up the keyboard, it will shift the header like it's not fixed. Sometimes the header gets pushed off the screen and other times it gets put half way down the screen. One possible fix is to scroll the page before the keyboard gets popped up. The header is still not fixed but it at least would appear in the correct part on the page. The main issue with this fix still is in cases like his or mine where the input is actually in the header, this means you'd have to have a very header to make this work as it appears like putting the input around 25% down the screen or there abouts.

@selvaraj-contus
Copy link

Yea, Its worked for me. Thanks for the solution to fix the header position on top of iphone/ipad header, when my page moving to top by clicking on the input fields,

$(document).on('blur', 'input, textarea', function() {
setTimeout(function() {
window.scrollTo(document.body.scrollLeft, document.body.scrollTop);
}, 0);
});

@mallika89
Copy link

Above solution worked for me too. Thanks

@Yankey36
Copy link

I have two ways to solve this,but they have little shortcoming.
way 1: shortcoming is Editing text is a bit slow for 'position', 'absolute'.
$(document).on('focus', 'input,textArea', function () {
$('div[data-role="header"]').css('position', 'absolute');
$('div[data-role="footer"]').css('position', 'absolute');
$(document).scrollTop(document.body.scrollHeight);
$(document).trigger("refresh");
})
.on('blur', 'input,textArea', function () {
$('div[data-role="header"]').css('position', 'fixed');
$('div[data-role="footer"]').css('position', 'fixed');
});

way 2: shortcoming is page margins top and bottom.
$(document).on('focus', 'input,textArea', function () {
$('div[data-role="header"]').css('position', 'relative');
$('div[data-role="footer"]').css('position', 'relative');
$(document).scrollTop(document.body.scrollHeight);
$(document).trigger("refresh");
})
.on('blur', 'input,textArea', function () {
$('div[data-role="header"]').css('position', 'fixed');
$('div[data-role="footer"]').css('position', 'fixed');
});

@sdmarshall
Copy link

I recently had the issue where the keyboard on I0S7 would make the fixed footer with a navbar contained within it would. a) jump around when the keyboard was displaying.. b) remain in the middle of the screen after the keyboard had been hidden by clicking white space to remove focus from the text box / text area.

I used the following code, building on what someone had already posted here to provide a really smooth action for the fixed toolbar.

    $(document).on('focus', 'input, textArea', function () {
        $('div[data-role="footer"]').hide();
    })  

    $(document).on('blur', 'input, textarea', function() {
        setTimeout(function() {
            window.scrollTo(document.body.scrollLeft, document.body.scrollTop);
            $('div[data-role="footer"]').show();
        }, 0);
    });

Hope this helps anyone struggling with the same issue

@kennardconsulting
Copy link

This is a difficult problem to get 'right'. You can try and hide the footer on input element focus, and show on blur, but that isn't always reliable on iOS. Every so often (one time in ten, say, on my iPhone 4S) the focus event seems to fail to fire (or maybe there is a race condition), and the footer does not get hidden.

After much trial and error, I came up with this interesting solution:

<head>
    ...various JS and CSS imports...
    <script type="text/javascript">
        document.write( '<style>#footer{visibility:hidden}@media(min-height:' + ($( window ).height() - 10) + 'px){#footer{visibility:visible}}</style>' );
    </script>
</head>

Essentially: use JavaScript to determine the window height of the device, then dynamically create a CSS media query to hide the footer when the height of the window shrinks by 10 pixels. Because opening the keyboard resizes the browser display, this never fails on iOS. Because it's using the CSS engine rather than JavaScript, it's much faster and smoother too!

Note: I found using 'visibility:hidden' less glitchy than 'display:none' or 'position:static', but your mileage may vary.

@xavisavvy
Copy link

I love it. Anytime CSS can take place of Javascript I am game (as far as I understand it can utilize hardware acceleration features, whereas javascript is more limited.) Thanks for the fresh look at things 👍

@kennardconsulting
Copy link

Thanks!

I actually came up with this a little while ago, but didn't know about this issue thread. I originally posted it here: http://stackoverflow.com/questions/20069352/jquery-mobile-hide-fixed-footer-when-keyboard/20092755#20092755 and some kind people have added some improvements since then. So you may want to incorporate those too.

@oliveryang10
Copy link

Very good suggestion to fix this problem! However, If I'm not use jQuery mobile template, just using own html, how to fix this problem?

@kennardconsulting
Copy link

@visionyang my solution is not specific to, and can be used outside of, JQuery Mobile

@oliveryang10
Copy link

Thanks so much! I will try it! And asking you more details if it still not
work.

On Wednesday, October 8, 2014, Richard Kennard notifications@github.com
wrote:

@visionyang https://github.com/visionyang my solution is not specific
to, and can be used outside of, JQuery Mobile


Reply to this email directly or view it on GitHub
#5532 (comment)
.

Best Regards,
Oliver Yang

UI/UX Designer
Bay Area, CA, United States

@ghost
Copy link

ghost commented Feb 2, 2015

A solution is to change the size of page content. It redraws the content and replace the fixed toolbars in place.

$(document).on('blur', 'input:not(:submit), select, textarea', function () {
    window.setTimeout(function () {
        var $content = $(".ui-content");
        if ($content.css("padding-bottom") === "15px") {
            $content.css("padding-bottom", "16px");
        } else {
            $content.css("padding-bottom", "15px");
        }
    }, 0);
});

_Note: The scroll solution block the focused input iOS navigation._
And it still fails with panels on iPad and so we ended with the following code:

For iOS 6, 7 and 8, this hack seems to solve the problem and trigger a redraw to correctly replace the fixed header (with or without panel) on iPod, iPhone and iPad. Note: We test for iOS device and only add this event in that case *.

if (iOS()) {
    $(document).on('blur', 'input:not(:submit), select, textarea', function () {
        var paddingBottom = parseFloat($(".ui-mobile-viewport, .ui-page-active").css("padding-bottom"));
        $(".ui-mobile-viewport, .ui-page-active").css("padding-bottom", (paddingBottom + 1) + "px");
        window.setTimeout(function () {
            $(".ui-mobile-viewport, .ui-page-active").css("padding-bottom", paddingBottom + "px");
        }, 0);
    });
}

* Test for iOS:

var iOS() = function () {
    var userAgent = window.navigator.userAgent.toLowerCase();
    return (/iphone|ipad|ipod/).test(userAgent);
}
christopherdavid pushed a commit to christopherdavid/hybrid_app that referenced this issue Apr 2, 2015
Issue #142 Header redraws low and obscures text after tapping outside text entry field while on screen keyboard active on iPhones on iOS 6 and 7.
this is a bug in jquery mobile, added workaround found on jquery-archive/jquery-mobile#5532
@selvam75
Copy link

selvam75 commented Aug 9, 2016

The (data-position="fixed") header misalignment in iOS has been fixed by adding following code ,

$(document).on('touchstart', function(e) {
setTimeout(function() {
document.activeElement.blur();
}, 0);
});

and then,
$(document).on('focus', 'input, textarea', function() {
setTimeout(function() {
window.scrollTo(document.body.scrollLeft, document.body.scrollTop);
}, 0);
});

@kennardconsulting
Copy link

I don't believe this works reliably. The blur/focus events do not always fire (say, one time in ten, on my iPhone). A better solution is here:

http://stackoverflow.com/a/20092755/384157

On 10/8/2016 12:52 AM, Selvam75 wrote:

The (data-position="fixed") header misalignment in iOS has been fixed by adding following code ,

$(document).on('touchstart', function(e) {
setTimeout(function() {
document.activeElement.blur();
}, 0);
});

and then,
$(document).on('focus', 'input, textarea', function() {
setTimeout(function() {
window.scrollTo(document.body.scrollLeft, document.body.scrollTop);
}, 0);
});


You are receiving this because you commented.
Reply to this email directly, view it on GitHub #5532 (comment), or mute the thread
https://github.com/notifications/unsubscribe-auth/AA8QstYiVnEKRGausWigun3dxvpkrpIZks5qeJRKgaJpZM4AYs4Z.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.