Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2009-204
$eventId
ID of event: wwdc2009
$eventContentId
ID of session without event part: 204
$eventShortId
Shortened ID of event: wwdc09
$year
Year of session: 2009
$extension
Extension of original filename: m4v
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2009] [Session 204] Building Co...

WWDC09 • Session 204

Building Compatible Websites Using Emerging Web Technologies

Mac • 48:37

Safari pushes the web forward by implementing the latest, most innovative HTML and CSS technologies. Adopt these technologies without creating multiple versions of your website to stay compatible with other browsers. Discover how to use object detection and fallback techniques to create a single web application that takes advantage of cutting-edge web standards and gracefully remains compatible with less sophisticated rendering engines.

Speaker: Vicki Murley

Unlisted on Apple Developer site

Downloads from Apple

SD Video (156.1 MB)

Transcript

This transcript has potential transcription errors. We are working on an improved version.

This is Session 204, Building Compatible Websites Using Emerging Web Technologies and my name is Vicki Murley, I'm the Safari Technologies Evangelist at Apple. So you have seen some pretty cool web technology this week. You saw a great presentation on CSS Effects, how to create rich user interfaces and add rich animations to your websites.

We had a great session on HTML 5 Audio and Video Elements, where you saw how easy it was to add audio and video to web pages and then integrate that media with other elements in the page. And just before this session we had another great session on HTML 5 Offline Data, so you saw how you could store data locally from your web application.

The one thing that is generally very high on the list of concerns for web developers is compatibility. So generally we think about compatibility in kind of a one to one relationship. Like this plug works with this socket or this mouse works with this computer or this website works with this web browser.

And this one to one relationship would be fine if we were back in 2002 for instance. Back then a little over 2% of people were using Netscape Navigator, a little over 1% were using something else, but the vast majority of people were using one version of Internet Explorer. Over the last couple of years the landscape for browser market share has definitely changed. Now almost 40% of people are using something other than Internet Explorer and even that Internet Explorer chunk is divided into different versions.

So lots of people visiting your website could be using you know, any different browser. So instead now from that kind of one to one compatibility relationship that I described, compatibility for websites and web browsers really means a one to many kind of relationship. If you want your website to get lots of traffic, have lots of visitors, have lots of users, you have to be compatible with a wide range of browsers. So that is the conundrum today.

We have all of these amazing web technologies and we want to make them available to users, to deliver the best possible user experience, but at the same time we want your site to be available to the largest number of users that is possible as well. So this is the problem that we're going to address today.

How to merge these two issues. The way we're going to address it, first we're going to talk about some common coding pitfalls and how to avoid them and after that we're going to move on to specific fallback techniques for each of the three technologies that I just mentioned, CSS Effects, HTML 5 Media Elements and HTML 5 Offline Data.

So let's get started with common coding pitfalls. Probably the number one most common coding pitfall that most people run into, that I've ever seen has got to be browser sniffing. And this is the practice of looking to see which web browser the webpage is currently being viewed in and then serving up code that is specific to that browser.

The way that people do browser sniffing is by looking at the user agent string. The user agent string is a string that is sent by the web browser to the web server when a webpage is requested and you can also see what the user agent string is in JavaScript if you execute user, navigator.useragent, ask Safari.

Safari will again return its user agent string. So you can fetch this user agent string in JavaScript and then do some parsing on that string to figure out exactly with browser and which version of that browser and on which platform you are running in. So let's take a quick look at the anatomy of a user agent string. So this is Safari 4's user agent string on 10_5_7 and it has some key components. The first is the Safari version number, it's version 4.0.

The second we have the Safari build number, it's 530.17. The next one is the WebKit build number, it's also 530.17. And then we also have some platform information. So this particular version of Safari is running on MacOS X 10_5_7. So you can look at this user agent string and determine which browser your page is being viewed in and serve up code that is specific to that browser.

To do that you have to parse the string. To take a closer look at parsing, we're going to go all the way back to 1998, because this is kind of when the whole browser sniffing, user agent string, examining that string, that's when all of this kind of started. At this time in 1998, there were really two browsers that almost everyone was using.

It was like a 55/45 split almoUst and those two browsers were Internet Explorer and Netscape Navigator. So we have an example here. We have both of their user agent strings and we have some code to examine those strings and set variables to let us know whether the user is looking at our page in IE or in Netscape Navigator. And what our code might look like, in pseudo code of course, is we might have three branches of our code.

