Category: Web



Internet Explorer User Agent Style Sheets

The UA Style Sheet is a simple set of css styles that each web browser uses before any other css styles are applied.

This chart lists and compares the different default style sheets used to render HTML in the four major versions of Internet Explorer; IE6, IE7, IE8, and IE9 Platform Preview.

You can download each of these UA stylesheets by using the links at the top of this chart.

IE6 IE7 IE8 IE9
a
color: #00F;
text-decoration: underline;
color: #06C;
text-decoration: underline;
a:visited
color: #800080;
address
display: block;
font-style: italic;
b
font-weight: bold;
bdo
direction: rtl;
unicode-bidi: bidi-override;
blockquote
display: block;
margin: 14pt 30pt;
display: block;
margin: 1em 40px;
body
display: block;
margin: 15px 10px;
zoom: 1;
display: block;
margin: 8px;
zoom: 1;
button
background-color: #D4D0C8;
border-style: outset;
border-width: 2px;
font-family: sans-serif;
font-size: 10pt;
overflow: hidden;
text-align: center;
zoom: 1;
background-color: #F0F0F0;
border-width: 2px;
font-family: sans-serif;
font-size: 10pt;
overflow: hidden;
text-align: center;
zoom: 1;
background-color: #F0F0F0;
border-width: 3px;
box-sizing: border-box;
font-family: sans-serif;
font-size: 10pt;
overflow: hidden;
padding: 1px 8px;
text-align: center;
zoom: 1;
caption
display: block;
padding: 1px 3px 5px;
text-align: center;
zoom: 1;
display: block;
padding: 1px 300zzz 500zzz;
text-align: center;
zoom: 1;
display: table-caption;
text-align: center;
zoom: 1;
cite
font-style: italic;
code
font-family: monospace;
font-size: 10pt;
col
border-color: #D4D0C8;
display: block;
border-color: #F0F0F0;
display: block;
box-sizing: border-box;
display: table-column;
colgroup
border-color: #D4D0C8;
display: block;
border-color: #F0F0F0;
display: block;
box-sizing: border-box;
display: table-column-group;
dd
display: block;
margin: 0 0 0 30pt;
display: block;
margin: 0 0 0 40px;
[dir=rtl] dd
margin: 0 30pt 0 0;
margin: 0 40px 0 0;
del
text-decoration: line-through;
dfn
font-style: italic;
div
display: block;
dl
display: block;
margin: 14pt 0;
display: block;
margin: 1em 0;
dt
display: block;
em
font-style: italic;
embed
zoom: 1;
fieldset
border-style: groove;
border-width: 2px;
display: block;
zoom: 1;
border-style: groove;
border-width: 2px;
display: block;
padding: 0 2px 3px;
zoom: 1;
border-style: groove;
border-width: 2px;
display: block;
margin: 0 2px;
padding: 0 2px 3px;
zoom: 1;
form
display: block;
margin: 14pt 0;
display: block;
h1
display: block;
font-size: 24pt;
font-weight: bold;
margin: 14pt 0;
display: block;
font-size: 2em;
font-weight: bold;
margin: 0.67em 0;
page-break-after: avoid;
h2
display: block;
font-size: 18pt;
font-weight: bold;
margin: 14pt 0;
display: block;
font-size: 1.5em;
font-weight: bold;
margin: 0.83em 0;
page-break-after: avoid;
h3
display: block;
font-size: 13.55pt;
font-weight: bold;
margin: 14pt 0;
display: block;
font-size: 1.17em;
font-weight: bold;
margin: 1em 0;
page-break-after: avoid;
h4
display: block;
font-weight: bold;
margin: 14pt 0;
display: block;
font-size: 1em;
font-weight: bold;
margin: 1.33em 0;
page-break-after: avoid;
h5
display: block;
font-size: 10pt;
font-weight: bold;
margin: 14pt 0;
display: block;
font-size: 0.83em;
font-weight: bold;
margin: 1.67em 0;
page-break-after: avoid;
h6
display: block;
font-size: 7.55pt;
font-weight: bold;
margin: 14pt 0;
display: block;
font-size: 0.67em;
font-weight: bold;
margin: 2.33em 0;
page-break-after: avoid;
head
display: none;
hr
text-align: center;
zoom: 1;
outline: 0 none;
text-align: center;
zoom: 1;
border-style: inset;
border-width: 1px;
display: block;
margin-bottom: 0.5em;
margin-top: 0.5em;
overflow: hidden;
text-align: center;
zoom: 1;
html
overflow-x: auto;
overflow-y: scroll;
zoom: 1;
display: block;
zoom: 1;
i
font-style: italic;
iframe
background-color: #FFF;
height: 150px;
width: 300px;
zoom: 1;
img
zoom: 1;
input
background-color: #FFF;
border-style: inset;
border-width: 2px;
font-family: sans-serif;
font-size: 10pt;
overflow: hidden;
padding: 1px;
zoom: 1;
background-color: #FFF;
border-width: 2px;
font-family: sans-serif;
font-size: 10pt;
overflow: hidden;
padding: 1px;
zoom: 1;
ins
text-decoration: underline;
kbd
font-family: monospace;
font-size: 10pt;
legend
zoom: 1;
padding: 0 2px;
zoom: 1;
li
display: block;
display: list-item;
menu
display: block;
list-style-image: none;
list-style-position: outside;
list-style-type: disc;
margin: 14pt 0 14pt 30pt;
display: block;
list-style-image: none;
list-style-position: outside;
list-style-type: disc;
margin: 1em 0;
padding: 0 0 0 30pt;
[dir=rtl] menu
margin: 14pt 30pt 14pt 0;
padding: 0 30pt 0 0;
object
zoom: 1;
ol
display: block;
list-style-image: none;
list-style-position: outside;
list-style-type: decimal;
margin: 14pt 0 14pt 30pt;
display: block;
list-style-image: none;
list-style-position: outside;
list-style-type: decimal;
margin: 1em 0;
padding: 0 0 0 30pt;
[dir=rtl] ol
margin: 14pt 30pt 14pt 0;
padding: 0 30pt 0 0;
optgroup
font-family: sans-serif;
font-size: 10pt;
font-style: italic;
font-weight: bold;
option
font-family: sans-serif;
font-size: 10pt;
font-style: italic;
font-weight: bold;
p
display: block;
display: block;
margin: 1em 0;
pre
display: block;
font-family: monospace;
font-size: 10pt;
white-space: pre;
display: block;
font-family: monospace;
font-size: 10pt;
margin: 1em 0;
white-space: pre;
rp
font-size: 6pt;
rt
font-size: 6pt;
display: ruby-text;
font-size: 6pt;
ruby
display: ruby;
samp
font-family: monospace;
font-size: 10pt;
script
display: none;
zoom: 1;
select
border-color: #FFF;
border-style: inset;
border-width: 2px;
font-family: sans-serif;
font-size: 10pt;
zoom: 1;
background-color: #FFF;
border-color: #FFF;
border-width: 2px;
font-family: sans-serif;
font-size: 10pt;
overflow: hidden;
zoom: 1;
background-color: #FFF;
border-width: 1px;
box-sizing: border-box;
font-family: sans-serif;
font-size: 10pt;
overflow: hidden;
zoom: 1;
small
font-size: 10pt;
font-size: 0.83em;
strong
font-weight: bold;
style
display: none;
zoom: 1;
sub
font-size: 10pt;
font-size: 0.83em;
sup
font-size: 10pt;
font-size: 0.83em;
table
border-color: #D4D0C8;
display: block;
zoom: 1;
border-color: #F0F0F0;
display: block;
zoom: 1;
border-color: #F0F0F0;
border-spacing: 2px 2px;
box-sizing: border-box;
display: table;
zoom: 1;
tbody
border-color: #D4D0C8;
display: block;
border-color: #F0F0F0;
display: block;
box-sizing: border-box;
display: table-row-group;
vertical-align: middle;
td
border-color: #D4D0C8;
display: block;
padding: 1px;
zoom: 1;
border-color: #F0F0F0;
display: block;
padding: 1px;
zoom: 1;
display: table-cell;
padding: 1px;
vertical-align: middle;
zoom: 1;
textarea
background-color: #FFF;
border-style: inset;
border-width: 2px;
font-family: monospace;
font-size: 10pt;
overflow-x: hidden;
overflow-y: scroll;
padding: 1px;
white-space: pre;
zoom: 1;
background-color: #FFF;
border-width: 2px;
font-family: monospace;
font-size: 10pt;
overflow-x: hidden;
overflow-y: scroll;
padding: 2px;
white-space: pre;
zoom: 1;
background-color: #FFF;
border-width: 1px;
font-family: monospace;
font-size: 10pt;
overflow-x: hidden;
overflow-y: scroll;
padding: 2px;
white-space: pre-wrap;
zoom: 1;
tfoot
border-color: #D4D0C8;
display: block;
border-color: #F0F0F0;
display: block;
box-sizing: border-box;
display: table-footer-group;
vertical-align: middle;
th
border-color: #D4D0C8;
display: block;
font-weight: bold;
padding: 1px;
text-align: center;
zoom: 1;
border-color: #F0F0F0;
display: block;
font-weight: bold;
padding: 1px;
text-align: center;
zoom: 1;
display: table-cell;
font-weight: bold;
padding: 1px;
text-align: center;
vertical-align: middle;
zoom: 1;
thead
border-color: #D4D0C8;
display: block;
border-color: #F0F0F0;
display: block;
box-sizing: border-box;
display: table-header-group;
vertical-align: middle;
tr
border-color: #D4D0C8;
display: block;
zoom: 1;
border-color: #F0F0F0;
display: block;
zoom: 1;
box-sizing: border-box;
display: table-row;
vertical-align: middle;
zoom: 1;
ul
display: block;
list-style-image: none;
list-style-position: outside;
list-style-type: disc;
margin: 14pt 0 14pt 30pt;
display: block;
list-style-image: none;
list-style-position: outside;
list-style-type: disc;
margin: 1em 0;
padding: 0 0 0 30pt;
[dir=rtl] ul
margin: 14pt 30pt 14pt 0;
padding: 0 30pt 0 0;
var
font-style: italic;

