Mobile Safari bug on fixed positioned button after scrollTop programmatically changed...?
Asked Answered
V

11

35

I'm just about done a webpage but there is one bug in Mobile Safari (iPhone and iPad iOS 5.0.1) with two buttons that are fixed to the upper and lower right corners..

The buttons are not faded in until after clicking submit on a textbox which opens up to the rest of the page... After the rest of the page is loaded and the buttons are faded in you can click on either of them and they both work...

However, clicking them causes a programmatic scroll and after that scroll is complete you can no longer click on either of the buttons until you physically scroll the page with your finger even just a tiny one pixel scroll...

What I have noticed is that after the programmatic scrolling if you tap just slightly below the TOP button you see the highlight as if you were tapping the BOTTOM button and the action of the bottom button is processed, which tells me the bug is that when scrolling programmatically the fixed position button still moves with the rest of the page and doesn't go back to it's fixed position until an actual touch scroll is performed....

Does anyone know a way around this..?

I've added a popup that shows which button was pressed so you can test it, remember after the first press of the down button (which works) trying pressing down again, it won't work, but click just below the up button and you'll see the down button actions happening....

http://www.tsdexter.com/ceos

thanks for the help.

Thomas

(also if you can point me to where I can submit a bug to Apple that'd be good too, unless one already has been)

EDIT: just click either of the submit arrows, you don't need to enter a wage/salary it has defaults

EDIT 2: Here is a simpler example to show the same issue..

http://www.tsdexter.com/MobileSafariFixedPosBug.html

EDIT 3: Bug reported to Apple

Victualage answered 6/1, 2012 at 1:13 Comment(5)
Is there any way to look up the problem in Apples bug-tracker (even if I have to register for that) to find out about the current status of the bug?Lanny
@Victualage can you post the URL to this bug you reported on Apple's bug report site?Brazilin
@Brazilin the bug reporter on Apple is not showing anything in my account anymore for some reason? It's not even showing under closed bugs.Victualage
@Victualage Very odd, perhaps I'll log the bug as well. Two complaints are better than one. I'll post the link once I get it up there.Brazilin
+1 as this question helped me solve a mad issue identical to how you describeHighpowered
F
13

I got around it by adding a 101% high div then (almost) immediately removing it.

Try:

<style>
.iosfix {
  height: 101%;
  overflow: hidden;
}
</style>

and when you scroll:

window.scrollTo(0, _NEW_SCROLLTOP_);
$('body').append($('<div></div>').addClass('iosfix'));
setTimeout(function() {
  $('.iosfix').remove();
}, 500);

It also works with jQuery.scrollTo.

See an example here.

Football answered 3/5, 2012 at 21:14 Comment(4)
confirming that is does have a slight flicker. Seems less noticeable than the accepted answer.Inopportune
Thanks this helped me, too. Mine works fine with a timeout of 0ms and it then doesn't have the flicker. https://mcmap.net/q/450072/-mobile-webkit-reflow-issueCida
This answer is excellent. No flickering. Exactly what I needed. Thank you.Moulder
This answer works better for me, so I'm accepting it instead.Victualage
Y
28

We also encountered this bug on 2 different iPad applications, for us the best fix was to temporarily remove the fixed position from the fixed element once the animated scroll had finished, then use window.scroll with the vertical value we’d just performed the animated scroll to, then finally re-apply the position fixed style. It does cause a very minor blip as the ipad re-renders the element but its preferable to the bug.

var $fixedElement = $('#fixedNavigation');
var topScrollTarget = 300;
$("html:not(:animated),body:not(:animated)").stop().animate({ scrollTop: topScrollTarget }, 500, "swing", function(evt) {
    $fixedElement.css({ "position": "relative" });
    window.scroll(0, topScrollTarget );
    $fixedElement.css({ "position": "fixed" });
});
Yoakum answered 21/3, 2012 at 16:26 Comment(4)
Thanks for the comment. I no longer need this functionality. If I have some time though, I will test it on the page I needed it for and see if it works there as well. Thanks again!Victualage
Thanks for the solution! The blip is quite noticeable in my app but it does the trick.Prem
This solution works for me too. If you're using jquery mobile, your position is defined in classes so replace "position": "fixed" with "position": "" so that it's not permanently fixed.Leanneleanor
This workaround will not work for all layouts. If it doesn't work on your layout try other answers on this page or slight variations. In my case changing the width of body did the trick.Phrensy
F
13

I got around it by adding a 101% high div then (almost) immediately removing it.

Try:

<style>
.iosfix {
  height: 101%;
  overflow: hidden;
}
</style>

and when you scroll:

window.scrollTo(0, _NEW_SCROLLTOP_);
$('body').append($('<div></div>').addClass('iosfix'));
setTimeout(function() {
  $('.iosfix').remove();
}, 500);

It also works with jQuery.scrollTo.

See an example here.

Football answered 3/5, 2012 at 21:14 Comment(4)
confirming that is does have a slight flicker. Seems less noticeable than the accepted answer.Inopportune
Thanks this helped me, too. Mine works fine with a timeout of 0ms and it then doesn't have the flicker. https://mcmap.net/q/450072/-mobile-webkit-reflow-issueCida
This answer is excellent. No flickering. Exactly what I needed. Thank you.Moulder
This answer works better for me, so I'm accepting it instead.Victualage
P
7

I had multiple links on separate fixed elements (a modal popup + fixed blackout div + normal fixed toolbar) and none of these answers were working so I had a tinker about trying variations on the same theme. Like all these suggest the key is getting elements re-rendered.

Initially I tried adding 1px to the width of fixed elements and removing it. This did cause re-rendering, but re-rendered elements became mis-aligned with non re-rendered elements - another result of this iOS bug I suspect. The answer was to simply add to the width of the body and subtract again (or set to auto), ie:

//jQuery will calculate the current width and then +1 to this and set it
$('body').css('width', '+=1');

//Remove width css
setTimeout(function() {
  $('body').css('width', '');
}, 1);

If not using jquery you will need to get the current width of body +1px to it and then set the width.

Phrensy answered 9/7, 2012 at 13:1 Comment(2)
+1 this helped me the most - been going round in circles for literally DAYS because of this!!Highpowered
Yeah! This also helped me out after searching for two hours.. Tx Ed!Jailbreak
D
4

Here is my solution if like me, none of the previous solution is working for you.

The trick is:

  • Do your scroll (Animate or scrollTo, etc.)
  • Just after your scroll, position:absolute your fixed elements
  • On the 'touchmove' event, restore the position:fixed

Here an example:

  $('body').animate({
       scrollTop: newPos}, 1000, 'jswing', function () {
          $('header').css({position:'absolute', top:newPos});
  });

  $(document).bind('touchmove',function(){
       $('header').css({position:'fixed', top:'0px'});
  });   

I used the same trick for sticky footer and other floating fixed elements.

Deth answered 3/8, 2012 at 20:21 Comment(0)
E
3

A variation of this worked for me as well. Trying to not use frameworks where I can on mobile.

    var d = document.createElement("div");
    d.style.height = "101%";
    d.style.overflow = "hidden";
    document.body.appendChild(d);
    window.scrollTo(0, scrollToM);
    setTimeout(function() {
        d.parentNode.removeChild(d);
    }, 10);
Efficient answered 16/5, 2012 at 1:55 Comment(1)
Thanks! I reduced your answer slightly more (also without flicker) and both halves can be run back to back: https://mcmap.net/q/450072/-mobile-webkit-reflow-issueCida
Y
2


After spending a couple of hours on this, I found a workaround: try scrolling (maybe with an animation) and then scrolling again to the same point (without animation).
This way you force the browser to delete the wrong rendering from the view.

Example:

$('body, html')
    .animate({scrollTop: 0})
    .scrollTop(0);
Yuki answered 11/1, 2012 at 15:18 Comment(3)
Do you have an example of this working? I cannot get it to work on either the actual site I'm building or this bug report page I made. Please see the page, I've added your fix and still nothing. (maybe your fix is working because you're scrolling to 0 - so you're not actually scrolling at all?) tsdexter.com/MobileSafariFixedPosBug.htmlVictualage
the fixed is added at line 34 of the source.Victualage
Yeah this seems to fix the rendering of the page we had but not the buttons triggers which remain in the old position. I found that if you do: $('body, html') .animate({scrollTop: 0}, function(){ $element.css('display', 'none'); setTimeout(function() { $element.css('display', 'block'); }, 0); }); You can see where the $element actually is..Yuki
M
1

I was having the same problem with iOS5 and JQueryMobile. Fixed Header & Footer. Expandable content and suddenly i had a ghost footer that you could see but not touch. I had a bit of a problem getting a straight change position to absolute then back to work. It seemed to only work some of the time. I ended up using this.

        $(myFixedFooter).css("position", "relative").hide(0, function () {
            $(this).show(0).css("position", "");
        });

This defiantly creates a "blip" as the footer does its thing. Hoever i found that some 98% of the time the footer stayed at the bottom of the page. All the other work arounds and tweaks i found and tried didn't always leave the footer at the bottom or they didn't solve the problem in the first place.

Hopefully Apple will fix soon.

Middle answered 19/4, 2012 at 5:49 Comment(1)
Thank you so much, this was the only fix that worked for me! :-)Hightower
C
0