One is the Internet Explorer branch, one is the Netscape Navigator branch and the third is sort of everybody else. So I may have some you know, it's 1998, so maybe I have an image rollover effect and I'm using document.all to do that, but with Netscape I have to use document.layers, so I have a different branch for that and my everybody else branch, they don't get a rollover at all. I'm just going to use a static image for everyone else. So you know, here's the idea.

We've got these three branches but what happens when the new information is added to the user agent string? So this was the Internet Explorer user agent string back in 1998, this is MS IE4, this is the user agent string of IE8 today, 2009. So you can see, a lot of change has happened in you know, more than ten years but those changes happened incrementally and the code that you're using to parse that string has to be robust enough to handle all of those unexpected changes.

You also can run into a situation where the version number of the same browser changes and this isn't you know, a problem that only happens in the past. This is currently happening as well. Opera is getting ready to ship Opera 10 and in their testing they found that some very high profile key banking websites, identified them as Opera version 1, not version 10. Just because of an error in the way that they were parsing the user agent string.

You can read more about that on one of their blog entries there. So we talked about you know, what happens if the user agent string changes, well there are other problems with this approach of browser sniffing. So what if new features are added? Well if it's supported in one browser, I have to add the code there, then maybe it's later supported in another browser, I'm going to add it there as well, modify my code again. The everyone else branch, it never gets access to the new feature so sorry for them. Also not quite as common but sometimes features are removed. So we all know document.layers is not around anymore.

Now that code path is totally broken. And often as we know now, especially in the last couple years, new browsers are sometimes released. So any new browser that would come out and you haven't updated your parsing code and the way you've structured these branches, it's just going to get, your kind of everybody else branch and it could be that the new browsers that have come out support every feature that you're looking for but the users that are visiting your site in those browsers are going to get a very minimal, even degraded experience.

So it becomes obvious as we think about the user agent string changing, new versions of existing browsers, features being added, features being removed, new browsers coming out, it becomes obvious that this approach becomes unmanageable, very quickly. Luckily there is a better approach and that is feature detection. And the premise of feature detection is that you test and branch on features, not the current browser that the page is being viewed in.

So a lot of the problems that we talked about user agent string changes, new versions of existing browsers, features being added, features being removed, new web browsers, a lot of these go away if you're using feature detection. It's not to say that feature detection is sort of the end all, be all solution and you're never going to have to update your code again and your life is going to be perfect, but it does alleviate a lot of the pain of unexpected changes and adopting new features in other browsers, et cetera.

So let's look at some common feature detection techniques. You can check to see if a certain object exists, you can check to see if a certain function exists if you want to use a particular feature. I would see if the open database method exists if I wanted to use the HTML 5 SQL API.

You can test a property like innerHTML or contentEditable. So you can test all sorts of things and branch your code accordingly. But there are some pitfalls that I would like to make you aware of. First of all let's look at this one. Here I'm calling window.opendatabase. I'm actually executing the function here.

I really don't need to. I just want to see if it exists. In this one I'm actually calculating the value of .interhtml. So if I had a very large webpage and I got all the .interhtml of that webpage, just to see if this property existed, that would be really bad. And also at times you can get false negatives.

So document.body.contenteditable, is actually checking to see not if content editable exists, but if it is set on the body and set to true. You know, it could be set to false or not set at all, which would evaluate to 0, but then that would be telling me that this feature isn't supported if I were to use this approach.

So there's actually a better approach for testing just for the existence of objects, properties and methods and that's to use In. So we can change all of these to use In if XMLHttpRequest with IN window, open database in window, enter HTML In document.body and content editable, indocument.body and we'll save ourselves from the problems that I just mentioned.

So this is a much better approach. Another best practice if you're using feature detection, always set construct your code branches so that you're branching to web standards first. Not only do you have a better chance of having the same rendering across multiple browsers that all support standards, so your website is likely to look and behave the same in more browsers if you're using standards, but also it's just you know, best practices to test probable conditions first. Lastly, make sure that you don't use feature detection as kind of a way to do browser sniffing you know.

We all know that looking at the user agent string is bad because parsing is often fragile, but you don't want to look at a given feature that you know only one browser implements and then have a long, long block of code all specific to that browser. OK, so that covers some common coding pitfalls and how to avoid them. Now we're going to get into specific fallback techniques and we're going to start off with CSS Effects.

[ no speaking ]