Interested in other UA Style Sheets?

Firefox 3.6.3
Webkit (r57042)
Opera 10.51

This chart was last updated March 30th, 2010.

Internet Explorer and the Internet Explorer logo are either registered trademarks or trademarks of Microsoft Corporation in the United States and/or other countries.

HTML DOCTYPE Collection

from http://www.w3.org/QA/2002/04/valid-dtd-list.html

Recommended Doctype Declarations to use in your Web document.

When authoring document is HTML or XHTML, it is important to Add a Doctype declaration. The doctype declaration must be exact (both in spelling and in case) to have the desired effect, which makes it sometimes difficult. To ease the work, below is a list of recommended doctype declarations that you can use in your Web documents.

Template

Use the following markup as a template to create a new XHTML 1.0 document using a proper Doctype declaration. See the list below if you wish to use another DTD.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
	<title>An XHTML 1.0 Strict standard template</title>
	<meta http-equiv="content-type"
		content="text/html;charset=utf-8" />
</head>

<body>

     <p>… Your HTML content here …</p>

</body>
</html>

(X)HTML Doctype Declarations List

HTML 4.01
Strict
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
   "http://www.w3.org/TR/html4/strict.dtd">
Transitional
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">
Frameset
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN"
   "http://www.w3.org/TR/html4/frameset.dtd">
	
XHTML 1.0
Strict (quick reference)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
Transitional
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
Frameset
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
XHTML 1.1DTD:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
   "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
	
XHTML Basic 1.1 (quick reference):
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN"
    "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">
	
HTML 5 [NOT a standard yet]
<!DOCTYPE HTML>

MathML Doctype Declarations

MathML 2.0DTD:
<!DOCTYPE math PUBLIC "-//W3C//DTD MathML 2.0//EN"
	"http://www.w3.org/TR/MathML2/dtd/mathml2.dtd">
	
MathML 1.01DTD:
<!DOCTYPE math SYSTEM
	"http://www.w3.org/Math/DTD/mathml1/mathml.dtd">
	

Compound documents doctype declarations

XHTML + MathML + SVGDTD:
<!DOCTYPE html PUBLIC
    "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN"
    "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">
	
XHTML + MathML + SVG Profile (XHTML as the host language) – DTD:
<!DOCTYPE html PUBLIC
    "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN"
    "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">
	
XHTML + MathML + SVG Profile (Using SVG as the host) – DTD:
<!DOCTYPE svg:svg PUBLIC
    "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN"
    "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">
	

Optional doctype declarations

Beyond the specificities of (X)HTML processing, Doctype declarations in XML languages are only useful to declare named entities and to facilitate the validation of documents based on DTDs. This means that in many XML languages, doctype declarations are not necessarily useful.

The list below is provided only if you actually need to declare a doctype for these types of documents.

SVG 1.1 FullDTD:
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
	"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
	
SVG 1.0DTD:
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
	"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
	
SVG 1.1 BasicDTD:
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Basic//EN"
	"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-basic.dtd">
	
SVG 1.1 TinyDTD:
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN"
	"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
	

Historical doctype declarations

The doctype declarations below are valid, but have mostly an historical value — a doctype declaration of a more recent equivalent ought to be used in their stead.

HTML 2.0DTD:
<!DOCTYPE html PUBLIC "-//IETF//DTD HTML 2.0//EN">
HTML 3.2DTD:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
XHTML Basic 1.0DTD:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.0//EN"
    "http://www.w3.org/TR/xhtml-basic/xhtml-basic10.dtd">
	

About the Robots tag

from http://www.robotstxt.org/meta.html

About the Robots <META> tag

In a nutshell

You can use a special HTML <META> tag to tell robots not to index the content of a page, and/or not scan it for links to follow.

For example:

<html>
<head>
<title>...</title>
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
</head>

There are two important considerations when using the robots <META> tag:

  • robots can ignore your <META> tag. Especially malware robots that scan the web for security vulnerabilities, and email address harvesters used by spammers will pay no attention.
  • the NOFOLLOW directive only applies to links on this page. It’s entirely likely that a robot might find the same links on some other page without a NOFOLLOW (perhaps on some other site), and so still arrives at your undesired page.

Don’t confuse this NOFOLLOW with the rel="nofollow" link attribute.

The details

Like the /robots.txt, the robots META tag is a de-facto standard. It originated from a “birds of a feather” meeting at a 1996 distributed indexing workshop, and was described in meeting notes.

The META tag is also described in the HTML 4.01 specification, Appendix B.4.1.

The rest of this page gives an overview of how to use the robots <META> tags in your pages, with some simple recipes. To learn more see also the FAQ.

How to write a Robots Meta Tag

Where to put it

Like any <META> tag it should be placed in the HEAD section of an HTML page, as in the example above. You should put it in every page on your site, because a robot can encounter a deep link to any page on your site.

What to put into it

The “NAME” attribute must be “ROBOTS”.

Valid values for the “CONTENT” attribute are: “INDEX“, “NOINDEX“, “FOLLOW“, “NOFOLLOW“. Multiple comma-separated values are allowed, but obviously only some combinations make sense. If there is no robots <META> tag, the default is “INDEX,FOLLOW“, so there’s no need to spell that out. That leaves:

<META NAME="ROBOTS" CONTENT="NOINDEX, FOLLOW">
<META NAME="ROBOTS" CONTENT="INDEX, NOFOLLOW">
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
from http://www.theserverside.com/tt/articles/article.tss?l=RedirectAfterPost

All interactive programs provide two basic functions: obtaining user input and displaying the results. Web applications implement this behavior using two HTTP methods: POST and GET respectively. This simple protocol gets broken when application returns web page in response to POST request. Peculiarities of POST method combined with idiosyncrasies of different browsers often lead to unpleasant user experience and may produce incorrect state of server application. This article shows how to design a well-behaved web application using redirection.

Double Submit problem

Two most frequently used HTTP request methods are GET and POST. GET method retrieves resource from a web server. Resource is identified by base location and optional query parameters. Generally, parameters of GET request are used to narrow the result and do not change server state. The same GET request can be sent to the server as many times as needed.

On the contrary, parameters of POST request usually contain input data, which can change state of server application. Same data submitted twice may produce unwanted results, like double withdrawal from a bank account or storing two identical items in a shopping cart of an online store. Submission of the same data more than once in a POST request is undesirable and got its own name: Double Submit problem.

Take the standard use case with HTML FORM submitted to the server. Form data is processed and stored in the database, then server replies with a page containing results of operation.

In the above use case the same POST request can be resubmitted using three methods:

  • reloading result page using Refresh/Reload browser button (explicit page reload, implicit resubmit of request);
  • clicking Back and then Forward browser buttons (implicit page reload and implicit resubmit of request);
  • returning back to HTML FORM after submission, and clicking Submit button on the form again (explicit resubmit of request)

Considering the importance of POST data, browsers display a warning when the same POST request is about to be resent to the server. But the message is too technical and obscure for an average user. Also, some browsers do not ask for confirmation at all. Because of that many web sites show their own warnings. How often do you see messages like “Please do not click Back button or refresh this page” after you made an online payment?

The warning messages and confirmation dialogs clutter the interface and make a user feel nervous and uneasy, always afraid to make a mistake. If a web site relies on browser warnings but does not really check for double submit, the server database may become incorrect, while a user would lose confidence in internet transactions.

Is it possible to get rid of irritating warnings? Yes. HTML FORM submission method can be changed from POST to GET. Browsers are not required to ask confirmation when GET request is resubmitted, so this change makes user interface friendlier. But this “solution” breaks the semantic of GET method. It does not prevent resubmitting, it just hides the problem from a user.

The PRG pattern

The answer to double submit problem is redirection. This is a known technique, but it has not become a standard for “after-POST” results yet. As far as I know it does not have a well-known name. I suggest calling it PRG pattern for POST-REDIRECT-GET.

PRG pattern splits one request into two. Instead of returning a result page immediately in response to POST request, server responds with redirect to result page. Browser loads the result page as if it were an separate resource. After all, there are two different tasks to be done. First is to POST input data to the server. Second is to GET output to the client.

This approach provides a clean Model-View-Controller solution. All input data is stored, permanently or temporarily, in the Model on the server during the first step. The second step loads a View reflecting current Model state. When a user tries to refresh the result page, browser resends an “empty” GET request to the server. This request does not contain any input data and does not change server status. It only loads the View again. If server state was not changed by other processes/users, server responds with the same page as before refresh.

Loading resources using GET method is the cornerstone of suggested approach. Page loaded with GET request can be refreshed safely and transparently. Safely, because no input data is sent to the server. Transparently, because browser does not show warning message. The vehicle which makes transition from POST to GET possible is redirection.

With this technique user experience improves tremendously. No more scary messages with hard to decipher warnings. No trepidation to click Back, Forward or Refresh buttons. No fear to damage server data. Refresh button reloads result page with simple GET request. Back button returns a user to the page with the form. Following click on Forward button reloads the result page using GET again. Absolute freedom of browsing.

But wait, what about clicking on Submit button after returning back to the form page? The form would be resubmitted again, would not it? So all the trouble with redirection just to prevent inadvertent resubmit caused by page refresh?

Keep View Alive

Browsers did not always cache web pages. In the stone ages they were simple. Given the same address they pulled the same resource from the server again and again. Modern browsers are more intelligent. Based on different factors they try to determine should a page be reloaded from a server or not. If not, they can retrieving the page from a cache. For those still using dialup connection this is an instant save in terms of both traffic and time.

But the convenience of caching affects standard behavior. Here is the question: what would a user see if after submitting a form he clicks Back browser button? Did you say that he would see the same form he just submitted with the same values filled in? Why? Because the browser saved the page in the cache in case it would be needed again?

Well, forget smart browsers and caching. How this is for you: each window or page in an interactive application is a View representing an application Model. In order for the View to be correct and consistent with the Model it must be rendered anew each time it is presented to the user.

In plain English: caching must be prohibited for web applications. Online books, dictionaries, pictures can be cached. But please dear browser, do not save snapshots of a live program, because they may not represent actual Model state anymore. It is bad if the saved View is just looked at (you’d rather cache images of naked chicks), but it is tenfold worse when a stale View is used to modify the Model.

