Chapter 16

HTML5 Scripting

WHAT’S IN THIS CHAPTER?

  • Using cross-document messaging
  • Drag-and-drop APIs
  • Working with audio and video

As discussed earlier in the book, the HTML5 specification defines much more than HTML markup. A significant portion of the HTML5 defines JavaScript APIs that are intended to work in concert with the markup changes. The goal of these APIs is to make previously difficult tasks easier with the ultimate goal of allowing the creation of dynamic web interfaces.

CROSS-DOCUMENT MESSAGING

Cross-document messaging, sometimes abbreviated as XDM, is the ability to pass information between pages from different origins. For example, a page on www.wrox.com wants to communicate with a page from p2p.wrox.com that is contained in an iframe. Prior to XDM, achieving this communication in a secure manner took a lot of work. XDM formalizes this functionality in a way that is both secure and easy to use.

At the heart of XDM is the postMessage() method. This method name is used in many parts of HTML5 in addition to XDM and is always used for the same purpose: to pass data into another location. In the case of XDM, that other location is an <iframe> element or pop-up window owned by the page.

The postMessage() method accepts two arguments: a message and a string indicating the intended recipient origin. The second argument is very important for security reasons and restricts where the browser will deliver the message. Consider this example:

//note: all browsers that support XDM also support iframe contentWindow 
var iframeWindow = document.getElementById("myframe").contentWindow;
iframeWindow.postMessage("A secret", "http://www.wrox.com");

The last line attempts to send a message into the iframe and specifies that the origin must be “http://www.wrox.com”. If the origin matches, then the message will be delivered into the iframe; otherwise postMessage() silently does nothing. This restriction protects your information should the location of the window change without your knowledge. It is possible to allow posting to any origin by passing in "*" as the second argument to postMessage(), but this is not recommended.

A message event is fired on a window when an XDM message is received. This message is fired asynchronously so there may be a delay between the time at which the message was sent and the time at which the message event is fired in the receiving window. The event object that is passed to an onmessage event handler has three important pieces of information:

  • data — The string data that was passed as the first argument to postMessage().
  • origin — The origin of the document that sent the message, for example, “http://www.wrox.com”.
  • source — A proxy for the window object of the document that sent the message. This proxy object is used primarily to execute the postMessage() method on the window that sent the last message. If the sending window has the same origin, this may be the actual window object.

It’s very important when receiving a message to verify the origin of the sending window. Just like specifying the second argument to postMessage() ensures that data doesn’t get passed unintentionally to an unknown page, checking the origin during onmessage ensures that the data being passed is coming from the right place. The basic pattern is as follows:

EventUtil.addHandler(window, "message", function(event){
 
    //ensure the sender is expected
    if (event.origin == "http://www.wrox.com"){
 
        //do something with the data
        processMessage(event.data);
 
        //optional: send a message back to the original window
        event.source.postMessage("Received!", "http://p2p.wrox.com");
    }
});

Keep in mind that event.source is a proxy for a window in most cases, not the actual window object, so you can’t access all of the window information. It’s best to just use postMessage(), which is always present and always callable.

There are a few quirks with XDM. First, the first argument of postMessage() was initially implemented as always being a string. The definition of that first argument changed to allow any structured data to be passed in; however, not all browsers have implemented this change. For this reason, it’s best to always pass a string using postMessage(). If you need to pass structured data, then the best approach is to call JSON.stringify() on the data, passing the string to postMessage(), and then call JSON.parse() in the onmessage event handler.

XDM is extremely useful when trying to sandbox content using an iframe to a different domain. This approach is frequently used in mashups and social networking applications. The containing page is able to keep itself secure against malicious content by only communicating into an embedded iframe via XDM. XDM can also be used with pages from the same domain.

XDM is supported in Internet Explorer 8+, Firefox 3.5+, Safari 4+, Opera, Chrome, Safari on iOS, and WebKit on Android. XDM was separated out into its own specification, which is now called Web Messaging and is found at http://dev.w3.org/html5/postmsg/.

NATIVE DRAG AND DROP

