New Immissions/Updates:
boundless - educate - edutalab - empatico - es-ebooks - es16 - fr16 - fsfiles - hesperian - solidaria - wikipediaforschools
- wikipediaforschoolses - wikipediaforschoolsfr - wikipediaforschoolspt - worldmap -

See also: Liber Liber - Libro Parlato - Liber Musica  - Manuzio -  Liber Liber ISO Files - Alphabetical Order - Multivolume ZIP Complete Archive - PDF Files - OGG Music Files -

PROJECT GUTENBERG HTML: Volume I - Volume II - Volume III - Volume IV - Volume V - Volume VI - Volume VII - Volume VIII - Volume IX

Ascolta ""Volevo solo fare un audiolibro"" su Spreaker.
CLASSICISTRANIERI HOME PAGE - YOUTUBE CHANNEL
Privacy Policy Cookie Policy Terms and Conditions
User:Xan.phung - Wikipedia, the free encyclopedia

User:Xan.phung

From Wikipedia, the free encyclopedia

This is a Wikipedia user page.

This is not an encyclopedia article. If you find this page on any site other than Wikipedia, you are viewing a mirror site. Be aware that the page may be outdated and that the user this page belongs to may have no personal affiliation with any site other than Wikipedia itself. The original page is located at http://en.wikipedia.org/wiki/User:Xan.phung.

Xan Phung
This user is male.
en This user is a native speaker of English.
This user comes from Australia.
pno-1 This user is a novice pianist.
This user is a physician.
This user eats chocolate.
This user drinks coffee.
This user drinks tea.

Below is just a scrapbook...



Contents

[edit] Display Morphing

Evidence: 3/3

Tags: Reference DOM Display


[edit] In A Blink

TODO LinkToAnImage.jpg

TODO Image caption - where's it from, with [[1]]

Dynamically change the display by modifying the Document Object Model.

What does "display" mean in this context?


[edit] Initial State

You're planning the evolution of a page's presentation.

Even though this is a higher-end book on patterns, I don't want the language to get too stale or academic. I think plain speaking is best; so I'd change this to something more along the lines of, "You're converting your current site from static pages to dynamic ones." Or something along those lines, that reads a little more "sitting across the way from the author", and not so much "listening to a lecture or talk from the author." Make sense?


[edit] Goal Story

Stuart is trying an online quiz which has a time limit for each question. A countdown box shows the number of seconds remaining. The value dynamically changes each second, and the colour shifts from green to red. As Stuart cannot figure the answer out within the time limit, a "Time's Up" message suddenly appears on the countdown area.

I like that you have a relate-able use-case, or scenario. But I feel when you start putting in people, it feels odd. For some reason, the current story just read really goofy to me; but when I read it like this:

You're creating on online quiz, where each question has a time limit. A countdown box shows the number of seconds remaining to answer the question. The countdown value changes each second, and the color shifts from green to red as time runs out. When the time is up, a "Time's Up" message appears in the countdown area.

-for some reason it feels fine.


[edit] Problem

How can you dynamically update display elements?

Let's make these active, like our cookbooks, instead of an actual question. So you could say, "You need to dynamically update elements on the page."


[edit] Forces

  • As the user's task and context changes, applications need to present different types of information.
  • As information the user is working on changes, information being displayed becomes stale.
  • Stale information is a great problem in conventional web applications. Because the information cannot change until it's out of date, you sometimes see disclaimers such as "May Be Out Of Date" or instructions like "Click to Refresh".
  • Submitting and redrawing the entire page breaks continuity.


[edit] Solution

Morph display elements by altering styles and values in the DOM, such as text and colour properties.

This is good, but I'm still not sure it draws enough relation to the Web. I realize it's implied, but I think you're going to actually have to talk more about HTML and the Web, to avoid this feeling more intellectual than it needs to. "display elements" is accurate, but so much more formal than "elements on a Web page".

The DOM makes it straightforward to dynamically change the appearance of page elements. Yes! "page elements" seems so much more focused on the Web. You get a reference to the DOM object for an object on your page, and simply change the appropriate property of that object. The Web browser immediately updates, without any page refresh. This allows you to morph any element on the page - headings, DIVs, images, and even the entire page body. For instance, the AjaxPatterns Assistive Search Demo morphs an image by first getting a reference to the image object according to its id attribute:

categoryImg = document.getElementById(imgId);

I'd like to see the actual HTML img element with an id attribute, and then see that same id used in the JavaScript. In other words, make this slightly more concrete. Then, if you want to generalize that, you can.

Then, the title and style of the image are altered:

categoryImg.title = "Category '" + categoryName + "'" + " probably has results";
categoryImg.style.backgroundColor="yellow";

Note the use here of a particularly important property of HTML objects: style. Because the element's style is exposed, anything that you'd specify in a CSS stylesheet is fair game for changing dynamically. Fonts, backgrounds, borders, and other properties can all be changed after a page has been loaded.

Another common task is changing text on the page. With DOM, this can actually be a surprisingly complex task, especially when markup's involved. Text in the DOM is actually represented by text nodes. Look at the following text, which seems to have some text freely roaming inside a <p> tag:

<p>Oh no! It's <strong>Mr. T!

What the DOM holds here is actually a paragraph node (p) with three children: a text node, a strong node, and then another text node. Another way to think about it is like this:

 
  <p><textnode>Oh no! It's </textnode><strong>Mr. T</strong><textnode>!</textnode></p>
  

As [/run/demo/displayMorphing/counterTextNode/ this counter example] demonstrates, simple text manipulation can get quite complex. To switch to a simple message involving an embedded strong tag, the following JavaScript is required:

 counterDiv.className = "statusOff";
 counterText = counterDiv.firstChild;
 counterText.nodeValue = counterValue;
 counterDiv.appendChild(document.createElement("br"));
 strongText = document.createElement("strong");
 strongText.appendChild(document.createTextNode("That's Odd!"));
 counterDiv.appendChild(strongText);

I felt like this above code was disconnected from the text. You were using the Mr. T example, so I'd stick with that, and show how to create that text. As it is, the className property, as well as the text being created here, come a bit out of the blue, and don't have much context.

Fortunately, there's a workaround, albeit one that is popular though somewhat controversial. The trick involves a property called innerHTML, which lets you change the entire HTML contained within an element. As demonstrated in [/run/demo/displayMorphing/counter/ this innerHTML equivalent of the counter example], you can change text like this:

counterDiv.innerHTML = counterValue; // counterValue is just a number, e.g. "5"

And, because any HTML can be used - not just plain text - the example also includes some markup to produce the exact same message as the DOM manipulation above:

 counterSpan.innerHTML = counterValue + "<br> <strong>That's Odd!</strong>";
 

I'd go back to the Mr. T example again here, as well.

IE also offers an innerText property to change the text in blissful ignorance of the related markup. But it's not portable, and not especially useful; the more portable innerHTML can achieve the same effect, and should be used instead of innerText. Do we have a tagging in place for notes? This above para would be a perfect note in our books.

Needless to say, innerHTML a powerful beast. It makes manipulation code easy to write and easy to understand. It's important to be aware that browser implementations vary, but with modern browsers, it's usually the best option for morphing text.


[edit] Decisions

[edit] What Sort of Morphing Should Be Used?

In modern browsers, the DOM is very powerful, so you can change just about anything you can see. Some typical transformations include:

[edit] Colour - style.backgroundColor, style.font-color

  • Change to a distinguishing colour to highlight an entire image or control, for example to draw the user's attention or indicate that an item has been selected.
  • Change to a distinguishing colour to highlight a range of text. This could be combined with a font colour change to indicate that text has been selected.
  • Change to a symbolic colour to denote some status change, e.g. "red" for stopped, "green" for running.
  • Provide an animation effect by fading or brightening a control. This can draw attention more effectively than a sudden change in the control itself.
  • Change colour according to a variable, e.g. the brightness of a blog posting's header varies according to the number of times it's been viewed.

[edit] Background Image - style.backgroundImage

  • Change the image to indicate the status of an element, e.g. a control might be stopped or started, or source code might be OK, have warnings, or have errors.

[edit] = Border Style - style.borderWidth, style.borderColor, style.borderColor

  • Highlight/Select an element to draw attention to it.
  • Indicate whether some text is editable or read-only.

[edit] = Font Style - stylefontSize, style.fontWeight, style.fontStyle

  • Change the size, weight, and/or slant of a font to highlight some text.

[edit] = Inner/Outer HTML- style.innerHTML, style.outerHTML

  • Change some text content.
  • Change arbitrary HTML, possibly changing the nested HTML of an element as well.mhm

[edit] = Image Source - src

  • Change an image's appearance by modifying the source URL.


[edit] Setting Colour and Other Style Properties Based on Dynamic Information

We have mostly discussed styles that are specified as literals in the code. That is, code sets the background colour of an element to yellow. Yellow (represented in code simply by the string "yellow") is a literal value, as are "bold" and "150%". In other words, an element is morphed after the page has loaded, but the the style it changes to has been predetermined by the programmer.

The great thing about DOM morphing, though, is that the style itself can be dynamic. It can vary according to any state information that is available. The online quiz provides a simple example of this behaviour, with the colour shifting according to remaining time (the time is changing as the quiz proceeds). Some websites have been offering a <a href="- http://codecubed.com/map.html">tag soup layout</a>, showing a mix of tags varying font size, with the more popular tags being physically larger. This is another example of dynamic content, as tags are always being reevaluated, and the display updated. In a more realistic context, dynamic styles can be particularly important in decision support systems such as online trading venues or climate monitors, or even e-commerce websites.

Colour is particularly useful in these contexts, because it is essentially a continuous variable; it can be used to represent a wide range of values at a fine degree of precision. Furthermore, it can be used orthogonally to any text on the element. Whereas the text might already be bolded and italicized as part of natural writing style or some othe site convention, the background and font colours can be changed according to a completely different variable (without affecting the formatting text itself).

How do you vary colour dynamically? When you set colour in the DOM, you can either provide a literal value (e.g. "orange") or an RGB value indicating the relative proportion of red, green, and blue. Clearly, it is the RGB format that lets us vary according to continuous variables. You can specify RGB in different ways--#1a5afa is equivalent to rgb(26, 90, 250), which in turn is roughly equivalent to rgb(10%, 35%, 98%)--and the percent value is best for most purposes since it abstracts away low-level details.

So let's say you have a screen element representing three variables--x, y, and z--and you want to change its colour depending on the value of these three values. The simplest solution is to represent x as red, y as green, and z as blue. Then, you set color percentages according to the values of each variable, in proportion possible range the variables can take. If x can vary between 0 and 1000, and it is currently set to 300, then the red percentage is set to 30% (300/1000).

Make the following a note Be sensible about scaling and what the user depicts as continous. If each element represented a country, and one of the variables represented its population, it might make sense to assume some kind of logarithmic scale. So India, with a population of about 1 billion, would be 100%, followed by Mexico, with 100 million, at 90%, followed by Hungary at 10 million at 80%, and so on.

TODO Diagram/formulae

I'm not sure that this section -- from here to my "end" comment -- really belongs here. It's a bit of a rabbit to chase, I think. RGB has a long tradition in programming because it is how screens actually display the details. So it's used in a low-level context and has bubbled all the way up to HTML and is the value provided to the DOM. But in fact, the RGB format has no monopoly on colour specification, and other formats may be more meaningful to humans. In particular, the best-known alternative is HSV - Hue, Saturation, Value (sometimes "Volume" or "Intensity"). What's useful about this format is the breakdown may be more apparent to humans. A user can probably detect countries become brighter as their population increases, even if the hues and saturations all vary in completely different ways. But the user will have a lot more difficulty detecting that the green component increases with population, when the variation in red and blue components is creating a virtually random mosaic of colours. HSV components are simply more natural to humans.

Using HSV or another format is certainly feasible - come up with an algorithm to convert the value into HSV, then just convert it to RGB to get a value which can be given to the DOM. There's various code around to convert to and from RGB (e.g. [2]).

RGB, HSV, and other prominent formats break colours down to three components, so it's natural to represent "trebles" - three-facet variables - in this way. However, it's certainly possible to represent fewer than three variables, or even more than three variables. One variable can be represented by mapping it to one component and fixing the others. Alternatively, you can come up with some formula as long as the mapping is isomorphic, i.e. that each value of the variable maps to a single colour and vice-versa. You can do similarly for two variables. Isomorphism is an impossibility for four or more variables, unless they are constrained to certain combinations. Use your creativity, and consider dropping a variable or two in order to make the picture clearer. end note

What's the benefit of all this ... is it just about the coolness factor of shifting color? (Not that there's anything wrong with that!) Actually, there's much more to it. If you've explored work by Edward Tufte and others on visualisation and data mining, you'll be aware that presentation can have a mind-bogglingly powerful effect on how data is interpreted, and how much important information can be picked up. Professionals in any discipline tend to become very apt at picking up patterns; in fact, there's good evidence that neural networks form in the brain to support recognition. Some traders, for instance, are prone to use charts and can detect subtle features which are invisible to others. The take-home point for designers is that this pattern recognition is only effective if your system supports it. That's why colours are so effective; you can slap hundreds of numbers on the screen, but those digits don't provide a very good basis for humans to pick up patterns. The number 1 and the number 7 may look quite similar amid a blur of text, but a brightness of 1 will look markedly different to a brightness of 7. It's just the way our brains our wired.

