Fixing One Pixel in Safari

Published 2 years ago, mid-February under Design, Software

Safari BugI’ve been frustrated by a 1 pixel background-image shift in Safari ever since I discovered the bug last May. The problem occurs when positioning background images using the ‘center’ keyword. It’s a rounding problem - odd window widths cause centered background images to misalign with centered div’s. I spent hours tweaking style sheets, but met no success. After months of discontent I finally sat down and wrote a quick javascript fix for the problem.

Update: I discovered a very simple general solution, though it presents new problems.
Update 2: I’ve found a simple solution that works for all rounding cases with no apparent bugs.

The Problem

Below are some screenshots of the home page before applying the fix:
1 Pixel Shift
1 Pixel Shift
My fix hasn’t yet been applied to subpages, so the problem is still visible at the bottom of this page as well.

Obviously a one pixel shift isn’t a design-breaking bug. In fact, only one other person has actually noticed it. But it’s certainly an annoyance and worth fixing.

Heres the bits of CSS that cause the problem:

#main {
    background: url(images/mid.gif) top center repeat-y;
    ...
}
#footer {
    width: 100%;
    text-align: center;
    margin: auto;
    ....
}
#footcontent {
    background: url(images/footer.gif) top center no-repeat;
    width: 980px;
    margin: auto;
    ...
}

UPDATE: General Solution

This code works in all cases, but it forces a browser resize when an odd browser width is detected. In other words, the browser is forced into an even width where the rounding error doesn’t occur. Unfortunately the fix itself produces another bug: the window will sometimes momentarily flash to white when resizing. Update: By adding a simple delay, the flash-to-white problem has been solved.

window.onload = resizeIfOdd;
window.onresize = forceSafariEven;

function resizeIfOdd(){
if(navigator.userAgent.indexOf('Safari') != -1)
 if(document.documentElement.clientWidth % 2 == 1)
  window.resizeBy(-1,0);
};

function forceSafariEven(){
 window.setTimeout('resizeIfOdd()',5);
};

Shortcomings

  • Screen occasionally flashes white when resizing browser window.

Download the Script

Download .zip file Download (.zip) - 0.6 KB

Thanks to Mike Vierow for sending me his research on this subject.

If anybody has any suggestions for how to improve my code, please leave them in the comments below or drop me a line.

[tags]Javascript, DOM, Safari, Apple[/tags]


 