Internet Explorer 4 first introduced JavaScript support for drag-and-drop functionality for web pages. At the time, only two items on a web page could initiate a system drag: an image or some text. When dragging an image, you simply held the mouse button down and then moved it; with text, you first highlighted some text and then you could drag it the same way as you would drag an image. In Internet Explorer 4, the only valid drop target was a text box. In version 5, Internet Explorer extended its drag-and-drop capabilities by adding new events and allowing nearly anything on a web page to become a drop target. Version 5.5 went a little bit further by allowing nearly anything to become draggable. (Internet Explorer 6 supports this functionality as well.) HTML5 uses the Internet Explorer drag-and-drop implementation as the basis for its drag-and-drop specification. Firefox 3.5, Safari 3+, and Chrome have also implemented native drag and drop according to the HTML5 spec.

Perhaps the most interesting thing about drag-and-drop support is that elements can be dragged across frames, browser windows, and sometimes, other applications. Drag-and-drop support in the browser allows you to tap into that functionality.

Drag-and-Drop Events

The events provided for drag and drop enable you to control nearly every aspect of a drag-and-drop operation. The tricky part is determining where each event is fired: some fire on the dragged item; others fire on the drop target. When an item is dragged, the following events fire (in this order):

1. dragstart

2. drag

3. dragend

At the moment you hold a mouse button down and begin to move the mouse, the dragstart event fires on the item that is being dragged. The cursor changes to the no-drop symbol (a circle with a line through it), indicating that the item cannot be dropped on itself. You can use the ondragstart event handler to run JavaScript code as the dragging begins.

After the dragstart event fires, the drag event fires and continues firing as long as the object is being dragged. This is similar to mousemove, which also fires repeatedly as the mouse is moved. When the dragging stops (because you drop the item onto either a valid or an invalid drop target), the dragend event fires.

The target of all three events is the element that is being dragged. By default, the browser does not change the appearance of the dragged element while a drag is happening, so it’s up to you to change the appearance. Most browsers do, however, create a semitransparent clone of the element being dragged that always stays immediately under the cursor.

When an item is dragged over a valid drop target, the following sequence of events occurs:

1. dragenter

2. dragover

3. dragleave or drop

The dragenter event (similar to the mouseover event) fires as soon as the item is dragged over the drop target. Immediately after the dragenter event fires, the dragover event fires and continues to fire as the item is being dragged within the boundaries of the drop target. When the item is dragged outside of the drop target, dragover stops firing and the dragleave event is fired (similar to mouseout). If the dragged item is actually dropped on the target, the drop event fires instead of dragleave. The target of these events is the drop target element.

Custom Drop Targets

When you try to drag something over an invalid drop target, you see a special cursor (a circle with a line through it) indicating that you cannot drop. Even though all elements support the drop target events, the default is to not allow dropping. If you drag an element over something that doesn’t allow a drop, the drop event will never fire regardless of the user action. However, you can turn any element into a valid drop target by overriding the default behavior of both the dragenter and the dragover events. For example, if you have a <div> element with an ID of "droptarget", you can use the following code to turn it into a drop target:

var droptarget = document.getElementById("droptarget");
                   
EventUtil.addHandler(droptarget, "dragover", function(event){
    EventUtil.preventDefault(event);
});
                   
EventUtil.addHandler(droptarget, "dragenter", function(event){
    EventUtil.preventDefault(event);
});

After making these changes, you’ll note that the cursor now indicates that a drop is allowed over the drop target when dragging an element. Also, the drop event will fire.

In Firefox 3.5+, the default behavior for a drop event is to navigate to the URL that was dropped on the drop target. That means dropping an image onto the drop target will result in the page navigating to the image file, and text that is dropped on the drop target results in an invalid URL error. For Firefox support, you must also cancel the default behavior of the drop event to prevent this navigation from happening:

EventUtil.addHandler(droptarget, "drop", function(event){
    EventUtil.preventDefault(event);
});

The dataTransfer Object