Of course, some websites are likely to induce a severe bout of colour-induced queaziness, as it's easy to go overboard with colours. It's worth noting that choosing colours and themes is virtually a discipline in its own right, involving considerations of graphic arts, neurophysiology, HCI, and electronics. It's no different when colours are varied dynamically; if anything, the right colour scheme can be critical. So you would be advised to consult an expert for any serious design work. The only advice I'll give here is to consider that some users may be colour-blind. That doesn't mean you should design in black-and-white--you should get the most out of colour because it's a wonderful device for most people-but your should be aware of what proportion of users will be affected, the extent to which they will miss out by not having colour, and how you might compensate for that loss of clarity.


[edit] Real-World Examples

[edit] Refactoring Illustration

The example is a countdown demo based on the story above.

TODO Walkthrough of applying this pattern, with code examples. Before and after states. The example should be related to the initial state and final scenario above.

[edit] Code Examples

None for this example.


[edit] <TODO Example 1, e.g. "Panning and Zooming (http://maps.google.com)">

 <TODO Describe example 1>


[edit] <TODO Example 2, e.g. "Panning and Zooming (http://maps.google.com)">

 <TODO Describe example 2>


[edit] Alternatives

[edit] Images Prepared Earlier

Sometimes, images are used instead of changing the style of an element programmatically. For instance, a row of tabs is sometimes created using an image for each selection. Using images in this way has the following advantages:

  • Requires less technical effort.
  • More portable, since CSS is newer and has greater discrepancies among browser implementations.
  • Can be more certain about what the user sees, due to implementation discrepancies.

However, there are some serious downsides:

  • Can lead to layout problems if actual image dimensions are used, and suffer from scaling degradation if resized.
  • Must anticipate all possible values in advance.
  • Increases download time.
  • Can only represent discrete data, whereas CSS style properties such as colours and thickness support essentially continuous ranges.

If images are being used to achieve graphics too fancy to be achieved by basic CSS properties, it's worth considering the background-image property, which gives you full control of arbitrary images, combined with the dynamic support of DOM morphing. For example, you can show a tab selection by changing the background images of the previous and current selections.


[edit] Related Patterns

Just because you can deal with frequent requests doesn't mean you should. If you've taken a modeless approach, these requests will not be showstoppers (literally). But they will nonetheless slow down loading of valuable information, making the application feel less responsive. Use SubmissionThrottling and ExplicitSubmission to cut down on the frequency of server requests.

It was very unclear to me what relevance this had to the page, other than by making a lot of assumptions. I think you need to more clearly relate this to the rest of the pattern.

[edit] Visual Metaphor

Display morphing is like using a magic paintbrush and eraser to change the appearance of anything on the page.

[edit] Want to Know More?



[edit] Page Rearrangement

Evidence: 3/3

Tags: Add Adjust Change Delete DOM Move Rearrange Restructure Remove


[edit] In A Blink

Diagram: Portlets/Portal


[edit] Goal Story

Doc is looking at an X-Ray image in an online diagnostics system. He mouse-clicks on the bottom-right region to reveal the time and date of the X-Ray. He then decides to annotate the image, so double-clicks on a critical point to pop up an editable "sticky note".


[edit] Problem

How can you dynamically restructure the page?


[edit] Forces

  • As the user's task and context changes, Ajax applications need to present different information.
  • Changes to page structure should be as smooth as possible.
  • Refreshing the page breaks continuity and also clears Javascript state.
  • Refreshing the page only allows for a discrete transition from one appearance to another - it's not possible to gradually fade or move an item.


[edit] Solution

Add, remove, move, and overlay elements by manipulating the DOM. The DOM is a hierarchical structure that defines how elements relate to one another. By adjusting the DOM structure, you can adjust where elements appear on the page. There are also critical CSS styles that affect page structure - you can affect these by manipulating DOM elements' style attributes.

Note: An online demo illustrates most of the code concepts throughout this Solution and the code snippets loosely follow from the demo.

Adding is normally achieved by introducing a new element as a child of an existing container element, such as body or div. For example, you can create a new div element and add it to a parent div.

 var message = document.createElement("span");
 $("container").appendChild(message);

Another way to insert an element is to append to the innerHTML property of the container element.

 $("container").innerHTML += "";

The opposite action is removing. By removing an element from the DOM, you take it off the display.

 $("container").removeChild($("message"));

As a variant of adding and removing, you can keep an element on the page, but toggle its visibility using CSS. There are two alternatives here. The visibility and display. The former will always preserve layout while the latter will cause the element to squeeze in and out when it's shown or hidden. So with visibility, it's like the element's still there but wearing invisible paint; whereas. This makes layout easy, because everything stays where it is, but it can be ugly to have a big patch of whitespace on the page. With the display style, it's more like the element's shoved into a temporary store to make room for other elements, which adds more layout complexity but is often more appropriate from a visual perspective. For visibility, the style property is switched between visible and hidden.

   $("message").style.visibility = "visible"; // Now you see me
   $("message").style.visibility = "hidden";  // Now you don't

display is actually used for more than just showing and hiding an element - it also defines how an element appears when it is visible. It's beyond the scope of these patterns to cover the options in detail, but the main options are block (the default for div elements) and inline (the default for span) elements. To show and hide using display, you switch its value between none and one of these values.

   $("message").style.display = "block"; // Now you see me, with block layout
   $("message").style.display = "none";  // Now you don't

You can move an element around in a couple of ways. Firstly, you can remove it from one place in the DOM and add it to another.

   container.removeChild(message);
   extraContainer.appendChild(message);

Second, you can adjust CSS properties. The most direct styles are left, right, top, and bottom, which define the co-ordinates of the element in question.

   message.style.top = "150px";
   message.style.left = "50px";

But what is the (0,0) point these co-ordinates are relative to? The precise meaning of these styles is modulated by the positioning element.

  • If static, the co-ordinates have no effect - the element is positioned according to standard CSS layout rules.
  • If relative, the co-ordinates are relative to the position it would normally be positioned under standard CSS layout rules - they suggest how far its displaced.
  • If absolute, the co-ordinates are relative to the top-left of the entire document.
  • If fixed, the co-ordinates are relative to the top-left of the browser viewport (the portion of the browser window that shows the page). Even if you scroll the document, the element will stick right on the same point on the screen as before.

Positioning is set as a standard CSS style.

   message.style.positioning = "150px";

Finally, you can also overlay elements. An HTML document has "2.5" dimensions, which is to say that it has a limited notion of depth in which elements are able to overlap each other. The critical CSS style here is zIndex, which signifies an element's depth. When two elements occupy the same portion of the page, the element which will appear in front is that with the higher zIndex. The zIndex is not a real depth, because all that matters is the relative ordering of zIndex values. Against a zIndex of zero, it makes no difference whether an element's zIndex is 1, 10, or 100. The default value is 0 and a zIndex can take on any positive or negative value.

   message.style.zIndex = -100; // Behind of all elements with default zIndex
   message.style.zIndex = 100;  // In front of all elements with default zIndex


[edit] Decisions

[edit] Which positioning style to use?

A single application can combine different types of positioning. In most cases, static - the default positioning - suffices. Non-static positioning is most commonly used with more free-floating elements, such as Sprites or elements suitable for Drag-And-Drop. For non-static positioning, relative positioning tends to be the most useful, because it allows you to move the element around within a defined container.

[edit] How will you protect against memory leaks?

Continuously adding and removing elements leads to the risk of memory leaks. Javascript is supposed to perform garbage collection, automatically removing variables that are no longer referenced. Some general guidelines:

  • Avoid global variables where possible. Local variables go out of scope, so if a local variable points to a deleted element, the element will disappear. But if a global variable points to such an element, it will stick around.
  • Explicitly nullify references. Where you're sure a variable will no longer need to use the value it references, set the variable to null.
  • Avoid or destroy circular references You can sometimes end up with a circular structure that no-one's using anymore, but sticks in memory because garbage collection isn't smart enough to remove it (it concludes each object is still relevant because at least one reference exists). This can happen, for instance, when an object's event handler refers back to the object (see "Javascript Closures" for more details).
  • Test, test, test It may not be fun, but you need to stress test your application under different browser environments, monitoring memory usage to ensure it's stable.


[edit] Real-World Examples

[edit] Tadalist

Ta-da List (Screencast available) allows users to manage TODO items. Each TODO item is a phrase like "Finish homework" and the user can add, remove, and rearrange items.

[edit] = Super Maryo World

Super Maryo World is an outright video game, a clone of the classic Super Mario Bros game implemented with standard Ajax technologies. The manipulation of game characters and fixtures illustrates how elements can rapidly be added, removed, and moved around.

[edit] Kiko

Kiko is a visual Ajax calendar application. You can add and remove appointments, drag them around to change the time, and stretch them out to increase duration.


[edit] Code Examples

[edit] Basic Wiki Demo

The Basic Wiki Demo periodically polls for a fresh list of messages to display. Each time the list is retrieved, it removes all existing messages and adds the new list. The containing element is called "messages" and removing all messages runs a loop across each of its children.

 while ($("messages").hasChildNodes()) {
   $("messages").removeChild($("messages").firstChild);
 }

Each message is used to create a new textarea element (among other things), which is then added to a new div, which in turn is added to the messages container.

 for (var i=0; i<wikiMessages.length; i++) {
   var messageArea = document.createElement("textarea");
   ...
   messageDiv = document.createElement("div");
   ...
   messageDiv.appendChild(messageArea);
   ...
   $("messages").appendChild(messageDiv);
   ...
 }


[edit] Alternatives

[edit] Related Patterns

The Display Morphing pattern addresses an element's appearance; this pattern talks about elements' location, visibility, and "height above the page" (z-index). Together, the two patterns cover any


[edit] Visual Metaphor

Page rearrangement is like adding, removing, and rearranging Post-It notes on a whiteboard.


[edit] Want to Know More?

[edit] Web Service

Tags: API, HTTP, Microcontent, REST, RPC, Share, SOAP, Platform

[edit] In A Blink

Provide well-defined HTTP API.

[edit] Goal Story

In a financial forecasting app, Tracy runs an algorithm to forecast the next four interest rates. The browser script uploads the parameters to an "Interest Rate Forcaster" Web Service, which eventually outputs a concise list of future rates, free of any HTML markup: 4.5, 3.4, 4.0, 4.1.

[edit] Problem

What will Ajax Apps call on the server?

[edit] Forces

  • Ajax Apps can access the server directly and require well-scoped services instead of the conventional-style scripts that output an entire HTML page.
  • Many organisations like to expose their functionality and data for third-parties to access. The API needs to be clean and easy to use.

[edit] Solution

Expose server-side functionality as fine-grained, easy-to-use, Web Services. "Web Service" is an overloaded term, and the Ajax Patterns use a fairly broad definition:

  • A Web Service is a standard HTTP service.
  • A Web Service has a well-defined, consistent, interface - input and output assumptions, message formats, exception-handling are all clearly understood by its developers and ideally documented in some manner.
  • A Web Service accepts fine-grained input parameters and outputs fine-grained responses, such as an XML document, a simple string, or a snippet of HTML. If it outputs an entire HTML document, it's probably not a Web Service.

Under this definition, there is considerable flexibility involved in creating a Web Service:

  • A Web Service might be open to the public, might be protected by a secure authentication mechanism, or might be hidden from the public by a firewall.
  • A Web Service might be built by a tool or hand-written.
  • A Web Service might use messages according to any protocol, such as SOAP, or might use custom messages.

Deciding on all these things is the main task involved in creating a Web Service.

Web Services are the latest in a long tradition of distributed computing, with technologies like SunRPC, CORBA and EJB along the way. They've arisen quite separately from Ajax, as a general-purpose way to expose services to interested clients. These clients are usually standalone processes with no user-interface as well as desktop clients and web server scripts. Until recently, they were irrelevant to anything inside a web browser, which could only communicate with services capable of returning an entire HTML page. But thanks to remoting technologies, Javascript can now make use of such services. Indeed, a clean Web Service is actually the best thing for a remoting technologies to be calling - it makes the call easier and having a fine-grained response is good for the response handler. Thus, there's a strong synergy between Ajax and Web Services.

Here's the PHP code for a one-liner web service to perform a sum (imaginative, I know):

   <?
     echo $_GET["figure1"] + $_GET["figure2"];
   ?>

You can try the service by entering http://ajaxify.com/run/xmlHttpRequestCall/sumGet.phtml?figure1=5&figure2=10 in your browser's address bar. The entire response will be "15". As the example shows, a web service's response can be as simple as a single value. Other times, it might be some HTML, but even then, it will usually only be a snippet rather than an entire page. For example, the above service could be refactored to show the result as a heading.

    <?
      echo "<h1>" . ($_GET["figure1"] + $_GET["figure2"]) . "</h1>";
    ?>

A collection of related services like these forms an HTTP-based API that exposes server functionality. Most Ajax apps will access the API from the browser via the “XMLHttpRequest Call” and alternative remoting techniques. In addition, third-parties can access Web Services and use them for their own applications, web or not.

