Frames or Iframes with CSS
A frequent request from people new to CSS is trying to get a behaviour similar to frames while only using CSS. Many have read that frames have a lot of problems but do not know much more than that. This article will try to explain why frames are bad and what to do about it.
- Why are traditional frames such a bad thing?
- Getting a scrollbar on any element using CSS
- Mimicking traditional frames
Update: I've updated this technique using position: fixed in a new article, have a look at those templates instead! (Although this article is still good for background information).
Why are traditional frames such a bad thing?#
Let me start by just listing a few bad things about frames.
- Prevents users from bookmarking the exact page they're on. Instead the bookmark will lead to the frameset. This is clearly not a good choice for larger sites where finding a certain page might be a problem, but even users on smaller sites might find it annoying. I do.
- Poor support by some parsers leads to problems. Many screen readers, mobile phones, and search engines have problems parsing frames. This means some of your users might miss out on your content.
- Content order problems. Just like with tables, frames give screen readers problems when deciding which order to read the frames. If it reads a 2×2 frameset will not know whether to read the content like two rows or like two columns. The order might be important for understanding the content and frames has nothing built-in to help with that.
- Search engines might split the ranking up on the different pages. I just discovered this by looking at one of my old pages (no, I won't show it, it's terrible :). There, the PageRank has been split up on the framed pages and the frameset has no rank whatsoever.
- Invalid code. If you're using separate frames for navigation and content your links will need the
targetattribute to make links from the navigation frame open in the content frame. That attribute is not supported when using a strict doctype so you will have to use transitional. The doctype won't matter if you want to get rid of borders cross-browser though, to do that you need invalid markup. - Printing is a problem. How should the different frames be handled while printing? There's no good solution to this.
People that are good with javascript sometimes say they like frames because they make it possible to save information about the user even though the user clicks links. A variable can be saved in a frame and be accessible to the other frames even after they are reloaded. Another pro-frames reason frequently heard is that it saves overhead since not the entire page gets reloaded.
Personally I think that the first is a bad reason to use frames. Using server side scripting to save session info is much more robust since that will mean the applications still work if the user reloads the page. The other reason of only reloading a part of the page is nowadays done by using javascript together with XMLHttp (some like to call it AJAX). To me, the bad parts of frames greatly weigh over, even for applications.
Getting a scrollbar on any element using CSS#
Aside from all of the above frames are not structure. Think about it. When working with frames you define that you want a couple of rows and columns, which should scroll and which shouldn't, and which should have borders. These are things that many of us would never dream of doing with tables but some still use frames like that.
Instead we should define that kind of design in our CSS files. How? By using a few lines of code you can easily limit any element to a certain width/height and let it scroll if the content overflows that size.
<p>Here's some sample content that I would like to scroll</p>
p {
width: 200px;
height: 80px;
overflow: auto;
}
It's easy, you're saying: "limit the paragraph to this size and give the box a scrollbar if the content gets too big" (Other valid values for overflow are "hidden" and "scroll". The former just cuts everything that sticks out, that latter adds the scrollbars even if they're not needed).
The above method works when you want something that's similar to iframes but it doesn't work right away for traditional frames. Those usually span the whole width or height of the page and leave the rest of the page scrolling. But what stops us from taking any element and stretching and placing it like a frame? Nothing. Let's do it.
Mimicking traditional frames#
The idea here is to use divisions to group our content. Then position those divisions as if they where frames and limit their site and set overflow: auto like we did above. Frames use the syntax cols="200,*" to say that the first column should be 200px wide and the second one "cover what's left". The second part is a bit harder to do with CSS.
There's no way to say 100%-200px but there's a trick we can use: floats push the content away. So if we set a width on the left block to 200px and float it left, the other block will be as wide as possible (default for block level elements) but it's content will be pushed 200px to the right. Here's an example of two columns with a fixed left in action. By just changing float: left; on navigation to float: right; we get the other example: Two columns with the right one fixed.
(Technical note: we don't need to add any margin-left since overflow: auto; clears too).
The above works well for columns but what if we need rows instead? When using frames we specify rows="100,*" and as I said this is not possible directly with CSS. We can't use the above trick either since we're dealing with height here. Setting height: 100% will just make it as high as the window, leaving no space for the other row. The solution here is to cheat a bit. If we make the first column 100px high and the lower one 100% high but then remove the first one from the flow we will be pretty close. The first row will be on top of the other one but that can easily be solved by adding a padding-top the same height as the first row.
We're almost there now, one last thing: the latter row scrolls on top of the first one because of the order in the source (latter elements stack on top of earlier ones). So what we do is force the first one on top with position: relative. This solves the problem except that it also covers the scrollbar now. An easy (and ugly fix) is to add a margin-left of 16px to the top column making the scrollbar visible again. Browsers seem to use the same scrollbar width so that's it, an example of frame rows using CSS.
I've tested this in IE 6, FF 1.5 and Opera 8.5 on WinXP and it seems to work fine. Broken in any other browsers? Let me know through the comments.
When looking at this I see that this is also a good way to mimick blocks that are position: fixed, something that doesn't work in IE. This method might be good in a situation where you need that one.
Comments
By: Ezell (#1)
Ezell
By: Michael Schreiber (#2)
Whith all that nice specifications that are meant to replace the evil we used before we seam again to get stuck in what should work but doesn't.
Anyway thanks for this and your other articles. They are very helpful.
By: Emil Stenström (#3)
In your case I think your should just let that be. I have 1% users with 800x600 resultion on this site, does your bug affect many enough so that it warrants a hack?
[Update: Setting a width on that columns stops it from happening, is that a solution in your case? (might make linelengths a little nicer too...]
By: Jonas Ahlberg (#4)
Har sett en del ha en sida med div-element vars placering styrs med css. I varje sådan lägger de sedan ett html-dokument.
Det fungerar men verkar inte riktigt rätt. Vad är det som gäller?
Hälsningar // Jonas
By: Emil Stenström (#5)
Men låt mig ta det igen lite kort: du använder först CSS för att få frames-liknande beteende. Sedan använder du något server-side språk (PHP, ASP eller liknande) för att lägga in t.ex. navigationen på alla sidor.
By: Jonathan (#6)
However, this page is not cross compatible. The scrolling paragraph does not work in IE6.
By: Emil Stenström (#7)
By: Michael Schreiber (#8)
your post 04 in reply to 03
Thanks for your suggestion. However it doesn't really solve the topic. As on larger window size the scrollbar is not on the right any more and the content still vanishes when the window width is smaller than width of navigation + width of content.
The image size was just a sample.
If you assume an image of 1280x1024 the percentage of visitors where this fits on the screen would be lower.
However I have no concrete problem. I just discovered this effect when I was playing around with your (great!) samples.
My comments may have been capable of being misunderstood as I think that CSS is a step in the right direction and using it has many advantages compared to coding the layout directly in HTML.
The problem that persists is that (browser) developers interpret and implement the specifications differently, forcing us to use workarounds or browser specific techniques.
Your site is a great help to achieve this. I was just moaning about the imperfect world...
By: shobha (#9)
I liked it very much and i used it in my personal website.
Thank you,
By: Ariel (#10)
By: Hamid tarkhorani (#11)
By: Pascal (#12)
I suppose you meant position:fixed
Regards!
Pascal
By: Emil Stenström (#13)
By: William (#14)
Excuse my ignorance here, I stumbled upon your site looking for some CSS help. I'm trying to use a 1024X768 jpeg as a background for my site. Simple but I would like to fix the size so the browser reflects the size of the background. I'm trying to prevent tiling when it's too small and underscan when the image is too big like it is now at 1024X768. I'm looking at it on a 14 inch iBook. Hope this makes sense.
Thanks for any help.
By: Emil Stenström (#15)
I'm not sure if that will work in your case, what will you do if the content overflows that area? how will you handle people will a smaller resolution? Designing for the web is quite different from print design.
By: Alina (#16)
Example: the top row would be stationary and have the links and the bottom row would display the pages of the links.
is this possible to do with only CSS?
please help.
By: Emil Stenström (#17)
By: Carlos el hormigo (#18)
I have spent some hours finding much more difficult and useless solutions to the "position:fixed;" problem in IE. Yours is simple, elegant... and works!! I have to test it in some more browsers but it looks great.
And easy to understand for an spaniard like me ;-)
By: Emil Stenström (#19)
By: Justin Miller (#20)
By: Emil Stenström (#21)
By: tom (#22)
By: Emil Stenström (#23)
By: rikard lassenius (#24)
By: Emil Stenström (#25)
By: CRG (#26)
By: |► Piccolo consiglio su sito web (#27)
By: Bill Roberson (#28)
I am attempting to get away from using frames. I have only one page using frames with selection buttons on the left selecting what is placed in the right frame.
I read your post above about using PHP, and will have to study that some to see if I can make it work.
My PHP knowledge is limited, but in your post, I did not see a closing ?> and wonder if it is required? Currently, all I am doing with PHP is making sure any changes to my menu is applied to every page, and every page uses the same footer.
Again thanks for the PHP code that I am now off to study.
Bill
By: Sllopez21 (#29)