Browser Management - Applied JavaScript - JavaScript The Complete Reference (2012)

JavaScript The Complete Reference (2012)

PART III Applied JavaScript

CHAPTER 16 Browser Management

Given the wide variety of browser versions and capabilities, building a Web site that works optimally everywhere can be quite daunting. If this challenge weren’t enough, we often aim to provide more than just compatibility—we aim to adjust viewing experience optimally for the user. In this chapter, we explore how JavaScript can be used to manage the browser and user experience. We cover not only browser and capability detection, but also state, storage, and script management. In a few places, we cover some advanced and even proprietary browser features. However, we warn that the ideas presented in this chapter may be misused and encourage some developers to create “exclusionary” Web sites. We will remind the reader throughout that browser detection, control, and emerging facilities should be used to improve the use of Web sites for all users, rather than a select few.

Browser Detection Basics

Anyone who has built more than a few Web pages has surely come across some of the numerous differences between browser types and versions. A page that looks perfect on your screen just doesn’t look quite the same on your friend’s or neighbor’s, and sometimes it looks vastly different. The variances range from minor cosmetic inconsistencies, such as a small shift of content or container size, to catastrophic situations in which the page causes errors or doesn’t render at all.

What’s a developer to do when faced with such an unpredictable medium as the Web? Some throw up their hands and just build their site to suit their current browser of choice. If you’ve ever noticed statements on sites such as, “This site is best viewed in…,” then you have encountered this approach already. Others simplify their site technology to the so-called lowest common denominator in order to meet the needs of older browsers, often lacking support for even CSS or JavaScript and viewing pages in a low-resolution environment. Falling somewhere in between these extremes is the more adaptive type of site that modifies itself to suit the browser’s capabilities or indicates to users their inability to use the site. This “sense and adapt” concept is often termed browser detection or browser sniffing and is an integral part of JavaScript tasks of any complexity.


NOTE Capability detection is the preferred way to detect things in a browser, but pragmatically we must acknowledge that raw browser detection still has its place.

The Navigator Object

JavaScript’s Navigator object provides properties that indicate the type of browser and other useful information about the user accessing the page. The most commonly used Navigator properties that are standard per HTML5 are detailed in Table 16-1. Note that we omit many properties supported by browsers. A few will be included in some later sections that are more applied, but we warn readers that many areas of the Navigator object are a bit proprietary; so we avoid them purposefully if they are not commonly used.

Table 16-1 Selected Navigator Object Properties

image


NOTE The non-standard clientInformation object contains many of the same properties as the Navigator object. However, the Navigator object is supported by all the major browsers, while the clientInformation object is not. There should never be a need to use theclientInformation object because the Navigator object will always be the better choice.

The examination of user-agent strings for typical browsers reveals a variety of cryptic numbers and abbreviations, and because of the misreading of the user-agent string into the various properties we tend to find developers running regular expressions on the whole string.

The following simple script outputs the basic properties of the Navigator object. An example of the script’s rendering in some common browsers shown in Figure 16-1 is interesting because of the diversity of answers:

image

Figure 16-1 Browser detection results under various browsers

image


ONLINE http://www.javascriptref.com/3ed/ch16/navigator.html

Notice already from Figure 16-1 that the navigator.appName appears to misreport in some browsers, citing the ancient Netscape, despite the fact that the navigator.userAgent values are quite different and contain no such value. What we are seeing here is the downside of relying on anything but the user-agent string.


NOTE The non-standard clientInformation object contains many of the same properties as the Navigator object. However, the Navigator object is supported by all the major browsers, while the clientInformation object is not. There should never be a need to use theclientInformation object because the Navigator object will always be the better choice.

The examination of user-agent strings for typical browsers reveals a variety of cryptic numbers and abbreviations, and because of the misreading of the user-agent string into the various properties we tend to find developers running regular expressions on the whole string.

The following simple script outputs the basic properties of the Navigator object. An example of the script’s rendering in some common browsers shown in Figure 16-1 is interesting because of the diversity of answers:

Generally, you need to delve deep into the navigator.userAgent value to make sure you know what you are looking at. For example, here we might write a script that could pull out a browser name from the userAgent property:

image

image


ONLINE http://www.javascriptref.com/3ed/ch16/useragent.html

Using a more developed script such as the one just given, it is possible to create conditional markup, style, or even script based on which browser is hitting the page. Of course, there is obviously trouble in addressing all possible values we might run across in the userAgent property.

Sometimes, though, we only want to deal with things we actually spot. As an example, we might combine our browser detection with HTML5 data-* attributes to present different styles. Right as a page loads, use JavaScript to detect browser types and then modify the DOM tree with that data:

image

With this data applied to an element, you might target style sheet rules with this user agent–specific attribute:

image

Here, we just set the background-color based on the type of browser we see.

Regardless of how applied, we are going to find a number of problems with browser detection. First, you are making an assumption that the browser will correctly identify itself. You may find, in fact, that many browsers—particularly older ones—will report themselves incorrectly because they do not want to be prevented from viewing sites coded to detect the major browser variants. You can start diving into the value found in the userAgent property, but even that can be completely spoofed, both for privacy purposes and for debugging. Here we see a simple example of built-in tools available in Safari browsers:

image

Even if we could somehow trust browsers to correctly identify themselves, we have to have some mapping between browser types and capabilities. A more appropriate way to address visitor diversity is to detect capabilities first and then add in any special details that may be specific to a particular browser type. More simply put, developers should not focus on detecting the browser brand and version and then understand what that browser can or cannot do, but should instead focus on detecting the capabilities of the browser in use.

What to Detect

When using JavaScript to detect the capabilities of your site or application’s visitors, you can roughly break up the useful detectable information into four categories:

• Technical issues (for example, JavaScript support, Java, and plug-ins)

• Visual issues (for example, color depth and screen size)

• Delivery issues (for example, connection speed or type)

• User issues (for example, language setting and previous visitor)

We can use JavaScript to obtain information about each one of these categories. First, let’s take a look at what technical facilities can be detected via JavaScript.

Technology Detection

When it comes to browser technology, you would usually like to know the browser’s support for the following things:

• Markup versions and specific elements

• Style sheets and specific proprieties

• Scripting language version and specific objects, methods, and properties

• Java

• Object technology (plug-ins and ActiveX controls)

We’ll briefly look at each of these items, in turn, to present the approach you might employ to understand what your application is dealing with, technology-wise.

Markup and Style Detection

Markup and style sheets can be a bit difficult to detect. You might try to use the DOM to check basic markup support by probing to create a particular object or using the document.implementation.hasFeature() method. This method returns a Boolean value if a particular HTML or XML level of binding is supported, as shown in the following example:

image

Of course, as discussed in Chapter 10, this particular DOM feature is not very useful, and in some cases browsers indicate support when they have none or don’t indicate support when they include partial support.

While we can’t detect DOM features terribly well, we might detect for the particular rendering mode we find ourselves in. Usually this can be performed looking at the document.compatMode property. The value of the property will be “BackCompat” if we are in a quirks mode and “CSS1Compat” if we are in a standards-compliant mode. Internet Explorer 8 and beyond add in the document.documentMode property, which contains a number value. If the property holds a value of 7, 8, 9, or beyond, we are in a standards mode corresponding to that version of Internet Explorer. If we see a value of 5, the browser is in a quirks mode.

Rather than just detecting the render mode, we might like to get a bit more granular. For example, given the rise of HTML5, you might desire to use a particular element such as a <progress> tag, which might be useful in the page; but how do we know that the browser actually supports this emerging feature? Well, we could use JavaScript to create the element and if a special attribute specific to that element is found then we know the browser correctly created it. This simple example shows how this might be done:

image

We can see the results of the script here:

image

Obviously, we probably would want to address this dynamically with some JavaScript-based progress mechanism in the case of nonsupport. Even beyond the simplicity of the example, there can be quirks and details to these detections; so we should be a bit careful, particularly because most browsers will add unknown elements quite happily.

Detecting the support of a CSS property is fairly similar to element detection. You can either look at some existing element or instantiate an element to see if the CSS properties in question are mapped there. For example, the CSS3 transform property is a relatively recent addition to browsers. To make sure it is supported, we might look at the body element and see if its style object shows the property or one of the vendor-specific equivalents:

image

JavaScript Detection

JavaScript support is probably the easiest technology to detect; if a script doesn’t run, this condition implicitly shows that the browser doesn’t support JavaScript or that it is turned off. Consider the use of the <noscript> tag here with a <meta> redirection:

image

If users disabled scripting or have accessed the site with a very old browser, they are redirected to a “noscript.html” page that would contain information on the site’s requirements.

In contrast to the previous approach, some developers opt instead to do a positive check: the use of JavaScript redirecting the user to a particular page using the Location object. For example,

image

The problem with this approach is that it tends to be used as a single detection point and disrupts the back button facility in the browser. The first technique is a more passive approach and can be easily included on all pages without serious worry. Obviously, trying to build a site that degrades gracefully is an even better idea than detect and deny schemes, but we must acknowledge that sometimes that simply may not be possible.

JavaScript Version Detection

While it is easy to detect if JavaScript is on or off, what about version or feature support? One way to deal with different versions of JavaScript is to utilize the nonstandard language attribute of the <script> tag. While in most of this book we have used the standard type attribute to indicate the scripting language in use, the language attribute is actually commonly used and has some extra value. Recall from Chapter 1 that JavaScript-aware browsers will ignore the contents of <script> tags with language attributes they do not support. Because browsers act in this way, it is possible to create multiple versions of a script for various versions of the language or to set a variable to indicate the highest version supported, as in this example:

image

It is also possible that we use the type attribute, which is standard. For example, to see JavaScript version 1.8, we might have the following markup:

image

Regardless of the method, once we employ it we might be tempted to declare dummy functions or objects and then redefine them in higher versions. The problem with this is that we are not talking about the basic features of the language. Usually we are talking about constructs such aslet or yield and really our code is likely a bit different. Our general attitude is that unless we are going to lock users out, using the newer features might be a bit too much of a tradeoff because we then have to have parallel code that is more a function of style than capability. However, when we do talk about capability such as DOM support or browser features, a forked-code approach makes good sense.

JavaScript Object Detection

In some cases, we don’t care about whether a particular version of JavaScript is being used but rather whether certain objects or methods are available. Instead of knowing everything about which browsers support which versions of JavaScript, it is probably better just to detect for capabilities by checking whether or not the appropriate object is available. For example, the script here checks to see if your browser could support rollover images by determining whether the images[] collection is defined:

image

Obviously, that isn’t a very modern example, but we see the same idea with Ajax:

image

We also see it with various HTML5 capabilities. For example, here we look for geolocation support:

image

In these cases, we relied on the fact that JavaScript’s dynamic type conversion will convert a nonexistent object to false, and if it exists it will evaluate as true. We’ll see variations of this approach abound using the conditional operator (?), try-catch blocks, and switch statements.

As the previous examples showed, object detection is a simple way to figure out if a feature is supported or not. However, be careful with relying on object detection too much. Far too often in JavaScript, we assume that the existence of one object implies the existence of other objects or the use of a particular browser, but this is not always the case. For example, you might too often see code such as the following to detect if Internet Explorer is in use:

image

However, does the existence of document.all really mean that Internet Explorer is in use? The truth of the matter is that another browser could support document.all but not necessarily provide all the features found in Internet Explorer. The developer or a developer of an included third-party script might even be simulating document.all with their own code. Given all the possibilities for trouble, it might be better to check for each object specifically, so instead we might use

image

and so on. In some ways, object detection is the best method to use, but it should be used carefully and assumptions shouldn’t be made.

Another consideration with object detection is not to go too far too quickly. Remember that probing a property of a nonexistent object throws an error, so first check to see if the parent object exists. As an example, if you were to check directly for window.screen.height, as shown here, you would throw an error in browsers that did not support the screen object:

image

Instead, you could rely on short-circuit evaluation to do the test incrementally, like so:

image

Given the possibility of failure when trying to use nonstandard features, we suggest that the object detection approach fits nicely with try-catch blocks.

Java Detection

Detecting Java’s availability is fairly easy using the Navigator object’s method javaEnabled():

image

This method returns true if Java is available and turned on, and false otherwise:

You can find out more about Java once you know it is available by accessing a Java applet included in the page. You can even determine what type of Java Virtual Machine is supported. In order to do this, you will have to access the public methods and properties of a Java applet. Interacting with applets is discussed in more detail in Chapter 17.

Plug-in Detection

In plug-in–supporting browsers, each plug-in installed in the browser has an entry in the plugins[] array of the Navigator object. Each entry in this array is a Plugin object containing information about the specific vendor and version of the component. A simple detection scheme checks for a plug-in’s existence using the associative array aspect of JavaScript collections. For example, to look for a Flash plug-in, you might write the following:

image

Of course, you need to be careful to use the exact name of the particular plug-in you are interested in. It is important to note that different versions of the same plug-in can have different names, so you need to check vendor documentation carefully when detecting plug-ins in this fashion. Also, be aware that Internet Explorer defines a faux plugins[] array as a property of Navigator. It does so in order to prevent poorly written scripts from throwing errors while they probe for plug-ins or simply to prevent them from returning the wrong result. We would need to deal with this cross-browser nuance by checking to make sure that any Plugins object has a length property defined when doing the plugins[] array probe, as shown here:

image

Fortunately, if Internet Explorer is in use, we can rely on the <object> tag to install the appropriate object handler if the user allows it. More information about detecting and interacting with objects such as Netscape plug-ins and Microsoft ActiveX controls can be found in Chapter 17.

Visual Detection: Screen Object

The Screen object contains the basic screen characteristics for the browser. It is actually a child of the Window object, although it would seem to make more sense as a parent of Window if you think about things logically. The following example shows the common screen characteristics that can be detected in browsers that support the Screen object:

image


ONLINE http://javascriptref.com/3ed/ch16/screen.html

A rendering of the example is shown here:

image

One thing that is rather troublesome with this detection is that the availHeight and availWidth properties indicate the height and width of the screen minus any operating system chrome, rather than the actual size of the available browser window, as one might expect. In order to detect actual window size, you have to use properties of the Window object in most browsers, but in the case of older Internet Explorer browsers, you need to look into the Document object and examine the <body> itself. However, in the case of the DOM, you might want to look at the size of the root element, namely the <html> tag, and not the <body> if you are trying to get the dimensions of the window. Of course, which tag to look at depends on what rendering mode your browser is in, either loose or strict, which is generally determined by the doctype statement in the document. This example shows how you might check all this. Invariably, something might change given the lack of agreement among browser vendors as to how to implement certain CSS, XHTML, and JavaScript ideas, but the example should nonetheless demonstrate the concept:

image

image


ONLINE http://javascriptref.com/3ed/ch16/screen-cb.html

A rendering of the example in Firefox and Internet Explorer is shown here:

image

In browsers that permit manipulation of page content and styles at runtime, we can set the size of screen objects such as fonts in a manner appropriate to the current window size. Consider the following example:

image


ONLINE http://javascriptref.com/3ed/ch16/screen-dynamic.html

A typical rendering is shown here, but readers are encouraged to try this example themselves to verify its usefulness:

image

Under browsers such as Internet Explorer that support expressions within CSS rules, we might use a cleaner, like this:

image

However, this is dumped later both for standards and security purposes. Further, we wonder if it might be better to avoid using JavaScript all together and rely on CSS with relative sizing units such as percentage or em values or use media queries, which will be discussed in the next section. Before doing that, we should present how JavaScript may be employed to address color concerns.

We might also address color issues on the Web dynamically using JavaScript. For example, many designers still use reduced color images that stick to a limited 216-color palette, called the “browser safe” palette, when they might be able to use richer images in many situations. The following code could be used to insert different types of images conditionally:

image

Besides being a question of the appropriateness of using JavaScript here (as you will find in the next section, media queries are better suited), we wonder if this is really needed anyway. A little Web research will show that the browser-safe palette has been dead for approaching a decade, as it is a function of a VGA world. This isn’t to say there aren’t color reproduction issues in browsers and across operating systems. There are, so we imagine JavaScript might eventually play a role in addressing them.

A summary of all the properties we used in the Navigator object for size and color issues can be found in Table 16-2.

Table 16-2 Navigator Properties for Visual Detection

image

Media Queries

A media query takes the CSS media attribute and extends it with conditions that avoid using JavaScript for simple device capability detection. For example, Web developers commonly are familiar with one style rule for print and one for screen. Media queries add to this a query on the media, such as what is the available width or color, to then determine whether to apply rules or not. Such a query system allows Web developers to easily apply different styles to different conditions, such as one style for a wide screen and one for a narrow one, without resorting to JavaScript. As an example, here we employ a style sheet called wide.css if the screen resolution is at least 1024 pixels, a different one for a midrange window size, and one for a small window size:

image

Interestingly, most modern browsers support this, as shown in Figure 16-2.

image

Figure 16-2 Media queries in action


ONLINE http://javascriptref.com/3ed/ch16/mediaquery.html

Media queries can be used inline as well using the @media syntax and may also apply to different mediums; for example, here we might apply different CSS rules, depending on the print style:

image

Table 16-3 details all of the media queries defined by the specification, though implementations currently focus mostly on width-related features.

Table 16-3 CSS Media Query Values

image

image

image

It is also possible to access media queries via JavaScript. A MediaQueryList object can be created by calling the window.matchMedia (rule) method:

image

The object returned contains a Boolean property matches, which indicates if the current state of the page matches the rule specified. An event listener can be added via the addListener(eventHandler) method. This event will fire whenever the matches status changes:

image

Whether you use JavaScript or media queries, the days of fixed designs for assumed devices should start to wind down. Hopefully, as programmers and designers alike discover the flexibility available to them, we will more commonly see side-by-side designs for wide monitors and stacked layouts for narrow ones.

User Characteristics

When using JavaScript to detect the characteristics of our visitors, we can focus on simple characteristics such as where they are currently located and what kind of language they claim to prefer. Ultimately, we would probably like to record their various habits such as whether they have been to our site or application, whether they have returned, what they did, and so on, so that we can personalize their experience. We’ll talk a bit later in the chapter about how cookies might be used for this, but ultimately we are getting into the arena of analytics and personalization, which is far outside the scope of this book, even if it involves using JavaScript. For now, we keep it simple and focus on the basic syntax of some interesting user detection features supported by JavaScript.

Geolocation

With the advent of mobile technology, Web sites often have a desire to view the user’s current location. Even in the realms of nonmobile systems, it can be useful to know a user’s position. For example, offering relevant advertisements is often based on location. In the past, an IP lookup on the server side was used to narrow down the user’s location. Now, it is possible to do this via JavaScript, though admittedly this may call a server that does a Geo-IP conversion or even a Wi-Fi or GPS lookup. However, many users may wish to hide this information from the Web site and this is possible. Browsers do not pass the information along to Web pages unless users explicitly give their consent:

image

The location lookup methods are held in the navigator.geolocation object. Given the newness of this facility, we should verify the existence of it:

image

We may wish to further look for methods, but we’ll assume that if the object is in place the basics are as well. The first method you want to use obtains the user’s location information and is navigator.geolocation.getCurrentPosition(successCallback [,errorCallback, options ]). This is an asynchronous call to retrieve the current position. The getCurrentPosition() method takes up to three parameters. The first is the callback in the case of a successful retrieval. The second is the callback in the case of an error. Finally, the last parameter is a set of options. These options are presented in the way of an object, and they currently have three properties. The first is enableHighAccuracy, a Boolean that when set to true will attempt to get a more accurate location. This may take longer and may take up more resources—battery usage, in the case of mobile devices. The next property is timeout, which is a number in milliseconds indicating when the call to getCurrentPosition() should time out:

image

If the timeout occurs, then the error callback will be invoked. It is important to note that the time spent getting the user’s permission is not included in the timeout time. The last option is maximumAge, which is a number in milliseconds to hold a previously looked up value before recalculating the location.

On successful retrieval of the location, the success callback will be invoked with a Position object sent as a parameter. The Position object will contain a timestamp and a Coordinates object that will hold all the information about the location. The Coordinates object is made up of the properties in Table 16-4.

Table 16-4 Properties of the CoordinatesObject

image

In our fragment, we call the printData() function, shown here, which shows the current position information:

image

However, if the getCurrentPosition() call does not return successfully, the error callback will be invoked with a PositionError object parameter. The PositionError object is made up of the properties in Table 16-5, which we use in this simple callback function:

Table 16-5 PositionError Object Properties

image

image

We should note that the first call to look up a user’s position may not be the most accurate, as it uses quicker methods of retrieval. In order to get a better sense of a user’s location, it is possible to use the navigator.geolocation.watchPosition(successCallback[,errorCallback, options ]) method. This method takes the same parameters as getCurrentPosition() and passes the same information to the callback functions. The difference is that the watchPosition() method continuously calls and will call the callback functions on a change of position. As the device gets a better sense of the user’s location, the watchPosition() success callback will be called to update the Web page. It is also called in the case that the user is moving. When initially setting up watchPosition(), an ID will be returned. This ID can be used to cancel the watchPosition() action:

image

These code fragments and syntax should give you an idea of how this new API is used, but a full example found online and shown in Figure 16-3 may be more instructive for showing how everything fits together.


ONLINE http://javascriptref.com/3ed/ch16/geolocation.html

image

Figure 16-3 Geolocation demo in action

Language Detection

Another useful user detection is to employ JavaScript to sense which language the user’s browser is set to support. We might use this to send users to a Spanish page if they have the Spanish language set as a preference in their browser. Browsers provide access to this information in slightly different ways. Most browsers use the navigator.language or navigator.browserLanguage to indicate the language of the browser or the language preferred by the browser, respectively. Internet Explorer and other browsers also supportwindow.navigator.userLanguage or window.navigator.systemLanguage, which can also give an indication of end-user language.

You might imagine using these types of properties to fork code to present different strings:

image

A better way might be to define a strings file such as lang-es.js or lang-en.js and include it based on the sense. Within that file, you might have constants or an object of the strings to use:

image

While these properties, as well as the geolocation of the user, might serve as a good guess about user desires, we probably should still have a link or a button for switching languages in case it is not detected properly.

Network State and Performance

In these days of mobile devices, it is becoming increasingly common for users to want to access their Web pages even when they are offline. A developer can provide different content and functionality based on the user’s online status. In order to check if the user is online, simply check thenavigator.onLine property. Once the initial state is known, it is possible to be alerted to any change in status by listening to the ononline and onoffline events. The following simple example shows a status message alerting if the user is online or offline:

image

image


ONLINE http://javascriptref.com/3ed/ch16/online.html

Simple Page Load Metrics

Even if a browser is online, it is quite possible, of course, that its network connection is just plain slow. It would be a good idea to have a sense of exactly what the user’s connection rate is before you decide what data you will send them. It would be easy enough to use JavaScript to set a timer to see how long a page takes to load. For example, at the top of an HTML document, start a script timer:

image

Ajax or any other JavaScript communication mechanism could be used to transmit the user connection data back to the server for statistical purposes.


NOTE Internet Explorer supports a feature called Client Capabilities that can easily be used to determine if a user is on a LAN or dial-up connection. However, because it is so browser specific and does nothing in terms of determining the actual connection rate, it is not discussed in detail here.

However, this method of determining user connection data is somewhat inaccurate and we might further consider looking at delivery details in more depth. An emerging API discussed next just does that.

window.performance.timing

Various browser vendors have aimed to improve our insight into page loading time by working on the Navigation Timing API under the W3C. The API specifies the window.performance object, which contains a number of properties populated when the page loads. For example,window.performance.navigationStart would contain a timestamp for when the request begins, window.performance.domainLookupStart and window.performance.domainLookupEnd would contain timestamps for the start and end of the DNS resolution phase, and so on. The following illustration does a good job of showing the various properties you’ll encounter:

image

Now, given that the performance data will often depend on how an end user arrives at a page, the API also specifies the performance.navigation object, which has two attributes— redirectCount, which holds the number of redirects followed to hit the current document, andtype, which holds a numeric value indicating how the page was reached. A value of 0 in the type property means a link was pressed or a URL was typed in, a value of 1 is a page reload, and a value of 2 means movement through a browser’s history list (clicking the back or forward button). A simple example of how these APIs might be used is found online and shown in Figure 16-4.

image

Figure 16-4 The performance.timing object provides detailed page loading time information.


ONLINE http://www.javascriptref.com/3ed/ch16/performance.html

Browser Control

Once we have mastered the detection of visitors’ browsers and their various features, we might be interested in trying to control these browsers. Using the Window object as discussed in Chapter 12, it is possible to change window appearance. For example, we might scroll or resize the window using window.scrollTo() or window.resizeTo(), or set the browser status message using window.status or window.defaultStatus. For more control, we might consider opening a new window (window.open) and removing the browser’s chrome or even going full screen. We could even send the user to another page using window.location or use timeouts and intervals (window.setTimeout and window.setInterval) to perform activity at set moments. Yet we can even go beyond these possibilities in some instances using mostly nonstandard though commonly supported features that we discuss next.

Simulating Browser Button Presses

Some browsers support methods that allow the developer to fake various browser activities, such as pressing a particular button, while others support only the most useful ones such as window.print(), which triggers the printing of the page. To test what your browser supports, try this simple example that uses object detection to avoid causing an error:

image


ONLINE http://www.javascriptref.com/3ed/ch16/buttoncontrol.html

In addition to these button controls, a find action can be simulated in some browsers using the window.find() method. This method takes a text string to search for as its first parameter and then a series of optional Boolean parameters:

image

Note that the last parameter gives the option to display the search box in the user’s browser, so this method can be used as a launching pad for the browser’s find functionalities.

Given that some buttons can be simulated, you might wonder if it is possible to control other aspects of the user’s browser such as the user’s preferences. In some browsers, you used to be able to set home pages and other preferences, though you often needed to ask for the privilege to do so. HTML5 has, however, codified one aspect of browser control for handling searching and certain MIME types.

Search Providers

In modern browsers, a search bar is built into the browser so that users can search, no matter what Web site they are currently visiting. The search bar is generally tied to a search engine site such as Google:

image

However, many other Web sites offer search capabilities, and these sites can allow users to search their sites directly through this browser bar:

image

Doing a search through the browser search bar takes the user directly to the search results page for the given search site:

image

To add your Web site to the list in the browser search bar, it is first necessary to create a search page. We make a very simple one here in PHP that will simply echo what is being searched for:

image

Now, to tie it into the site or browser, an OpenSearch description document must be created. The OpenSearch description document is an XML file that is used to describe the search engine. We provide a simple one here, but if you need more information about OpenSearch, seewww.opensearch.org.

image

The most important thing we need to point out in the example is the <Url> tag that specifies the page that will be called to handle the search. Note that the search terms can be passed through via the { searchTerms} parameter.

Now that the files are configured, JavaScript can be used to plug the search engine into the search bar. First, a check is made to see if the search engine has already been added:

image

If it has not been added in, a simple call is used to prompt the user to add the engine:

image

The user will receive a prompt confirming the addition:

image

As long as the user confirms the addition, he or she will be able to begin using the search provider from the browser bar, as demonstrated here:

image


ONLINE http://javascriptref.com/3ed/ch16/searchProvider.html

Protocol Handlers

In the previous section, we saw how to handle searches coming from outside of the realm of your Web site. It is also possible to hook handlers up to specific URL protocols as well as specific mime types. In order to add a handler for a specific protocol, the Navigator object’sregisterProtocolHandler (schema, url) method is used. This will give the user a prompt to register the given schema so that it will redirect to the url when encountered in a link or address. For example, here we register an action for the mailto: protocol:

image

The %s is used to pass the handler the value that is set in the mailto: URI. The user will be given a prompt:

image

If the prompt is confirmed, the next time the user accesses a mailto: URI, a picker list will allow the choice of which handler to use to perform the mailto: action, like so:

image

If our Custom Email handler is chosen, the user will be redirected to the sendMail.php URL specified in the registerProtocolHandler() method:

image


ONLINE http://javascriptref.com/3ed/ch16/registerProtocolHandler.html

Currently, registerProtocolHandler() is not supported by all modern browsers. In addition, isProtocolHandlerRegistered() and unregisterProtocolHandler() are described in the current HTML5 specification, but they currently have no browser support.

Content Handlers

Similar to protocol handlers, content handlers give the ability to register a mime type to be handled by a specific URL. Some browsers limit this to only the RSS feed mime types:

image

As with protocol handlers, a prompt will be given to confirm the addition of the handler, and when a link with the relevant mime type is clicked, the user will be prompted as to what handler should handle the content. The content is passed to the handler via the %s parameter.

Also, like protocol handlers, the isContentHandlerRegistered() and unregisterContentHandler() methods are described in the specification, but at the time of this book’s release they have no browser support and, like many areas of HTML5, may be subject to change.

As a quick summary and reference, all of the discussed protocol and content handler methods are described in Table 16-6.

Table 16-6 HTML5 Protocol and Content Handler Methods for the Navigator Object

image

image

State Management

Browser cookies are the subject of much myth and misunderstanding. While popular wisdom has it that they’re detrimental to user privacy, the truth is that while cookies can certainly be abused, they’re an almost indispensable tool in Web programming.

The main value of cookies comes from the fact that HTTP is a stateless protocol. There is no easy way to maintain a connection or user information across multiple requests to the same server by the same client. Netscape addressed this issue in the early stages of the Web with the introduction of cookies. A cookie is a small piece of text data set by a Web server that resides on the client’s machine. Once it’s been set, the client automatically returns the cookie to the Web server with each request that it makes. This allows the server to place values it wishes to “remember” in the cookie, and have access to them when creating a response.

During each transaction, the server has the opportunity to modify or delete any cookies it has already set and also has the ability to set new cookies. The most common application of this technology is the identification of individual users. Typically, a site will have a user log in and will then set a cookie containing the appropriate username. From that point on, whenever the user makes a request to that particular site, the browser sends the username cookie in addition to the usual information to the server. The server can then keep track of which user it is serving pages to and modify its behavior accordingly. This is how many Web-based e-mail systems “know” that you are logged in.

There are several parts to each cookie, many of them optional. Here is the syntax for setting cookies:

image

The tokens enclosed in brackets are optional and may appear in any order. The semantics of the tokens are described in Table 16-7.

Table 16-7 The Anatomy of a Cookie

image

image

Cookies that are set without the expires field are called session cookies. They derive their name from the fact that they are kept for only the current browser session; they are destroyed when the user quits the browser. Cookies that are not session cookies are called persistent cookiesbecause the browser keeps them until their expiration date is reached, at which time they are discarded.

When a user connects to a site, the browser checks its list of cookies for a match. A match is determined by examination of the URL of the current request. If the domain and path in a cookie match the given URL (in some loose sense), the cookie’s name =value token is sent to the server. If multiple cookies match, the browser includes each match in a semicolon-separated string. For example, it might return the following:

image

Cookies in JavaScript

One nice thing about cookies is that nearly every browser in existence with JavaScript support also provides scripts with access to cookies. Cookies are exposed as the cookie property of the Document object. This property is both readable and writeable.

Setting Cookies

When you assign a string to document.cookie, the browser parses it as a cookie and adds it to its list of cookies. For example, the following sets a persistent cookie named username with value “thomas” that expires in 2012 and will be sent whenever a request is made for a file under the “/home” directory on the current Web server:

image

Whenever you omit the optional cookie fields (such as secure or domain), the browser fills them in automatically with reasonable defaults—for example, the domain of the current URL and path to the current document. It is possible, but not recommended, to set multiple cookies of the same name with differing paths. If you do so, then both values may be returned in the cookie string, and if so you have to check to see if you can tell the difference using their order in the string. Attempting to set cookies for inappropriate domains or paths (for example, domain names other than domains closely related to the current URL) will silently fail.

The cookie-parsing routines used by the browser assume that any cookies you set are well formed. The name-value pair must not contain any whitespace characters, commas, or semicolons. Using such characters can cause the cookie to be truncated or even discarded. It is common practice to encode cookie values that might be problematic before setting them in the cookie. The global escape() and unescape() methods available in all major browsers are usually sufficient for the job. These functions URL-encode and URL-decode the strings that are passed to them as arguments and return the result. Problematic characters such as whitespace, commas, and semicolons are replaced with their equivalent in URL escape codes. For example, a space character is encoded as %20. The following code illustrates their use:

image

image

When you assign a new cookie value to document.cookie, the current cookies are not replaced. The new cookie is parsed and its name-value pair is appended to the list. The exception is when you assign a new cookie with the same name (and same domain and path, if they exist) as a cookie that already exists. In this case, the old value is replaced with the new. For example:

image

The result is:

image

Reading Cookies

As you can see from the previous example, reading cookies is as simple as examining the document.cookie string. Because the browser automatically parses and adds any cookies set into this property, it always contains up-to-date name-value pairs of cookies for the current document. The only challenging part is parsing the string to extract the information you are interested in. Consider the following code:

image

Following is the value of document.cookie after these statements are executed:

image

If you are interested in the favoritecolor cookie, you could manually extract everything after “favoritecolor=“ and before “; jsprogrammer=true”. However, it is almost always a good idea to write a function that will do this for you automatically.

Parsing Cookies The following code parses the current cookies and places them in an associative array indexed by name.

image

Note that invoking unescape() on a string that hasn’t been escaped generally will not result in any harm. Unescaping affects only substrings of the form %hh, where the h’s are hex digits.

Consider the following example:

image

The output in Firefox is shown here:

image

And our cookies array contains the following:

image

However, in Internet Explorer, the outputs are as shown here:

image

and

image

As you can see, it is possible for cookies to exist without explicit values. Additionally, the representation of the cookie named “second” is different under Internet Explorer and Firefox. Though you should always use complete name-value pairs in the cookies set with JavaScript, some of the cookies the browser has might have been set by a server-side program over which you have no control.

Deleting Cookies

A cookie is deleted by setting a cookie with the same name (and domain and path, if they were set) with an expiration date in the past. Any date in the past should work, but most often programmers use the first second after the epoch in order to accommodate computers with an incorrectly set date. To delete a cookie named username that was set without a domain or path token, you would write the following:

image

This technique deletes cookies set with a value, but as previously discussed, some cookies can exist without explicit values. Such cookies require that the equal sign be omitted. For example, the following would define and then immediately delete a cookie without an explicit value:

image

With defensive programming in mind, you might want to write a deleteCookie() function that tries both techniques to delete cookies:

image

Remember that if a cookie was set with path or domain information, you need to include those tokens in the cookie you use to delete it.

Security Issues

Because cookies reside on the user’s machine, there is nothing stopping the user from modifying a cookie’s value after it is set by the server (or from creating fake values the server did not set). For this reason, it is never a good idea to keep sensitive information in a cookie without some sort of cryptographic protection. For example, suppose you set the username for a mail site in a cookie. Then, without any extra protection, there would be nothing stopping a user with the cookie username=fritz from changing the value to read username=thomas, thereby accessing someone else’s account.

The different techniques you can use to protect your cookies from unauthorized modification or creation are well beyond the scope of this book. Some server-side Web programming platforms can add cookie tampering protection automatically, but if you need to do it yourself, assume that nothing on the client side can be trusted and is likely manipulated. If you want to learn more about the proper handling of cookies and other application security challenges, a good starting place is the Open Web Application Security Project (www.owasp.org), which provides a document covering this issue, and a whole lot more.

Using Cookies for User State Management

Cookies are used to store state information. The kind of information you store in your cookies and what you do with that information is limited only by your imagination. The best applications of cookie technology enhance page presentation or content based on user preference or profile. Functionality critical to the operation of the site is probably not appropriate for cookies manipulated by JavaScript. For example, it is possible to write fully functional “shopping cart” code that stores state information in the client’s browser with cookies from JavaScript. However, doing so automatically prevents anyone who chooses to disable JavaScript from using your site.

Some simple applications are discussed briefly in the next few sections. We’ll use the extractCookies() function defined previously to read cookies.

Redirects

Oftentimes, it is useful to send your site’s visitors to different pages on the basis of some criterion. For example, first-time visitors might be redirected to an introductory page, while returning users should be sent to a content page. This is easily accomplished:

image

Note how the script first attempts to set a cookie in order to see if the user has cookies enabled. If not, no redirection is carried out.

One-Time Pop-ups

One-time pop-up windows are used to present users with information the first time they visit a particular page. Such pop-ups usually contain a welcome message, reminder, special offer, or configuration prompt. An example application targeting a “tip of the day” page that is displayed once per session is shown here:

image

If the user doesn’t have cookies enabled, we choose not to show the pop-up window. This prevents users from becoming annoyed by the pop-up if they frequently load the page with cookies disabled.

Customizations

Cookies provide an easy way to create customized or personalized pages for individual users. The user’s preferences can be saved in cookies and retrieved by JavaScript code that modifies stylistic attributes for the page. While server-side scripts often use cookies to customize content, it is usually easier to modify style characteristics in JavaScript. The following example allows the user to select one of three color schemes for the page (you can see the result in Figure 16-5.). While this particular example is rather simplistic, the basic concept can be used to provide very powerful customization features:

image

Figure 16-5 Using cookies for saving style customization

image

image

image


ONLINE http://javascriptref.com/3ed/ch16/cookiepreference.html

We could extend this example to save a selected style sheet or any other user preference. One interesting possibility would be to allow users to define if they want HTML5 or Flash features in a site and then have their preference saved.

Cookie Limitations

Because cookies are useful for such a wide variety of tasks, many developers are tempted to use them for anything and everything they can. While it is a good idea to provide the user with a maximally customizable site, the browser places limitations on the number and size of cookies that you can set. Violating these limitations can have a range of effects from silent failure to full-on browser crashes. You should be aware of the following guidelines:

• The total number of cookies a browser can store at one time is limited to several hundred.

• The total number of cookies a browser can store at one time from one particular site is often limited to 20.

• Each cookie is usually limited to about 4,000 characters.

To get around the limitation of 20 cookies per site, it is often useful to “pack” multiple values into one cookie. Doing so usually requires encoding cookie values in some specific manner that makes it easy to recover the packed values. While this technique increases the size of each cookie, it decreases the total number of cookies required.

One other issue to be aware of is that many users disable cookies for privacy reasons. Because persistent cookies can be set for an arbitrary length of time and they are tied to the domain that the script is running on, advertisers use them to track user browsing habits and product interest across multiple sites. Many people feel that this is an invasion of privacy. For this reason, you should use persistent cookies only if you really need them.


NOTE As this edition went to publication, the United Kingdom and EU have introduced regulations requiring cookie usage to be disclosed. We expect more regulations that may affect cookies in the future, so JavaScript developers would be wise to figure out how to handle cookies for more than just technical reasons.

Storage

DOM Storage has been introduced as a way around many of the shortfalls of cookies. Simply put, DOM Storage is a way to store key/value pairs in the browser. As the data is stored in the browser and not sent to the server with each request, it has performance benefits over cookies. It is also useful to use for offline applications. DOM Storage doesn’t have a fixed limit, but the specification recommends, and most of the browsers use, a 5MB limit per Web site. DOM storage does not send data back and forth to the server as cookies do, so there is still an important place for cookies.