[edit] Decisions

This pattern only introduces the concept of Web Services. There are a number of decisions you need to make when designing such services for your own application. Will the service output XML to be processed in the browser, HTML to be displayed directly, or some other format? How will the URLs look? What sort of input will it take?

[edit] How will the Web Service be used?

The single most important decision about a web service, as with any form of API, is to know how it will be used. In particular, will it be available for third-parties as a generic interface to your system, or are you developing it purely for an Ajax browser app to access. If the latter, then you might want to practice feature-driven development and let the browser app drive web service design - see “Simulation Service”

If there are third-parties involved, it becomes a balancing act between their needs and the needs of your browser app. Since they will probably be accessing your server from more powerful environments than the average web browser, you might need to add some redundancy to support each type of client. The API for third-parties might be more general, whereas the browser sometimes needs an API that knows something about the application. In an extreme case, it tracks application state and outputs the actual HTML to be shown to the user.

[edit] How will you prevent third-party usage?

Web services are seen as having a kind of synergy with Ajax, as Ajax Apps can talk to the same interface already being offered to third-party clients. However, sometimes you want the web service to be restricted to third parties. That's going to be difficult on the public web, since pretty much anything a browser can access will also be available to third-party clients. So, you might have spent ages designing a nice, clean, web service to make browser scripting as painless as possible, only to discover that third parties are reaping the benefits. There's no magic bullet here, but here are a few suggestions:

  • Require users to be logged in, with an authenticated email address, in order for the application to make use of web services. You can then use cookies or upload unique session IDs to authenticate the user.
  • Consider using a system like Captcha (TODO link), where the user is forced to perform a challenge that should be about impossible for an automated script to perform. You might require a challenge be solved once an hour, say.
  • Use standard filtering techniques, like blocking certain IP addresses and refusing requests if they arrive too frequently.
  • Use obfuscation and encryption. This idea is based on a tricky little anti-spam plugin for Wordpress, TODO. By outputting a customised piece of obfuscated Javascript that's used to decrypt web service content, you force any client programmer to do two things: first, hook into a Javascript engine in order to execute the code; second, consume resources performing the decryption. Neither are impossible, but they do make the task less appealing. Unfortunately, they also break the clean nature of web services, though you should be able to abstract the messy details with a suitable Javascript API.

[edit] Real-World Examples

Just about any Ajax App has some kind of web service. In these examples, we'll look at a public API designed for third-party usage, then at a couple of services only accessed from a corresponding Ajax browser script.

[edit] Technorati API

Technorati exposes its search capability for third-party use. To perform a query, you run an HTTP GET on the following URL: http://api.technorati.com/search?key=1234&query=serendipity, where 1234 is your personal technorati key and serendipity is the search query. What you'll get back is an XML document listing the results.

[edit] NetVibes

Netvibes is an Ajax portal which gets it content from several Web Services. To get weather data, the browser invokes a service like this:

   http://netvibes.com/xmlProxy.php?url=http%3A//xoap.weather.com/weather/local/USNY0996%3Fcc%3D*%26unit%3Dd%26dayf%3D4


The service is actually a proxy because it passes through to a real weather service at weather.com. As the URL shows, you can specify parameters such as location (USDNYC0996), units (d), number of days ahead (4). The output is an XML document without any HTML markup.

   <weather ver="2.0">
     <head>
       <locale>en_US</locale>
       <form>MEDIUM</form>
       ...
     </head>
     <dayf>
       ...
       <hi>N/A</hi>
       <low>46</low>
       <sunr>7:18 AM</sunr>
       <suns>4:34 PM</suns>
       ...
     </dayf>
     ...
   </weather>

[edit] Wish-O-Matic

Alexander Kirk's Wish-O-Matic provides Amazon recommendations. The browser posts the following information to a service at http://alexander.kirk.at/wish-o-matic/search=grand&catalog=Books&page=1&locale=US&_=:

   search=grand&catalog=Books&page=1&locale=US&_= 

The service then responds with a list of books. Unlike the previous service, the data is pure HTML, ready for immediate display in the browser.

    <table><tr><td style="width: 500px">
        <b>Author:</b> Kevin Roderick<br/><b>Title:</b> Wilshire Boulevard: The
        Grand Concourse of Los Angeles<br/><b>ISBN:</b> 1883318556<br/><a
        href="javascript:void(add_item('1883318556', 'Wilshire Boulevard: The Grand
        Concourse of Los Angeles',
        'http://images.amazon.com/images/P/1883318556.01._SCTHUMBZZZ_.jpg'))">My
        friend likes this item!</a></td>
        ...
        </td></tr>
        </table>
        <br/>
        <a href="javascript:void(prev_page())">< prev</a> |
        <a href="javascript:void(next_page())">next ></a>

[edit] Code Example

[edit] Ajax Caller

AjaxCaller is a Javascript HTTP client, explained in the Code Example for XMLHttpRequest Call. Here, we'll look at a web service used in the testAjaxCaller application, a very simple service that just echoes its input.

httpLogger begins by outputting request method (e.g. "GET" or "POST"), its own request URL, and CGI variables present in the URL and body.

    echo "<p>Request Method: $requestMethod</p>";
    echo "<p>Request URL: ".$_SERVER['REQUEST_URI']."</p>";
    echo "<p>Vars in URL: ".print_r($_GET, TRUE)."</p>";
    echo "<p>Vars in Body: ".print_r($_POST, TRUE)."</p>";

A separate function is used to read the body, which can also be outputted with a regular print or echo statement.

   function readBody() {
     $body="";
     $putdata = fopen("php://input", "r");
     while ($block = fread($putdata, 1024)) {
       $body = $body.$block;
     }
     fclose($putdata);
     return $body;
   }

[edit] Related Patterns

All the patterns explain various strategies for designing Web Services with clean, maintainable, interfaces. The remaining patterns explain how the browser invokes a server-side Web Service.

[edit] Cross-Domain Proxy

The main point of this pattern is to guide on designing your own Web Services. However, there are times when your server script will need to call on an external web service, as explained in “Cross-Domain Proxy”.

[edit] Simulation Service

A “Simulation Service” is a "dummy" service that produces canned responses, a useful device while developing the browser-side of an Ajax App.

[edit] Service Test

The nice thing about Web Services, compared to most aspects of web development, is that it's easy to write automated tests, as described in “Service Test”.



[edit] XMLHttpRequest

Evidence: 3/3

Tags: Call Callback Download Live Query Remoting RemoteScripting Upload XMLHttpRequest


[edit] In A Blink

Diagram: Script in Web page issuing XMLHttpRequest Call to server


[edit] Goal Story

Reta's purchasing some items from a wholesaler's website. Each time she adds an item to the shopping cart, the website issues an XMLHttpRequest to save the latest cart contents. There's no form submission, so the item is added instantaneously, which saves Reta time as well as helping her understand what's going on.


[edit] Problem

How can the browser communicate with the server?


[edit] Forces

  • Ajax applications require browser-server communication. User-generated information must be uploaded and new server information must be downloaded.
  • Because Ajax applications should have a smooth and continuous feel, browser-server communication must be unobtrusive.
  • Because Ajax applications should be highly responsive, browser-server communication must be fast.
  • The conventional means of browser-server communication - hyperlinks and form submissions - are obtrusive and slow. They are obtrusive because they require a page refresh and slow because the response must contain an entire page.
  • The conventional means of browser-server communication - hyperlinks and form submissions - make browser-side programming tedious because they force page refreshes, which in turn resets Javascript state. Any state worth retaining must therefore be uploaded to the server and sent back to the browser.


[edit] Solution

Use XMLHttpRequest objects for browser-server communication. XMLHttpRequest is a Javascript class capable of calling the server and capturing its response. Just like a browser or a command-line web client, an XMLHttpRequest issues standard HTTP requests and grabs responses. Note: The code shown throughout this demo is closely based on an online companion demo.

To begin with, let's think about where the call goes. An XMLHttpRequest calls on a particular URL, possibly passing in some arguments on the URL or the message body. But what sort of thing resides at the URL?

That sort of thing's not very useful for XMLHttpRequest Calls, which tend to be quite specific in nature, e.g. a query like "What's the user's account balance?" or a submission like "The user wants to trade 5000 units". There's no need to output an entire page of HTML because it's the script that will decide how to change the page, if at all. To the contrary, output should be small and easily parseable. Thus, input is constrained to the bare minimum required by the script, and the output is a concise return value, like "$20,510" or "Trade Accepted". In these patterns, I refer to a resource like this as a "web service", or simply, a "service". A collection of related web services is sometimes called a "web API". There are a number of patterns about Web Services, but the important thing here is that a web service has relatively simple input and output, more like that of a function call than a conventional web application.

Here's a one-liner "web service" that's running at Ajaxify, where all the demos reside:

  <?php
    echo $_GET["figure1"] + $_GET["figure2"];
  ?>

You can try the service by entering http://ajaxify.com/run/xmlHtttpRequestCall/sumGet.phtml?figure1=5&figure2=10 in your browser's address bar. The entire response will be "15".

Now the fun part: calling the service from Javascript. In the following script, XMLHttpRequest achieves the same thing as the browser just did, but programatically.

 var xhReq = new XMLHttpRequest();
 xhReq.open("GET", "sumGet.phtml?figure1=5&figure2=10", false);
 xhReq.send(null);
 var serverResponse = xhReq.responseText;
 alert(serverResponse); // Shows "15"

The sequence begins by creating a new instance of XMLHttpRequest. xhReq.open() then prepares a call on the test service, sumGet.phtml (the code's running from the same path, so the domain and path need not be qualified). The "GET" signifies the request method to be used. The "false" option says the call is synchronous, meaning that the code will hang until a response comes back. The send command completes the request. Because the call is synchronous, the result is ready on the next line. The XMLHttpRequest object has saved the response from the server and you can access it with the responseText field.

The above example hopefully illustrates that the fundamental technology is pretty simple. However, be aware that it's a very basic usage, not yet fit for production. Fundamental questions remain:

  • How can XMLHttpRequests be created?
  • How does an asynchronous call work?
  • How does our code detect errors?
  • What if the service requires a "POST" or "PUT" request rather than a "GET"?
  • Which URLs can be accessed?
  • How can you deal with XML responses?
  • What's the API?

The following sections address these questions and show how the code above needs to be modified.

[edit] Creating XMLHttpRequest Objects

In most browsers, XMLHttpRequest is a standard Javascript class, so you just create a new instance XMLHttpRequest. However, Microsoft were the inventors of XMLHttpRequest, and until IE7, IE only offered as an ActiveX object. To make things even more fun, there are different versions of that object. The following code shows a factory function that works on all browsers that support XMLHttpRequest.

 function createXMLHttpRequest() {
   try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) {}
   try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {}
   try { return new XMLHttpRequest(); } catch(e) {}
   alert("XMLHttpRequest not supported");
   return null;
 }
 ...
 var xhReq = createXMLHttpRequest();

With a function like this, creation is easy for all browsers. The basic functionality and API are pretty consisent across browsers, but be sure to test carefully as there are a few subtle implementation differences in some browsers.

You can also reuse an XMLHttpRequest. Just make sure it's either delivered a full response or it's been reset using the abort() method described below.

[edit] Asynchronous Calls

Just above, I explained that under synchronous mode, "the code will hang until a response comes back". Hardened readers might have writhed uncomfortably at the thought. We all know that some requests take a long time to process, and some don't come back at all. Pity the web user who's stuck waiting for a server that's buried in an infinite loop.

In practice, XMLHttpRequest Calls should almost always be asynchronous. That means the browser and the user can continue working on other things while waiting for a response to come back. How will you know when the response is ready? The XMLHttpRequest's readyState always reflects the current point in the call's lifecycle. When the object is born, it's at 0. After open() has been called, it's 1. This continues until the response is back, at which point the value is 4.

So, to catch the response, you need to watch for a readyState of 4. That's an easy chore, because XMLHttpRequest fires readystatechange events. You can declare a callback function using the onreadystatechange field. The callback will then receive all state changes. The states below 4 aren't especially useful and are somewhat inconsistent across browser types anyway. So most of the time, all we're interested in is state 4, when the response is complete.

Based on all that, here's an asynchronous version of the code shown earlier.

 var xhReq = createXMLHttpRequest();
 xhReq.open("GET", "sumGet.phtml?figure1=5&figure2=10", true);
 xhReq.onreadystatechange = onSumResponse;
 xhReq.send(null);
 ...
 function onSumResponse() {
   if (xhReq.readyState != 4)  { return; }
   var serverResponse = xhReq.responseText;
   ...
 }

As shown, you declare the callback method in XMLHttpRequest's onreadystatechange property. In addition, the third argument of open() is now true. This argument is actually called the "asynchronous flag", which explains why we're now setting it to true. The callback function, "onSumResponse", is registered using onreadystatechange and contains a guard clause to ensure the readyState is 4 before any processing can occur. At that point, we have the full response in responseText.