So many, many applications on MacOS X use animation and rich effects to enhance the experience for the user. So some really good example animations are when I minimize a window to the dock, it doesn't just disappear and then reappear in the dock, there's an animation that tells me exactly where it went.

In iPhoto, if I want to get a closer look at one of these photos, I can double-click it, it doesn't just appear at a larger size, it actually animates out to the larger size for me to view. People are coming to expect these same sort of rich interactions and rich animations in web applications as well. So here we have the same photo album in MobileMe and I have the exact same experience that I have on the desktop.

When I double-click a photo, it animates out to a larger size so that I can get a closer look at it. One of the technologies that makes this possible in web browsers is CSS Effects. So there are many different kinds of CSS Effects, there's text and image effects, there's CSS Transforms which allow you to translate, skew, scale or rotate an element in space on the page.

There's CSS Transitions which are just simple animations. And we also have CSS Animations which give you fine-grained control over the behavior of the animation path and there are also a couple dom events associated with CSS Animations, so that you can know when an animation has started, stopped or iterated.

So I'm not going to go too in depth on the details of how to implement CSS Effects in your web pages, because we already had a whole session on this earlier this week and if you weren't able to catch it, you'll be able to watch it on video through ADC and I highly recommend that you do that because it was a great session. Instead, we're going to dig into the fallback techniques for CSS Effects. We're going to tackle three areas.

First we're going to talk about Automatic Graceful Fallback, next we're going to talk about Dynamic Fallback with JavaScript and third we're going to talk about Media Queries. So let's get started with Automatic Graceful Fallback. So the concept here is incredibly simple. It could not be simpler. Automatic Graceful Fallback really just means that any CSS property that a browser doesn't understand, it just ignores it. It drops it. It doesn't render it.

That's it. That's the only thing that you need to know. What this requires though, this concept, it requires you kind of letting go of that idea that every website has to look exactly the same in any browser. So here's a great example. My text on the left is enhanced. It has a couple shadows behind it that are two different colors and it looks really cool. The text on the right doesn't have those shadows behind it, but it's still readable you know.

So this is a great example of how Graceful Fallback kind of works. Browser on the right doesn't understand these text shadow properties and so it just simply doesn't render them but the page is still functional. So to illustrate this idea we have a demo and we are going to invite Dean Jackson to the stage.

[ applause ]

I'm pretty sure when they think graceful they come straight to me. OK, so as Vicki said, CSS has this great feature where effectively it breaks easily for you. It ignores things that it doesn't know about. And it's important to realize that this was actually a conscious design decision from the start of CSS. Because the people writing the specifications wanted to be able to update the specification and innovate without having to wait for browsers to catch up. And also you're not always sure what type of browser you're going to be using.

You might have users that are on an old version of the current browser, say Safari 3, haven't upgraded to Safari 4 yet. Or they might be browsing with a completely different context such as a voice browser or you know have a completely different device characteristics or they might be stuck on a browser that has a huge market share but hasn't had many new features added to it for a while.

But so it would have been quite easy to sort of shame some browsers in this bit so I'm trying to avoid that. So what am I going to do here is I've got this blog, it's quite a prestigious blog, it's my WWDC blog and I believe I've stuck to the terms of the NDA by not giving out too much information. You can see it's pretty, it's a pretty standard, it's pretty minimal feel, something you want to do.

Now this is in fact the display you'd get on a legacy browser, sort of designed for the absolute base you know, lowest common denominator type thing. But it's a pretty common view. What I want to do is add that bling that Vicki mentioned on the slide. I want to add a few simple things in the CSS, using the new features of CSS, but I don't want to break the page. So there's three important rules I want to do. I don't want to ever touch the content of the page.

I'm not going to, let's assume that I can't touch the content. I don't own it in this case because it's my blog but I don't, can't touch it. Secondly, I'm not going to even touch the style sheet that's currently applied to the page. I don't want to change any of the style rules.

What I am going to do is add new style rules to the page in the new style sheet. The most important rule number three is, the new rules I add, every one of them has to gracefully degrade. So they have to be either ignored or have no effect on an old browser. So basically just adding functionality to the page.

You can sort of think of this as what you might be able to do tonight to sort of you know, bling up your page. Because it turns out believe it, it was a surprise to me, but lots of attendees at WWDC use modern browsers like Safari so. So OK, I'm going to show you what I do.