Now I ask the same question again: what would a user see if he clicks Back button after submitting a form? You know the correct answer already: the user of a well-designed web application would see a View which represents current Model state. This View would be presented in a way that resubmitting of the same data would be impossible.

New trick for old FORM

Let us take a closer look at the standard use case of an HTML FORM and a result page. The form can be used to edit an existing business entity or a new one. After form is submitted, its data is stored in the database and result of operation is displayed.

According to the PRG pattern, result page must not be returned in response to POST request, because attempt to reload it would cause double submit problem. Instead, browser must load result page separately, using GET method.

We can define the following processing modules (actions in Struts-speak) for this use case: Create Item, Display Item, Store Item and Display Stored.

These modules are combined in input/output pairs:

  • Create Item/Display Item – creates new empty item, then shows new item using Item Page HTML form and allows to enter item value;
  • Store Item/Display Stored – stores item, then shows persisted item from database in read-only mode on Stored Result page.
  • Store Item/Display Item – if fails to store, shows invalid item along with errors using the same HTML form;
  • Display Item is used separately to show and update item which already exists in the database.

(1) Create Item is called from a link on some other web page when a new object should be created. This action constructs empty business object and stores it in the temporary area called Current Items, which itself can be stored in the database or in the session; then redirects to Display Item.

(2-1) Display Item loads constructed business object from the Current Items and shows it on the Item Page, which is HTML form. The form can be refreshed at any time, browser would just ask Display Item action to obtain and show business object again.

(2-2) User fills out object value and submits HTML form to the Store Item action. If object is not accepted, it is kept in the Current Items area, server redirects back to Display Item action, which reads invalid object along with error messages from the Current Items and redisplays it in the form. If Item Page needs to be is refreshed, it loads the same object from Current Items again.

(3) If the object is accepted by Store Item action, it is persisted in the database and removed from temporary area. After that browser is redirected to Display Stored action which shows the Stored Result page. It can display the object which was just persisted. The result page can be safely refreshed, it would load the object from the database again.

If a user clicks Back button on result page (3) after successfully creating and storing new object, he returns to Display Item action (2). The temporary object has been already removed from the temporary area. Display Item has nothing to show and displays an error page instead of the item form, notifying the user that the object cannot be shown simply because it does not exist anymore. Thus a user cannot resubmit the object again.

Similar situation should happen if during creation of a new object the user leaves the form page (2) to a page preceding it (1). For application that means that the user decided to discard the new object. New object is removed from the Current Items. When the user clicks Forward button and returns to Display Item (2), he would see an “Object not found” error.

Instead of displaying an error page when an object is gone from the temporary area, we can do smarter. Create Item generates unique object ID and redirects to Display Item with object ID as request parameter. Display Item action reads object from the session and compares its ID with the one passed in the argument, then shows object to the user. After the user entered object value and submitted it to the Store Item, object ID becomes the primary key of the object.

Now, when the user returns back from result page (3) after submitting an object, browser invokes Display Item action passing it the same request, which contains object ID (2). Object was removed from the session, but it was stored in the database. Display Item action reads the object from the database, copies it to the Current Items and shows it to the user. Depending on business rules, this object may become read-only, so the form would change to a simple page, showing object content, but not allowing submitting it again. Or, conversely, the form would allow to edit it and submit changes. In the latter case the title of the form would change from “Create New Object” to “Edit Existing Object”. If the user submits this object, this is not considered as double submit case. It is an intentional update of existing object.

Editing of existing object is simple, this case is basically already covered. We just need to make sure that Display Item makes no difference between new and existing object. Display Item takes object ID as request parameter, then looks up business object in the Current Items first. If object is not found, it is looked up in the database, copied to temporary area and then displayed (2). After object is updated and submitted, it is stored in the database. When the user clicks Back button from the result page (3), the item form reloads object from the database again (2) so it can be modified and submitted once again. Is this a double submit case? No. It is a deliberate modification of the same object by a user. Of course, you can create all the business rules you want, for example prohibit modification of the same object within certain timeframe.

Let us complete the use case and take a look at how the object is deleted.

Deletion is simple. Get ID as request parameter, pass it to Delete Item action (4), it deletes object in the Model and redirects to result page (5) which verifies with the database that particular object does not exist anymore. Result page can be safely refreshed without producing another delete request and without warning messages. When Back button is clicked on result page (5), browser returns to the page which invokes Delete Item action (4). If this action is called again with the same object ID, then apply it to the Model, get “object not found” exception, show error page. Again this is not a double submit, this is an explicit attempt to delete the same object again. Big deal, it was already deleted.

I think you got the idea. Just another quick example: an online store.

Storing several identical items in the shopping basket is not a problem while a user is still shopping. It is enough to show the basket content and the quantity of each item. What is really important is to ensure that the payment is processed only once. It may look something like this:

  • A shopping basket is created, the unique basket ID is assigned to the basket.
  • If a user clicks on Back button after adding an item to the basket, browser reloads up-to-date basket information from the server and shows to the user that the item is already in the basket. It is up to the user to add another identical item.
  • When the basket is submitted, its content is sent to a purchasing subsystem; the basket is invalidated; its transaction number is saved in history table if needed and destroyed from application context.
  • When a user clicks Back button after purchase was made, browser attempts to load the basket and fails because the basket, its ID and its content already have been destroyed. Browser shows error message instead of the basket. Submitting the same basket twice is impossible.
  • In case of caching browser or proxy a user who clicked Back button would see the same basket which was already submitted to purchasing subsystem. User’s attempt to resubmit the basket would fail because basket tracking ID has been already destroyed along with the basket itself. As a courtesy for users of caching browsers the server can reply with error stating that the submitted basket does not exist any longer.

The Mantra

PRG pattern can be rephrased like this:

Never show pages in response to POST
Always load pages using GET
Navigate from POST to GET using REDIRECT

Repeat these lines before going to bed.

Think in terms of resources

Desktop applications are presentation-centric. When you select menu item you pretty much know which window would be displayed and how it would look like. Depending on Model state the window may display different information, but the overall window layout would be the same. Desktop user interface is relatively static and is largely defined at development stage.

Web applications should be resource-centric. They can attain greater presentation flexibility instead of fixating on delivering a particular page. Browser should request from a server a resource, a business entity, not a page. Depending on resource availability and state server would generate different presentation for that resource. It can be a regular “read-only” web page, or a form with input controls, or a message that resource is not available or it was permanently removed. Think in terms of resources, not pages.

Work with objects

When you obtain input data, you should know which objects it belongs to. When you display data, you should know content of which object is shown. At any time you must know which object you are working with. Use object ID to load, display and store an object. Pass object ID as request parameter.

Use the session or other short-term server storage as a buffer for currently edited or viewed objects. Ensure that your Views always represent current Model state.

Protect the Model

A web page is only a wrapper of what lies beneath: the Model, the business objects, the database. What is displayed to a user is important, but more important is what is stored in the Model. Protect your Model, nurture it, build all kind of error handling around it. After all, inconsistent user interface is just a nuisance; the chaos begins when the Model blows up.

Model should be accessed and updated using few well-defined ways. Generally, Model should not allow concurrent updates by the same client. Keeping Model valid and up-to-date is the best guarantee from inconsistency between presentation and business/persistence layers.

Define clear business/persistence rules, do not rely on web layer to validate input data. Data can come from anywhere: from a user of your web page, from web-service, from third-party application or from aliens, and all of them cannot be trusted. Validate input data directly in the heart of the application, in the Model.

Prevent resubmits

Include object ID and modification timestamp in a form page, provide time of modification for all business objects in the Model. Use ID to look up business object in the persistent storage, and timestamp to distinguish double submit from a cached page.

Consider applicability of tokens. A token allows to detect a double submit from a stale form page. Token is stored in the session before the form submitted for the first time; the same token value is planted on the HTTP form. When the form is submitted, the token value submitted as well. Application verifies that the token is present in the session, accepts input data, and removes token from the session. If a stale form is resubmitted, the form token would not have its session counterpart anymore. Tokens can be used as a pure web layer solution, Struts have built-in support for tokens.

Model can deal with resubmission more reliably than tokens. If a form is used to add or delete data, apply input values to the Model directly. Properly designed Model would throw insert or delete exception. If a form is used for editing of existing object, compare timestamp on the form with timestamp in the database and do not accept input with timestamp earlier than persisted data.

Controlling data with Model makes things easy. You can notify a user that the data being resubmitted is already in the database. “Thank you, stop clicking that button and refresh the page. The original input form has gone long ago, but your browser still keeps it in the cache.”

Prohibit caching of application pages. Insert

<meta HTTP-EQUIV=”Pragma” content=”no-cache”> and
<meta HTTP-EQUIV=”Expires” content=”-1″>

in your pages. A page would be considered expired right after it loaded from the server.

Separate input from output

Use different classes to process input and output. If you use Struts, create separate input and output form classes, this works very well with two-stage PRG pattern:

  • POST request is received by the server
  • Struts populates input form class with request parameters
  • Input form class validates input data
  • Model is updated, information related to current operation is saved in the session for use by consequent GET requests
  • Browser is redirected to output action and loads the result page using GET
  • Server looks up current object in the session and/or in the Model and fills an output form
  • View is created using output form data and is sent back to the browser

You can define only setters in the input form, and only getters in the output form to make form classes easier to read and to ensure that Struts would not populate output form fields with request parameters.

You may want to split large action classes into input an output actions as well.

Use session-scoped UI objects

PRG pattern implies roundtrip to a browser, so the request data is lost. There are two choices to keep POST data: either to transfer it a redirecting response and the in a GET request, or to store it on the server. The first approach is bulky and is non-idempotent. You would have two different kinds of GET request, one to redisplay the HTML form with all its previous data, another to display business object from the database, using just its ID.

Thus, the proper way is to store temporary data on the server and provide GET request with object ID only. That way output action would not even know, was the object just created or loaded from database.

Temporary data corresponds to currently edited or viewed object, and includes both business and presentation data, like:

  • object value;
  • error messages related to this object;
  • page title.