Javascript also supports "closures" - a form of anonymous function - which suggests a more concise boilerpolate structure for asynchronous calls:

 var xhReq = createXMLHttpRequest();
 xhReq.open("get", "sumget.phtml?figure1=10&figure2=20", true);
 xhReq.onreadystatechange = function() {
   if (xhReq.readyState != 4)  { return; }
   var serverResponse = xhReq.responseText;
   ...
 };
 xhReq.send(null);

A warning: if you look carefully at the callback mechanism, you might notice the potential for a subtle, but serious, bug. The problem arises when the same instance of XMLHttpRequest is simultaneously used for different calls. If Call 2 is issued while the object is still waiting for the response of Call 1, what will the callback function receive? In fact, it's even possible the callback function itself is changed before the call comes back. There are ways to deal with this problem, and they're the topic of the Call Tracking pattern.

[edit] Detecting Errors

Sometimes, a request doesn't come back as you expected it, maybe not at all. You scripted the call wrong, or there's a bug in the server, or some part of the infrastructure just screwed up. Thinking asynchronously is the first step to dealing with these problems, because at least your application isn't blocked. But you need to do more than that.

To detect a server error, you can check the response status using XMLHttpRequest's status flag. This is just a standard HTTP code. For example, if the resource is missing, XMLHttpRequest.status will take on the famous "404" value. In most cases, you can assume anything other than 200 is an error situation. This suggests adding a new check to the callback function of the previous section.

 xhReq.onreadystatechange = function() {
   if (xhReq.readyState != 4)  { return; }
   *** if (xhReq.status != 200)  {
     // Handle error, e.g. Display error message on page
     return;
   } ***
   var serverResponse = xhReq.responseText;
   ...
 };

That's great if the browser knows a problem occurred, but sometimes the request will be lost forever. Thus, you usually want some kind of timeout mechanism as well. Using Event Scheduling, establish a timer to track the session. If the request takes too long, the timer will kick in and you can then handle the error. XMLHttpRequest has an abort() function which you should also invoke in a timeout situation. Here's a code example:

   var xhReq = createXMLHttpRequest();
   xhReq.open("get", "infiniteLoop.phtml", true); // Server stuck in a loop.
   *** var requestTimer = setTimeout(function() {
       xhReq.abort();
       // Handle timeout situation, e.g. Retry or inform user.
     }, MAXIMUM_WAITING_TIME); ***
   xhReq.onreadystatechange = function() {
     if (xhReq.readyState != 4)  { return; }
     ** clearTimeout(requestTimer); **
     if (xhReq.status != 200)  {
       // Handle error, e.g. Display error message on page
       return;
     }
     var serverResponse = xhReq.responseText;
     ...
   };

Compared to the previous example, a timer has been introduced. The onreadystatechange() callback function will clear the timer once it receives the full response (even if that response happens to be erroneous). In the absence of this clearance, the timer will fire, and in this case, the setTimeout sequence stipulates that abort() will be called and some recovery action can then take place.

[edit] Handling POSTs and Other Request Types

So far, we've only looked at a simple GET request - pass in a URL and get a response. As dicussed in the RESTful_Service pattern, it's important to work with other request types as well. "POST", for example, is suited to calls which affect server state or upload substantial quantities of data. To illustrate, let's now create a new service, sumPostGeneric.phtml, that does the same thing as postGet.phtml but with a POST message. It's called "generic" because it reads the full message body text, as opposed to a CGI-style form submission. In this case, it expects a body like "Calculate this sum: 5+6", and returns the sum value.

 <?php

   $body = readBody();
   ereg("Calculate this sum: ([0-9]+)\+([0-9]+)", $body, $groups);
   echo $groups[1] + $groups[2];

   // A PHP method to read arbitrary POST body content.
   function readBody() {
     $body="";
     $putData = fopen("php://input", "r");
     while ($block = fread($putData, 1024)) {
       $body = $body.$block;
     }
     fclose($putData);
     return $body;
   }

 ?>

To POST an arbitrary body, we give XMLHttpRequest a request type of "POST" and pass the body in as an argument to send(). Note that with "GET" queries, the send() argument is null as there's no body content.

   var xhreq = createxmlhttprequest();
   ** xhreq.open("post", "sumPostGeneric.phtml", true); **
   xhreq.onreadystatechange = function() {
     if (xhreq.readystate != 4) { return; }
     var serverresponse = xhreq.responsetext;
     ...
   };
   ** xhreq.send("calculate this sum: 5+6"); **

Quite often, though, you'll be posting key-value pairs, so you want the message to look as if it were submitted from a POST-based form. You'd do that because it's more standard, and server-side libraries make it easy to write web services that accept standard form data. The service below, sumPostForm.php shows how PHP makes light work of such submissions, and the same is true for most languages.

  <?php
    echo $_POST["figure1"] + $_POST["figure2"];
  ?>

For the browser script to make a CGI-style upload, two additional steps are required. First, declare the style in a "Content-Type" header; as the example below shows, XMLHttpRequest lets you directly set request headers. The Second step is to make the body a set of name-value pairs.

   var xhreq = createxmlhttprequest();
   xhreq.open("post", "sumPostForm.phtml", true);
   ** xhReq.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); **
   xhreq.onreadystatechange = function() {
     if (xhreq.readystate != 4) { return; }
     var serverresponse = xhreq.responsetext;
     ...
   };
   ** xhreq.send("figure1=5&figure2=6"); **

"GET" and "POST" are virtually ubiquitous, but RESTful Service points out there's a time and place for other request methods too, such as "PUT" and "DELETE". You don't have to do anything special with those other methods; just set the request type in the open() call, and send() an appropriate body (the item you're putting in the case of "PUT"; a null argument in the case of "GET).

[edit] Accessible URLs

On learning about XMLHttpRequest, a common reaction is to start dreaming up an interface that pulls in content from popular websites and mashes it altogether to into one big Web 2.0 souffle. Unfortunately, it's not so simple because of a key security rule imposed by all major browsers: XMLHttpRequest can only access content from the originating server. If your application lives at http://ajax.shop/admin, then your XMLHttpRequest objects can happily read http://ajax.shop/admin/products.html and http://ajax.shop/products/contents.html, might be able to read http://books.ajax.shop/contents.html (there's not much documentation on subdomain access, making this a portability concern at best), and definitely won't have access to http://google.com.

The constraint will be familiar to developers of Java applets and Flash, where the same kind of restriction has always been in place. It's there to prevent several kinds of abuse, such as a malicious script uploading confidential user data to an external server. Some have questioned whether it's overkill, but the rule's likely to be sticking around.

So how, then, do all those Ajax mashup sites work? The answer is that all cross-domain transfers run through the originating server, which acts as a kind of proxy - or tunnel - allowing XMLHttpRequests to communicate with external domains. Cross-Domain Proxy elaborates on the pattern.

  • correction: Cross-domain xhr is possible with changes to the client. In IE adding the domain as a trusted site will permit cross-domain requests to the trusted sites. In Firefox/Mozilla/Gecko changing signed.applets.codebase_principal_support to true (in about:config) as well as using "netscape.security.PrivilegeManager.enablePrivilege('UniversalBrowserRead');" when the request is created will allow cross-domain requests. In most cases a confirmation warns the client about the cross-domain access when the request is attempted.

[edit] XML Responses

So far, the discussion has swiftly ignored the big elephant in the room: XML. XMLHttpRequest, as its name suggests, was originally designed with XML in mind. As we've seen here, it will actually accept any kind of response, so what's special about XML? So far, responses have been accessed via the responseText field, but XMLHttpRequest also offers an alternative accessor, responseXML. If the response is XML, responseXML represents the result of parsing it and storing it as a DOM object.

The DOM Manipulation patterns have already illustrated how Javascript supports manipulation of DOM objects. In those patterns, we were only interested in one particular DOM object, that representing the current page. But you can manipulate any other DOM object just as easily. Thus, it's sometimes convenient to have a web service output XML content and manipulate the corresponding DOM object.

The prerequisite here is a web service that outputs valid XML. There are many libraries and frameworks around for automatically generating XML from databases, code objects, files, or elsewhere. But don't think you have to start learning some fancy XML library in order to create XML web services, because it's fairly easy to hand-code them too. The service just needs to output an XML Content-type header followed by the entire XML document. Here's an XML version of the sum service shown earlier - it outputs an XML document containing the input figures as well as the sum result.

  <?php

    header("Content-Type: text/xml");
    $sum = $_GET["figure1"] + $_GET["figure2"];
    echo <<< END_OF_FILE
  <sum>
    <inputs>
      <figure id="1">{$_GET["figure1"]}</figure>
      <figure id="2">{$_GET["figure2"]}</figure>
    </inputs>
    <outputs>$sum</outputs>
  </sum>
  END_OF_FILE

  ?>

The call sequence is the same as before, but the callback function now extracts the result using responseXML. It then has a first-class DOM object and can interrogate it using the standard DOM API.

   var xhReq = createXMLHttpRequest();
   xhReq.open("GET", "sumXML.phtml?figure1=10&figure2=20", true);
   xhReq.onreadystatechange = function() {
     if (xhReq.readyState != 4) { return; }
     ** xml = xhReq.responseXML;
     var figure1 = xml.getElementsByTagName("figure")[0].firstChild.nodeValue;
     var figure2 = xml.getElementsByTagName("figure")[1].firstChild.nodeValue;
     var sum = xml.getElementsByTagName("outputs")[0].firstChild.nodeValue; **
     ...
   };
   xhReq.send(null);
 });

The name "XMLHttpRequest" relates to its two primary functions: handling HTTP requests and converting XML responses. The former function is critical and the latter is best considered a bonus. There are certainly good applications for XML responses - see XML_Message, XML_Data_Island, and Browser-Side_XSLT - but keep in mind that XML is not a requirement of Ajax systems.

You can also upload XML. In this case, XMLHttpRequest doesn't offer any special XML functionality; you just send the XML message as you would any other message, and with an appropriate request type (e.g. "POST" or "PUT"). For the sake of the receiving web service, the Javascript should generally declare the XML content type in a request header.

 xhReq.setRequestHeader('Content-Type',  "text/xml");

[edit] The XMLHttpRequest API: A Summary

We've looked at how to achieve typical tasks with XMLHttpRequest, and now here's a quick summary of its properties and methods. It's based on a well-cited Apple Developer Connection article which outlines an API that's supported by IE5+, the Mozilla family (which should include all versions of Firefox), and Safari 1.2+.

XMLHttpRequest has the following properties:

  • onreadystatechange: The callback function that's notified of state changes. (See "Asynchronous Calls" above.)
  • readyState: State within the request cycle. (See "Asynchronous Calls" above.)
  • responseText: The response from the server, as a String.
  • responseXML: The response from the server, as a Document Object Model, provided that the response was valid XML.
  • status: HTTP response code received from the server, e.g "404".
  • statusText: HTTP response code description received from the server, e.g. "Not Found".

And these are XMLHttpRequest's methods:

  • abort(): Stops the request and resets its readyState back to zero. (See "Detecting Errors" above.)
  • getAllResponseHeaders(): Returns a string of all response headers, separated by a newline as in the original message.
  • getResponseHeader(headerField): Returns the value for a particular header field.
  • open(requestMethod, url, asynchronousFlag, username, password): Prepares XMLHttpRequest. Only the first two parameters are required. username and password can be used for authentication. (See Solution introduction above.)
  • send(bodyContent): Sends the message along with specified body content (null if no body content is to be sent, e.g. for GET requests). (See Solution introduction above.)
  • setRequestHeader(headerField, headerValue): Sets a request header. (See "Handling POSTs and Other Request Types" above).


[edit] Decisions

[edit] What kind of content will web services provide?

As mentioned in the solution, XML is not the only kind of content that XMLHttpRequest can deal with. As long as you can parse the message in Javascript, there are various response types possible. The Web Services patterns highlight a number of response types, including HTML, XML, JSON, and plain-text.

[edit] How will caching be controlled?

It's possible that an XMLHttpRequest response will be cached by the browser. Sometimes, that's what you want and sometimes it's not, so you need to exert some control over caching.

With cache control, we're talking about "GET" based requests. Use "GET" for read-only queries and other request types for operations that affect server state. If you use "POST" to get information, that information won't be cached. Likewise, if you use "GET" to change state, you run the risk that the call won't always reach the server, because the browser will cache the call locally. There are other reasons to follow this advice too; see RESTful Service.

Often, you want to suppress caching in order to get the latest server information, in which case a few techniques are relevant. Since browsers and servers vary in their behaviour, the standard advice is spread the net as wide as possible, by combining all of these techniques:

  • You can add a header to the request:
 xhReq.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2005 00:00:00 GMT");
 var url = "sum.phtml?figure1=5&figure2=1&timestamp=" + new Date().getTime();
 header("Expires: Sat, 1 Jan 2005 00:00:00 GMT");
 header("Last-Modified: ".gmdate( "D, d M Y H:i:s")."GMT");
 header("Cache-Control: no-cache, must-revalidate");
 header("Pragma: no-cache");

Caching is sometimes a good thing when the service is time-consuming to call and when it's unlikely to have changed recently. To encourage caching, you can adjust the above advice, i.e. set the "If-Modified-Since" and "Expires" headers to a suitable time in the future. In addition, a good approach for smaller data is to cache it yourself, using a Javascript data structure. Browser-Side Cache explains how.