Like I said, I don't want to shame a browser, so I'm actually going to use a trick and normally you would just load you know, you'd load the new styles in your existing style sheet. I've just got a little button up here and it's got a little bit of JavaScript that adds a new style element to the page with the new rules. So I can switch between sort of on or off.

So here's what we're going for, here's where we're coming from. Like I said, normally you would just put the new rules. So let's just have a look at the style sheet. These are the new rules on the page. I'm not going to worry too much about the syntax because all this is documented on the developer.apple.com/safari website. Let me just point out some things.

So one thing that's quite obvious is we've got a custom style sheet. Now I guess most of you know that browsers have, there's a very limited set of fonts that can be used across on all platforms and I assume if you know a web developer they'll talk you to death about how annoyed they are that there's only a limited set of browsers. So what I've done here is I want to, I've added two new fonts to the page.

I want to have a different font for the heading and I've got a nice font down here for the, you see this is something that doesn't ship with the operating system. And all I've done in the CSS is I've linked to the OTF, the Open Type File. You can link to a True Type or Open Type file. I just give it a name and then I put it directly into my Font Family rule.

Now what's happened is because Safari supports embedded fonts, it sees this font first in the list and uses it. Other browsers that don't support the embedded font, just go on to the list and get the default fonts that I've already written for the page. You can see here I use Anivers, you can use any name you want and you can link to any file. Next up let's have a look at this element here, the blue navigation. Just added some rounded corners to it, as I toggle between it.

It's a really simple rule. I've just added a border radius of 12 pixels. Really simple one rule. On a browser that doesn't support border radius, it just gets ignored and you can see actually how the extensions to CSS works because I've also included the rule for border radius that Mozilla uses and this is running in Safari but because Safari doesn't understand the Mozilla rule it just ignores it. It doesn't have any different, it doesn't change the page in any way.

So sort of using the extensions in reverse. Next, I've got this bright and bubbly personality so I wanted to have a sort of glow effect on the header. This is the sort of purple little bit at the top and I could have used an image and that would have been quite simple, but let's say I wanted to cut down on the amount of resources I'm downloading.

I want to declare a gradient in CSS and again I won't get into the syntax, but here's the rule for the header element and I have a gradient, I just define, I wanted a circular gradient, a radial gradient, I define where it is and how big I want it to be and then I just say the two colors that I want to blend from and I get this sort of nice effect that you see is pretty common in lots of the Apple designs. Next up shadows.

Really wanted these sort of boxy elements to pop out from the page. You can see here got this nice shadow. I ... actually do I... no. But anyway, you can see there's a little shadow on the edge here. This is like again the simple, simple property to add, WebKit box shadow and I just say where I want the shadow to be, offset, how blurred I want it to be and I can specify color. Again, WebKit box shadow isn't supported on some other browsers, but that's not going to matter.

They'll just get the default fallback of no shadow. But it's not just limited to boxes. If you look really closely, its very subtle effect is that the text here has a shadow. So it's hard to see, if you look while the font's changing, you can see it actually just jumps out a bit from the background. It's got a shadow too.

If you've ever looked really, really closely at the iPhone UI, if you look at the top of the navigation bars, you'll see that the text, even though it's really small, has this very, very small shadow inside it, these text shadows can really add to the page without sort of being completely in your face. So just like box shadow, text shadow you don't even need the prefix for this one, same rules and you can apply any color.

Just the way shadows work in real life, well not really no. OK, another great example. I wanted to style the list here a little differently, this is just a plain list but on my enhanced page I wanted to have alternate rows in a slightly different background color. Now while I said, we mentioned that property rules are the things, if the browser doesn't understand it it'll just ignore it, in this case, background color is sort of supported by nearly every browser out there.

But it's not just property rules that gets ignored, here I've used a CSS selector. So I've used a CSS 3 selector called Nth Child and what this means is every odd numbered child in the element is going to have this rule applied to it and so if the browser doesn't support in this case the selector, all the property definitions within that selector are just ignored. Same way property rules are ignored, the selector rules can be ignored.

So in this case, Safari gets background color. I've also added a little transition because I wanted an effect where I say well, what happens when you put your mouse over it and I'll sort of slide it down and you probably would do it faster in real life, but I wanted this nice effect to give sort of interactive feedback. I didn't want it to just snap to a color.

And I can do this again because transition isn't supported. Now lastly to the cool one. Now you'll notice here the difference between this image, oh I've added sort of a feathered edge to it, I've also added a little pop effect, so as I put my mouse over it, it does this. In the old browser of course, it's not doing anything.