Because this temporary object defines presentation of business object, I call it UI object. If you use Struts, you can use form classes with session scope as UI objects. It is the easiest way to convert regular “forwarding” application into “redirecting” one.

Apparently, the same form class would be used in both input and output actions, so the output action could get access to values set in the input action. The attractiveness of session-scoped form classes is undermined by the fact, that Struts repopulates form fields with each request. This is undesirable, so the mutators would need to verify the name of current action mapping and do not update field values for output action. Struts calls reset method before populating the form, and validate after that. If these methods are used for both input and output, current mapping name should be verified, so the appropriate code could be used.

Session-scoped form classes are kept in memory during client session, which may become an issue. If your form class have references to large objects, you may need to release these objects manually.

Another issue arises when more that one form instance is needed to be created. How this can be done from application code, if form classes are maintained by Struts?

So, despite of the certain convenience of session-scoped form classes I suggest to create your own UI objects. You can have better control over them, you can decide do you want to store them in the session or in database. You will have better abstraction from Struts framework, and porting to other frameworks would be easier.

Form classes are intended for two simple things: deliver input data from HTML form, and render output data on web page. Form classes are just value objects, enhanced with additional functionality like validation. Keep them in request scope, do not use them to store UI or business data.

Struts: use ForwardAction class in output actions

If you have separate input and output form classes, you have got two sets of reset and validate methods. These methods are called by Struts before passing control to an action class. You can use validate in the input form for its original purpose: to verify input data. Output form, on the other hand, does not have much to validate, it is used just to build the result page. So, you can move code from execute method of action class to validate method of output form class and to get rid of custom action class altogether.

Struts: do not expose Views

Views, which are usually JSP pages, must not be available for direct access from a browser. Forget that JSP can process the request. Regard JSP as HTML with data access, use it for output only. Always pass control through action class and/or form class. This ensures clean separation between components and allows Controller to monitor all requests. Hide web pages in WEB-INF directory and display them from their respective actions.

Configure caching

Browsers are not required to process cache control tags on the web pages, but they usually obey HTTP response header fields. Add <controller nocache=”true”/> to your struts-config.xml file. Struts would modify each response header as follows:

response.setHeader(“Pragma”, “No-cache”);
response.setHeader(“Cache-Control”, “no-cache”);
response.setDateHeader(“Expires”, 1);

Corresponding HTTP header fields produced by Tomcat 4.0.6 looks like this:

“Pragma: No-cache”
“Cache-Control: no-cache”
“Expires: Thu, 01 Jan 1970 00:00:00 GMT”

Use better browsers

Despite efforts to prohibit caching some web browsers like Firefox just do not care. Caching works great with simple forwarding applications, preventing implicit resubmits. But caching a page which supposed to reflect current sever state breaks the user experience and introduces the double submit problem again.

Other browsers like Opera can resubmit POST request without confirmation message. This may invalidate state of an application which does not check for double submit, and a user would not even know about it.

Old Netscape Navigator works fine for me, but for some reason it freezes for several seconds when submitting a POST request on Tomcat server. Other browsers do not inhibit this strange behavior.

Internet Explorer does almost everything right, but is very annoying. When you resubmit POST request, it shows you a “Page expired” window first and a dialog box next before allowing to proceed. And if you decide not to, it loses your current page. But because your application would not have resubmit problems, your customers would not suffer much.

Why redirect works

It is interesting that PRG pattern exploits non-standard behavior of browsers and web servers. HTTP 1.1 defines several redirect response codes in 3xx range. Some of these codes require browser to use the same request type, some require to change POST to GET, some require to obtain user confirmation when request is redirected. Turns out that many of these requirements are not implemented by popular browsers. Instead, they have common de-facto behavior, like redirecting POST to GET without confirmation if received 302 code. This feature is used by PRG pattern.

This behavior is wrong for 302 (“Found”) code, but is absolutely correct for 303 (“See Other”) code. Still, few servers return 303 when redirect with GET method is required. HttpResponse.sendRedirect method does not allow to set response code, it always returns 302. It is possible to emulate sendRedirect(url) behavior using the following methods:

res.setStatus(res.SC_SEE_OTHER);
res.setHeader(“Location”,url);

where SC_SEE_OTHER is the proper 303 code, but sendRedirect provides some additional service like resolving relative addresses, so this is not a direct snap-in. The discrepancy between browser behavior and HTTP standard can be resolved, if 302 and 303 codes considered equal, and another code for proper 302 behavior were created.

In any case, I doubt that browser vendors will change implementation of 302 response code, because too many applications relay on it. The good thing is that modern browsers understand and correctly process 303 code, so if you want to be sure, return 303 instead of 302.

References


from http://samsami2u.wordpress.com/2008/06/04/calling-external-javascript-on-button-click-in-flash/

Calling external javascript in flash is really easy. just follow the guidelines given below:

create a flash button in flash.

single click on the flash button and open the action script window related to the flash button.

in the action script window please write down the following action script:

on (release) {
getURL(“javascript:JavascriptFunction();”,”_self”);
}

where JavascriptFunction() is the external javascript function that is to be called by the click of the flash button.

If you have any queries or questions please do contact me.

Thanks,

Sachin (samsami2um@gmail.com)

Expert Exchange 免费注册地址

http://www.experts-exchange.com/registerFree2.jsp

List of HTTP status codes

from http://en.wikipedia.org/wiki/List_of_HTTP_status_codes

1xx Informational

Request received, continuing process.

This class of status code indicates a provisional response, consisting only of the Status-Line and optional headers, and is terminated by an empty line. Since HTTP/1.0 did not define any 1xx status codes, servers must not send a 1xx response to an HTTP/1.0 client except under experimental conditions.

100 Continue
This means that the server has received the request headers, and that the client should proceed to send the request body (in the case of a request for which a body needs to be sent; for example, a POST request). If the request body is large, sending it to a server when a request has already been rejected based upon inappropriate headers is inefficient. To have a server check if the request could be accepted based on the request’s headers alone, a client must send Expect: 100-continue as a header in its initial request (see RFC 2616 §14.20 – Expect header) and check if a 100 Continue status code is received in response before continuing (or receive 417 Expectation Failed and not continue).[2]
101 Switching Protocols
This means the requestor has asked the server to switch protocols and the server is acknowledging that it will do so.[3]
102 Processing (WebDAV) (RFC 2518 )

[edit]2xx Success

The action was successfully received, understood, and accepted.

This class of status code indicates that the client’s request was successfully received, understood, and accepted.

200 OK
Standard response for successful HTTP requests. The actual response will depend on the request method used. In a GET request, the response will contain an entity corresponding to the requested resource. In a POST request the response will contain an entity describing or containing the result of the action.
201 Created
The request has been fulfilled and resulted in a new resource being created.
202 Accepted
The request has been accepted for processing, but the processing has not been completed. The request might or might not eventually be acted upon, as it might be disallowed when processing actually takes place.
203 Non-Authoritative Information (since HTTP/1.1)
The server successfully processed the request, but is returning information that may be from another source.
204 No Content
The server successfully processed the request, but is not returning any content.
205 Reset Content
The server successfully processed the request, but is not returning any content. Unlike a 204 response, this response requires that the requestor reset the document view.
206 Partial Content
The server is delivering only part of the resource due to a range header sent by the client. This is used by tools like wget to enable resuming of interrupted downloads, or split a download into multiple simultaneous streams.
207 Multi-Status (WebDAV)
The message body that follows is an XML message and can contain a number of separate response codes, depending on how many sub-requests were made.

[edit]3xx Redirection

The client must take additional action to complete the request.

This class of status code indicates that further action needs to be taken by the user agent in order to fulfil the request. The action required may be carried out by the user agent without interaction with the user if and only if the method used in the second request is GET or HEAD. A user agent should not automatically redirect a request more than five times, since such redirections usually indicate an infinite loop.

300 Multiple Choices
Indicates multiple options for the resource that the client may follow. It, for instance, could be used to present different format options for video, list files with different extensions, orword sense disambiguation.
301 Moved Permanently
This and all future requests should be directed to the given URI.
302 Found
This is the most popular redirect code[citation needed], but also an example of industrial practice contradicting the standard. HTTP/1.0 specification (RFC 1945 ) required the client to perform a temporary redirect (the original describing phrase was “Moved Temporarily”), but popular browsers implemented it as a 303 See Other. Therefore, HTTP/1.1 added status codes 303 and 307 to disambiguate between the two behaviours. However, the majority of Web applications and frameworks still use the 302 status code as if it were the 303.
303 See Other (since HTTP/1.1)
The response to the request can be found under another URI using a GET method. When received in response to a PUT, it should be assumed that the server has received the data and the redirect should be issued with a separate GET message.
304 Not Modified
Indicates the resource has not been modified since last requested. Typically, the HTTP client provides a header like the If-Modified-Since header to provide a time against which to compare. Utilizing this saves bandwidth and reprocessing on both the server and client, as only the header data must be sent and received in comparison to the entirety of the page being re-processed by the server, then resent using more bandwidth of the server and client.
305 Use Proxy (since HTTP/1.1)
Many HTTP clients (such as Mozilla[4] and Internet Explorer) do not correctly handle responses with this status code, primarily for security reasons.
306 Switch Proxy
No longer used.
307 Temporary Redirect (since HTTP/1.1)
In this occasion, the request should be repeated with another URI, but future requests can still use the original URI. In contrast to 303, the request method should not be changed when reissuing the original request. For instance, a POST request must be repeated using another POST request.

[edit]4xx Client Error

The request contains bad syntax or cannot be fulfilled.

The 4xx class of status code is intended for cases in which the client seems to have erred. Except when responding to a HEAD request, the server should include an entity containing an explanation of the error situation, and whether it is a temporary or permanent condition. These status codes are applicable to any request method. User agents should display any included entity to the user. These are typically the most common error codes encountered while online.