Simply dragging and dropping isn’t of any use unless data is actually being affected. To aid in the transmission of data via a drag-and-drop operation, Internet Explorer 5 introduced the dataTransfer object, which exists as a property of event and is used to transfer string data from the dragged item to the drop target. Because it is a property of event, the dataTransfer object doesn’t exist except within the scope of an event handler for a drag-and-drop event. Within an event handler, you can use the object’s properties and methods to work with your drag-and-drop functionality. The dataTransfer object is now part of the working draft of HTML5.

The dataTransfer object has two primary methods: getData() and setData(). As you might expect, getData() is capable of retrieving a value stored by setData(). The first argument for setData(), and the only argument of getData(), is a string indicating the type of data being set: either "text" or "URL", as shown here:

//working with text
event.dataTransfer.setData("text", "some text");
var text = event.dataTransfer.getData("text");
                   
//working with a URL
event.dataTransfer.setData("URL", "http://www.wrox.com/");
var url = event.dataTransfer.getData("URL");

Even though Internet Explorer started out by introducing only "text" and "URL" as valid data types, HTML5 extends this to allow any MIME type to be specified. The values "text" and "URL" will be supported by HTML5 for backwards compatibility, but they are mapped to "text/plain" and "text/uri-list".

The dataTransfer object can contain exactly one value of each MIME type, meaning that you can store both text and a URL at the same time without overwriting either. The data stored in the dataTransfer object is available only until the drop event. If you do not retrieve the data in the ondrop event handler, the dataTransfer object is destroyed and the data is lost.

When you drag text from a text box, the browser calls setData() and stores the dragged text in the "text" format. Likewise, when a link or image is dragged, setData() is called and the URL is stored. It is possible to retrieve these values when the data is dropped on a target by using getData(). You can also call setData() manually during the dragstart event to store custom data that you may want to retrieve later.

There is a difference between data treated as text and data treated as a URL. When you specify data to be stored as text, it gets no special treatment whatsoever. When you specify data to be stored as a URL, however, it is treated just like a link on a web page, meaning that if you drop it onto another browser window, the browser will navigate to that URL.

Firefox through version 5 doesn’t properly alias "url" to "text/uri-list" or "text" to "text/plain". It does, however, alias "Text" (uppercase T) to "text/plain". For best cross-browser compatibility of retrieving data from dataTransfer, you’ll need to check for two values for URLs and use "Text" for plain text:

image
var dataTransfer = event.dataTransfer;
 
//read a URL
var url = dataTransfer.getData("url") ||dataTransfer.getData("text/uri-list");
 
//read text
var text = dataTransfer.getData("Text");

DataTransferExample01.htm

It’s important that the shortened data name be tried first, because Internet Explorer through version 10 doesn’t support the extended names and also throws an error when the data name isn’t recognized.

dropEffect and effectAllowed

The dataTransfer object can be used to do more than simply transport data to and fro; it can also be used to determine what type of actions can be done with the dragged item and the drop target. You accomplish this by using two properties: dropEffect and effectAllowed.

The dropEffect property is used to tell the browser which type of drop behaviors are allowed. This property has the following four possible values:

  • "none" — A dragged item cannot be dropped here. This is the default value for everything except text boxes.
  • "move" — The dragged item should be moved to the drop target.
  • "copy" — The dragged item should be copied to the drop target.
  • "link" — Indicates that the drop target will navigate to the dragged item (but only if it is a URL).

Each of these values causes a different cursor to be displayed when an item is dragged over the drop target. It is up to you, however, to actually cause the actions indicated by the cursor. In other words, nothing is automatically moved, copied, or linked without your direct intervention. The only thing you get for free is the cursor change. In order to use the dropEffect property, you must set it in the ondragenter event handler for the drop target.

The dropEffect property is useless, unless you also set the effectAllowed. This property indicates which dropEffect is allowed for the dragged item. The possible values are as follows:

  • "uninitialized" — No action has been set for the dragged item.
  • "none" — No action is allowed on the dragged item.
  • "copy" — Only dropEffect "copy" is allowed.
  • "link" — Only dropEffect "link" is allowed.
  • "move" — Only dropEffect "move" is allowed.
  • "copyLink"dropEffect "copy" and "link" are allowed.
  • "copyMove"dropEffect "copy" and "move" are allowed.
  • "linkMove"dropEffect "link" and "move" are allowed.
  • "all" — All dropEffect values are allowed.