Various forms of browser storage have been available in older versions of browsers, but now the focus is on localStorage and sessionStorage as defined in the W3C Web Storage specification. The sessionStorage object provides a way to save data in a single tab or window. This data will persist throughout the Web site, but not if the window is closed, and it will not persist across multiple tabs. This can be very useful if a user is doing different things in multiple tabs and you would like to keep the behavior separate. The localStorage object behaves more like cookies in that it persists throughout the Web site and in multiple windows and tabs. It will also persist if the browser window is closed and reopened. It does not get sent to the server, though, so larger data can be saved without performance worries. The nonstandardglobalStorage behaves very similarly to localStorage. However, it is now of the class StorageObsolete, so we will not look into it in great detail.

Both localStorage and sessionStorage contain the same properties and methods. They are used in an identical manner. These properties and methods are described in Table 16-8.

Table 16-8 Storage API Methods

image

Using the storage objects is quite easy. Mostly, the built-in methods provide all the functionality necessary. In these short code snippets, we’ll use the object storage. This can be replaced with either localStorage or sessionStorage. In order to add a key/value pair, simply call:

image

To access the value of an item:

image

In order to remove an item, call:

image

And to remove all items:

image

Finally, to print out a list of all items, it is possible to loop through the storage object and get the items:

image


ONLINE http://www.javascriptref.com/3ed/ch16/storage.html

image

IndexedDB

So far, all the storage options we have seen involve key/value pairs with simple strings set as the value. In order to store larger amounts of structured data, a database would be a more appropriate solution. The IndexedDB API offers a method of storing client-side data in an object store and allows for quick retrieval and querying. The database can be accessed offline, which not only helps enable offline browsing, but also makes offline data entry a possibility as well. One important thing to note about IndexedDB is that there are two APIs, one that is asynchronous and one that is synchronous. In most cases, the asynchronous API will be used because synchronous calls are only allowed via Web workers. When using the asynchronous API, a method call will return an IDBRequest object that has onsuccess and onerror properties that should be assigned callback functions.

At the time of this writing, IndexedDB is still an emerging feature. Due to this, it is necessary to check browser-specific prefixes when setting up to use IndexedDB. In addition, if the webkit prefix is found, a few other properties must be set to the webkit equivalent:

image

To start using a database, an open(name) call is performed with the database name passed in. If the database does not exist yet, a new one will be created. The database will be set as the event.target.result in the callback function.

When a database is created, it is automatically given the version number 1. In order to add or remove an object store, it is necessary to change the version number. In the onsuccess callback for the version number change, the structure can be modified. In our example, we compare our most recent version with the database’s version. If a user has an old database, the version number and the structure are updated to the most current. The createObjectStore(name, properties) method can be used to add a new object store to the database. Within theproperties object, keypath can be specified as the property that makes the record unique. The removeObjectStore(name) method can be used to remove an object store. Note that these two methods are synchronous and no callbacks need to be specified:

image

In order to add, modify, or delete records from the object store, a transaction must be initialized. The transaction will lock the specified store(s) so that the page can modify them without worry. The call to initialize the transaction must specify if the transaction is READ_ONLY, READ_WRITE, or VERSION_CHANGE:

image

Once the transaction begins, a reference to the store can be attained directly off of the newly created transaction variable:

image

To add records to a store, first create the object to add. Make sure the keyPath exists if it was specified in the store creation. Then it is as simple as calling the put(object) method on the store. Remember to hook up the callback functions if action is required after the addition is complete:

image

Modifying a record uses the exact same code. As long as the keyPath already exists, it will update that record rather than creating a new one.

Deleting a record is similar, except that it uses the delete(key) method:

image

Retrieving a single entry can be done using the get(key) method:

image

To retrieve multiple entries based on the index value, first a key range is created. This range sets the upper and lower bounds to retrieve. If specifying both upper and lower bounds, the bound(lower, upper [, lowerOpen, upperOpen ]) method can be used. ThelowerOpen and upperOpen are Booleans indicating if the results should include the key at the respective bound position. The default, false, indicates that it will be included:

image

In our example, to retrieve all records, we’ll just set a lower bound to 0, as all timestamps will be greater than 0. We won’t set an upper bound:

image

Next, a cursor is created with the keyRange as an argument. Events are associated with the cursor:

image

The onsuccess event handler will be called on the first record. The event that is sent to the event handler will contain e.target.result. You should check this value to ensure that it is not null. If it is not null, e.target.result.value will contain the first matching data object from the object store. After processing the data, call result.continue() to get the next matching entry from the object store:

image

Obviously, consulting the full example online would be useful for understanding these ideas in context, so take a look.


ONLINE http://javascriptref.com/3ed/ch16/indexedDB.html

AppCache

As we saw earlier in the chapter, it is possible to detect when a user is offline and present different content in that case. It is also possible to save many of a Web site’s resources so that a user can still use them when they are offline. These resources will be downloaded when the user is online and the browser is idle. Then, when the user goes offline, the user will be able to browse as normal. In order to specify the files that should be downloaded, an application cache manifest file is created. This file should have the .appcache extension, and the mime type should betext/cache-manifest. The manifest file is specified in the <html> tag of the page that will be downloading the other files. In many cases, it is worthwhile to put this in every page of your site:

image

The first line of the manifest file must be as follows:

image

After the opening line, there are three sections of a manifest file. These sections are preceded by CACHE, NETWORK, and FALLBACK.

The files that are listed under the CACHE line are the list of files that should be cached for offline usage. These files can also be listed right under the CACHE MANIFEST line:

image

Note the relative paths; it is also okay to specify absolute URLs, but the pages must be from the same origin as the Web page. It is not necessary to specify the file that includes the manifest file, as it is automatically cached, but if the same manifest file is included from multiple pages, all of them should be listed. Wildcards cannot be used in this section.

The next section is NETWORK. This section specifies the list of files that should not be cached. In this section, wildcards can be used:

image

The last section is the FALLBACK. In this case, two files are listed on each line. The second file will be shown if the first file is requested while the user is offline. Wildcards can be used in this section, so it is possible to redirect all offline activity to a single offline page:

image