400 Bad Request
The request contains bad syntax or cannot be fulfilled.
401 Unauthorized
Similar to 403 Forbidden, but specifically for use when authentication is possible but has failed or not yet been provided. See Basic access authentication and Digest access authentication.
402 Payment Required
The original intention was that this code might be used as part of some form of digital cash or micropayment scheme, but that has not happened, and this code has never been used.
403 Forbidden
The request was a legal request, but the server is refusing to respond to it. Unlike a 401 Unauthorized response, authenticating will make no difference.
404 Not Found
The requested resource could not be found but may be available again in the future. Subsequent requests by the client are permissible.
405 Method Not Allowed
A request was made of a resource using a request method not supported by that resource; for example, using GET on a form which requires data to be presented via POST, or using PUT on a read-only resource.
406 Not Acceptable
The requested resource is only capable of generating content not acceptable according to the Accept headers sent in the request.
407 Proxy Authentication Required
408 Request Timeout
The server timed out waiting for the request.
409 Conflict
Indicates that the request could not be processed because of conflict in the request, such as an edit conflict.
410 Gone
Indicates that the resource requested is no longer available and will not be available again. This should be used when a resource has been intentionally removed; however, it is not necessary to return this code and a 404 Not Found can be issued instead. Upon receiving a 410 status code, the client should not request the resource again in the future. Clients such as search engines should remove the resource from their indexes.
411 Length Required
The request did not specify the length of its content, which is required by the requested resource.
412 Precondition Failed
The server does not meet one of the preconditions that the requestor put on the request.
413 Request Entity Too Large
The resource that was requested is too large to transmit using the current protocol.
414 Request-URI Too Long
The URI provided was too long for the server to process.
415 Unsupported Media Type
The request did not specify any media types that the server or resource supports. For example the client specified that an image resource should be served as image/svg+xml, but the server cannot find a matching version of the image.
416 Requested Range Not Satisfiable
The client has asked for a portion of the file, but the server cannot supply that portion (for example, if the client asked for a part of the file that lies beyond the end of the file).
417 Expectation Failed
The server cannot meet the requirements of the Expect request-header field.
418 I’m a teapot
The HTCPCP server is a teapot. The responding entity MAY be short and stout. Defined by the April Fools’ specification RFC 2324. See Hyper Text Coffee Pot Control Protocol for more information.
422 Unprocessable Entity (WebDAV) (RFC 4918 )
The request was well-formed but was unable to be followed due to semantic errors.
423 Locked (WebDAV) (RFC 4918 )
The resource that is being accessed is locked
424 Failed Dependency (WebDAV) (RFC 4918 )
The request failed due to failure of a previous request (e.g. a PROPPATCH).
425 Unordered Collection
Defined in drafts of WebDav Advanced Collections, but not present in “Web Distributed Authoring and Versioning (WebDAV) Ordered Collections Protocol” (RFC 3648 ).
426 Upgrade Required (RFC 2817 )
The client should switch to TLS/1.0.
449 Retry With
A Microsoft extension. The request should be retried after doing the appropriate action.

[edit]5xx Server Error

The server failed to fulfil an apparently valid request.

Response status codes beginning with the digit “5″ indicate cases in which the server is aware that it has encountered an error or is otherwise incapable of performing the request. Except when responding to a HEAD request, the server should include an entity containing an explanation of the error situation, and indicate whether it is a temporary or permanent condition. Likewise, user agents should display any included entity to the user. These response codes are applicable to any request method.

500 Internal Server Error
A generic error message, given when no more specific message is suitable.
501 Not Implemented
The server either does not recognise the request method, or it lacks the ability to fulfil the request.
502 Bad Gateway
The server was acting as a gateway or proxy and received an invalid response from the upstream server.
503 Service Unavailable
The server is currently unavailable (because it is overloaded or down for maintenance). Generally, this is a temporary state.
504 Gateway Timeout
The server was acting as a gateway or proxy and did not receive a timely request from the upstream server.
505 HTTP Version Not Supported
The server does not support the HTTP protocol version used in the request.
506 Variant Also Negotiates (RFC 2295 )
Transparent content negotiation for the request, results in a circular reference.
507 Insufficient Storage (WebDAV) (RFC 4918 )
509 Bandwidth Limit Exceeded (Apache bw/limited extension)
This status code, while used by many servers, is not specified in any RFCs.
510 Not Extended (RFC 2774 )
Further extensions to the request are required for the server to fulfil it.

from http://developer.yahoo.com/performance/rules.html

Best Practices for Speeding Up Your Web Site

The Exceptional Performance team has identified a number of best practices for making web pages fast. The list includes 34 best practices divided into 7 categories.

Filter by category:

 

  • Content
  •  

  • Server
  •  

     

  • CSS
  •  

  • Javascript
  •  

  • Images
  •  

  • Mobile
  •  

  • All

Minimize HTTP Requests

tag: content

80% of the end-user response time is spent on the front-end. Most of this time is tied up in downloading all the components in the page: images, stylesheets, scripts, Flash, etc. Reducing the number of components in turn reduces the number of HTTP requests required to render the page. This is the key to faster pages.

One way to reduce the number of components in the page is to simplify the page’s design. But is there a way to build pages with richer content while also achieving fast response times? Here are some techniques for reducing the number of HTTP requests, while still supporting rich page designs.

Combined files are a way to reduce the number of HTTP requests by combining all scripts into a single script, and similarly combining all CSS into a single stylesheet. Combining files is more challenging when the scripts and stylesheets vary from page to page, but making this part of your release process improves response times.

CSS Sprites are the preferred method for reducing the number of image requests. Combine your background images into a single image and use the CSS background-image and background-position properties to display the desired image segment.

Image maps combine multiple images into a single image. The overall size is about the same, but reducing the number of HTTP requests speeds up the page. Image maps only work if the images are contiguous in the page, such as a navigation bar. Defining the coordinates of image maps can be tedious and error prone. Using image maps for navigation is not accessible too, so it’s not recommended.

Inline images use the data: URL scheme to embed the image data in the actual page. This can increase the size of your HTML document. Combining inline images into your (cached) stylesheets is a way to reduce HTTP requests and avoid increasing the size of your pages. Inline images are not yet supported across all major browsers.

Reducing the number of HTTP requests in your page is the place to start. This is the most important guideline for improving performance for first time visitors. As described in Tenni Theurer’s blog post Browser Cache Usage – Exposed!, 40-60% of daily visitors to your site come in with an empty cache. Making your page fast for these first time visitors is key to a better user experience.

top | discuss this rule

Use a Content Delivery Network

tag: server

The user’s proximity to your web server has an impact on response times. Deploying your content across multiple, geographically dispersed servers will make your pages load faster from the user’s perspective. But where should you start?

As a first step to implementing geographically dispersed content, don’t attempt to redesign your web application to work in a distributed architecture. Depending on the application, changing the architecture could include daunting tasks such as synchronizing session state and replicating database transactions across server locations. Attempts to reduce the distance between users and your content could be delayed by, or never pass, this application architecture step.

Remember that 80-90% of the end-user response time is spent downloading all the components in the page: images, stylesheets, scripts, Flash, etc. This is the Performance Golden Rule. Rather than starting with the difficult task of redesigning your application architecture, it’s better to first disperse your static content. This not only achieves a bigger reduction in response times, but it’s easier thanks to content delivery networks.

A content delivery network (CDN) is a collection of web servers distributed across multiple locations to deliver content more efficiently to users. The server selected for delivering content to a specific user is typically based on a measure of network proximity. For example, the server with the fewest network hops or the server with the quickest response time is chosen.

Some large Internet companies own their own CDN, but it’s cost-effective to use a CDN service provider, such asAkamai TechnologiesMirror Image Internet, or Limelight Networks. For start-up companies and private web sites, the cost of a CDN service can be prohibitive, but as your target audience grows larger and becomes more global, a CDN is necessary to achieve fast response times. At Yahoo!, properties that moved static content off their application web servers to a CDN improved end-user response times by 20% or more. Switching to a CDN is a relatively easy code change that will dramatically improve the speed of your web site.

top | discuss this rule

Add an Expires or a Cache-Control Header

tag: server

There are two things in this rule:

  • For static components: implement “Never expire” policy by setting far future Expires header
  • For dynamic components: use an appropriate Cache-Control header to help the browser with conditional requests

Web page designs are getting richer and richer, which means more scripts, stylesheets, images, and Flash in the page. A first-time visitor to your page may have to make several HTTP requests, but by using the Expires header you make those components cacheable. This avoids unnecessary HTTP requests on subsequent page views. Expires headers are most often used with images, but they should be used on all components including scripts, stylesheets, and Flash components.

Browsers (and proxies) use a cache to reduce the number and size of HTTP requests, making web pages load faster. A web server uses the Expires header in the HTTP response to tell the client how long a component can be cached. This is a far future Expires header, telling the browser that this response won’t be stale until April 15, 2010.

 

      Expires: Thu, 15 Apr 2010 20:00:00 GMT

 

If your server is Apache, use the ExpiresDefault directive to set an expiration date relative to the current date. This example of the ExpiresDefault directive sets the Expires date 10 years out from the time of the request.

 

      ExpiresDefault "access plus 10 years"

 

Keep in mind, if you use a far future Expires header you have to change the component’s filename whenever the component changes. At Yahoo! we often make this step part of the build process: a version number is embedded in the component’s filename, for example, yahoo_2.0.6.js.

Using a far future Expires header affects page views only after a user has already visited your site. It has no effect on the number of HTTP requests when a user visits your site for the first time and the browser’s cache is empty. Therefore the impact of this performance improvement depends on how often users hit your pages with a primed cache. (A “primed cache” already contains all of the components in the page.) We measured this at Yahoo! and found the number of page views with a primed cache is 75-85%. By using a far future Expires header, you increase the number of components that are cached by the browser and re-used on subsequent page views without sending a single byte over the user’s Internet connection.

top | discuss this rule

Gzip Components

tag: server

The time it takes to transfer an HTTP request and response across the network can be significantly reduced by decisions made by front-end engineers. It’s true that the end-user’s bandwidth speed, Internet service provider, proximity to peering exchange points, etc. are beyond the control of the development team. But there are other variables that affect response times. Compression reduces response times by reducing the size of the HTTP response.

Starting with HTTP/1.1, web clients indicate support for compression with the Accept-Encoding header in the HTTP request.

      Accept-Encoding: gzip, deflate

