Author: Janis Chinn (janis.chinn@gmail.com) Maintained by: David J. Birnbaum (djbpitt@gmail.com) Last modified: 2021-12-27T22:03:47+0000
Web design has come a long way since the early days of the internet. Although there are,
of course, many ways to pull off a desired layout, these days the go-to technique for
arranging panels in a window involves using a combination of <div>
elements (to contain the content areas) and CSS styling (to position them). In the past,
many used HTML <frameset>
elements (now deprecated and scheduled to
be eliminated completely from
HTML5) and HTML <table>
elements to construct their webpages.
It’s obvious why frames are no longer a good choice—their deprecated status is reason
enough. Tables, on the other hand, might still seem tempting, but it’s frowned upon to
use HTML tables to display anything but tabular data, that is, data where the rows and
columns both represent values from a range and the cells represents intersections of
those values. As HTML moves away from the use of elements for presentational purposes in
favor of their use to describe what the data is, instead of what it looks like
in a particular view, the usage of any given element is being constrained to those
descriptive roles. Your site layout isn’t really a table because the pseudo-rows and
pseudo-columns don’t have meaning, let alone systematic meaning, and there are now
easier, more representative ways of laying out your site in rectangles.
In your design endeavors, you should always work to make your pages as accessible as possible. This means that ideally they should:
This page viewed best in browser X, and no standards-conformant page should ever need to say that.
Typically we divide the layout of our webpages into rectangles, each of which will
correspond to an HTML element. These rectangles may come in a variety of sizes and can
be arranged however you like (and shapes other than rectangles are also possible). The
rectangular division of pages is what makes tables such a tempting mechanism for laying
out a webpage, but manipulating HTML tables into doing this is, frankly, awful and often
requires some mind-boggling nesting of tables. Luckily, we can avoid this tedious fate
by using the <div>
element. By naming these <div>
elements with unique @id
attributes, we can then style and position our
rectangles in different ways in order to give our page structure. @id
attribute values have to be unique in a document; for recurring structural elements
within a page (that is, multiple elements that you want to style and position
identically), you can use the @class
attribute.
A typical webpage that wants to position structural components otherwise than one after
another in a single vertical, full-width column will usually have
<div>
elements for the header, navigation bar, table of contents,
main body of the site, and natural divisions within the main body. You might further use
<div>
s for sidebars, such as to show data side by side with an
explanation or description of what’s displayed. The various tales documented on The Annotated Afanas′ev library project
showcase how you can use <div>
s to create a multi-paned website.
Whatever layout you might come up with can almost certainly be created through the
application of appropriate CSS styling to a page constructed of
<div>
s, without recourse to frame or table markup. We refer to these
rectangles as panes or panels, rather than frames, to avoid any
confusion with obsolete <frame>
elements.
As an example of using CSS to control layout we will refer here to the aforementioned Annotated Afanas′ev library project, a collection of Russian fairy-tale texts with linguistic and cultural annotations used for language-teaching. The most common use of these texts by students involves reading the main text in the left frame and clicking on unfamiliar words or concepts, which causes the linguistic or cultural annotations in the right frames to scroll to information about the text that was clicked. Because it might be useful to review new words or concepts in a list (for example, before a test), the associations are backlinked, so that clicking on a headword in the right-side linguistic or cultural panels will cause the main panel, on the left, to scroll to the appropriate location. This means that a user can scroll down either of the right panes and click on an item to view it in context within the main text pane on the left.
The site is built out of separate pages for the individual tales, where each such page
consists of four <div>
elements: the header, the text (left pane),
linguistic commentary (upper right pane), and cultural commentary (lower right pane).
These panes scroll independently and, as we mention above, are interlinked, so that
clicking on a link in any individual pane scrolls a different pane to the corresponding
section. This last feature we will address later, but for now let’s focus on the overall
structure. The header stretches the full width of the page, the text pane below it
includes a vertical scrollbar and appears to the left of the linguistic and cultural
commentary, which are stacked to the right of the page. The HTML looks roughly like
this:
<html> <head>...</head> <body> <div id="header"> <h1>...</h1> <hr /> </div> <div id="text"> <p>...</p> </div> <div id="ling"> <h1>Linguistic Commentary</h1> <p>...</p> </div> <div id="culture"> <h1>Cultural Commentary</h1> <p>...</p> </div> </body> </html>
Once you’ve structured your HTML so that structurally related pieces are grouped together
within various <div>
s, you must write the CSS that will arrange them
within the page according to your design aesthetic. To style the Afanas′ev project, let’s first consider the
header.
#header { height:65px; /* there’s 20px inherited margin as well, this comes from the overall styling on all obdurodon pages*/ margin:0; padding:0; }
This piece is fairly straightforward. Since the header is meant to stretch across the
page anyway, the formatting here simply handles setting the height, margins, and
padding. The height
property value here is specified in pixels, which means
it will not scale with the page if the window is resized. You must consider whether this
is the behavior you want, or whether you would prefer to use percentages or ems, so that as
the page stretches and shrinks, the header will follow suit. The margin
and
padding
property values here will aid in making the rest of the page
rest flush against the header, keeping the page more tightly laid out.
Next is the <div>
for the text of the tale, which spans most of the
page horizontally and the rest of the page vertically.
#text { position:absolute; top:0; bottom:0; overflow:auto; width:73%; margin:90px 1em 10px 0; /* top, right, bottom, left */ padding:0 0 0 0; border-right-width:1px; border-right-style:solid; }
By setting the value of the position
property to absolute
, we gain
access to the properties top
and bottom
(and also
left
and right
, which aren’t used here). In this case,
we’ve set both to 0
, which means this <div>
will sit flush
against the top and bottom of the window. The overflow
property value is
set to auto
, which means that scrollbars will appear when necessary, and
otherwise will not be displayed. The width
property value limits the pane
to the specified portion of the screen, leaving room for our remaining two panes. The
margin
property value lowers the text pane to sit five pixels under the
header, keeps the text from sitting flush against the right edge of the window by adding
a one-em gap, and fixes it exactly ten pixels from the bottom of the window and flush
against the left edge. The last two lines of CSS above create a border along the right
side to separate the text pane visually from the linguistic and cultural panes.
The CSS below places the pane for linguistic commentary in the upper right and features a scrollbar that appears when needed, that is, when there is too much text to be displayed all at once in the available space. The length of the tales and extent of the markup are such that this is usually the case, that is, there are usually too many entries to fit in the pane without scrolling.
#ling { position:absolute; top:0; left:74%; bottom:0; height:60%; overflow:auto; /* top, right, bottom, left; add 10px to header height create space above scrollable text */ margin:90px 0 0 1em; padding:0; border-bottom-width:1px; border-bottom-style:solid; }
We already configured the text pane to extend from the left edge of the screen 73% of the
way across, and we now use the left
property to position the linguistic
pane just past the edge of the text pane, so that it starts 74% from the left edge of
the window. It is allotted 60% of the height of the window and granted a scrollbar where
necessary, with margins used to position the pane just under the header, as we did for
our text pane, and with no bottom margin, so that it sits flush above the cultural
pane.
The cultural commentary pane is more of the same, but we will discuss it briefly for the sake of thoroughness.
#culture { position:absolute; top:60%; left:74%; bottom:0; overflow:auto; /* top, right, bottom, left; add to header height and to bottom to create space above scrollable text */ margin:95px 0 10px 1em; padding:0; }
Again, this pane is positioned just past the edge of the text pane. It’s positioned
directly under the linguistic pane, with the value of the top
property set
to the height of #ling
and a top margin of 95px (90px to match the other panes,
and an additional 5 to keep it from running right into the bottom of the linguistic
pane).
Before moving on, it’s important to discuss a few other properties that frequently play a
role in positioning <div>
elements. This will be a brief discussion;
for more details you should examine the relevant pages on w3schools:
z-index
value will allow you to layer overlapping pieces
to your specification: the lower the z-index, the further back (away from the
viewer, that is, under other, overlapping content) an element will be
displayed.position:fixed
keeps an element from scrolling with the rest of the
page, while position:relative
causes the element’s position to be
calculated relative to the location of the elements around it.resize
allows you to make an element resizeable. Accepted values
include horziontal,
vertical, and
both. Since
resize
controls the size of only one element, and not those
surrounding it, it cannot be used to dynamically change the overall structure of
your page. If you wish to do this, you should research using JavaScript to make
resizeable panels. At this time resize
is fully supported only in
Chrome, Firefox, and Safari (no support in IE, partial support in Opera, no support
in mobile browsers).These are just a handful of the properties you may end up using in laying out your page. We encourage you to explore other properties on w3schools and to examine the source code of sites that include features you admire when building your own project sites.
Users of the Afanas′ev site, while reading a fairy tale, may come across a word they
don’t know and want to read the linguistic notes on it. That is, they may be operating
inside the left pane when they want something to happen in the upper right pane.
Conversely, they may be using the site to review vocabulary before a test, reading down
the linguistic pane, and they want to see a word in context. Rather than requiring that
users scroll tediously to find this information out on their own, or use the generic
browser find functionality (which can’t tell which instance of a word you want
to find if it occurs more than once), we can link between the appearances of these words
across panes. By giving each appearance of this word the same @id
in each
<div>
(with a pane-specific prefix, since @id
values
have to be unique in the document), we can link simply to that @id
, and the
page will scroll each pane to display the proper context. For instance, in Волк-дурень
(The foolish wolf
), the word сторожила
in the
main text pane has an @id
attribute value of text_word3
and an
@href
attribute value of #ling_word3
, while the same word in the
linguistic pane has an @id
attribute value of ling_word3
and
@href
attribute value of #text_word3
. This means that clicking
on the word in the main pane will scroll the linguistic pane to the corresponding word,
and vice versa. Remember that the @id
is the target of the link, and an
@href
that points to it has the same value, but with a preceding hash
mark (#
). Thus, the word in the main pane is tagged as follows:
<a href="#ling_word3" id="text_word3">сторожила</a>
Needless to say, we don’t create those @id
and @href
values
manually! The XML source for these pages contains all of the information in a single
file, with the linguistic and cultural markup in line with the text. We use XSLT to
create the separate output files, and we use AVTs to construct the attribute values that
we use for linking dynamically.
float
The CSS float
property has several applications, examples of which can be
seen on the course page and in various course projects, e.g.:
One use, illustrated on the main course page, floats an image to the right of the page. Even if you resize the window, you can see that the text flows around the image. This is pretty easy to achieve:
<p><img src="float.jpg" class="float" alt="The image to float right" /> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc nec tincidunt nisi, hendrerit ultrices augue. Fusce mollis ultricies urna vitae porttitor. Nunc eget lectus eu purus egestas commodo.</p>
img.float{ float:right; margin-left:1em; margin-right:1em; }
All that’s necessary is to set the desired element’s float
property to
right
(or left
). The margin
settings hold the image in a
bit from the edges of the page and keep the text from running into the image. This is an
easy and common usage of float
. This example is rendered below:
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc nec tincidunt nisi, hendrerit ultrices augue. Fusce mollis ultricies urna vitae porttitor. Nunc eget lectus eu purus egestas commodo. Nulla eleifend sed tellus id volutpat. Proin mattis velit mauris, nec gravida massa rhoncus sodales. Ut mattis blandit leo, at consectetur purus pulvinar nec. Mauris quis interdum nibh. Duis non metus eget libero viverra tempor. Pellentesque laoreet velit dolor, nec feugiat magna placerat id. Proin tincidunt tincidunt ultricies. Morbi porttitor sem at mauris consectetur aliquet. Mauris ac commodo nisi. Mauris cursus diam tellus, vel iaculis mi sollicitudin eget. Curabitur vulputate quis dui ut tristique. Duis elementum lobortis tortor eu imperdiet.
Perhaps instead you want to display multiple images (or other elements) lined up in a row and centered on the page. This turns out to be rather more complicated. For instance to create the following row of images:
These images will need to be contained in a <div>
with an
@id
or @class
, and you’ll also need to give each image an
@id
or @class
so that you can style them individually.
Your HTML should look something like something this:
<div class="images"> <img class="one" src="english.jpg" alt="left-most image"/> <img class="two" src="french.jpg" alt="center image(s)"/> <img class="three" src="slovak.jpg" alt="right-most image"/> </div>
The CSS, then, will look like this:
.images{ margin-left:auto; margin-right:auto; display:block; width:427px; } .images img{ height:200px; margin-left:5px; margin-right:5px; } .one, .two{ float:left; } .three{ float:right; }
Centering is handled by our first three lines of code. To center anything other than
text, set both the left and right margins to auto
and display
to
block
. You must also set the width
property; in this case,
width
is set to the sum of the widths of each image (plus margins to
keep the images from running into each other). All of this styling goes on the container
<div>
. Next, we handle the styling that will apply to all our
images. The height
property is set here because the images in this example
are not all the same size. Depending on your project, this may or may not suit your
purposes. The left and right margins are set to 5px. We’ve used pixels here so that the
width of our <div>
will always be correct, even when the window is
resized. Finally, we handle getting the images to display all on the same line. In order
to keep the text on the page from flowing around the images here, we must set the
float
property for some of the images to left
and some to the
right
. In this case, the first two images are floated left, while the final
image is floated right. Depending on the number of images you are working with, you may
need to float more things to the left.
There are a few different methods for floating panels next to each other. On the Caesar project site, the project
text is displayed next to a Google Earth map. This is accomplished by floating the
<div>
containing the map to the left and the
<div>
containing the text to the right. In order to keep the
panels next to each other, each has a width
value, and the text panel has
an overflow-y
property to give it a separate scrollbar. Here are the HTML
and CSS needed to achieve this:
<div class="map">...</div> <div class="text">...</div>
.map{ width:47%; float:left; padding-left:2%; .text{ overflow-y:scroll; width:45%; padding-left:1%; padding-right:5%; float:right; }
Depending on the structure of the rest of your page, you may or may not need to float the
right-hand panel to the right. Often it works just as well simply to float both panels
left, as seen on the RuNet site. The difference
is often a matter of how closely together the two panels appear, which you can also
manipulate by changing the left and right margins. To see this in action, try going to
the O. Henry site, bringing up
the Inspect Elements panel in your web browser, and changing the value of the
float
property on <div id="korean">
to
left
.
width
and
min-width
properties on your floated <div>
elements.display
property set
to block.
calc()
function. (Note that the Sitepoint article on
calc()
is fairly old; calc()
is now supported in all
major browsers. You can always investigate what browsers support a given feature on
w3schools or Can I Use.)<iframe>
Suppose you have a two-paned website, and you want to load new content into one pane
without having to refresh the entire page. This is the sort of scenario where you should
use <iframe>
. This kind of approach saves users from having to load
more data than they need by keeping all of the current page except the
<iframe>
, and loading only the new <iframe>
content from the server. Furthermore, it lets the browser cache previously loaded data,
so that if the user flips back and forth between content, the data doesn’t have to be
fetched anew each time from the server.
An <iframe>
can take an @src
attribute, which specifes
the URL of the content to load. If this is left out, the <iframe>
will just appear as an empty panel. To load content into that frame, give it an
@id
attribute and use this @id
attribute value as the
value for the @target
attribute on a link within the page. For example:
<body> <div id="links"> <p>Click a language to read various translations of chapter one.</p> <ul style="margin-left:40%;"> <li><a href="en.html" target="read">English</a></li> <li><a href="fr.html" target="read">French</a></li> <li><a href="de.html" target="read">German</a></li> <li><a href="sk.html" target="read">Slovak</a></li> </ul> </div> <iframe id="read" src="en.html"> </iframe> </body>
In this example, the <iframe>
will load the English text by default,
and then users can select other languages to view by clicking on the links. The
<iframe>
has an @id
value of read
, which must
be referred to (using the @target
attribute) in any link that should open
content within the <iframe>
.
<iframe>
elements need to be given height
and
width
properties in CSS, or they will not display as you would expect.
Aside from this, they behave like any other element and can be styled accordingly. CSS
for the example above might look like:
#links{ float:left; width:47%; } #read{ width:45%; margin:1em; position:absolute; top:5%; height:80%; left:50%; }
The <div id="links">
is floated left and allotted roughly half the
screen for its width
. The <div id="read">
element is
also allotted around half the screen for width
, but additionally has its
height
set to 80% of the screen. It’s absolutely positioned at just
past half the screen, and the top
and margin
properties are
set to bring the <iframe>
in from the edge of the screen somewhat.
You can see a small sample of this code in action below.
The advantage of using an <iframe>
instead of a positioned or floating
<div>
when you’re going to swap in new content is that the
<iframe>
element takes an @id
attribute and is
attentive to a @target
attribute on a link elsewhere, but that isn’t true
of a <div>
. If you aren’t going to load new content into the
<iframe>
dynamically, there isn’t much difference in
functionality. We think of the <iframe>
as a sort of hole in the
canvas; see, for example, the various inner pages on the Twilight princess project. Because the
developers wanted the content pane to be sized differently on different pages, they load
complete new pages each time, instead of just changing the content of the
<iframe>
. Nonetheless, the <iframe>
feels
natural because the text sits in a window inside the larger page (the name of the
<iframe>
element stands for inline frame
.
In addition to the <iframe>
element, some of you may have heard of the
old HTML elements <frameset>
and <frame>
for
designing paneled or windowed interfaces. Remember that those elements have been removed
from HTML5 and should not be used for new development.
It’s important to note that XHTML 1.0 Strict removes @target
. If you want to
use @target
, you’ll need to use XHTML 1.0 Transitional or HTML5 in order to
have valid HTML. You can read more about this on the W3C site.
Though they work somewhat similarly insofar as they insert content from one file into a
page that is otherwise the content of a different file, you should not use
<iframe>
elements where you could instead use server side includes (SSI). SSI is more
transparent to the end user, and makes site navigation more straightforward. Abuse of
<iframe>
elements can be confusing to the user, as it makes it
unclear what will happen when using the back button.
Although <iframe>
elements can be used effectively, keep in mind that
they also represent a potential security risk in situations where you have your
<iframe>
load content from an external site, one that you don’t
control yourself. Pulling content from another site means trusting the code on this
other site not to infect your users, to link to similarly trustworthy sites, and to stay
up indefinitely. Instability is a more likely risk than malice; if you link to content
on an external site and it is taken down someday, that will leave your site with broken
pages without warning. We’ve become used to the risk of broken links, where the user
clicks and gets an error message, but in that case at least your original page looks
intact. If your original page contains an <iframe>
that pulls content
from a site that goes down, the damage winds up painted on your page, and not just on
the other end of a clickable link.