This property must be set inside the ondragstart event handler.

Suppose that you want to allow a user to move text from a text box into a <div>. To accomplish this, you must set both dropEffect and effectAllowed to "move". The text won’t automatically move itself, because the default behavior for the drop event on a <div> is to do nothing. If you override the default behavior, the text is automatically removed from the text box. It is then up to you to insert it into the <div> to finish the action. If you were to change dropEffect and effectAllowed to "copy", the text in the text box would not automatically be removed.

image

Firefox through version 5 has an issue with effectAllowed where the drop event may not fire when this value is set in code.

Draggability

By default, images, links, and text are draggable, meaning that no additional code is necessary to allow a user to start dragging them. Text is draggable only after a section has been highlighted, while images and links may be dragged at any point in time.

It is possible to make other elements draggable. HTML5 specifies a draggable property on all HTML elements indicating if the element can be dragged. Images and links have draggable automatically set to true, whereas everything else has a default value of false. This property can be set in order to allow other elements to be draggable or to ensure that an image or link won’t be draggable. For example:

<!-- turn off dragging for this image -->
<img src="smile.gif" draggable="false" alt="Smiley face">
 
<!-- turn on dragging for this element -->
<div draggable="true">...</div>

The draggable attribute is supported in Internet Explorer 10+, Firefox 4+, Safari 5+, and Chrome. Opera, as of version 11.5, does not support HTML5 drag and drop. In order for Firefox to initiate the drag, you must also add an ondragstart event handler that sets some information on the dataTransfer object.

image

Internet Explorer 9 and earlier allow you to make any element draggable by calling the dragDrop() method on it during the mousedown event. Safari 4 and earlier required the addition of a CSS style -khtml-user-drag: element to make an element draggable.

Additional Members

The HTML5 specification indicates the following additional methods on the dataTransfer object:

  • addElement(element) — Adds an element to the drag operation. This is purely for data purposes and doesn’t affect the appearance of the drag operation. As of the time of this writing, no browsers have implemented this method.
  • clearData(format) — Clears the data being stored with the particular format. This has been implemented in Internet Explorer, Firefox 3.5+, Chrome, and Safari 4+.
  • setDragImage(element, x, y) — Allows you to specify an image to be displayed under the cursor as the drag takes place. This method accepts three arguments: an HTML element to display and the x- and y-coordinates on the image where the cursor should be positioned. The HTML element may be an image, in which case the image is displayed, or any other element, in which case a rendering of the element is displayed. Firefox 3.5+, Safari 4+, and Chrome all support this method.
  • types — A list of data types currently being stored. This collection acts like an array and stores the data types as strings such as "text". Internet Explorer 10+, Firefox 3.5+, and Chrome implemented this property.

MEDIA ELEMENTS

With the explosive popularity of embedded audio and video on the Web, most content producers have been forced to use Flash for optimal cross-browser compatibility. HTML5 introduces two media-related elements to enable cross-browser audio and video embedding into a browser baseline without any plug-ins: <audio> and <video>.

Both of these elements allow web developers to easily embed media files into a page, as well as provide JavaScript hooks into common functionality, allowing custom controls to be created for the media. The elements are used as follows:

<!-- embed a video -->
<video src="conference.mpg" id="myVideo">Video player not available.</video>
 
<!-- embed an audio file -->
<audio src="song.mp3" id="myAudio">Audio player not available.</audio>

Each of these elements requires, at a minimum, the src attribute indicating the media file to load. You can also specify width and height attributes to indicate the intended dimensions of the video player and a poster attribute that is an image URI to display while the video content is being loaded. The controls attribute, if present, indicates that the browser should display a UI enabling the user to interact directly with the media. Any content between the opening and the closing tags is considered alternate content to display if the media player is unavailable.

You may optionally specify multiple different media sources, because not all browsers support all media formats. To do so, omit the src attribute from the element and instead include one or more <source> elements, as in this example:

<!-- embed a video -->
<video id="myVideo">
  <source src="conference.webm" type="video/webm; codecs='vp8, vorbis'">
  <source src="conference.ogv" type="video/ogg; codecs='theora, vorbis'">
  <source src="conference.mpg">
  Video player not available.
</video>
 
<!-- embed an audio file -->
<audio id="myAudio">
  <source src="song.ogg" type="audio/ogg">
  <source src="song.mp3" type="audio/mpeg">
  Audio player not available.
</audio>

It’s beyond the scope of this book to discuss the various codecs used with video and audio, but suffice to say that browsers support a varying range of codecs, so multiple source files are typically required. The media elements are supported by Internet Explorer 9+, Firefox 3.5+, Safari 4+, Opera 10.5+, Chrome, Safari for iOS, and WebKit for Android.

Properties

The <video> and <audio> elements provide robust JavaScript interfaces. There are numerous properties shared by both elements that can be evaluated to determine the current state of the media, as described in the following table.

PROPERTY NAME DATA TYPE DESCRIPTION
autoplay Boolean Gets or sets the autoplay flag.
buffered TimeRanges An object indicating the buffered time ranges that have already been downloaded.
bufferedBytes ByteRanges An object indicating the buffered byte ranges that have already been downloaded.
bufferingRate Integer The average number of bits per second received from the download.
bufferingThrottled Boolean Indicates if the buffering has been throttled by the browser.
controls Boolean Gets or sets the controls attribute, which displays or hides the browser’s built-in controls.
currentLoop Integer The number of loops that the media has played.
currentSrc String The URL for the currently playing media.
currentTime Float The number of seconds that have been played.
defaultPlaybackRate Float Gets or sets the default playback rate. By default, this is 1.0 seconds.
duration Float The total number of seconds for the media.
ended Boolean Indicates if the media has completely played.
loop Boolean Gets or sets whether the media should loop back to the start when finished.
muted Boolean Gets or sets if the media is muted.
networkState Integer Indicates the current state of the network connection for the media: 0 for empty, 1 for loading, 2 for loading meta data, 3 for loaded first frame, and 4 for loaded.
paused Boolean Indicates if the player is paused.
playbackRate Float Gets or sets the current playback rate. This may be affected by the user causing the media to play faster or slower, unlike defaultPlaybackRate, which remains unchanged unless the developer changes it.
played TimeRanges The range of times that have been played thus far.
readyState Integer Indicates if the media is ready to be played. Values are 0 if the data is unavailable, 1 if the current frame can be displayed, 2 if the media can begin playing, and 3 if the media can play from beginning to end.
seekable TimeRanges The ranges of times that are available for seeking.
seeking Boolean Indicates that the player is moving to a new position in the media file.
src String The media file source. This can be rewritten at any time.
start Float Gets or sets the location in the media file, in seconds, where playing should begin.
totalBytes Integer The total number of bytes needed for the resource (if known).
videoHeight Integer Returns the height of the video (not necessarily of the element). Only for <video>.
videoWidth Integer Returns the width of the video (not necessarily of the element). Only for <video>.
volume Float Gets or sets the current volume as a value between 0.0 and 1.0.

Many of these properties can also be specified as attributes on either the <audio> or the <video> elements.

Events

In addition to the numerous properties, there are also numerous events that fire on these media elements. The events monitor all of the different properties that change because of media playback and user interaction with the player. These events are listed in the following table.

EVENT NAME FIRES WHEN
abort Downloading has been aborted.
canplay Playback can begin; readyState is 2.
canplaythrough Playback can proceed and should be uninterrupted; readyState is 3.
canshowcurrentframe The current frame has been downloaded; readyState is 1.
dataunavailable Playback can’t happen because there’s no data; readyState is 0.
durationchange The duration property value has changed.
emptied The network connection has been closed.
empty An error occurs that prevents the media download.
ended The media has played completely through and is stopped.
error A network error occurred during download.
load All of the media has been loaded. This event is considered deprecated; use canplaythrough instead.
loadeddata The first frame for the media has been loaded.
loadedmetadata The meta data for the media has been loaded.
loadstart Downloading has begun.
pause Playback has been paused.
play The media has been requested to start playing.
playing The media has actually started playing.
progress Downloading is in progress.
ratechange The speed at which the media is playing has changed.
seeked Seeking has ended.
seeking Playback is being moved to a new position.
stalled The browser is trying to download, but no data is being received.
timeupdate The currentTime is updated in an irregular or unexpected way.
volumechange The volume property value or muted property value has changed.
waiting Playback is paused to download more data.