If the web server sees this header in the request, it may compress the response using one of the methods listed by the client. The web server notifies the web client of this via the Content-Encoding header in the response.

      Content-Encoding: gzip

Gzip is the most popular and effective compression method at this time. It was developed by the GNU project and standardized by RFC 1952. The only other compression format you’re likely to see is deflate, but it’s less effective and less popular.

Gzipping generally reduces the response size by about 70%. Approximately 90% of today’s Internet traffic travels through browsers that claim to support gzip. If you use Apache, the module configuring gzip depends on your version: Apache 1.3 uses mod_gzip while Apache 2.x uses mod_deflate.

There are known issues with browsers and proxies that may cause a mismatch in what the browser expects and what it receives with regard to compressed content. Fortunately, these edge cases are dwindling as the use of older browsers drops off. The Apache modules help out by adding appropriate Vary response headers automatically.

Servers choose what to gzip based on file type, but are typically too limited in what they decide to compress. Most web sites gzip their HTML documents. It’s also worthwhile to gzip your scripts and stylesheets, but many web sites miss this opportunity. In fact, it’s worthwhile to compress any text response including XML and JSON. Image and PDF files should not be gzipped because they are already compressed. Trying to gzip them not only wastes CPU but can potentially increase file sizes.

Gzipping as many file types as possible is an easy way to reduce page weight and accelerate the user experience.

top | discuss this rule

Put Stylesheets at the Top

tag: css

While researching performance at Yahoo!, we discovered that moving stylesheets to the document HEAD makes pages appear to be loading faster. This is because putting stylesheets in the HEAD allows the page to render progressively.

Front-end engineers that care about performance want a page to load progressively; that is, we want the browser to display whatever content it has as soon as possible. This is especially important for pages with a lot of content and for users on slower Internet connections. The importance of giving users visual feedback, such as progress indicators, has been well researched and documented. In our case the HTML page is the progress indicator! When the browser loads the page progressively the header, the navigation bar, the logo at the top, etc. all serve as visual feedback for the user who is waiting for the page. This improves the overall user experience.

The problem with putting stylesheets near the bottom of the document is that it prohibits progressive rendering in many browsers, including Internet Explorer. These browsers block rendering to avoid having to redraw elements of the page if their styles change. The user is stuck viewing a blank white page.

The HTML specification clearly states that stylesheets are to be included in the HEAD of the page: “Unlike A, [LINK] may only appear in the HEAD section of a document, although it may appear any number of times.” Neither of the alternatives, the blank white screen or flash of unstyled content, are worth the risk. The optimal solution is to follow the HTML specification and load your stylesheets in the document HEAD.

top | discuss this rule

Put Scripts at the Bottom

tag: javascript

The problem caused by scripts is that they block parallel downloads. The HTTP/1.1 specification suggests that browsers download no more than two components in parallel per hostname. If you serve your images from multiple hostnames, you can get more than two downloads to occur in parallel. While a script is downloading, however, the browser won’t start any other downloads, even on different hostnames.

In some situations it’s not easy to move scripts to the bottom. If, for example, the script uses document.writeto insert part of the page’s content, it can’t be moved lower in the page. There might also be scoping issues. In many cases, there are ways to workaround these situations.

An alternative suggestion that often comes up is to use deferred scripts. The DEFER attribute indicates that the script does not contain document.write, and is a clue to browsers that they can continue rendering. Unfortunately, Firefox doesn’t support the DEFER attribute. In Internet Explorer, the script may be deferred, but not as much as desired. If a script can be deferred, it can also be moved to the bottom of the page. That will make your web pages load faster.

top | discuss this rule

Avoid CSS Expressions

tag: css

CSS expressions are a powerful (and dangerous) way to set CSS properties dynamically. They’re supported in Internet Explorer, starting with version 5. As an example, the background color could be set to alternate every hour using CSS expressions.

 

      background-color: expression( (new Date()).getHours()%2 ? "#B8D4FF" : "#F08A00" );

 

As shown here, the expression method accepts a JavaScript expression. The CSS property is set to the result of evaluating the JavaScript expression. The expression method is ignored by other browsers, so it is useful for setting properties in Internet Explorer needed to create a consistent experience across browsers.

The problem with expressions is that they are evaluated more frequently than most people expect. Not only are they evaluated when the page is rendered and resized, but also when the page is scrolled and even when the user moves the mouse over the page. Adding a counter to the CSS expression allows us to keep track of when and how often a CSS expression is evaluated. Moving the mouse around the page can easily generate more than 10,000 evaluations.

One way to reduce the number of times your CSS expression is evaluated is to use one-time expressions, where the first time the expression is evaluated it sets the style property to an explicit value, which replaces the CSS expression. If the style property must be set dynamically throughout the life of the page, using event handlers instead of CSS expressions is an alternative approach. If you must use CSS expressions, remember that they may be evaluated thousands of times and could affect the performance of your page.

top | discuss this rule

Make JavaScript and CSS External

tag: javascript, css

Many of these performance rules deal with how external components are managed. However, before these considerations arise you should ask a more basic question: Should JavaScript and CSS be contained in external files, or inlined in the page itself?

Using external files in the real world generally produces faster pages because the JavaScript and CSS files are cached by the browser. JavaScript and CSS that are inlined in HTML documents get downloaded every time the HTML document is requested. This reduces the number of HTTP requests that are needed, but increases the size of the HTML document. On the other hand, if the JavaScript and CSS are in external files cached by the browser, the size of the HTML document is reduced without increasing the number of HTTP requests.

The key factor, then, is the frequency with which external JavaScript and CSS components are cached relative to the number of HTML documents requested. This factor, although difficult to quantify, can be gauged using various metrics. If users on your site have multiple page views per session and many of your pages re-use the same scripts and stylesheets, there is a greater potential benefit from cached external files.

Many web sites fall in the middle of these metrics. For these sites, the best solution generally is to deploy the JavaScript and CSS as external files. The only exception where inlining is preferable is with home pages, such asYahoo!’s front page and My Yahoo!. Home pages that have few (perhaps only one) page view per session may find that inlining JavaScript and CSS results in faster end-user response times.

For front pages that are typically the first of many page views, there are techniques that leverage the reduction of HTTP requests that inlining provides, as well as the caching benefits achieved through using external files. One such technique is to inline JavaScript and CSS in the front page, but dynamically download the external files after the page has finished loading. Subsequent pages would reference the external files that should already be in the browser’s cache.

top | discuss this rule

Reduce DNS Lookups

tag: content

The Domain Name System (DNS) maps hostnames to IP addresses, just as phonebooks map people’s names to their phone numbers. When you type www.yahoo.com into your browser, a DNS resolver contacted by the browser returns that server’s IP address. DNS has a cost. It typically takes 20-120 milliseconds for DNS to lookup the IP address for a given hostname. The browser can’t download anything from this hostname until the DNS lookup is completed.

DNS lookups are cached for better performance. This caching can occur on a special caching server, maintained by the user’s ISP or local area network, but there is also caching that occurs on the individual user’s computer. The DNS information remains in the operating system’s DNS cache (the “DNS Client service” on Microsoft Windows). Most browsers have their own caches, separate from the operating system’s cache. As long as the browser keeps a DNS record in its own cache, it doesn’t bother the operating system with a request for the record.

Internet Explorer caches DNS lookups for 30 minutes by default, as specified by the DnsCacheTimeout registry setting. Firefox caches DNS lookups for 1 minute, controlled by the network.dnsCacheExpirationconfiguration setting. (Fasterfox changes this to 1 hour.)

When the client’s DNS cache is empty (for both the browser and the operating system), the number of DNS lookups is equal to the number of unique hostnames in the web page. This includes the hostnames used in the page’s URL, images, script files, stylesheets, Flash objects, etc. Reducing the number of unique hostnames reduces the number of DNS lookups.

Reducing the number of unique hostnames has the potential to reduce the amount of parallel downloading that takes place in the page. Avoiding DNS lookups cuts response times, but reducing parallel downloads may increase response times. My guideline is to split these components across at least two but no more than four hostnames. This results in a good compromise between reducing DNS lookups and allowing a high degree of parallel downloads.

top | discuss this rule

Minify JavaScript and CSS

tag: javascript, css

Minification is the practice of removing unnecessary characters from code to reduce its size thereby improving load times. When code is minified all comments are removed, as well as unneeded white space characters (space, newline, and tab). In the case of JavaScript, this improves response time performance because the size of the downloaded file is reduced. Two popular tools for minifying JavaScript code are JSMin and YUI Compressor. The YUI compressor can also minify CSS.

Obfuscation is an alternative optimization that can be applied to source code. It’s more complex than minification and thus more likely to generate bugs as a result of the obfuscation step itself. In a survey of ten top U.S. web sites, minification achieved a 21% size reduction versus 25% for obfuscation. Although obfuscation has a higher size reduction, minifying JavaScript is less risky.

In addition to minifying external scripts and styles, inlined <script> and <style> blocks can and should also be minified. Even if you gzip your scripts and styles, minifying them will still reduce the size by 5% or more. As the use and size of JavaScript and CSS increases, so will the savings gained by minifying your code.

top | discuss this rule

Avoid Redirects

tag: content

Redirects are accomplished using the 301 and 302 status codes. Here’s an example of the HTTP headers in a 301 response:

 

      HTTP/1.1 301 Moved Permanently
      Location: http://example.com/newuri
      Content-Type: text/html

 

The browser automatically takes the user to the URL specified in the Location field. All the information necessary for a redirect is in the headers. The body of the response is typically empty. Despite their names, neither a 301 nor a 302 response is cached in practice unless additional headers, such as Expires or Cache-Control, indicate it should be. The meta refresh tag and JavaScript are other ways to direct users to a different URL, but if you must do a redirect, the preferred technique is to use the standard 3xx HTTP status codes, primarily to ensure the back button works correctly.

The main thing to remember is that redirects slow down the user experience. Inserting a redirect between the user and the HTML document delays everything in the page since nothing in the page can be rendered and no components can start being downloaded until the HTML document has arrived.