19 Comments from Snooze-Ville

  1. I’ve come across this exact same problem once before. Forcing the window to an even width is an interesting solution, although a better solution might be to resize some container element within the document to an even width rather than the window itself. I don’t know if that’s even possible to do - body or html would have to be resized by -1px. I wound up just leaving the bug, but this script seems like an easy fix. Thanks.

  2. I see I am not the only person that has experienced this issue. However, this solution is not complete. On browser window resize when the window is smaller than the page, this script produces some strange results. For example, it locks the height of the browser window.

  3. D.S., what version of Safari are you using? I created and tested it on Safari 2.0.4, so it may produce strange results with other versions that I’d be happy to address.

  4. Tim Van Damme April 30th at 6:23 am

    Hey, thanks a million for this solution!

    However, the bug appears in more browsers than just Safari. In fact, the bug appears in all browsers built upon WebKit…

    Solution:
    Change “if(navigator.userAgent.indexOf(’Safari’) != -1)â€? into “if(navigator.userAgent.indexOf(’WebKit’) != -1)â€?

  5. Chris Hill May 24th at 6:41 pm

    I’ve developed a method that overrides the positioning of the background and does it by hand. More elaborate, but doesn’t resize the browser.

    http://zomgforeelz.blogspot.com/2007/05/centering-in-safari-and-ie-off-by-one.html

  6. pestilence669 May 26th at 12:41 am

    I just finished writing my code when I stumbled across your post. This is my version.

    if (/(?:AppleWebKit|KHTML)/.test(navigator.userAgent)) {
    window.force2pxWidths = function()
    {
    if (window.innerWidth & 1) window.resizeBy(1, 0);
    }

    window.onload = window.onresize = function()
    {
    window.setTimeout(force2pxWidths, 1);
    }
    }

  7. Thanks for this great hack. I’ve been searching awhile for something like this and it seems this does the trick. Thanks much.

  8. David Kilzer August 13th at 9:01 am

    A bug was just filed about this on the WebKit open source project:

    http://bugs.webkit.org/show_bug.cgi?id=14956

  9. Jason Sawtelle November 2nd at 2:18 pm

    The javascript works great, good hack. But why not try a CSS property tweak?

    Instead of using “center” for the positioning of the bg img, use percentages. e.g., “top center no-repeat” becomes “50% 0 no-repeat”

    CSS reference for BG pos as follows
    keyword: y-axis x-axis
    percent: x% y%

    Worked for me! Check out my site (in Safari). The header and bg imgs are positioned with 50%.

    This is cross-commented at zomgforeelz’s blog.

  10. This was happening to me in IE 6 and 7. With the 50% instead of center, that fixes it in Safari. With IE instead of 900 px (an even number) I made the inner content div 899px and added a little to the background (if it’s not a repeating x or y image) and it works great. An odd number on the containing div that you’re wanting to center and match up with the background image that is centering, works great.

  11. Martijn Megens November 21st at 9:31 am

    @Jason Sawtelle
    Your site still shifts, 1 pixel, even with the alternative IE_bg

    What i did was adding a transparent pixel to the right side of the bgimage, it worked for me.

  12. Tipped Bowler Tapes site launches | Carter Web Design: Photography Art Writing Design Blog March 21st at 1:48 pm

    […] side note of thanks to Rob Goodlatte for coming up with a fix to an annoying bug in Safari having to do with half-pixel background […]

  13. Dennis Bunskoek June 11th at 5:02 am

    What works for me is the following Safari-only css rule:

    @media screen and (-webkit-min-device-pixel-ratio:0) {
    body {
    background-position: 50.05% top;
    }
    }

    Hope this helps someone…

  14. Dennis’ method works beautifully! Thanks for the tips! :D

  15. It seems to be a very rare problem, and this might be the reason:
    The problem only seems to occur when the background image for the body is wider than the browser window.

    And there might be an issue with Dennis’ code: some browsers apparently don’t like it when you mix keywords and numerical values. But this should get rid of the potential issue:

    body {
    background-position: 50.05% 0;
    }

    By the way, the same problem occured in Opera 9.51/Mac, so restricting the rule to Safari might not be enough.

  16. I found that if I made the background image an odd number then this worked for safari (basically if your using 2000 pixel wide image tiled downwards

    background-position: 50% 0;

    shave a pixel of the left edge of the graphic and voila seems to work, without any need for javascript or anything

  17. Steve…. you are a GENIUS! I’ve been struggling with that issue for a few hours now. Tried making a giant div that has the background in it and it worked well until I noticed the scrollbar at the bottom of the browser. Tried your solution and it works PERFECTLY! Shaved the pixel off the right edge though which works for IE7 and Firefox 3. Didn’t try the background position of 50.05% but that looks like a good one as well.

    Thanks!!!

  18. Firefox 3 now has this problem, too.

    I know zero about javascript, but I was using the above solution, which is:

    window.onload = resizeIfOdd;
    window.onresize = forceSafariEven;

    function resizeIfOdd(){
    if(navigator.userAgent.indexOf(’Safari’) != -1)
    if(document.documentElement.clientWidth % 2 == 1)
    window.resizeBy(-1,0);
    };

    function forceSafariEven(){
    window.setTimeout(’resizeIfOdd()’,5);
    };

    Anyone know how to add Firefox 3 to this code also?

  19. Just make the background image width wider then the div and it should solve your problem (use 50% 0 background-positioning also).

 

Shout into the Forest