Sort of just an extra feature you can add. Now this one looks a bit complicated. We'll start with the mask. It's actually got quite a detailed syntax, but I've just basically added some images that I've put into the page that will cut out of the image and give you a blurred effect.

You can actually do any sort of shape you want. I also said that when the image is being drawn normally, I'm applying a transform at a scale of .97. What I've mean is I've just made the image a little bit smaller and when it's in the hover state, I make the scale 1.03, just a little bit bigger and I also give it this little 5 degree rotation. So I give it a twist. I've also added a transition, let's say over half a second, I want you to animate between these two values and that's what you get, this pop in and pop out effect.

So as you can see like that's all I did? The existing page hasn't changed. My grandfather who's still running an old browser is going to see my blog. I don't know why he would be reading a WWDC blog, but all the important users that are using modern browsers are going to get a better experience. So that's it.

[ applause ]

Thanks Dean. So just a couple points to reiterate from the demo. Don't forget, if you're using web font or embedded font, to always specify a fallback font as Dean did. Also if your layout is dependent on the metrics of your current font, make sure that the embedded font that you choose, the web font that you choose, has comparable metrics.

So here maybe I'm going to use this stencil font in my web page and my fallback font is Helvetica, well this isn't you know, quite as wide as the stencil font, so maybe Verdana would be a better choice for me. Also as Dean pointed out, pay attention to prefixes.

Prefixes are really in place so that we can get the style property out to early adopters before the final standard is in place and people can use these properties safely, essentially. What happens is we have the property now. When the property becomes part of a final standard, that property is dropped and your website remains compatible regardless of whether there were changes between where we first implemented the property with the prefix and when the final standard was ratified. So that covers Automatic Graceful Fallback.

Now let's move on to Dynamic Fallback with JavaScript. So this is very simple. We're actually using a technique that we saw earlier in the presentation. We're just looking to see, we want to see if CSS Transitions are supported. So we're going to check to see if the WebKit transition property is in document.body.style and if that expression evaluates to true, then transitions are supported. This is very useful because if you're already using JavaScript to do some animations, CSS Animations are often triggered by JavaScript. So it's very easy to go and modify your code to now use CSS Animations.

This is actually really important, especially when you consider devices with limited CPU power such as iPhone, that would benefit from the hardware acceleration that you get in a CSS Animation that of course would never be possible with a JavaScript animation. OK, lastly we're going to talk about media queries for CSS.

So a media query allows you to tailor your CSS based on the characteristics of the display and they generally consist of a media type, some that you may have heard of are ScreenPrint, TTY, TV, All is another media type and a list of media features. So in this case we're talking about CSS Effects, so our list of media features that we're going to talk about are transformed 2D, transformed 3D, transition and animation.

There's many more, but this is the list that we're talking about today. So here's an example where we have a media query that loads a given style sheet. If it's a screen device and 2D transforms are supported. So here is our media type screen and here is our media feature, transformed 2D. You can also use media queries as we just saw, to load a style sheet or you can query inline.

So here we have an example where we have a media query inline in our CSS style sheet and we're saying if it's a screen device and 2D transforms and transitions are supported, then make all my divs scale down by 80% but when I hover over them, scale them up by 20% greater that their original size. So you can do these inline and also as you notice, you can have a list of operators to compose more complex queries.

Here we're using And to say that we want a device that supports 2D transforms and transitions, but you can also use Or and Only I believe. So again, here's our media type and here is our list of media properties. OK, so that covers CSS Effects, now let's move on to HTML 5 Media Elements.

All right, so we're going to talk about a few things here. First we're going to just briefly touch on why HTML to embed media in web pages. Second we're going to talk about creating compatible static markup and after that we're going to move on to talking about creating compatible content on the fly. So why HTML? Well HTML Media Elements greatly simplify media playback on the web.

Not only for you the developer, you just have to add one line of code to now have a video in your webpage, but also for your users. They don't have to download a plug-in or install anything to be able to see video. Also, this is part of the HTML 5 standard.

It means that many, by virtue of being a standard, it means it's available for anyone to adopt and implement and in fact, many browsers out there today already implement these elements. It's also the best way to display video in Safari on all platforms. You can have the same exact code and get the optimized user experience whether you're on the desktop or in Safari on iPhone.