One of the most wasteful redirects happens frequently and web developers are generally not aware of it. It occurs when a trailing slash (/) is missing from a URL that should otherwise have one. For example, going tohttp://astrology.yahoo.com/astrology results in a 301 response containing a redirect tohttp://astrology.yahoo.com/astrology/ (notice the added trailing slash). This is fixed in Apache by using Alias ormod_rewrite, or the DirectorySlash directive if you’re using Apache handlers.

Connecting an old web site to a new one is another common use for redirects. Others include connecting different parts of a website and directing the user based on certain conditions (type of browser, type of user account, etc.). Using a redirect to connect two web sites is simple and requires little additional coding. Although using redirects in these situations reduces the complexity for developers, it degrades the user experience. Alternatives for this use of redirects include using Alias and mod_rewrite if the two code paths are hosted on the same server. If a domain name change is the cause of using redirects, an alternative is to create a CNAME (a DNS record that creates an alias pointing from one domain name to another) in combination with Alias or mod_rewrite.

top | discuss this rule

Remove Duplicate Scripts

tag: javascript

It hurts performance to include the same JavaScript file twice in one page. This isn’t as unusual as you might think. A review of the ten top U.S. web sites shows that two of them contain a duplicated script. Two main factors increase the odds of a script being duplicated in a single web page: team size and number of scripts. When it does happen, duplicate scripts hurt performance by creating unnecessary HTTP requests and wasted JavaScript execution.

Unnecessary HTTP requests happen in Internet Explorer, but not in Firefox. In Internet Explorer, if an external script is included twice and is not cacheable, it generates two HTTP requests during page loading. Even if the script is cacheable, extra HTTP requests occur when the user reloads the page.

In addition to generating wasteful HTTP requests, time is wasted evaluating the script multiple times. This redundant JavaScript execution happens in both Firefox and Internet Explorer, regardless of whether the script is cacheable.

One way to avoid accidentally including the same script twice is to implement a script management module in your templating system. The typical way to include a script is to use the SCRIPT tag in your HTML page.

 

      <script type="text/javascript" src="menu_1.0.17.js"></script>

An alternative in PHP would be to create a function called insertScript.

 

      <?php insertScript("menu.js") ?>

In addition to preventing the same script from being inserted multiple times, this function could handle other issues with scripts, such as dependency checking and adding version numbers to script filenames to support far future Expires headers.

top | discuss this rule

Configure ETags

tag: server

Entity tags (ETags) are a mechanism that web servers and browsers use to determine whether the component in the browser’s cache matches the one on the origin server. (An “entity” is another word a “component”: images, scripts, stylesheets, etc.) ETags were added to provide a mechanism for validating entities that is more flexible than the last-modified date. An ETag is a string that uniquely identifies a specific version of a component. The only format constraints are that the string be quoted. The origin server specifies the component’s ETag using the ETagresponse header.

 

      HTTP/1.1 200 OK
      Last-Modified: Tue, 12 Dec 2006 03:03:59 GMT
      ETag: "10c24bc-4ab-457e1c1f"
      Content-Length: 12195

 

Later, if the browser has to validate a component, it uses the If-None-Match header to pass the ETag back to the origin server. If the ETags match, a 304 status code is returned reducing the response by 12195 bytes for this example.

 

      GET /i/yahoo.gif HTTP/1.1
      Host: us.yimg.com
      If-Modified-Since: Tue, 12 Dec 2006 03:03:59 GMT
      If-None-Match: "10c24bc-4ab-457e1c1f"
      HTTP/1.1 304 Not Modified

 

The problem with ETags is that they typically are constructed using attributes that make them unique to a specific server hosting a site. ETags won’t match when a browser gets the original component from one server and later tries to validate that component on a different server, a situation that is all too common on Web sites that use a cluster of servers to handle requests. By default, both Apache and IIS embed data in the ETag that dramatically reduces the odds of the validity test succeeding on web sites with multiple servers.

The ETag format for Apache 1.3 and 2.x is inode-size-timestamp. Although a given file may reside in the same directory across multiple servers, and have the same file size, permissions, timestamp, etc., its inode is different from one server to the next.

IIS 5.0 and 6.0 have a similar issue with ETags. The format for ETags on IIS isFiletimestamp:ChangeNumber. A ChangeNumber is a counter used to track configuration changes to IIS. It’s unlikely that the ChangeNumber is the same across all IIS servers behind a web site.

The end result is ETags generated by Apache and IIS for the exact same component won’t match from one server to another. If the ETags don’t match, the user doesn’t receive the small, fast 304 response that ETags were designed for; instead, they’ll get a normal 200 response along with all the data for the component. If you host your web site on just one server, this isn’t a problem. But if you have multiple servers hosting your web site, and you’re using Apache or IIS with the default ETag configuration, your users are getting slower pages, your servers have a higher load, you’re consuming greater bandwidth, and proxies aren’t caching your content efficiently. Even if your components have a far future Expires header, a conditional GET request is still made whenever the user hits Reload or Refresh.

If you’re not taking advantage of the flexible validation model that ETags provide, it’s better to just remove the ETag altogether. The Last-Modified header validates based on the component’s timestamp. And removing the ETag reduces the size of the HTTP headers in both the response and subsequent requests. This Microsoft Support article describes how to remove ETags. In Apache, this is done by simply adding the following line to your Apache configuration file:

      FileETag none

top | discuss this rule

Make Ajax Cacheable

tag: content

One of the cited benefits of Ajax is that it provides instantaneous feedback to the user because it requests information asynchronously from the backend web server. However, using Ajax is no guarantee that the user won’t be twiddling his thumbs waiting for those asynchronous JavaScript and XML responses to return. In many applications, whether or not the user is kept waiting depends on how Ajax is used. For example, in a web-based email client the user will be kept waiting for the results of an Ajax request to find all the email messages that match their search criteria. It’s important to remember that “asynchronous” does not imply “instantaneous”.

To improve performance, it’s important to optimize these Ajax responses. The most important way to improve the performance of Ajax is to make the responses cacheable, as discussed in Add an Expires or a Cache-Control Header. Some of the other rules also apply to Ajax:

 

Let’s look at an example. A Web 2.0 email client might use Ajax to download the user’s address book for autocompletion. If the user hasn’t modified her address book since the last time she used the email web app, the previous address book response could be read from cache if that Ajax response was made cacheable with a future Expires or Cache-Control header. The browser must be informed when to use a previously cached address book response versus requesting a new one. This could be done by adding a timestamp to the address book Ajax URL indicating the last time the user modified her address book, for example, &t=1190241612. If the address book hasn’t been modified since the last download, the timestamp will be the same and the address book will be read from the browser’s cache eliminating an extra HTTP roundtrip. If the user has modified her address book, the timestamp ensures the new URL doesn’t match the cached response, and the browser will request the updated address book entries.

Even though your Ajax responses are created dynamically, and might only be applicable to a single user, they can still be cached. Doing so will make your Web 2.0 apps faster.

top | discuss this rule

Flush the Buffer Early

tag: server

When users request a page, it can take anywhere from 200 to 500ms for the backend server to stitch together the HTML page. During this time, the browser is idle as it waits for the data to arrive. In PHP you have the functionflush(). It allows you to send your partially ready HTML response to the browser so that the browser can start fetching components while your backend is busy with the rest of the HTML page. The benefit is mainly seen on busy backends or light frontends.

A good place to consider flushing is right after the HEAD because the HTML for the head is usually easier to produce and it allows you to include any CSS and JavaScript files for the browser to start fetching in parallel while the backend is still processing.

Example:

      ... <!-- css, js -->
    </head>
    <?php flush(); ?>
    <body>
      ... <!-- content -->

Yahoo! search pioneered research and real user testing to prove the benefits of using this technique.

top

Use GET for AJAX Requests

tag: server

The Yahoo! Mail team found that when using XMLHttpRequest, POST is implemented in the browsers as a two-step process: sending the headers first, then sending data. So it’s best to use GET, which only takes one TCP packet to send (unless you have a lot of cookies). The maximum URL length in IE is 2K, so if you send more than 2K data you might not be able to use GET.

An interesting side affect is that POST without actually posting any data behaves like GET. Based on the HTTP specs, GET is meant for retrieving information, so it makes sense (semantically) to use GET when you’re only requesting data, as opposed to sending data to be stored server-side.

 

top

Post-load Components

tag: content

You can take a closer look at your page and ask yourself: “What’s absolutely required in order to render the page initially?”. The rest of the content and components can wait.

JavaScript is an ideal candidate for splitting before and after the onload event. For example if you have JavaScript code and libraries that do drag and drop and animations, those can wait, because dragging elements on the page comes after the initial rendering. Other places to look for candidates for post-loading include hidden content (content that appears after a user action) and images below the fold.

Tools to help you out in your effort: YUI Image Loader allows you to delay images below the fold and the YUI Get utility is an easy way to include JS and CSS on the fly. For an example in the wild take a look at Yahoo! Home Page with Firebug’s Net Panel turned on.

It’s good when the performance goals are inline with other web development best practices. In this case, the idea of progressive enhancement tells us that JavaScript, when supported, can improve the user experience but you have to make sure the page works even without JavaScript. So after you’ve made sure the page works fine, you can enhance it with some post-loaded scripts that give you more bells and whistles such as drag and drop and animations.

top

Preload Components

tag: content

Preload may look like the opposite of post-load, but it actually has a different goal. By preloading components you can take advantage of the time the browser is idle and request components (like images, styles and scripts) you’ll need in the future. This way when the user visits the next page, you could have most of the components already in the cache and your page will load much faster for the user.

There are actually several types of preloading:

  • Unconditional preload – as soon as onload fires, you go ahead and fetch some extra components. Check google.com for an example of how a sprite image is requested onload. This sprite image is not needed on the google.com homepage, but it is needed on the consecutive search result page.
  • Conditional preload – based on a user action you make an educated guess where the user is headed next and preload accordingly. On search.yahoo.com you can see how some extra components are requested after you start typing in the input box.
  • Anticipated preload – preload in advance before launching a redesign. It often happens after a redesign that you hear: “The new site is cool, but it’s slower than before”. Part of the problem could be that the users were visiting your old site with a full cache, but the new one is always an empty cache experience. You can mitigate this side effect by preloading some components before you even launched the redesign. Your old site can use the time the browser is idle and request images and scripts that will be used by the new site

