Fixing One Pixel in Safari
Published 2 years ago, mid-February under Design, SoftwareI’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:
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
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]
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.
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.
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.
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)â€?
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
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);
}
}
Thanks for this great hack. I’ve been searching awhile for something like this and it seems this does the trick. Thanks much.
A bug was just filed about this on the WebKit open source project:
http://bugs.webkit.org/show_bug.cgi?id=14956
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.
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.
@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.
[...] 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 [...]
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…
Dennis’ method works beautifully! Thanks for the tips! :D
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.
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
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!!!
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?
Just make the background image width wider then the div and it should solve your problem (use 50% 0 background-positioning also).
THANK YOU STEVE!
Hello,
I’m struggling with the same issue here. It looks like a couple of people got “Steve’s” fix to work (from his comment above). His explanation is not exactly thorough, however. Could someone break down exactly what they did to fix the issue.
While Steve’s solution seems much quicker and easier to implement, I don’t see how you can solve it without having to resize the window – Being that even if you fix the bug with css, as soon as I resize my window by 1px, the issue re-appears. Thanks for any information you might have…
You can probably avoid this problem all together by choosing your slices wisely, but if i’m ever left with no choice, thanks for the hack.
thanks a lot, Steve for your input!
Had the same problem; cropped my background-image to 1999px width (instead of 2000px), positioned it 50% 0 and works fine in Safari, IE6, IE7, Firefox2.
Great solution!
Steve’s approach wasn’t working for me at first, but I think I’ve figured it out.
Steve’s CSS works only when the image width > background width (very wide background images).
I was trying to center a background image of 1022px (just a little wider than my main div of 980px). To get that to work, I used:
body {
background-position: 50% 0;
}
@media screen and (-webkit-min-device-pixel-ratio:0) {
body {
background-position: 49.99% 0;
}
}
div#everything {
width: 980px;
margin: 0 auto;
}
Note that Steve’s 50.05% approach didn’t work for this case – I had to go slightly less than 50%. However, the problem with my approach is when the viewport is resized to be smaller than my background image (1022px), but still wider than my main div (980px) then I have the 1px problem again.
So my solution is to always make sure your background image is wider than any device you want to support (I made mine 4000px), and then to use an approach similar to Steve’s:
@media screen and (-webkit-min-device-pixel-ratio:0) {
body {
background-position: 50.001% 0;
}
}
Since this post is really about a JavaScript solution, I went ahead and wrote a blog post of my above findings. I linked to this blog as well.
http://philfreo.com/blog/fixing-safaris-1px-background-image-centering-problem/