[edit] How will you deal with errors?

The section on error detection left open the question of what to do once we discover a server timeout or non-standard error code. There are three possible reactions:

  • Try again: Retry a few times before giving up.
  • Inform the user: Tell the user what's gone wrong and what the consequences are. For instance, inform them that their data hasn't been submitted and they should try again in a few minutes.
  • Do nothing: Sometimes, you have the luxury of ignoring the response (or lack thereof). That might be because you're issuing a low-importance Fire-And-Forget, where you're uploading some data without waiting for any response.


[edit] Real-World Examples

[edit] Lace Chat

Brett Stimmerman's Lace Chat is an Ajax chat application that uses XMLHttpRequest in two ways: to upload messages you type, and to download all the latest messages from the server.

[edit] Ajaxium

AJAXIUM ajax for asp.net - replaces ASP.NET postbacks by AJAX (acts as universal AJAX container for ASP.NET pages and controls). Search engines and old browsers friendly due to automatic degradation to the classic ASP.NET if needed.

[edit] Backbase

Backbase offer a Demo RSS Reader, which uses XMLHttpRequest to pull down titles of recent articles. When you click on one of those titles, a new XMLHttpRequest will pull down the entire content.

[edit] Anyterm

Phil Endecott's Anyterm is an Ajax terminal emulator allowing you to run telnet or SSH within the browser. It uses XMLHttpRequest Calls to upload keystrokes and download the latest screen state.

[edit] Mint

Mint is a website statistics package. Site owners include Mint javascript on each page, which quietly inspects the user's browser settings and uploads them using an XMLHttpRequest.


[edit] Code Examples

[edit] AjaxCaller

The example referenced in the Solution above covers most typical XMLHttpRequest usage. In practice, many people adopt a wrapper library rather than calling XMLHttpRequest directly. That's the approach taken with all of the Ajax Patterns demos, which use a library called ajaxCaller.js that was developed in parallel to the demos themselves. It's a fairly basic library, but offers a simple interface for the functionality that's typically required of XMLHttpRequest. In this section, I'll introduce the library by showing a few usages within the AjaxCaller Test Demo.

The simplest call is getting some plain text: just specify the URL and the callback function.

 ajaxCaller.getPlainText(url, onResponse);

For all calls, the callback function always takes three arguments. The first argument is the result, either a string or a DOM object. The second is an associative array mapping header fields to header values. The third is a "calling context". You can think of this as an optional value that travels alongside the request and the corresponding response, returned to the callback function in exactly the same form as you passed it in when the call was issued. Usually it holds information about the call, e.g. if the call was made to send off a purchase order, the calling context might contain the item that was ordered. Then, ajaxCaller will pass the context into the callback function, which can mark the item as successfully ordered. In reality, the calling context is not actually passed to and from the server; ajaxCaller actually keeps it locally and tracks each pending request. If this all sounds a bit complicated, check out Call Tracking.

The callback function, then, looks like this (assuming ajaxCaller was given the name, "onResponse"):

 function onResponse(text, headers, callingContext) {
   // Use text (a string), headers, and callingContext
 }

And since it's only the text that's used most of the time, the function can be declared in a simpler form:

 function onResponse(text) {
   // Use text (a String)
 }

getPlainText() is on of four commonly-used methods. The others are getXML(), postForPlainText(), postForXML(). Together, these four cover both common request types ("GET" and "POST") and both response types (text and XML).

 ajaxCaller.getXML(url, callbackFunction);
 ajaxCaller.getPlainText(url, callbackFunction, callbackContext);
 ajaxCaller.postForPlainText(url, callbackFunction, callbackContext);

There are also a number of more general methods, for example get() provides a more flexible "GET" requests. In addition to a URL and a callback function, get() lets you specify some variables to be appended to the URL, a flag to indicate whether the response is XML, and the callingContext as discussed above.

 var vars = {
   flavour: "chocolate",
   topping: "nuts"
 };
 ajaxCaller.get("httpLogger.php", vars, onResponse, false, "iceCreamRequest");

There are general operations for other request types too. postVars() creates a CGI-style POST upload and postBody() creates an arbitrary-body POST upload. There are similar methods for other request types, e.g. PUT, TRACE, OPTIONS, DELETE, HEAD.


[edit] Alternatives

This section lists all alternatives I'm aware of. Some are more limited than others, and some of the more obscure techniques are included for the sake of completeness, and also in the hope they might spark a few ideas.

[edit] IFrame Call

IFrame Call is the main alternative to XMLHttpRequest. Like XMLHttpRequest, it allows for remote calls using GET, POST, and other request types. Here's a summary of XMLHttpRequest Call benefits over IFrame Calls:

  • XMLHttpRequest is cleaner, because it's designed specifically for web remoting. The API is easier to use, especially when it comes to non-GET request types (e.g. POST).
  • XMLHttpRequest offers functionality not available to IFrame, such as the ability to abort a call and track the call's state. This can have important performance implications.
  • XMLHttpRequest is typically faster, especially with shorter responses.
  • XMLHttpRequest parses XML in a simple, portable, manner; IFrame is unrelated to XML.
  • On those browsers that do support XMLHttpRequest, the API is more consistent than that of IFrame.
  • XMLHttpRequest is rapidly gaining the virtue of widespread familiarity.

For all these reasons, XMLHttpRequest should be the default choice. However, there are some specialised situations where IFrame Calls are superior. To wit:

  • IFrame works on older browsers, many of which don't support XMLHttpRequest.
  • IFrame has some benefits for browser history and bookmarkability, at least for IE, as discussed in Unique URLs.
  • XMLHttpRequest on IE won't work if security measures disable ActiveX, a policy sometimes enforced in the enterprise.
  • IFrame respects appropriate changes to document.domain, XMLHttpRequest does not. (In other words, if you have content on different named servers within your domain -- www.example.com, cgi.example.com, archive.example.com, etc.example.com -- XMLHttpRequest will only work for one of those hosts. IFrame can be made to work by putting <script type="text/javascript">document.domain='example.com'</script> into the response body.) THAT SAID: you can use an iframe to host an XMLHttpRequest context.

[edit] HTTP Streaming

HTTP Streaming also allows for web remoting, and unlike XMLHttpRequest, the connection remains open. Functionally, the key advantage over XMLHttpRequest is that the server can continuously push new information to the browser. From a resource perspective, streaming is good insofar as there's less connections, but has major scaleability problems as it's not feasible to keep open huge amounts of connections and server-side scripts.

[edit] Richer Plugin

The Richer Plugin pattern discusses Java, Flash, and other plugins and extensions. These components often have permission to call the server programmatically. and in some cases, can be used as proxies available Javascript code.

[edit] On-Demand Javascript

On-Demand Javascript describes a couple of ways to download Javascript on the fly. One involves XMLHttpRequests, but the other is an alternative transport mechanism. It works by adding a script element to the document body, which has the effect of automatically pulling down a named Javascript file.

Unlike XMLHttpRequests (or IFrames), browsers impose no domain restrictions on <script> elements. Unlike XMLHttpRequests, there seems to be no good way to detect HTTP errors.

[edit] Import XML Document

There's a technique specifically for retrieving XML documents which uses similar technology to XMLHttpRequest. Peter-Paul Koch described the technique in 2000, and recently suggested it can now be written off.

[edit] Image-Cookie Call

Brent Ashley's RSLite library is an unusual alternative based on images and cookies. An image's source property is set to the service URL, which is simply a means of invoking the service. The service writes its response into one or more cookies, which will then be accessible from the Javascript once the call has completed.

Note also that an image has three relevant event handlers (onload, onerror, onabort). The onload/onerror event handlers can, between them, represent a boolean result -- and sometimes this is adequate (for example: when all you need is confirmation that your information has been saved). While XMLHttpRequest also supports this, many alternatives (especially those which support cross-domain requests) do not. Browsers impose no domain restrictions on images.

[edit] Stylesheet Call

Another way to get at server state is to dynamically change a CSS stylesheet. Just like setting a new Javascript or image source, you set a stylesheet's href property to point to the web service. In Julien Lamarre's demo of this technique, the web service actually outputs a stylesheet, and the response is embedded in the URL of a background-image property!

[edit] 204 Response

When the server outputs a 204 "No Content" code, the browser won't actually refresh. Using Javascript, you can dynamically submit a form to that such a service if you want to perform a "fire-and-forget" call.


[edit] Related Patterns

[edit] Visual Metaphor

An XMLHttpRequest Call is like a quick side conversation - it doesn't have to interrupt the main flow of user-browser activity.


[edit] Want to Know More?



[edit] IFrame Call

Evidence: 1/3

Tags: Call Callback Download Frame IFrame Live Query Remoting RemoteScripting Upload


[edit] In A Blink

Diagram: Circle from page (button trigger) to script to server to iframe to script to body


[edit] Goal Story

Stuart's ordering a car online and the price is always synchronised to reflect his choice. This happens because of a hidden IFrame. Each time he changes something, the browser populates a form in the IFrame and submits it. The IFrame soon contains the new price, which the browser copies into the visible display.


[edit] Problem

How can the browser communicate with the server?


[edit] Forces

Refer to "Forces" for XMLHttpRequest.


[edit] Solution

Use IFrames for browser-server communication. IFrames are page-like elements that can be embedded in other pages. They have their own source URL, distinct from their parent page, and the source URL can change dynamically. IFrame Calls work by making the IFrame point to a URL we're interested in, then reading the IFrame's contents when the new content has loaded.

To begin with, you need an iframe in the initial HTML, and an event handler registered to catch the onload event. (It would be less obtrusive to register the handler with Javascript, but due to certain browser "features", that doesn't always work.)

  <iframe id='iFrame' onload='onIFrameLoad();'></iframe>                        

We're using the frame as a data repository, not as a user-interface, so some CSS is used to hide the frame. (The more natural choice would be display: none, but as an experimental demo shows, browser strangeness makes it infeasible.)

 #iFrame {
   visibility: hidden;
   height: 1px;
 }

On the server, we're initially going to use the web service discussed in XMLHttpRequest Call: sumGet.phtml. To call the service, we'll change the IFrame's source.

   $("iFrame").src = "sumGet.phtml?figure1=5&figure2=1";

We already registered an onload handler in the initial HTML. The handler will be called when the new content has loaded, and at that stage, the IFrame's body will reflect the content we're after. The following code calls a function, extractIFrameBody(), which extracts the body in a portable manner (based on [3]).

 function onIFrameLoad() {
   var serverResponse = extractIFrameBody($("iFrame")).innerHTML;
   $("response").innerHTML = serverResponse;
 }

That covers the GET call. To illustrate POSTing, we'll invoke the sumPost.phtml service, also discussed in XMLHttpRequest Call. POST calls are a bit more complicated because we can't just change the source. Instead, we dynamically inject a form into the IFrame and submit it. In this example, the arguments ('5' and '2') are hard-coded, but they could easily be scripted by manipulating the input elements.

   var iFrameBody = extractIFrameBody($("iFrame"));
   iFrameBody.innerHTML ==
      "<form action='sumPostForm.phtml' method='POST'>" +
        "<input type='text' name='figure1' value='5'>" +
        "<input type='text' name='figure2' value='2'>" +
      "</form>";
   var form = iFrameBody.firstChild;
   form.submit();

As with conventional form submission, the IFrame will be refreshed with the output of the form recipient. Thus, the previous onLoad handler will work fine.

Another approach with IFrames is to have the server output some Javascript which alters the parent, or calls a function defined in the parent. The code will automatically be called when the page loads, so there's no need for an onload handler. Unfortunately, this approach creates substantial coupling between the server and browser. In contrast, note how the examples above used completely generic web services - the services only know how to add numbers together and nothing about the calling context. That's possible because all behaviour was encapsulated in the onload() function.

As Call Tracking discusses, there are problems - and suitable workarounds - with simultaneous XMLHttpRequest Calls, and the same is true for IFrames. First, you need to create a separate IFrame for each parallel call - you don't want to change an IFrame's source while it's waiting for a response. Second, you need to investigate and test limitations, because the application can grind to a halt if too many calls are outstanding.

Make no mistake: IFrame Call is a pure hack. IFrames were never created to facilitate browser-server communication. It's likely the technique will slowly fade away as XMLHttpRequest gains popularity and as older browsers are replaced. Nevertheless, it does retain a couple of advantages over XMLHttpRequest Call, as discussed in the Alternatives section of that pattern.



Online references are made to XMLHttpRequest Call: for sumGet.phtml and sumPost.phtml. However there is no sumPost.phtml in the online version. The book on page 109 refers to a sumPost.phtml in sction XMLHttpRequest Call: but unfortunately it is not there. The closest to it, on page 97, is a file called "sumPostForm.php". I'm too new here to know if there is an errata of some sort. Perhaps somebody could fix this and do the appropriate followup.


I'm not sure what the function "extractIFrameBody" does. I did a google search and only find this string on your site. I follow the link to apple and there's nothing about it there. Perhaps the function could be copied to this site?