One important thing to note about the manifest file is that comments are allowed by starting a line with a pound sign (#). This is important to note because an application cache will not be updated until the actual manifest file has changed. Even if the files have changed, unless the manifest file has changed also, the old resources will be presented to the user. A common way around this is to have a comment that represents the version number in the manifest file. Any time a resource is changed, the version number is updated and, therefore, the application cache is refreshed:

image

In order to access the application cache, the window.applicationCache object is used. The applicationCache has a number of events that fire at the various stages of the caching process. These events are shown in Table 16-9:

Table 16-9 Application Cache Events

image

It is also possible to directly check the application cache status by looking at the window.applicationCache.status property. This property will contain a number that corresponds to a constant value set in window.applicationCache. These values can be seen in the following example:

image

Finally, the applicationCache contains a few methods. The first is update(), which attempts to update the applicationCache. This will only update if the manifest file has been modified. Once the update completes, the applicationCache.status will be set toapplicationCache.UPDATEREADY. At this point, the swapCache() method can be called to swap the new files for the old ones. If swapCache() is not called after an update, the swap will be made on the next load of a page with the manifest set. If this method is called before the update is complete, an error will be thrown. Finally, the abort() method can be called to stop the download progress. A demonstration can be found online.


ONLINE http://javascriptref.com/3ed/ch16/applicationCache.html

Script Execution

Normally, scripts are executed in a synchronous manner at the time they are encountered in the markup. This ensures that any scripts that depend on one another are executed in the proper order and any page modifying or collecting script executes at the right moment. However, there are instances where this capability is not necessary. It is possible that the script is coming from another server and may have a long delay before loading. The whole page will wait for the incoming script before continuing.

In the case that a script does not need to run in the order presented, it is possible to add the defer attribute to the script tag. This will tell the browser to load the script after scripts that are not set to defer. It does still keep an order, though, and the defer scripts are loaded one by one in the order encountered. Note that defer is only meant to be used with external scripts, though some browsers do support it with internal scripts as well:

image

In this case, externaljs.js will load first, then externaljs-slow.php, and finally externaljsdefer.js. Figure 16-6 shows the difference between normal execution and setting the defer attribute.

image

Figure 16-6 Standard execution order versus defer order


ONLINE http://javascriptref.com/3ed/ch16/executionNormal.html


ONLINE http://javascriptref.com/3ed/ch16/executionDefer.html

As we saw with the defer attribute, it is possible to push off a script that is not necessary to run in the order encountered. However, even using defer, a slow script will hold up other scripts, as the scripts are still executed in a linear fashion. This is where the async attribute comes in handy. The presence of an async attribute will cause the script to load in an asynchronous manner and not hold up the loading of other scripts:

image

It should begin loading when it is encountered, but the page will continue parsing even if the script is not ready to execute. Like defer, this attribute should only be applied to external scripts. It is necessary to be careful to avoid using document.write when using async because the document may finish loading while the script is running.

As we can see from Figure 16-7, the load progresses the same as in the normal flow, except that the slow script is executed last.

image

Figure 16-7 Execution order with async set


ONLINE http://javascriptref.com/3ed/ch16/executionAsync.html

Web Workers

As mentioned in the previous section, JavaScript typically executes in a synchronous fashion. In the last section, we saw ways around this for initial loading of JavaScript code blocks, but what about code blocks that run in a page either triggered on user action or set on a timer? In the past, developers have used an immediate setTimeout() to simulate asynchronous function, but this does not give us concurrent execution, as only one piece of the script will be running at a time. The introduction of Web workers allows for work to be done in an asynchronous and concurrent manner and prevents intensive or background tasks from blocking the rest of the page. As always with asynchronous methods, watch out for dependency between scripts. It is important to note that workers cannot manipulate the DOM themselves. They must pass information back to the main page, and the page should make the DOM changes. They also must run from the same origin and have no access to global variables in other scripts.

Web workers are presented as script files that are run in the background. To create a worker, invoke the Worker constructor and pass it the path to the worker script:

image

To send data to the worker, call the postMessage() method on the worker.

image

Messages from the worker can be caught by catching the onmessage event. The event will contain an event parameter, which will contain a data property holding the message sent from the worker. Errors can be caught by listening for the onerror event:

image

The worker can start doing work immediately or wait for a message from the main page. To wait for a message, the worker listens for the onmessage event. Like messages from the worker, the message from the page will contain an event parameter with a data property. The worker sends messages to the page using postMessage().

image


ONLINE http://javascriptref.com/3ed/ch16/worker.js

image

image


ONLINE http://javascriptref.com/3ed/ch16/workers.html

Shared Workers

In the previous section, we saw how to hook a background worker script up to a Web page. The worker is tied directly to the Web page and all messages get sent directly to the page. This type of Web worker is also known as a “dedicated worker.” There is another emerging type of worker called a “shared worker.” This allows multiple Web pages to connect to a single worker and potentially to share variables. Shared workers are not currently supported in Firefox or Internet Explorer.

Creating the shared worker is identical to creating a worker except that a SharedWorker object is instantiated instead of a Worker object.

image

The next steps are slightly different. In all cases of using the worker, the actions are performed on worker.port instead of directly on worker. In addition, before the worker is used, the start() method must be called.

image

The last difference for the Web page is that it is necessary to listen to the message and error events via addEventListener instead of directly through the onmessage and onerror properties:

image

In the worker file, there are a few more differences to be aware of. The worker file listens for the onconnect event in order to initialize communication with the Web pages. The onconnect event handler is passed an event that contains a ports array. The current connection can be retrieved by accessing event.ports[0]:

image

The onmessage and postMessage are used on the port object instead of the global object as with a dedicated worker. The entire sharedworker.js file is shown in the following code listing. Note that the count variable is shared among all connections, so it will be properly incremented with each request to the worker:

image


ONLINE http://javascriptref.com/3ed/ch16/sharedWorker.html

In conclusion, we should point out something that should be obvious: the line between the complexity of standard desktop programming and Web programming is pretty blurry now. Call them threads or call them workers, they are still complicated. Chapter 17 will show this march to browser–operating system capability and performance symmetry continuing as we address controlling media in the browser, including features that may be native to the browser.

Summary

JavaScript’s Navigator object indicates the type of browser accessing a page, as well as many of its characteristics. By using the Navigator object, Screen object, and a few other Window and Document properties, we should be able to detect just about everything we would want to control, including technology usage, screen properties, and user preferences. Using JavaScript, we can then output appropriate page markup or redirect the user to another page using the Location object. It is also possible to simulate some browser facilities, such as button presses or preference changes, but there are potential security problems that need to be considered. We will continue to need these facilities as more and more powerful, though often emerging and proprietary, features are introduced as part of HTML5 or related specifications. We took a look at some of these in this chapter, including state, storage, and processing management. While browser detection can be very useful, especially when employing them to determine if we can adopt a new technology, there is also a great deal of sophistication involved with their use in a Web site. Developers should make sure to test these approaches thoroughly before moving them to a production Web application.