So you saw for example at a session earlier this week, how you could have rich interactions with other elements on the page if you were using HTML 5 Audio or Video Elements on the desktop and on iPhone we saw that same exact code running full screen, looking great on iPhone. That session that I was just talking about happened on Wednesday and so I'm not going to go into much more detail about how to use just the basics of HTML 5 Media Elements, but I suggest that you catch this one as well, via ADC on iTunes.

Catch the video. So let's talk about creating compatible markup. There's really two things that you need to think about. There's basic content fallback and there's also working with multiple media resources. So the basic content fallback is dead simple. It's basically whatever is nested in between the opening and closing video tags, is your fallback content.

One thing to note here is this fallback content is only displayed if the video tag is not supported. It's not displayed for things like errors. Like if it tried to load the video and the file was not found or the file was corrupt or broken or even if you're pointing to an unsupported codec in none of those cases is the fallback content displayed.

Only displayed if the video tag is not supported. Moving on to working with multiple media resources. So on the video tag you know, you may be used to seeing like an embed tag that has a type attribute. Well there isn't one of the video tag here. Instead we just have this source attribute and whatever we throw at it, it will attempt to play. So we sort of have built in dynamic support with this source attribute. Safari will support any installed linear codec supported by QuickTime, so that includes the default set of installed codecs and also any codecs that a user may install later.

Those will all work with the source attribute on the video tag. You may want to be able to show different encodings on different platforms or if users are viewing your page in different browsers that have different support. The way to do that is to use the source tag.

So you can specify a list of sources within the video tag and basically what happens is the user agent, the web browser iterates through the list and it just attempts to play the first encoding that it can. The first supported encoding. It doesn't continue after there's a failure, so back to those failure cases again.

If there's you know, the file is corrupt or not found or something, this isn't really a fallback technique in the sense that if there's a failure it doesn't just try the next one. You can also use on the source tag a type, the type attribute to specify a mime type and this is great because you can query within the web browser, whether or not the web browser is going to be able to play a certain type. So without having to go hit the server and touch the video on the server at all. So this is great. It really saves you a lot of network requests and makes things better for your users.

We talked about media queries before. You can use media queries here in the source tag as well, using the media attribute. So this is just an example. Maybe I have in for Safari on the desktop, I want to serve up Safaridemo.mp4, so I would look for a screen device maybe with a minimum device width of 600 pixels and maybe for another example I want to serve up a different file for Safari on iPhone, so I might look for a screen device with a device width of 340 pixels, for instance.

That's the device width on iPhone. OK, now let's move on to Creating Compatible Dynamic Content. So you can detect audio and video support in JavaScript. It is very simple. You might want to do this if you're generating markup on the fly or if you're toggling the visibility of a customized user interface, we'll talk more in more detail about that later. You also might want to detect on the fly, whether or not a given codec is supported. Might want to do that if you want to direct your users to a certain type of content.

So the way that we check to see if audio and video tags are supported at all is we just again use this technique that we used earlier in the presentation, we checked to see if HTML video element is in window. If it's not, then video is not supported.

So in the session earlier this week, they defined custom controls to play, pause, scrub the video, et cetera. If the video tag isn't supported, then your users aren't going to be able to use those controls to interact with the video. So in this case you would want to probably hide those controls, fallback to a plug-in and just use those boring plug-in controls instead. You can also detect in JavaScript as I mentioned, whether or not a given codec is supported. So you do this using the canPlayType method and you supply it a mime type. The answer that you're going to get might surprise you.

It's going to be either probably, maybe or no. The reason is though that all we're really supplying to canPlayType is a mime type and codecs, there's so many details that are so complicated, it's not really possible to know conclusively if a given user agent can play a given codec just from the mime type. So "probably" is probably the best answer that you're going to get. So here's an example of using canPlayType.

We're checking to see if my video element that I fetched earlier, can play the type video/MP4 if the value returned is not equal to probably, I'm just going to fallback to a plug-in and if it is supported I'm going to use the video element and maybe implement some custom controls and do some very advanced, cool stuff. OK, so that covers it for HTML 5 Media Elements.

Now let's move on to Offline Data. OK, storing data offline. What can you currently use to store data offline? There's not a lot out there for you, outside of browsers that implement HTML 5 Offline Data capabilities. You have cookies and you have syncing to the cloud. Those are pretty much your options if your browser doesn't support these HTML 5 features. So here in HTML 5, this is just a totally, extraordinarily new and different way of thinking about things. You know, we have CSS Animations well you can fallback to JavaScript animations maybe. There's not really a great substitute for things like local storage and session storage.