Sorry for the confusion. I don't have the details to hand, but I'd say sumPost.phtml was refactored from sumPostForm.php, so they are likely the same file. extractIFrameBody is specific to the AjaxPatterns code, so you won't find it as a generic function on Google. I think you'll find it in the lib/php directory - try a recursive grep (grep -R /ajaxpatterns/path extractIFrameBody --Mahemoff 07:07, 6 December 2006 (EST)


If you want to access the body of the iframe object you can do it this way (I'm assuming the "extractIFrameBody" method does something similar):

   var iFrameBody = document.getElementById("iFrame").contentWindow.document.body;
   iFrameBody.innerHTML = "whatever html code you want";

--124.120.185.115 03:40, 9 March 2007 (EST)

[edit] Decisions

[edit] Real-World Examples

[edit] Google Maps

Google Maps, perhaps the most famous Ajax application, lets you navigate through a map of the world. Panning and zooming require Web Remoting in order to grab new image and location data from the server, and all of this uses IFrames. It's not clear why, but Google also use XMLHttpRequest, for downloading XSLT stylesheets (a technique described in Browser-Side XSLT).

[edit] PXL8 Demo

Michele Tranquilli has an excellent tutorial on IFrames with embedded examples.

[edit] JSRS

Brent Ashley's JSRS is a Web Remoting library based on IFrames. It's of interest as perhaps the earliest Web Remoting framework, with the initial version having been released back in 2000 and continues to be used today. A basic demo is also available.

[edit] HTMLHttpRequest

Angus Turnbull's HTMLHttpRequest is another Web Remoting library. It checks if XMLHttpRequest is supported, and if not, falls back to IFrame. The tool is documented in the Code Examples section of Cross-Browser Component.


[edit] Refactoring Illustration

The Basic Sum Demo uses XMLHttpRequest, and there's a version available that's been refactored to use IFrame. There's no walkthrough here as it's very similar to the GET component of the demo discussed in the Solution above.


[edit] Alternatives

[edit] XMLHttpRequest Call

XMLHttpRequest Call is the natural alternative to IFrame Call. The Alternatives section of that pattern compares the two approaches.

[edit] Frame Call

Before IFrames, there were plain old frames. As a user-interface concept, they've had a few public relations problems over the years, but they do at least provide a means of remote scripting similar to IFrame Calls (even if it was accidental). Since IFrames are now widely supported, it's unlikely you'll ever need to use frames for remote scripting (or anything else, for that matter).


[edit] Related Patterns

[edit] Visual Metaphor

A hidden IFrame is like having an assistant you can delegate queries to.


[edit] Want to Know More?

thgh



[edit] On-Demand Javascript

Evidence: 2/3

Tags: Behaviour Bootstrap Dynamic Javascript LazyLoading OnDemand ScriptTag


[edit] In A Blink

XML being pulled from the server using JavaScript after the page has loaded.


[edit] Technical Story

Devi's concerned about initial user experience, because her new website requires lots of Javascript. She refactors the application, extracting out a few Javascript modules. The initial page load is now much faster, because only a bare-bones initialisation module is pulled down, with the other modules loaded only when they're needed.


[edit] Problem

How can you deploy lots of Javascript code?


[edit] Forces

  • Ajaxian applications make heavy use of Javascript. Richer browser behaviour means more script code must be downloaded.
  • Downloading Javascript has a performance impact. Interaction cannot fully begin until all initial Javascript has been loaded.
  • Bandwidth is also a concern with heavy Javascript content. Often, not all Javascript that's loaded is actually used, leading to a waste of bandwidth.


[edit] Solution

Download Javascript as and when required, instead of downloading it all on page load. This is a "lazy loading" approach applied to Javascript, and has several benefits:

  • Initial page load is faster.
  • Overall bandwidth usage is less, since only Javascript that's required is used.
  • Deployment is easier as the code takes care of pulling in Javascript, with the coder only ensuring enough is there to kick the process off.
  • You can produce snippets of Javascript on the fly - effectively sending back a kind of "behaviour message" advising the browser on its next action. This is a variant of the core pattern described at the end of this section.
  • You can bypass the standard "same-domain" policy that normally necessitates a Cross-Domain Proxy. Provided a server exposes a segment of valid Javascript, it can be extracted by the browser.

Conventionally, best practice has been to include Javascript unobtrusively - by including it in one or more script tags.

  <html>
    <head>
      <script type="text/javascript" src="search.js"></script>
      <script type="text/javascript" src="validation.js"></script>
      <script type="text/javascript" src="visuals"></script>
    </head>
    ...
  </html>

On-Demand Javascript builds on this approach to suggest just a minimal initialisation module in the initial HTML:

  <html>
    <head>
      <script type="text/javascript" src="init.js"></script>
    </head>
    ...
  </html>

The initialisation module declares whatever behaviour is required to start up the page and perhaps enough to cover typical usage. In addition, it must perform a bootstrapping function, pulling down new Javascript on demand.

So how do you load Javascript after the page has loaded? One way is with an XMLHttpRequest Call. The browser can easily accept a Javascript response and evaluate it. Any code not inside a function will be executed immediately. To add new functionality for later on, the Javascript can add directly to the current window or to an object that's already known to exist. For example, the following can be sent:

 self.changePassword = function(oldPassword, newpassword) {
   ...
 }

The callback function just needs to treat the response as plain-text and pass it to eval(). A warning: here again, asynchronicity rears its ugly head. You can't assume the new function will be available immediately. So don't do this:

 if (!self.changePassword) {
   requestPasswordModuleFromServer();
 }
 changePassword(old, new) // Won't work the first time because
                          // changePassword's not loaded yet.

You either need to make a synchronous call to the server, explicitly make the call in the response handler, or use a closure callback - either manually or using a continuation transformer such as Narrative JavaScript or djax. Note that you can't just add a loop to keep checking for the new function, due to the single-threaded nature of Javascript.

A less obtrusive way to do the same thing involves DOM Manipulation. Simply insert a suitable script element into the DOM and the Javascript will be loaded. It can be a child of either the head or body and its src property must be pointing to a Javascript URL. There are several differences with XMLHttpRequest:

  • The Javascript will automatically be evaluated in much the same way as Javascript linked in the static HTML is evaluated when the <script> tag is first encountered. You can declare a bare function or variable without adding it to the window.
  • You can load Javascript from external domains.
  • The URL points to a specific Javascript resource. With XMLHttpRequest, there's more flexibility: you can, for example, send several Javascript snippets inside different XML nodes.
  • The DOM is affected in a different way. Even if the behaviour is transient, the script will be added to the DOM, whereas it would disappear after an XMLHttpRequest callback eval'd it.

(Watch for compatibility. See http://www.howtocreate.co.uk/loadingExternalData.html)

There's a variant of the pattern I'll call Javascript Response in reference to the HTML Response pattern. The standard pattern refers to the situation where you're downloading a new set of functions, classes, and variables. In reality, you're just adding new members to the DOM - some other code will then invoke it. In Javascript Response, you grab a snippet of Javascript and eval it - the script is not wrapped inside a function (although it may define some functions that it calls). Under this variant, then, the browser is asking the browser what to do next. Whereas in the standard pattern, the browser knows what to do, but not how to do it.

One important application is with JSON Messages. Being standard Javascript, they can be downloaded using On-Demand Javascript, which is a convenient way to transfer semantic content, i.e. a self-contained object. Because Javascript can be pulled from third-party sources, this provides a mechanism for transferring semantic content from an external server into the browser, and without a Cross-Domain Proxy. Some Web Services, designed for public browser-based usage, therefore output JSON Messages.

If you are exposing a JSON API from your server for external browsers to use, you should be aware of some important security concerns. Any JSON-based services that rely on cookie-based authentication are vulnerable. Otherwise, you allow a malicious website to make cross-domain calls on behalf of the user, to a JSON service. This is unlike [[[XMLHttpRequest Call]]s, which can't cross domains and IFrame Calls, where external domain details can't be accessed by the parent application. With JSON, there is no such protection...by redefining the ways of Javascript, such as rewriting the Array constructor, hackers from evil.com can submit an authenticated JSON call to bank.com, and then do what they like with the response, e.g. upload it back to their servers. To prevent this: (a) ensure JSON is used to serve only public data, i.e. that which requires no cookie-based authentication; OR (b) if your web app relies on calling cookie-based JSON services from your own server, make the services POST-based, so that they can't be invoked from script tag inclusion (which always uses GET). This means your browser script will have to issue a POST-based XMLHttpRequest Call and eval() the response. Fortunately, a browser script from an external server won't be allowed to issue the same request.

[edit] Real-World Examples

[edit] Authenteo

Authenteo is an integrated JavaScript framework and web content management system. Authenteo uses a persistent object model where the logic for applications is defined in the JavaScript within the persisted objects. The persisted objects as well as the JavaScript functions within are loaded on demand using the JSPON data format (an extension of JSON). Authenteo uses Narrative JavaScript for continuations, and by utilizing continuations, Authenteo can do true inline on-demand loading of JavaScript and data without making synchronous (locking) remote calls. On-demand loading is done transparently as data is needed. Authenteo uses a # symbol to denote persisted fields (used by the precompiler), so the following is an example of referencing a persisted object and function with Authenteo:

  myobj.#anotherObject.#render();

Authenteo will transparently retrieve the object referred to by "anotherObject" and the render function if they have not been loaded then execution will continue inline. Authenteo also utilizes prediction for object transfers.

[edit] MapBuilder

MapBuilder is a framework for mapping websites. It uses On-Demand Javascript to reduce the amount of code being downloaded. The application is based on a Model-View-Controller paradigm. Model information is declared in an XML file, along with Javascript required to load corresponding widgets. When the page starts up, only the required Javascript is downloaded, instead of the entire code base. This is therefore a partial implementation of this pattern; it does ensure that only the minimal subset of Javascript is ever used, but it doesn't load pieces of the code in a lazy, on-demand, style.

[edit] Dojo Package System

Dojo is a comprehensive framework aiming to simplify Javascript development. As such, it provides a number of scripts, of which an individual project might only use a subset. To manage the scripts, there's a Java-like pacakage system, which lets you pull in new Javascript as required. You only need include a single Javascript file:

  dojo.hostenv.moduleLoaded("dojo.js.*");

You then pull in further packages on demand like this:

  dojo.hostenv.moduleLoaded("dojo.aDojoPackage.*");

will download all relevant Javascript modules under dojo.aDojoPackage. The precise set of modules is defined by the dojo.aDojoPackage author, and can be made dependent on the calling environment (whether browser or command-line).

[edit] JSAN Import System

The JavaScript Archive Network (JSAN) is an online repository of scripts. As well, it includes a library for importing Javascript modules, also a convention similar to Java. The following call:

 JSAN.use('Module.To.Include');

is mapped to Module/To/Include.js. JSAN.includePath defines all the possible top-level directories where this path can reside. If the includePath is ["/", "/js"], JSAN will look for /Module/To/Include and /js/Module/To/Include.

[edit] twoBirds full AJAX

This AJAX system is providing on-demand web-object functionality. In addition to load the .js file on demand, it also loads .html.tpl and .css files dynamically into the browser cache structure. A given twoBirds web-object has an init() function, that checks whether all files needed are present in the cache, if not waits for them to be loaded, and then calculates the html code of the element to be included in the DOM model. The following call:

tb.element.show( 'target div id', '<module>', '<element>' );

will load and display an element on the webpage in the given DIV element. twoBirds includes a modular site structure based on module directories.

Since a twoBirds web-object may include other twoBirds web objects, the site overall complexity level is unlimited, still allowing quality management of the code.

twoBirds is not yet out in the open, see [4] for an early prototype. Contact the programmer if you want to get a copy of the current stage of development.


[edit] Refactoring Illustration

[edit] Introducing On-Demand Javascript to the Wiki Demo

In the Basic wiki Demo, all Javascript is downloaded at once. But many times, users will only read from the wiki - why download the code to write to it? So this demo refactors to [On-Demand Javascript], in three stages:

1. The uploadMessage function is extracted to a second Javascript file, upload.js. 2. A further refactoring pulls in the upload.js file only if and when an upload occurs. This version uses the DOM-morphing approach. 3. In yet another refactoring, the DOM-morphing approach is substituted with the XMLHttpRequest approach.

[edit] Separate Javascript: Extracting upload.js

In the first refactoring, the upload function is simply moved to a separate Javascript file. The initial HTML includes the new file.

  <script type="text/javascript" src="wiki.js"></script>
  *** <script type="text/javascript" src="upload.js"></script> ***

The new upload.js now contains the uploadMessage function. Reflecting the separation, a couple of parameters were introduced to decouple the function from the main wiki script.

 function uploadMessages(pendingMessages, messageResponseHandler) {
   ...
 }

The calling code is almost the same as before:

 uploadMessages(pendingMessages, onMessagesLoaded);

We've thus far gained a bit of modularity, but no boost to performance, since both files must be downloaded on startup.

[edit] DOM-Based On-Demand Javascript

This refactoring and the next both achieve On-Demand Javascript, in different ways. Neither approach is intrinsically superior - each approach has its own characteristics, as discussed in the Solution above.

With On-Demand Javascript, the upload.js is no longer required on startup, so it's reference no longer appears in the initial HTML, leaving just the main module, wiki.js.

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

To this file, a new function has been added to download the script. To avoid downloading the script multiple times, a check is made against uploadMessages, the function that's downloaded on demand. It adds a script element to the document's head, initialised with the upload.js URL and a standard Javascript type attribute. This new DOM element is at the heart of DOM-based On-Demand Javascript.

 function ensureUploadScriptIsLoaded() {
   if (self.uploadMessages) { // Already exists
     return;
   }
   var head = document.getElementsByTagName("head")[0];
   script = document.createElement('script');
   script.id = 'uploadScript';
   script.type = 'text/javascript';
   script.src = "upload.js";
   head.appendChild(script)
 }

The calling script just has to call this function. However, as mentioned in the Solution, it's downloaded asynchronously, so a check must be made here too.

 ensureUploadScriptIsLoaded();
 if (self.uploadMessages) { // If not loaded yet, wait for next sync
   uploadMessages(pendingMessages, onMessagesLoaded);
   ....

In most cases, the test will actually fail the first time, because the download function will return before the script's been downloaded. But in this particular application, that doesn't actually matter much - the whole synchronisation sequence is run every five seconds anyway. If the upload function isn't there yet, it should be there in five seconds, and that's fine for our purposes. If desired, we could be more aggressive - for instance, the script could be downloaded as soon as an edit begins.

[edit] XMLHttpRequest-Based On-Demand Javascript

The DOM manipulation technique is refactored here to use an XMLHttpRequest Call instead, where a server call retrieves upload.js and eval's it. As with the previous version, upload.js no longer appears in the initial HTML. Also, the invocation of ensureUploadScriptIsLoaded remains as before. The initial script - wiki.js differs only in the implementation of the Javascript retrieval. The text response is simply passed to eval.

 function ensureUploadScriptIsLoaded() {
   if (self.uploadMessages) { // Already exists
     return;
   }
   ** ajaxCaller.getPlainText("upload.js", function(jsText) { eval(jsText); }); **
 }

The actual Javascript must also change. If it simply defined a global function like before, i.e.:

 function(pendingMessages, messageResponseHandler) {
   ...
 }

the function would die when the evaluation completes. Instead, we can achieve the same affect by altering the browser window object (self).

 self.uploadMessages = function(pendingMessages, messageResponseHandler) {
   ...
 }


[edit] Related Patterns

[edit] HTML Response

The Javascript Response variant of this pattern is a companion to HTML Response and follows a similar server-centric philosophy in which it is the server-side that pushes instructions to the browser.

[edit] Predictive Fetch

Apply Predictive Fetch to On-Demand Javascript by downloading Javascript when you anticipate it will soon be required.

[edit] Browser-Side Cache

The nature of Javascript deployment means that a form of browser-side caching occurs naturally. That is, the results of calls are usually retained by virtue of the fact they automatically add new functions to the DOM.

[edit] Multi-Stage Download

On-Demand Javascript is a variant of Multi-Stage Download. The emphasis in Multi-Stage Download is on downloading semantic and display content rather than Javascripts In addition, that pattern is more about downloading according to a pre-scheduled sequence rather than downloading on demand.


[edit] Visual Metaphor

If you watch someone learning to use a new product, their experience will usually mimic On-Demand Javascript. They'll read the bare minimum, if anything, and get started playing with it. When they get stuck, they'll consult the instructions, but only enough to get over the next hurdle.


[edit] Want to Know More?

Dynamic data using the DOM and Remote Scripting A tutorial by Thomas Brattli.




[edit] User Action

Evidence: 3/3

Tags: Action Change Click Control DHTML DOM Events Keyboard Mouse Move Type Widget


[edit] In A Blink

arrow from mouse being clicked into widget into script.


[edit] Goal Story

Pam is booking a trip on the corporate travel planner. She sees a form with the usual fields, and clicks on location. Suddenly, a list of cities fades in beside the form, and Pam selects Paris. Beside the city list, a second list appears, this one showing approved hotels. Pam chooses the Hilton and both lists disappear. Pam's pleased to proced with the destination on the updated form, which now reads, "Paris Hilton".


[edit] Problem

How can the application respond to events such as user actions?


[edit] Forces

  • A rich application allows users to interact with it, frequently and in different ways.
  • Responses to user actions must be as quick as possible, so as to streamline performance, keep user attention, and clarify the effects of user actions.
  • Using form submissions as the only response to user activity is slow and limits interactivity.


[edit] Solution

Use Javascript event handlers to respond to user actions.

The essence of Ajax is rich browser-based interaction, and DOM events are the technology that make it happen. DOM objects can register event handlers, functions that are notified when events occurred. This callback model should be familiar to anyone who's worked with desktop GUI frameworks.

Let's say you want to run the following function when the user clicks a shutdown button.

 function shutdown() {
   if (confirm("Are you sure you want to shutdown?")) {
     postShutdownMessageToServer();
   }
 }

The simplest way to set this up is to declare a button with an onclick event handler.

 <button id="quit" onclick="shutdown();" />Quit</button> 

Now, the web browser will arrange for shutdown() to be called whenever the button is clicked. However, we can improve on this, because the above declaration mixes Javascript with HTML. Cleaner to just declare the button and deal with an event handler in a separate Javascript file. Since always want this behaviour, we should declare it as soon as the page loads. To run something when the page loads, we can use another event handler, one triggered by browser activity rather than a user action: onload.

 <button id="quit" />Quit</button>
 [.html]

 window.onload = function() {
   quit.onclick = shutdown;
 }
 [.js]

Instead of referencing a callback function, it's sometimes convenient to define the callback as a closure (anonymous function), as in:

 *** quit.onclick = function() { ***
   if (confirm("Are you sure you want to shutdown?")) {
     postShutdownMessageToServer();
     quit.onclick=null;
   }
 }

Registering events with Javascript, as opposed to in HTML tags, is an example of unobtrusive Javascript, the separation of Javascript and HTML. And defining the event handler in Javascript also has another benefit: you can dynamically redefine actions in response to system events. For example, our shutdown() method could include some code to avoid a double-shutdown.

 function shutdown() {
   if (confirm("Are you sure you want to shutdown?")) {
     postShutdownMessageToServer();
     *** quit.onclick=null; *** // Quit button no longer triggers an event.
   }
 }

Sometimes, it's not enough for the event handler to know that an event has occurred; it needs information about the event. For example, the same event handler might be used for three different buttons, in which case it will need to know which of the buttons was clicked. For this reason, the web browser creates an event object upon each user event, containing various bits of information. In Firefox, it's passed in to the event handler, so you just ensure an event paramter exists.

 function shutdown(ev) {
   ...
 }

Unfortunately, IE holds the event in a window attribute instead of passing it in, so for portability, include an "equaliser" statement to get hold of the event either way.

 function shutdown(ev) {
   ev = window.ev || ev;
   ....
 }

The event object contains various information, such as which element was clicked and whereabouts the mouse was. The various event types are covered in the Decisions.

Notice the model here involves a single handler for any event type; the above commands set the handler, rather than add and remove handlers. It's often more convenient to do the latter, but portability concerns make it difficult. The feature is so useful that a competition was recently held to find the best addEvent() and removeEvent() functions, and you can find the winner, a fifteen-line script, online. Dojo Toolkit also supports these functions within a sophisticated event library.


[edit] Decisions

[edit] What events will the script listen for?

Many events are made available to Javascript code, and more come out with each new browser upgrade. Following are some frequently-used and portable events, along with typical applications. Check these out for more info on events: [5], [6].

[edit] Key pressing - onkeypress, onkeydown, onkeyup

  • onkeypress and onkeydown occur immediately after a key is pressed, and will also repeat if the key is held down. onkeyup is called just once, upon the key's release.
  • They can be used to enhance standard text editing. For instance, you can confine a phone number text field to contain only numbers, or you can show a word count while the user types.
  • They're sometimes used to automatically leave a field once it's valid - e.g. to proceed to the next field after five digits have been added to a zipcode field. However, doing so is often counter-productive, as users generally perform faster when behaviour is consistent, even at the expense of minor technical shortcuts.
  • Can be used to create keyboard shortcuts for custom controls. You can determine if the user's mouse is over the control with the onmouse* functions, and if so, respond to particular keypresses.
  • Caution: Web browsers have their own keyboard events, such as Up and Down for scrolling and the popular Ctrl-D for bookmarking. In most cases, you won't be able to get hold of those combinations.

[edit] Keyboard focus - onblur, onfocus

  • In the case of editable fields, onblur indicates that the user has left the field. This can result from various actions, such as hitting the TAB key, clicking the mouse outside the field, or switching to another browser tab or application window. It is often used to initiate a remote call or some validation technique.
  • onfocus suggests the user has begun working on a particular object, so it can be used to show online help or change the display inside a Status Area.

[edit] Mouse button clicking - onclick, ondblclick, onmousedown, onmouseup

  • These indicate a button has been clicked or double-clicked, or has been depressed or released. The button control is specifically geared for catching click events to let the user do something, and radiobuttons and checkboxes can also be associated with click listeners to indicate changes.
  • onmousedown and onmouseup can be used for panning behaviour and for custom drag-and-drop functionality.

[edit] Mouse movement - onmouseover, onmouseout

  • These indicate the mouse is over, or has just left, an element. It can be useful to keep a "pointerElement" variable to track which element is currently selected.
  • They can be used to change an element's style when the mouse rolls over it. This shows it's active and can convey that this is the element that will be affected if the user clicks the mouse button right now, or perhaps hits a certain key.
  • They can also be used to provide help or further information in a Status Area or Popup Information object.

[edit] Selection - onselect

  • This event indicates when the user has selected some text.
  • By tracking the selection, the application can provide information based on

what the user's selected. For example, you could let the user search on a term by selecting it, and then morph the "Search Results" element.

  • By tracking the selection, the application can also allow transformations to

occur. For example, the textarea in many modern content management applications, such as mediawiki, allows the user to select some text and then change it, just like in a word processor. To italicise text on wikipedia, you select the text and click the <i> icon, which then wraps mediawiki markup () around the selected text.

[edit] Value change - onchange

  • This event indicates a value has changed, so it's often used to initiate a remote call or some validation technique. An alternative to onblur. Unlike onblur, will only be called if the value was actually altered.

[edit] What attributes of the Event should be inspected?

The event object contains several useful pieces of information about the event, and what was going on at the time. Note that some of these attributes are set even for events you may not expect. For example, the ctrlKey modifier will be set even for a mouse-click event. This would allow you to detect a Ctrl-Mousepress action. However, not all attributes are always set, so you need to be careful in testing for portability.

Following are some of the portable, more frequently used, attributes of the event object.

[edit] Element - target (Firefox), srcElement (IE)

  • These indicate which element the event occurred on. To equalise across browsers: element = event.target || event.srcElement
  • They allow multiple objects to have event notifications routed to the same function. It's best to break such functions into separate functions, one for each element, if there are lots of if statements, deciding what to do according to which element was pressed. But if the element argument is used generically (e.g. element.innerHTML = "Closed"), then this is superior to having an equivalent function for each element.

[edit] Event type - type

  • These indicate which event type took place, e.g. "click".
  • A potential code smell, because it suggests the same function has been configured to handle multiple events. If it then needs to distinguish among the different types of events, it might be worth breaking it out into a handler function for each event type, with any common routines placed elsewhere.

[edit] Key code - which (Firefox), keyCode (IE)

  • These indicate the Unicode value of the key that was pressed. Not strictly speaking portable, but easy enough to equalise. Since you can't directly register a function against a specific key, this property is the only way to decide if a certain key was pressed.
  • Find character codes with this interactive Unicode chart generator.

[edit] Key modifiers - altKey, ctrlKey, shiftKey

  • Each modifier is true if the corresponding special key was being held down while a key event occurred. You can use the modifiers to introduce keyboard shortcuts to the application. Since many single-modifier shortcuts are already used by one browser or another, portable applications usually need to use double-modifiers. Thus, key-handling function will need to perform a check like:
 if (event.ctrlKey && event.shiftKey) {
   ... // perform ctl-shift shortcut
 }
  • There is also a meta-key modifier, generally not advisable as it's not supported by IE, and in any event only available on restricted keyboards.

[edit] Mouse buttons - button

  • This indicates which mouse buttons were being associated with the event. * In IE, 1 is left, 2 is right, and middle is 4. Value represents the sum of all buttons being pressed.
  • In Firefox, 0 is left, 1 is middle, and 2 is right.
  • A painful area due to serious incompatibility issues. As well as the differences above, be aware of: (a) incompatibilities in handling of one button being depressed while another is already depressed; (b) incompatibilities in which events provide this property (sometimes only mouse-clicks; sometimes others). See, for instance, [7].

[edit] Mouse position - clientX, clientY

  • These indicates position of mouse pointer when the event took place, relative to browser window.
  • Useful for image-based applications, such as maps and simulators. It's often not practical to register event handlers here, so Javascript code - with possible help of web remoting - can determine exactly what the user clicked on by examining the co-ordinates.

[edit] Should event handlers be registered after the page has loaded?

Using Javascript and the DOM, redefining event handlers is easy enough to do, but should you do it? Redefining the effect of user events must be done with caution, as there is great potential to confuse users. Sometimes, event redefinition occurs simply because the programmer can't be bothered adding a new control, or the UI is so small that designers want to reuse an existing control. So before deciding to redefine an event, ask yourself if there are alternatives. For example, could you add a second button instead of redefining the first button's action?

A few examples where event redefinition might be worthwhile:

  • For state transitions. The javascript may have separate start() and stop() methods, and you need a toggle button to flip the state, since that is clearer and less error-prone than separate "on" and "off" buttons.
  • For enabling and disabling. There is already a disabled property available for standard controls, but for custom controls you may have created, you might use event redefinition to cancel or re-enable the effects of interacting with the control.
  • For actions which depend on context information such as which field has input focus.

However, in all of these cases, note that it is usually simpler to have a single method, always registered in the same way, and to allow that method's Javascript decide where to route the event to.

[edit] How should event handlers be dynamically registered?

There are several options for changing event listener at runtime.

Where Javascript-based registration is used, there are several available syntaxes. The easiest, and probably the most portable, is to use the oneventtype attribute:

 quitButton.onclick = shutdown;

This technique points the object's onclick attribute to a function. Note the lack of parentheses to indicate we're referring to the function itself, rather than trying to call it.

Another thing about this technique is that it follows a replacement model. Because you're working with a single attribute, each time you do so, you're going to remove the old one. A more sophisticated approach would be to hold a collection of listeners, all of which get notified of particular events. This would be a better representation of the standard Publish-Subscribe - or Observer (Gamma et. al.) - pattern, and match event-handling in modern GUI toolkits more closely.

And, in fact, that kind of model is supported by the W3C and implemented by Firefox. With the "multiple handlers" technique, here's an equivalent to the previous statement:

 quitButton.addEventListener('click', shutdown, false);

The third argument relates to the conflict resolution strategy that's necessary when ancestors and descendents are listening for the same event. For example, clicking on a map begins to pan it, but clicking on certain individual points inside the map pops up a photo. The simplest strategy is to always leave the argument false, so that only the lowest-level registered listener is notified. See Quirksmode Events Order for more details.

Internet Explorer also supports a similar, though non-standard, model, using attachEvent in place of addEventListener and with different semantics. You can use a facade function that will work on both browsers if you need the multiple listener functionality. But unless you're doing anything sophisticated, the standard oneventtype is often the most satisfactory approach.


[edit] Real-World Examples

[edit] Bloglines

Bloglnes is a web-based RSS aggregator. You can change the current article by mouse-clicking on article titles. An interesting new feature is keyboard shortcuts - when the page contains numerous articles, clicking "j" and "k" will scroll up and down to the previous or next story.

[edit] Google Maps

Google Maps uses a dragging action to pan the map, and the arrow keys can also be used.

[edit] Backpack

37Signals Backpack maintains items in a list, and illustrates how you can use Drag-And-Drop in an Ajax application. Drag-And-Drop relies on monitoring the mouse button as well as position.


[edit] Code Examples

Here are a couple of basic examples from the Ajax demos. The Basic Time Demo handles a button click like this:

 $("defaultTime").onclick=requestDefaultTime;

The wiki tracks which focus and blur events in order to show the user which message is being edited, and to upload any messages after a blur occurs. It also tracks mouse movement over each area, to provide an affordance indicating that the fields can be edited.

 messageArea.onmouseout = onMessageMouseOut;
 messageArea.onmouseover = onMessageMouseOver;
 messageArea.onfocus = onMessageFocus;
 messageArea.onblur = onMessageBlur;

Each of these passes to getMessage, which identifies which message element was clicked.

 function getMessage(event) {
   event = event || window.event;
   return event.target || event.srcElement;
 }


[edit] Alternatives

[edit] "Click 'N' Wait" Forms

The conventional web application follows the "click 'n' wait" pattern, popular in 1970s mainframe-based client-server applications and revived in time for the late-1990s web generation, albeit in colour. The only type of interactivity is the user submitting a static form to a server-side CGI script, or clicking on a link. The script then reads some variables, does something, and outputs a whole new page of HTML. Submitting every few minutes is probably okay, but basic display changes are best performed in Javascript.

[edit] Richer Forms

The "richer form" is richer than static HTML, but less so than Ajax. It involves enhancing a standard form with dynamic behaviour, so as to make things clearer and help prevent the frustrating validation errors that often come back from the server. For instance, DHTML can be used to ensure a user enters only digits into a credit card field, or to add some popup instructions for a form field.


[edit] Related Patterns

[edit] Display Morphing, Page Rearrangement

Dynamic display changes, as dicussed in Display Morphing and Page Rearrangement, are often triggered by User Events.

[edit] XMLHttpRequest Call, IFrame Call

Web Remoting calls, as dicussed in XMLHttpRequest Call and IFrame Call, are often triggered by User Events.


[edit] Visual Metaphor

A manager instructs his PA how to respond to particular events. "When the client arrives, sit her down in the meeting room". If desired, he can later redefine the event-handling strategy. "When the client arrives, show her into my office".

[edit] Want to Know More?

QuirksMode: Introduction to Events

Static Wikipedia (no images)

aa - ab - af - ak - als - am - an - ang - ar - arc - as - ast - av - ay - az - ba - bar - bat_smg - bcl - be - be_x_old - bg - bh - bi - bm - bn - bo - bpy - br - bs - bug - bxr - ca - cbk_zam - cdo - ce - ceb - ch - cho - chr - chy - co - cr - crh - cs - csb - cu - cv - cy - da - de - diq - dsb - dv - dz - ee - el - eml - en - eo - es - et - eu - ext - fa - ff - fi - fiu_vro - fj - fo - fr - frp - fur - fy - ga - gan - gd - gl - glk - gn - got - gu - gv - ha - hak - haw - he - hi - hif - ho - hr - hsb - ht - hu - hy - hz - ia - id - ie - ig - ii - ik - ilo - io - is - it - iu - ja - jbo - jv - ka - kaa - kab - kg - ki - kj - kk - kl - km - kn - ko - kr - ks - ksh - ku - kv - kw - ky - la - lad - lb - lbe - lg - li - lij - lmo - ln - lo - lt - lv - map_bms - mdf - mg - mh - mi - mk - ml - mn - mo - mr - mt - mus - my - myv - mzn - na - nah - nap - nds - nds_nl - ne - new - ng - nl - nn - no - nov - nrm - nv - ny - oc - om - or - os - pa - pag - pam - pap - pdc - pi - pih - pl - pms - ps - pt - qu - quality - rm - rmy - rn - ro - roa_rup - roa_tara - ru - rw - sa - sah - sc - scn - sco - sd - se - sg - sh - si - simple - sk - sl - sm - sn - so - sr - srn - ss - st - stq - su - sv - sw - szl - ta - te - tet - tg - th - ti - tk - tl - tlh - tn - to - tpi - tr - ts - tt - tum - tw - ty - udm - ug - uk - ur - uz - ve - vec - vi - vls - vo - wa - war - wo - wuu - xal - xh - yi - yo - za - zea - zh - zh_classical - zh_min_nan - zh_yue - zu -

Static Wikipedia 2007 (no images)

aa - ab - af - ak - als - am - an - ang - ar - arc - as - ast - av - ay - az - ba - bar - bat_smg - bcl - be - be_x_old - bg - bh - bi - bm - bn - bo - bpy - br - bs - bug - bxr - ca - cbk_zam - cdo - ce - ceb - ch - cho - chr - chy - co - cr - crh - cs - csb - cu - cv - cy - da - de - diq - dsb - dv - dz - ee - el - eml - en - eo - es - et - eu - ext - fa - ff - fi - fiu_vro - fj - fo - fr - frp - fur - fy - ga - gan - gd - gl - glk - gn - got - gu - gv - ha - hak - haw - he - hi - hif - ho - hr - hsb - ht - hu - hy - hz - ia - id - ie - ig - ii - ik - ilo - io - is - it - iu - ja - jbo - jv - ka - kaa - kab - kg - ki - kj - kk - kl - km - kn - ko - kr - ks - ksh - ku - kv - kw - ky - la - lad - lb - lbe - lg - li - lij - lmo - ln - lo - lt - lv - map_bms - mdf - mg - mh - mi - mk - ml - mn - mo - mr - mt - mus - my - myv - mzn - na - nah - nap - nds - nds_nl - ne - new - ng - nl - nn - no - nov - nrm - nv - ny - oc - om - or - os - pa - pag - pam - pap - pdc - pi - pih - pl - pms - ps - pt - qu - quality - rm - rmy - rn - ro - roa_rup - roa_tara - ru - rw - sa - sah - sc - scn - sco - sd - se - sg - sh - si - simple - sk - sl - sm - sn - so - sr - srn - ss - st - stq - su - sv - sw - szl - ta - te - tet - tg - th - ti - tk - tl - tlh - tn - to - tpi - tr - ts - tt - tum - tw - ty - udm - ug - uk - ur - uz - ve - vec - vi - vls - vo - wa - war - wo - wuu - xal - xh - yi - yo - za - zea - zh - zh_classical - zh_min_nan - zh_yue - zu -

Static Wikipedia 2006 (no images)

aa - ab - af - ak - als - am - an - ang - ar - arc - as - ast - av - ay - az - ba - bar - bat_smg - bcl - be - be_x_old - bg - bh - bi - bm - bn - bo - bpy - br - bs - bug - bxr - ca - cbk_zam - cdo - ce - ceb - ch - cho - chr - chy - co - cr - crh - cs - csb - cu - cv - cy - da - de - diq - dsb - dv - dz - ee - el - eml - eo - es - et - eu - ext - fa - ff - fi - fiu_vro - fj - fo - fr - frp - fur - fy - ga - gan - gd - gl - glk - gn - got - gu - gv - ha - hak - haw - he - hi - hif - ho - hr - hsb - ht - hu - hy - hz - ia - id - ie - ig - ii - ik - ilo - io - is - it - iu - ja - jbo - jv - ka - kaa - kab - kg - ki - kj - kk - kl - km - kn - ko - kr - ks - ksh - ku - kv - kw - ky - la - lad - lb - lbe - lg - li - lij - lmo - ln - lo - lt - lv - map_bms - mdf - mg - mh - mi - mk - ml - mn - mo - mr - mt - mus - my - myv - mzn - na - nah - nap - nds - nds_nl - ne - new - ng - nl - nn - no - nov - nrm - nv - ny - oc - om - or - os - pa - pag - pam - pap - pdc - pi - pih - pl - pms - ps - pt - qu - quality - rm - rmy - rn - ro - roa_rup - roa_tara - ru - rw - sa - sah - sc - scn - sco - sd - se - sg - sh - si - simple - sk - sl - sm - sn - so - sr - srn - ss - st - stq - su - sv - sw - szl - ta - te - tet - tg - th - ti - tk - tl - tlh - tn - to - tpi - tr - ts - tt - tum - tw - ty - udm - ug - uk - ur - uz - ve - vec - vi - vls - vo - wa - war - wo - wuu - xal - xh - yi - yo - za - zea - zh - zh_classical - zh_min_nan - zh_yue - zu

Static Wikipedia February 2008 (no images)

aa - ab - af - ak - als - am - an - ang - ar - arc - as - ast - av - ay - az - ba - bar - bat_smg - bcl - be - be_x_old - bg - bh - bi - bm - bn - bo - bpy - br - bs - bug - bxr - ca - cbk_zam - cdo - ce - ceb - ch - cho - chr - chy - co - cr - crh - cs - csb - cu - cv - cy - da - de - diq - dsb - dv - dz - ee - el - eml - en - eo - es - et - eu - ext - fa - ff - fi - fiu_vro - fj - fo - fr - frp - fur - fy - ga - gan - gd - gl - glk - gn - got - gu - gv - ha - hak - haw - he - hi - hif - ho - hr - hsb - ht - hu - hy - hz - ia - id - ie - ig - ii - ik - ilo - io - is - it - iu - ja - jbo - jv - ka - kaa - kab - kg - ki - kj - kk - kl - km - kn - ko - kr - ks - ksh - ku - kv - kw - ky - la - lad - lb - lbe - lg - li - lij - lmo - ln - lo - lt - lv - map_bms - mdf - mg - mh - mi - mk - ml - mn - mo - mr - mt - mus - my - myv - mzn - na - nah - nap - nds - nds_nl - ne - new - ng - nl - nn - no - nov - nrm - nv - ny - oc - om - or - os - pa - pag - pam - pap - pdc - pi - pih - pl - pms - ps - pt - qu - quality - rm - rmy - rn - ro - roa_rup - roa_tara - ru - rw - sa - sah - sc - scn - sco - sd - se - sg - sh - si - simple - sk - sl - sm - sn - so - sr - srn - ss - st - stq - su - sv - sw - szl - ta - te - tet - tg - th - ti - tk - tl - tlh - tn - to - tpi - tr - ts - tt - tum - tw - ty - udm - ug - uk - ur - uz - ve - vec - vi - vls - vo - wa - war - wo - wuu - xal - xh - yi - yo - za - zea - zh - zh_classical - zh_min_nan - zh_yue - zu