jquery-archive / jquery-mobile

jQuery Mobile Framework
https://jquerymobile.com
Other
9.69k stars 2.41k forks source link

Fixed header jumps around when iOS keyboard disappears #5532

Open jhogervorst opened 11 years ago

jhogervorst commented 11 years ago

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:

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

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 commented 11 years ago

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 commented 11 years ago

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

arschmitz commented 11 years ago

this has been fixed by commit 96a4f42

jhogervorst commented 11 years ago

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.

jhogervorst commented 11 years ago

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 commented 11 years ago

@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 commented 11 years ago

Closing this ticket in favor of re-opening #4113

jaspermdegroot commented 11 years ago

@jhogervorst

This should be fixed in lastest code.

jhogervorst commented 11 years ago

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 commented 11 years ago

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 commented 11 years ago

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 commented 11 years ago

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 commented 11 years ago

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 commented 11 years ago

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 commented 11 years ago

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 commented 11 years ago

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

benwwest commented 11 years ago

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 commented 10 years ago

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 commented 10 years ago

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 commented 10 years ago

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 commented 10 years ago

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 commented 10 years ago

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 commented 10 years ago

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 commented 10 years ago

Above solution worked for me too. Thanks

Yankey36 commented 10 years ago

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 commented 10 years ago

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 commented 9 years ago

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 commented 9 years ago

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 :+1:

kennardconsulting commented 9 years ago

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 commented 9 years ago

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 commented 9 years ago

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

oliveryang10 commented 9 years ago

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 https://github.com/jquery/jquery-mobile/issues/5532#issuecomment-58445137 .

Best Regards, Oliver Yang

UI/UX Designer Bay Area, CA, United States

ghost commented 9 years ago

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);
}
selvam75 commented 7 years ago

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 commented 7 years ago

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 https://github.com/jquery/jquery-mobile/issues/5532#issuecomment-238578670, or mute the thread https://github.com/notifications/unsubscribe-auth/AA8QstYiVnEKRGausWigun3dxvpkrpIZks5qeJRKgaJpZM4AYs4Z.