You could use a cookie but it's not as simple and easy. You also have the SQL API which you can use to store structured data in a local database. And you also have the Application cache which will store all the resources for your web application locally on the user's machine. The benefits here, they're open web standards so even if they're not implemented everywhere now, they are able to be implemented in any browser in the future. Your code complexity is greatly reduced.

It's much easier to use local storage or session storage. Just set a key value instead of doing some complicated cookie parsing. You have very fast data access because the data is right there on disk and it's also functional without a network connection. So without going into too much more detail about the nuts and bolts of how the basics of HTML 5 Offline Data, we just had a session about this.

So again, catch it on ADC on iTunes. So we talked a little bit about how offline data in HTML 5 is just a totally revolutionary technology. There's been nothing else like it up until this point. And so what is there really to fallback to? You know, there's not a while lot.

there/s really no substitute for the structured data storage that you're going to be able to get. There's really no substitute for storing all the resources for your application locally on the user's machine. So one thing that you could do is modify your user interfaces and modify your application to make sure that they work well even if offline functionality isn't supported.

So that's what we're going to talk about today. If you want to check for offline functionality, again we're going to visit the same concepts that we've been talking about throughout this presentation. If you want to see if local storage and session storage are supported, just look for localStorage or sessionStorage in window.

If you want to see if the SQL API is supported, just check for openDatabase in window. And also if you want to see if the offline application cache is supported, you can check for applicationCache in window. So to illustrate this idea, we would like to invite Beth Dakin back to the stage.

Hi everyone. Thanks Vicki. If you were here at the offline data session just about an hour ago, then you would have seen that my colleague Brady and I came up with a Checkers web application. So I'm going to show that to you now and show how you can make it gracefully fallback in browsers that don't support local storage.

OK cool. So here's our Checkers web application in Safari 4 and it's just this pretty fun little game and we implemented two local storage features. There's this Manage Games button is one of them. It auto-saves games as I play and I can manually save a game at any time. And then there's the High Scores feature too that it keeps track of high scores as you play the game like over time between sessions. So now I'm going to show you this same game in Safari 3.1, which I am going to load here from a script.

So this is Safari 3.1, which does not support local storage, an old version of Safari and so I can still play my game but you'll see that if I go to my Manage Games screen, it didn't auto-save anything and if I try to save a game, nothing happens. So really it's kind of silly to have these two features, these two buttons, the Manage Games button and the High Scores button showing at all in this browser or other browsers that don't support local storage.

So our solution here is just going to be to hide these buttons So let's take a look at the code. So I have, this is my JavaScript file that builds up the UI for the game and you'll see I'm adding all kinds of dizz and checkerboards and turn indicator lights and here's the relevant part to this.

I have these three functions to add the buttons and it's just the second two buttons that I don't want to include anymore, so I'm just going to add...

[ no speaking ]

... a conditional statement around them so that we'll only add these buttons when we have local storage support.

And I've been really careful with the way I've built up my application so that none of this code, none of this local storage code will run unless I click those buttons. So I don't have to worry about anything else. But I also added guards at all of those points, anytime that I use local storage just because in case anything changes, it's always good to have a guard in place so that we don't have a JavaScript exception anywhere.

So let's look at this again in Safari 3.1, you'll see the buttons have just disappeared and I can still play my Checkers game and if we go back to Safari 4 and reload, then I still have the buttons and I can still play Checkers and I can still access all of my great offline data features.

And so that's all.

[ Applause ]

Thanks Beth. That was really simple. Just like one line of code and it was fixed in any other browser that doesn't support local storage and that's the idea. So let's recap what you have learned today. So the first number one thing that we talked about was why you should not use browser sniffing to construct branches in your code.

Instead use feature detection. Next we looked at CSS and we saw that CSS enhancements are incredibly easy to add and add a lot of visual interest and polish to your website. We took a close look at HTML 5 Audio and Video Elements and we learned about simple fallback mechanisms and also some fine-grained control for how to handle different codec support.

And lastly, we talked about HTML 5 Offline Data and what a great feature it is and we saw that it's just incredibly simple to detect. So that is it for today. If you want more information you can contact me, Vicki Murley, the Safari Technologies Evangelist and also there's a wealth of documentation on the Safari Dev Center at developer.apple.com/Safari.