top

Reduce the Number of DOM Elements

tag: content

A complex page means more bytes to download and it also means slower DOM access in JavaScript. It makes a difference if you loop through 500 or 5000 DOM elements on the page when you want to add an event handler for example.

A high number of DOM elements can be a symptom that there’s something that should be improved with the markup of the page without necessarily removing content. Are you using nested tables for layout purposes? Are you throwing in more <div>s only to fix layout issues? Maybe there’s a better and more semantically correct way to do your markup.

A great help with layouts are the YUI CSS utilities: grids.css can help you with the overall layout, fonts.css and reset.css can help you strip away the browser’s defaults formatting. This is a chance to start fresh and think about your markup, for example use <div>s only when it makes sense semantically, and not because it renders a new line.

The number of DOM elements is easy to test, just type in Firebug’s console:
document.getElementsByTagName('*').length

And how many DOM elements are too many? Check other similar pages that have good markup. For example theYahoo! Home Page is a pretty busy page and still under 700 elements (HTML tags).

top

Split Components Across Domains

tag: content

Splitting components allows you to maximize parallel downloads. Make sure you’re using not more than 2-4 domains because of the DNS lookup penalty. For example, you can host your HTML and dynamic content onwww.example.org and split static components between static1.example.org andstatic2.example.org

For more information check “Maximizing Parallel Downloads in the Carpool Lane” by Tenni Theurer and Patty Chi.

top

Minimize the Number of iframes

tag: content

Iframes allow an HTML document to be inserted in the parent document. It’s important to understand how iframes work so they can be used effectively.

<iframe> pros:

  • Helps with slow third-party content like badges and ads
  • Security sandbox
  • Download scripts in parallel

<iframe> cons:

  • Costly even if blank
  • Blocks page onload
  • Non-semantic

top

No 404s

tag: content

HTTP requests are expensive so making an HTTP request and getting a useless response (i.e. 404 Not Found) is totally unnecessary and will slow down the user experience without any benefit.

Some sites have helpful 404s “Did you mean X?”, which is great for the user experience but also wastes server resources (like database, etc). Particularly bad is when the link to an external JavaScript is wrong and the result is a 404. First, this download will block parallel downloads. Next the browser may try to parse the 404 response body as if it were JavaScript code, trying to find something usable in it.

top

tag: cookie

HTTP cookies are used for a variety of reasons such as authentication and personalization. Information about cookies is exchanged in the HTTP headers between web servers and browsers. It’s important to keep the size of cookies as low as possible to minimize the impact on the user’s response time.

For more information check “When the Cookie Crumbles” by Tenni Theurer and Patty Chi. The take-home of this research:

 

  • Eliminate unnecessary cookies
  • Keep cookie sizes as low as possible to minimize the impact on the user response time
  • Be mindful of setting cookies at the appropriate domain level so other sub-domains are not affected
  • Set an Expires date appropriately. An earlier Expires date or none removes the cookie sooner, improving the user response time

top

tag: cookie

When the browser makes a request for a static image and sends cookies together with the request, the server doesn’t have any use for those cookies. So they only create network traffic for no good reason. You should make sure static components are requested with cookie-free requests. Create a subdomain and host all your static components there.

If your domain is www.example.org, you can host your static components on static.example.org. However, if you’ve already set cookies on the top-level domain example.org as opposed to www.example.org, then all the requests to static.example.org will include those cookies. In this case, you can buy a whole new domain, host your static components there, and keep this domain cookie-free. Yahoo! uses yimg.com, YouTube uses ytimg.com, Amazon uses images-amazon.com and so on.

Another benefit of hosting static components on a cookie-free domain is that some proxies might refuse to cache the components that are requested with cookies. On a related note, if you wonder if you should use example.org or www.example.org for your home page, consider the cookie impact. Omitting www leaves you no choice but to write cookies to *.example.org, so for performance reasons it’s best to use the www subdomain and write the cookies to that subdomain.

top

Minimize DOM Access

tag: javascript

Accessing DOM elements with JavaScript is slow so in order to have a more responsive page, you should:

  • Cache references to accessed elements
  • Update nodes “offline” and then add them to the tree
  • Avoid fixing layout with JavaScript

For more information check the YUI theatre’s “High Performance Ajax Applications” by Julien Lecomte.

top

Develop Smart Event Handlers

tag: javascript

Sometimes pages feel less responsive because of too many event handlers attached to different elements of the DOM tree which are then executed too often. That’s why using event delegation is a good approach. If you have 10 buttons inside a div, attach only one event handler to the div wrapper, instead of one handler for each button. Events bubble up so you’ll be able to catch the event and figure out which button it originated from.

You also don’t need to wait for the onload event in order to start doing something with the DOM tree. Often all you need is the element you want to access to be available in the tree. You don’t have to wait for all images to be downloaded. DOMContentLoaded is the event you might consider using instead of onload, but until it’s available in all browsers, you can use the YUI Event utility, which has an onAvailable method.

For more information check the YUI theatre’s “High Performance Ajax Applications” by Julien Lecomte.

top

tag: css

One of the previous best practices states that CSS should be at the top in order to allow for progressive rendering.

In IE @import behaves the same as using <link> at the bottom of the page, so it’s best not to use it.

top

Avoid Filters

tag: css

The IE-proprietary AlphaImageLoader filter aims to fix a problem with semi-transparent true color PNGs in IE versions < 7. The problem with this filter is that it blocks rendering and freezes the browser while the image is being downloaded. It also increases memory consumption and is applied per element, not per image, so the problem is multiplied.

The best approach is to avoid AlphaImageLoader completely and use gracefully degrading PNG8 instead, which are fine in IE. If you absolutely need AlphaImageLoader, use the underscore hack _filter as to not penalize your IE7+ users.

top

Optimize Images

tag: images

After a designer is done with creating the images for your web page, there are still some things you can try before you FTP those images to your web server.

  • You can check the GIFs and see if they are using a palette size corresponding to the number of colors in the image. Using imagemagick it’s easy to check using 
    identify -verbose image.gif 
    When you see an image useing 4 colors and a 256 color “slots” in the palette, there is room for improvement.
  • Try converting GIFs to PNGs and see if there is a saving. More often than not, there is. Developers often hesitate to use PNGs due to the limited support in browsers, but this is now a thing of the past. The only real problem is alpha-transparency in true color PNGs, but then again, GIFs are not true color and don’t support variable transparency either. So anything a GIF can do, a palette PNG (PNG8) can do too (except for animations). This simple imagemagick command results in totally safe-to-use PNGs:
    convert image.gif image.png 
    “All we are saying is: Give PiNG a Chance!”
  • Run pngcrush (or any other PNG optimizer tool) on all your PNGs. Example: 
    pngcrush image.png -rem alla -reduce -brute result.png
  • Run jpegtran on all your JPEGs. This tool does lossless JPEG operations such as rotation and can also be used to optimize and remove comments and other useless information (such as EXIF information) from your images. 
    jpegtran -copy none -optimize -perfect src.jpg dest.jpg

top

Optimize CSS Sprites

tag: images

  • Arranging the images in the sprite horizontally as opposed to vertically usually results in a smaller file size.
  • Combining similar colors in a sprite helps you keep the color count low, ideally under 256 colors so to fit in a PNG8.
  • “Be mobile-friendly” and don’t leave big gaps between the images in a sprite. This doesn’t affect the file size as much but requires less memory for the user agent to decompress the image into a pixel map. 100×100 image is 10 thousand pixels, where 1000×1000 is 1 million pixels

top

Don’t Scale Images in HTML

tag: images

Don’t use a bigger image than you need just because you can set the width and height in HTML. If you need 
<img width="100" height="100" src="mycat.jpg" alt="My Cat" /> 
then your image (mycat.jpg) should be 100x100px rather than a scaled down 500x500px image.

top

Make favicon.ico Small and Cacheable

tag: images

The favicon.ico is an image that stays in the root of your server. It’s a necessary evil because even if you don’t care about it the browser will still request it, so it’s better not to respond with a 404 Not Found. Also since it’s on the same server, cookies are sent every time it’s requested. This image also interferes with the download sequence, for example in IE when you request extra components in the onload, the favicon will be downloaded before these extra components.

So to mitigate the drawbacks of having a favicon.ico make sure:

  • It’s small, preferably under 1K.
  • Set Expires header with what you feel comfortable (since you cannot rename it if you decide to change it). You can probably safely set the Expires header a few months in the future. You can check the last modified date of your current favicon.ico to make an informed decision.

Imagemagick can help you create small favicons

top

Keep Components under 25K

tag: mobile

This restriction is related to the fact that iPhone won’t cache components bigger than 25K. Note that this is theuncompressed size. This is where minification is important because gzip alone may not be sufficient.

For more information check “Performance Research, Part 5: iPhone Cacheability – Making it Stick” by Wayne Shea and Tenni Theurer.

top

Pack Components into a Multipart Document

tag: mobile

Packing components into a multipart document is like an email with attachments, it helps you fetch several components with one HTTP request (remember: HTTP requests are expensive). When you use this technique, first check if the user agent supports it (iPhone does not).

Icon Themes from Wikimedia

The most commonly seen icon collections are free from WikiMedia!

http://commons.wikimedia.org/wiki/Category:Icons_themes

Web Icons by Mark James (FAMFAMFAM.com)

Appreciate for the work Mark has done, I guess most widely used web icons come from silk package on famfamfam.

Silk icon package is now released at version 1.3, last update at 12th March 2006.

Click here for a download.

Silk icon: Form Silk icon: Address Book Silk icon: Webcam Silk icon: Valid CSS Silk icon: Camera Silk icon: Date Silk icon: Exit Silk icon: CD Drive Silk icon: Feed Silk icon: Lorry/Truck Silk icon: Map Silk icon: Add picture Silk icon: Pictures Silk icon: Report Silk icon: Telephone

Let’s cheer for this!

Powered by WordPress. Theme: Motion by 85ideas.