These events are designed to be as specific as possible to enable web developers to create custom audio/video players using little more than HTML and JavaScript (as opposed to creating a new Flash movie).

Custom Media Players

You can manually control the playback of a media file, using the play() and pause() methods that are available on both <audio> and <video>. Combining the properties, events, and these methods makes it easy to create a custom media player, as shown in this example:

image
<div class="mediaplayer">
    <div class="video">
        <video id="player" src="movie.mov" poster="mymovie.jpg" 
               width="300" height="200">
            Video player not available.
        </video>
    </div>
    <div class="controls">
        <input type="button" value="Play" id="video-btn">
        <span id="curtime">0</span>/<span id="duration">0</span>
    </div>
</div>

VideoPlayerExample01.htm

This basic HTML can then be brought to life by using JavaScript to create a simple video player, as shown here:

//get references to the elements
var player = document.getElementById("player"),
    btn = document.getElementById("video-btn"),
    curtime = document.getElementById("curtime"),
    duration = document.getElementById("duration");
 
//update the duration
duration.innerHTML = player.duration;
                   
//attach event handler to button
EventUtil.addHandler(btn, "click", function(event){
    if (player.paused){
        player.play();
        btn.value = "Pause";
    } else {
        player.pause();
        btn.value = "Play";
    }
});
                   
//update the current time periodically
setInterval(function(){
    curtime.innerHTML = player.currentTime;
}, 250);

VideoPlayerExample01.htm

The JavaScript code here simply attaches an event handler to the button that either pauses or plays the video, depending on its current state. Then, an event handler is set for the <video> element’s load event so that the duration can be displayed. Last, a repeating timer is set to update the current time display. You can extend the behavior of this custom video player by listening for more events and making use of more properties. The exact same code can also be used with the <audio> element to create a custom audio player.

Codec Support Detection

As mentioned previously, not all browsers support all codecs for <video> and <audio>, which frequently means you must provide more than one media source. There is also a JavaScript API for determining if a given format and codec is supported by the browser. Both media elements have a method called canPlayType(), which accepts a format/codec string and returns a string value of "probably", "maybe", or "" (empty string). The empty string is a falsy value, which means you can still use canPlayType() in an if statement like this:

if (audio.canPlayType("audio/mpeg")){
    //do something
}

Both "probably" and "maybe" are truthy values and so get coerced to true within the context of an if statement.

When just a MIME type is provided to canPlayType(), the most likely return values are "maybe" and the empty string. This is because a file is really just a container for audio or video data; it is the encoding that really determines if the file can be played. When both a MIME type and a codec are specified, you increase the likelihood of getting "probably" as the return value. Some examples:

var audio = document.getElementById("audio-player");
 
//most likely "maybe"
if (audio.canPlayType("audio/mpeg")){
    //do something
}
 
//could be "probably"
if (audio.canPlayType("audio/ogg; codecs="vorbis"")){
    //do something
}

Note that the codecs list must always be enclosed in quotes to work properly. The following is a list of known supported audio formats and codecs:

NAME STRING SUPPORTING BROWSERS
AAC audio/mp4; codecs="mp4a.40.2" Internet Explorer 9+, Safari 4+, Safari for iOS
MP3 audio/mpeg Internet Explorer 9+, Chrome
Vorbis audio/ogg; codecs="vorbis" Firefox 3.5+, Chrome, Opera 10.5+
WAV audio/wav; codecs="1" Firefox 3.5+, Opera 10.5+, Chrome

You can also detect video formats using canPlayType() on any video element. The following is a list of known supported video formats and codecs:

NAME STRING SUPPORTING BROWSERS
H.264 video/mp4; codecs="avc1.42E01E, mp4a.40.2" Internet Explorer 9+, Safari 4+, Safari for iOS, WebKit for Android
Theora video/ogg; codecs="theora" Firefox 3.5+, Opera 10.5, Chrome
WebM video/webm; codecs="vp8, vorbis" Firefox 4+, Opera 10.6, Chrome

The Audio Type

The <audio> element also has a native JavaScript constructor called Audio to allow the playing of audio at any point in time. The Audio type is similar to Image in that it is the equivalent of a DOM element but doesn’t require insertion into the document to work. Just create a new instance and pass in the audio source file:

var audio = new Audio("sound.mp3");
EventUtil.addHandler(audio, "canplaythrough", function(event){
    audio.play();
});

Creating a new instance of Audio begins the process of downloading the specified filed. Once it’s ready, you can call play() to start playing the audio.

Calling the play() method on iOS causes a dialog to pop up asking for the user’s permission to play the sound. In order to play one sound after another, you must call play() immediately within the onfinish event handler.

HISTORY STATE MANAGEMENT

One of the most difficult aspects of modern web application programming is history management. Gone are the days where every action takes a user to a completely new page, which also means that the Back and Forward buttons have been taken away from users as a familiar way to say “get me to a different state.” The first step to solving that problem was the hashchange event (discussed in Chapter 13). HTML5 updates the history object to provide easy state management.

Where the hashchange event simply let you know when the URL hash changed and expected you to act accordingly, the state management API actually lets you change the browser URL without loading a new page. To do so, use the history.pushState() method. This method accepts three arguments: a data object, the title of the new state, and an optional relative URL. For example:

history.pushState({name:"Nicholas"}, "Nicholas' page", "nicholas.html");

As soon as pushState() executes, the state information is pushed onto the history stack and the browser’s address bar changes to reflect the new relative URL. Despite this change, the browser does not make a request to the server, even though querying location.href will return exactly what’s in the address bar. The second argument isn’t currently used by any implementations and so it is safe to either leave it as an empty string or provide a short title. The first argument should contain all of the information necessary to correctly initialize this page state when necessary.

Since pushState() creates a new history entry, you’ll notice that the Back button is enabled. When the Back button is pressed, the popstate event fires on the window object. The event object for popstate has a property called state, which contains the object that was passed into pushState() as the first argument:

EventUtil.addHandler(window, "popstate", function(event){
    var state = event.state;
    if (state){   //state is null when at first page load
        processState(state);
    }
});

Using this state, you must then reset the page into the state represented by the data in the state object (as the browser doesn’t do this automatically for you). Keep in mind that when a page is first loaded, there is no state, so hitting the Back button until you get to the original page state will result in event.state being null.

You can update the current state information by using replaceState() and passing in the same first two arguments as pushState(). Doing so does not create a new entry in history, it just overwrites the current state:

history.replaceState({name:"Greg"}, "Greg's page");

HTML5 history state management is supported in Firefox 4+, Safari 5+, Opera 11.5+, and Chrome. In Safari and Chrome, the state object passed into pushState() or replaceState() must not contain any DOM elements. Firefox properly supports putting DOM elements in the state object. Opera also supports the history.state property, which returns the state object for the current state.

image

When using HTML5 history state management, make sure that any “fake” URL you create using pushState() is backed up by a real, physical URL on the web server. Otherwise, hitting the Refresh button will result in a 404.

SUMMARY

HTML5, in addition to defining new markup rules, also defines several JavaScript APIs. These APIs are designed to enable better web interfaces that can rival the capabilities of desktop applications. The APIs covered in this chapter are as follows:

  • Cross-document messaging provides the ability to send messages across documents from different origins while keeping the security of the same-origin policy intact.
  • Native drag and drop allows you to easily indicate that an element is draggable and respond as the operating system does to drops. You can create custom draggable elements and drop targets.
  • The new media elements <audio> and <video> have their own APIs for interacting with the audio and video. Not all media formats are supported by all browsers, so make use of the canPlayType() method to properly detect browser support.
  • History state management allows you to change the browser history stack without unloading the current page. This allows the user of the Back and Forward buttons to move between page states that are handled purely by JavaScript.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.150.231