I discovered the exact behavior you describe in an iPhone app I'm writing. I load a bunch of HTML text with an index on the right side. After selecting an item from the menu and scrolling the text, the menu would then become unresponsive (because the landing zone had scrolled out from under it). I also saw that even a tiny scroll of the text would reenable the index menu.

I created a test case and uploaded the file here (If you view this on a non-iPhone browser, make the window small vertically to see the correct behavior):

http://www.misterpeachy.com/index_test.html

I figured out that the index menu was scrolling with the text (even though the visible menu didn't move) after I tapped B and then tapped B again. Instead of scrolling to B (basically not moving), it scrolled to D.

Right now I'm stuck. I'm hoping that I can add some JavaScript code (I've never programmed in JavaScript, so that is a slight problem) that will scroll the text one pixel after I lift my finger off a menu item (and after the text has scrolled to the selected place, of course). Maybe JavaScript can detect the scroll and then add another scroll to that.

Celluloid answered 23/2, 2012 at 4:28 Comment(3)
I reported this as a bug to Apple and they replied that they already knew about it. Presumably that means that a fix will appear in a later version of iOS.Celluloid
Sorry about the delay on this. Adding JavaScript to scroll the page won't work. A JavaScript scroll does not cause the position to rerender properly, only an actual physical scroll will. I've even tried to program a touch event that simulated a finger scroll and it still wouldn't work. Unfortunately I think we have to wait for Apple on this one. Although Dominic Warren above seems to have a workaround (I haven't tried since I no longer need it)Victualage
I added some JavaScript that detected the finger lift but it executed BEFORE the scroll, so it didn't work.Celluloid
U
0

In case it can help someone:

I had the exact same problem, and my code looked something like this (it's a single-page webapp):

window.scrollTo(0,0);
$('section.current').removeClass('current');
$(target).addClass('current');

I spent hours trying everything (101% height divs, changing the position type...), but finally the last suggestion described on Device-Bugs saved the day. In my case it was just a matter of scrolling when the divs aren't rendered:

$('section.current').removeClass('current');
window.scrollTo(0,0);
$(target).addClass('current');
Undercool answered 20/6, 2012 at 15:25 Comment(0)
C
0

Another variation of a solution is to increase the size of the document width by 1px and then immediately undo it. This has the advantage of not creating any more elements and there isn't any flicker that I've experienced.

https://mcmap.net/q/450072/-mobile-webkit-reflow-issue

Cida answered 13/7, 2012 at 22:22 Comment(0)
H
0

Example code

if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) {
    $(document).on('focus', 'input, textarea', function() {
        $('header').css({'position':'static'});
    });
    $(document).on('blur', 'input, textarea', function() {
        $('header').css({'position':'fixed'});
    });
}
Hartle answered 13/3, 2014 at 11:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.