Media • iOS, OS X • 47:59
People want a great web experience that doesn’t drain their battery and provides the ultimate in scrolling responsiveness. Find out how you can tune your content for optimum performance and efficiency. Learn how to work with Safari techniques and web features that help you save power. Discover how to get the smoothest possible scrolling for your content.
Speakers: Simon Fraser, Tim Horton
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript has potential transcription errors. We are working on an improved version.
[Simon Fraser]
Good morning. This session is about Power and Performance, Optimizing your Websites for Great Battery Life and Responsive Scrolling. I'm Simon Fraser and I'm Engineer on the Safari and WebKit Team. So as you've heard about all week, one of our primary goals with OS X Mavericks was to get great power efficiency but still maintain and even improve responsiveness. So through all the levels of the operating system we've optimized for fast performance and long battery life.
And we know that battery life is really important to you because we all use MacBook's every day and you really want that all day battery. And we also know that you spend a lot of time browsing the web so you want a web browser that's super power efficient and yet still feels fast and responsive.
But that's not an easy thing for a web browser. Web browsers have to load complex pages. Those pages contain scripts that have to be executed. They load plugins and so on. So this is quite a challenge but this is a challenge that we've met head on in Safari 7 so we have a great new set of features in Safari 7 that give us much better power efficiency.
But we also need your help. In many ways we can only be as power efficient as the web pages that we load and display so we need your help to make sure that your pages are power efficient and that they also play well with Safari's new power saving features. So I'll be talking about that later on in the session.
So there are three main sections in this talk today. First of all I'll talk about the new power saving features in Safari. Secondly I'll talk about how you can make power efficient web pages, some new web API you can use, the tools you use to detect whether your page is using too much power and also some common mistakes that we've seen pages make that cause them to use too much power than the author intended.
And finally I'll talk about Safari's new response to scrolling which will give your users a buttery smooth scrolling experience on your web pages and how you can make sure that your pages get that best scrolling. So let me start by talking about these new power saving features in Safari. And there are two features that I want to talk about today. The first one is called App Nap for Safari tabs and the second one is called Power Saver.
So if you were in any of the energy related sessions yesterday you'll know that there's a new feature in OS X Mavericks called App Nap. Now the purpose of App Nap is to focus system recourses on the task that's most important to the user. So what we've done is we've applied App Nap to tabs in Safari. And this means we can Nap background tabs and that saves us a lot of power.
So how does this work? Well you may already know that in Safari we actually use a separate process for loading and rendering the web page and we call this the Web Content Process. New in Safari 7 now, each tab has its own web content process and that means that the pages in different tabs are mostly isolated from each other.
But it also means that we can now apply App Nap to the processes for each tab independently and of course now we can do that, we can Nap background tabs and this allows Safari to focus all its resources on the tab you're working with right there which makes it really responsive. So when can we do this napping of tabs?
Well in general we'll Nap any tab that the user can't currently see so that means background tabs, tabs that minimize windows on another space or with the screen servers on covering up Safari and in general any window which is what we call occluded which just means that it's covered up by something else and the user can't see it.
And there's one other case where we can Nap tabs and that's what we call idle windows. So the system can now tell us whether a window has been updated recently. And if it hasn't then we can apply App Nap to that web process which means that if a web page is loaded, maybe it's in the background, and you can kind of see part of the window but it's not actually, the page is not really doing anything. That means that page is also eligible for napping.
So what is the impact on your web page of being in a tab that has been App Napped? Well if you were in any of the energy related sessions you'll know that one of the worst things for power efficiency is the firing of timers. Every time a timer fires the system has to wake up, the CPU has to ramp up from a low power mode to a how power mode and all this is very wasteful. So one of the best ways that we can save power is to rate limit the JavaScript timers and those are setTimeout and setInterval in JavaScript.
If you're using requestAnimationFrame, which I'll talk about a bit later on in the session, that will also get rate limited. But when we were working on this we found a couple cases where tabs in the background are actually doing useful work or things that you want to continue.
For example there are some audio players that you know periodically load data dynamically as they're running and that's something you want to continue to work because you want that audio to keep playing audio in the background. So we actually detect that and we don't Nap tabs in that case.
And if tabs are also dynamically loaded content we won't Nap them. So what should you do to make sure that your web pages play nicely with App Nap for Safari Tabs? Well in general, nothing. We hope this will just work very transparently. But you should be aware that your timers could be rate-limited.
So that's App Nap for Safari Tabs. We think this is going to be great for people we call tab holders, the kinds of people who have lots of tabs open and lots of windows. Those kinds of people should really investigate Safari Reading Lists because that's a much better way of keeping track of all of those URL's.
But if you do have lots of windows and lots of tabs open now, Safari is able to Nap all those background tabs which means that it's really focusing resources on the tab you're currently working with and that makes you feel much more responsive. So the second new feature I want to talk about today is called Safari Power Saver. One of the things we found when we were investigating power usage in Safari, and this didn't really surprise us, is that plugins use an enormous amount of power.
And this is a problem we didn't have to solve in iOS. Now in OS X we could have simply stopped running plugins but the problem is that would result in a broken user experience. Many people visit a page and not really understand why it didn't work the way it used to because the plugins not running.
So we had to fix this in a way that was a bit smarter than that. And we came up with something that we call Power Saver. This gave us, the impact of turning on Power Saver we got an up to 35% reduction in CPU power consumption so it's really great energy saving.
So you know why do we need this thing? Well this is typical of the web pages you visit every day, like a typical news site or a blog site. And one of the things you may not really pay attention to is all the marginal content, all the peripheral content which you don't really care about, often it's adds. But what you may not realize is how much of this content is actually running through plugins.
So without Power Saver these plugins are running all the time and that is, of course, causing the CPU to be busy all the time. The CPU is always active. It gets warm. The fans come on. And of course that's all really bad for battery life. Now that we can pause these plugins the CPU usage is much lower and it has a really positive impact on battery life.
So I said before, we do this in a way that most users won't notice. It's unobtrusive. So we call it Smart Power Saving. And generally the way we do this is through three techniques. First of all when you visit a page in order to see like an internet video or something, some plugin is up front and center. We'll just continue to run that because you probably went to that page to see that thing anyway. So that we call a primary plugin and we'll just run.
Secondly, plugins are often used for things like playing audio and those plugins might be like really small or positioned somewhere else on the page and we'll just let those continue to run. And finally if the user interacts with a plugin on the page we'll remember the fact that they clicked that plugin on the page and next time they visit we'll just start that plugin automatically.
So what's Power Saver doing under the hood? Well when a page loads we let the plugins run but just for a short time and we let them run until they've given us a useful snatch of their contents and then we actually replace the plugin in the page with that snapshot.
And then if the user clicks the snapshot we'll go ahead and recreate the plugin, we'll pass that click event through and then the plugin we'll run as normal. And in many cases the user won't even notice. When we do pause plugins, if the user happens to hover over one of those plugins, Safari will put up this banner that just indicates that it's done something to that plugin but the user can just go ahead and click anywhere on the plugin and the plugin will start running.
So what should you do to make sure that your pages play nicely with Safari Power Saver? Well of course the most obvious thing is to use fewer plugins. If you can use one of the great HTML 5 technologies like the audio and video elements or Canvas or Web Audio or SPG or any of those for rich media presentations, those are always going to be more power efficient then a plugin because we've optimized those really heavily.
We have seen a few cases where pages that communicate with a plugin through script have had problems. And generally in these cases the page is not prepared to handle the fact that there may be more than one instance of a plugin for a given object or embed element over the lifetime of the page.
So the page needs to be prepared for plugins to be destroyed and recreated. And generally what this means is that the plugin should request state for the page when it starts up, rather than the page pushing state down to the plugin and expecting that to persist. So that's Safari Power Saver. And it gives us a really great energy saving and we hope that most of users will not even notice that it's there.
So I talked about these two new features in Safari 7 that combined give us really great power savings. But we also need your help. We need you to make sure that your pages are really power efficient and we need to do this because we can't just have Safari do power saving things that break the way pages work.
So I'll tell you about how to make power efficient web pages now. Firstly we've got a couple new pieces of web API which you can use to make sure your pages save power. Secondly I'll talk about the tools you can use to detect whether your pages are using more power than they should. And finally I'll talk about some common mistakes we have seen that really is a pitfall that some pages fall into that cause them to use a lot more power.
So first the new web API, now there are two of these I'd like to talk about, the first is called Page Visibility and the second is the requestAnimationFrame API. So let's talk about Page Visibility. This is a W3 specification that allows a page to detect when it's hidden and get notified when the visibility changes.
Now I've already talked about hidden pages in the context of App Nap for Safari Tabs. The reason we have or the way you'd use Page Visibility is to make sure that your page can take its own steps to conserve power when it's in the background. And by in the background I mean the same things that I talked about in the context of App Nap for Safari Tabs. Any pages that are minimized or hidden or occluded.
So there are some things that the browser does to hidden pages that are kind of like App Nap for Safari Tabs. The browser will also rate-limit JavaScript timers and we're not the only browser that does this. Other browsers do this too. But there are some things that we know are related to visual updates like requestAnimationFrame and CSS Transition Animations. And we know with hidden pages none of this work has to happen so we pause both of these.
So how do you use this API? Well there are two pieces to it, there's request in the current state and there's getting notified about state changes. So to query the current state there are a couple of new attributes on the document object. There's a simple Boolean hidden property, which obviously is just true or false, and then there's a property that returns a string and this property is called visibility state. And it returns the strings hidden, visible or prerender. Now hidden and visible are pretty obvious but let me explain prerender for a second.
Now in Safari when you're typing the location field, the browser's actually going to preload the top head, the most likely thing that you're going to load so that when you actually hit the return key you've already mostly loaded that page and we can show it right away and this feels really fast.
But this means that the browser is actually loading a page in a way that's not currently visible to the user. And the way we indicate this to the content is to have document.visibilityState return prerender. And you might want to make use of this in your pages to do things like not counting add impressions in that case.
So the second piece of this API is a way to listen for state changes. And the way you do that is by adding an event listener just as you would for user event. So you add an event listener on the document. The events called the visibility change. And then in your event handler you simply check one of these properties like document.hidden to know whether your page is hidden or not. So that's page visibility new in Safari 6.1 and 7 and in iOS7.
So before I talk about the next new API I want to give a little bit of background on how people do animations in JavaScript currently. And pretty much the only way traditionally to do this would be to use one of the timing function, setTimeout or setInterval. But let's have a couple of problems.
The biggest problem is you really don't know how often to file those timers in order to get smooth animation. You don't really know what the display frequency is. You don't know what else the browser is doing. And often we see content that calls set time out with a 0 time out. And that's actually really bad. So under the hood 0 is actually clamped to 4 milliseconds. But most of our displays update at 60 hertz so that's a 16.7 millisecond interval.
But if you're generating frames every 4 milliseconds, it means that 3 out of every 4 frames is just never shown to the user so that's a real waste of power. Now the second problem with using timers is the browser can't turn them off, it can't optimize them away because it really doesn't know what you're doing inside that JavaScript callback. So requestAnimationFrame is designed to solve these two problems.
So it's a Web API for animation and it consists of two methods on the window object requestAnimationFrame and then there's a function that you can use just to cancel an early request. And its advantages though of using timers are twofold. First, the browser handles the display-refresh problem. It will call your request for animation during callback at just the right time so you can get work done to show up at the next display cycle. And secondly because we know that requestAnimationFrame is being used for visual updates we can pause it in background tabs. And requestAnimationFrame is described in the W3C Animation Timing Spec.
So before requestAnimationFrame you probably had an animation loop that looked something like this. You have a do animation function. It probably did some date math to figure out where it was in the animation timeline and then it would update the display and then call set timeout to get called again. And you know the author here has guessed that 60 milliseconds is a good interval.
So requestAnimationFrame is pretty much a drop in replacement for that. But it actually helps you a bit more. Your call back gets passed a time value which is the time in milliseconds since the page started to load so instead of doing date math you can just use that.
But the other thing to note is that requestAnimationFrame is just a one shot call back, just like setTimeout. So if you want to get called for another frame of animation then you just call requestAnimationFrame again inside your callback. So doing this will enable your callback called basically just before every display refreshed so you can do really smooth animation in JavaScript.
So requestAnimationFrame you may be familiar with as WebKit requestAnimationFrame in earlier releases but now it's just requestAnimationFrame and it's in Safari 6.1 and 7 and iOS7. So what should you do with these new JavaScript API's to make sure that your pages use less power? Well the most important thing is to use the page visibility API to stop doing work in pages that the users can't see.
For example, you might have a ticker on the page that's fetching data from the server to update stock prices or something like that and there's no point in doing that work in a background tab so you can just use page visibility to turn off that. And that also means that your server load is reduced so that's great on both sides. If you do need to use JavaScript for animation then you should use requestAnimationFrame instead of just doing setTimeout or setInterval.
One slight wrinkle we've seen here is you may remember that I said that CSS Transitions and Animations are paused when a tab is either in the background or for example when the user switches bases and the Safari window ends up being now visible. And we've seen this trip up a couple of pages where the page was relying on the fact that it would receive transition end events or animation end events in order to progress its business logic.
So there's a general rule here for well behaving pages which is to separate out the business logic from the visual updates. So in hidden pages you can let the business logic continue but will be able to save power by not running those visual updates. So at this point I'd like to invite my colleague Tim Horton onto the stage to show we can take a power inefficient page and fix it using page visibility. [Applause]
[Tim Horton]
So I'm Tim Horton. I also work on Safari WebKit with Simon. And so if any of you guys were in What's New in WebKit yesterday you met Enrika [phonetic] and you know that she really likes to cook. [laughter] So what you don't know, or may not know, is that she also has a giant pizza oven in her backyard and it takes a while for this oven to heat up.
So she's installed a thermometer into it, an internet connected thermometer into it, and we've written a web app so she can, from inside her house, monitor the temperature. So I'm going to show that to you now. So here it is. You can see the pizza oven is heating up.
This is a graph of temperature over time. This is temperature on a 500 millisecond JavaScript timer. The green ticks at the top display every time we fetch the temperature from the thermometer. So let's look at the source code for a second. It's just using setInterval, very basic. Just straight forward JavaScript timing, nothing fancy. So let's see what happens right now when we switch tabs.
So right now in the background the other tab, the timers are rate limited, as Simon mentioned, but we're still doing updates occasionally and you can see the green ticks coming at a lower rate but we're still getting data. So we're still not being a great power citizen here, right, we're still doing updates in the background. And we can do better. We can use 0 power in the background by adopting the page visibility guide that was just mentioned.
So let's replace this function, this setup function that just starts the timer explicitly with one that instead respects page visibility. We listen to the page visibility page event and when it changes we start and stop our timer, very simple, not much code. Let's refresh and now let's do a spaces swipe.
My friends just got back from Bali so I was checking out the Island to see where they had been. Now let's swipe back. And you can see here while the page was hidden we did no updates at all, no green ticks. You'll also notice while it was hidden the graph kind of looks broken. That's a big oopsy on our part. So maybe we should fix this.
So luckily the thermometer that Enrika chose stores five minutes of data back in time so we can ask for all of the temperatures since the last time we updated, when we come back from hiddency. So let's just make that simple change just to make our graph look more correct, and this is just, yeah.
And now let's see what it looks like. So now in the background we're doing no work at all but when we come back we've filled in the graph and it looks perfect. Now just pretend you remove the grey and the green and you just got the graph. It looks perfect. And the background uses no power. So now you've seen a simple example of how to use the page API to make a very effective power and bandwidth efficient website. Back to you, Simon. [Applause]
[Simon Fraser]
Thank you, Tim. So I talked about the new web API available to you so that your pages can use less power in the background. Now let me talk about how to figure out if your pages are using too much power. And there are two levels of tools here that I want to talk about. There are tools provided by the system and then there's some tools in Safari.
So first briefly I'll talk about the system tools. OS X Mavericks now has an energy tab and activity monitor. And so this is the kind of thing that users would go to to find out which applications have recently been using a lot of energy. So it's actually really bad for Safari to shop in this list. You don't want your site to be the site that causes Safari to be using all the power here.
Similarly in the battery menu we have a listing of applications that have been using power and again it's really bad if Safari is here so make sure your pages don't cause that. Of course as web developers you're probably more interested in the tools that are closer to your web content. There were a couple of great sessions yesterday on Safari Web Inspector and I encourage you if you didn't go to those to go back and watch the videos.
So briefly, to use the web inspector for investigating power issues you would enable the develop menu and then access the web inspector through Safari's develop menu. And then the tab that's most interesting in terms of power usage is the timelines tab. So you can switch to that tab and then you would start recording a timeline by pressing the button.
And then interact with your page in a way that you think is interesting, maybe you can scroll the page or reload it and see what happens at loading time or interact within other ways. So what this timeline panel shows you is all the activity that the page has been doing, loading resources, the layout and rendering work that the engine is doing, all the JavaScript is running, the events that are firing.
Now you may think that the amount of power used by the page is going to be roughly proportional to the amount of JavaScript that's running but that's actually not usually the case. Usually the most common cause of badly behaving pages that we see are interactions between the JavaScript and layout and rendering so I'll talk about some of those in the context of common mistakes we've seen that web pages make. And there are two basic areas here I'd like to talk about. First of all pages that cause too much layout to happen and secondly pages that just paint too much.
So what is layout? Well layout is the process by which the browser takes the [inaudible], applies style from the CSS and then computes the positions and the sizes of all the boxes and lays them out on the page. And that's quite an expensive process. And we've optimized this a lot. And we've also optimized when the browser does layout. We want to layout as infrequently as possible so that you know we're not doing excess work and generally we can layout just before we paint stuff on the page.
But there is a problem here and that's that there are various properties and functions exposed to JavaScript that force us to eagerly do layout. And there's quite a few of these. But there's a general theme which is they're all geometry related. They're all properties that are getting things like sizes, widths and heights and positions and scroll information and stuff like that.
But when JavaScript accesses any of these properties or calls these functions, the browser has to, if layout is currently what we call stale, the browser has to go ahead and do a layout so it can give you the correct answer to the question that you're asking and that can be expensive. Now when pages do this we tend to see a patent like this in the inspector. There'll be a repeated cycle of invalidate styles, recalc style, invalidate layout and layout.
And generally the kind of JavaScript that causes this problem is some JavaScript that has a loop and this is very common. And the loop does two things generally. There'll be a call to one of these geometry related functions like offsetHeight and that's the point at which the browser has to make sure the layouts up to date. Now the first line through the loop you're probably fine.
But then later on in the loop it's common for the JavaScript to invalidate style by changing something like you know food.style.something=whatever. So that invalidation means that next time through the loop the offsetHeight is going to have to recompute all the layout information and that's what gets expensive. So this is not good.
Now generally the way you fix this is to break this work into two separate loops. First you should fetch all the geometry information and then you can batch all of the style changing later on and that setup should just result in one layout, which will be much more efficient.
Now the second issue we see fairly often is pages that paint too much and there's one very common cause of this which is people leaving animated images in their background style. It's quite common for pages to have an animated image in the background style and then load stuff on top of it and they assume because the animated image is covered up the browser is going to be able to stop painting it. Well in many cases we can. We've done a lot of ultimizations to detect when animated images are covered up so we don't have to animate them. But we can't detect all the cases.
So the best advice I have for you is to make sure that you don't leave these animating images in your style once you finish loading a content. This is especially true on pages that have the infinite scrolling model. We see pages where there's an animated spin and right at the bottom it's normally off the screen and even this can cause painting to happen for reasons that I'll mention later on.
But of course the real way to tell if you're painting too much is to look in the web inspector. And if you see in the layout and rendering timeline a lot of painting like this, it means you've probably got one of those animated images sitting on your page so you need to go and find it and remove it.
So I've talked about a couple of common mistakes but there's a more general theme here which is to use the right tool for the job. If you're doing animation, CSS transitions and animations are always going to be more efficient then running animations in JavaScript. And if you need to do animations in JavaScript, requestAnimationFrame is much better than using timers because we can pause it and it gets the right display update frequency. There are many other cases where you might be running JavaScript right now but you could be using something that's built into the browser. If you're doing responsive layouts that change depending on window size, for example, you should be using CSS Media Queries instead of running JavaScript.
And there are a number of new layout models in WebKit now which Beth talked about yesterday in the session on What's New in Safari and WebKit. For example, CSS Flexible Boxes. So you can use these to get interesting layouts that change as the page changes size rather than running JavaScript. And there's a bunch of other features like this too. Beth yesterday also talked about CSS calc. So this is another great way where you can ditch in JavaScript, use a feature that's built into the browser and automatically get better power efficiency that way.
So I would like to take a moment to talk about Safari Extensions. If you are a Safari Extension Developer it's extremely important that your scripts don't fall into any of these common pitfalls and you make sure your scripts are super-efficient. And this is because your extension can inject scripts into every page the user visits.
There's something else that's important for extensions which is that extensions use the page visibility API to make sure that they're only doing work in pages that the users can see. There's also another reason that extensions should use page visibility. You remember I talked about the prerender state earlier and how that's involved in the browser preloading the top head? Extensions need to be very careful to not show UI to the user for pages that the user can't see.
So extensions need to check document.hidden and only show UI if the page is actually visible. So I talked about two new power saving features in Safari and I've talked about how you can make power efficient web content and how to detect where your content is less power efficient then it should be.
But you might think with all this talk about saving power they're actually going to have worse performance, maybe we need to you know sit power more carefully and we won't be as good. But in many ways, or in some cases, you can actually get better performance by using a different algorithm or actually using a new architecture that makes better use of the hardware and we've done this with responsive scrolling in Safari so I'd like to talk about that now.
Now people love to scroll. Now we all have iOS devices where the scrolling is buttery smooth and now we have track pads on our desks and on our notebooks. You feel that really direct connection with the web content and you want the web content to be really responsive and scroll right away. So it was very important for us that Safari had extremely responsive scrolling. But to explain how this works I need to step back a moment and explain how scrolling worked in the old days.
So it used to be that when you scrolled the browser would simply copy a chunk of bits up in the window and then it would paint the exposed strip at the bottom. So every scroll would be copy paint, copy paint and that all happened synchronously on the main thread.
But that also meant that if the web page was busy doing something like loading or running JavaScript, it means the web page could block scrolling and that would give a less than great user experience. So we knew we had to get to a model in Safari where we could scroll without doing painting. So I'll explain how that works now.
Instead of painting the page into the window we actually paint the page into a series of tiles. And these tiles are actually core animation layers under the hood. But it now means that we can do scrolling not by painting but simply by moving those tiles around. So the implementation is actually just setting the position of the layer that contains the tiles and that means scrolling is just changing a layer position. And that also means that we're leveraging the GPU because the window server is compositing this layer tree and that makes great use of the hardware so this is really efficient.
It does mean that we have to keep some extra tiles around so that we always have content that is ready to scroll into the window and that ties back to that problem with animated images I talked about before where an image might be off the page but still causing painting and that's because it might be on one of these tiles that we're keeping up to date so we can scroll within anytime. So that explains how we can scroll without painting but how do we scroll in such a way that the web page can't interfere, that can't block the scrolling?
And to explain that, let me get a bit nerdy for a minute and explain our Threading model in Safari. So of course there are two processes involved that I talked about the web content process earlier. But the user events initially go to this Safari application. But Safari then sends those events directly to the web content process and they're received in the web content process by a thread that we call the event thread that is always ready to receive those events. It's never blocked by anything else.
And then the event thread simply bounces those events to what we call the scrolling thread and the scrolling thread is the one that's changing the layer position there. And all this can happen even if the main thread is busy doing layout and painting. So this means that we can do scrolling even when layouts happening or the page is busy doing other stuff. So this is what gives us our really responsive scrolling.
Now this is all fine when the page is simply you know block of pixels that moves up and down but there are various features in CSS that makes scrolling a little more complicated than that. So let me just quickly explain some of those. The first one is fixed position, which I'm sure you're all familiar with as web developers, the position fix property.
And of course it's very common, for example, his social link aside, that the author has styled with position fixed and when the page scrolls of course that social link stays in the same place. Standard position fixed behavior. But the browser has to understand this now and be able to analyze the page and break the page up into a series of layers so it knows how to move all those layers around when you scroll and keep the right behavior.
There's a second sort of special scrolling behavior in CSS and that's fixed backgrounds, backgrounds that have the background attachment fixed style in them and these are also quite common. For example the author of this page may have put a background image on the page by starting the body in this case. And then they said they wanted background detachment fixed. And that means that when the page scrolls the background image actually stays in the same place in the view port so it's sort of the background equivalent of position fixed so it looks like this.
But again, this is a scrolling behavior the browser now understands and we do that by breaking the page into a series of layers. And then we know how to move those layers around at the right time when the user scrolls. So it's this functionality that allows us to get really responsive scrolling on many more pages in Safari 7.
Now I need to say a little bit more about fixed backgrounds. When we looked at pages that were using fixed backgrounds we found that by far the most common case was for pages to use fixed backgrounds to style the page background, normally the body. So that's the case that we optimized for.
And you can style the page background either by putting the fixed background style on the HTML element or the body element in your CSS but you really want to do both and not one of those. But actually CSS 2.1 has something to say here. If we go and read CSS 2.1 in detail it does recommend that you style the page background by putting that style on the body not on the HTML so we recommend that you do that.
Now there's something else when we're looking at scrolling that we saw that was really common and that is pages that use scroll events to do stuff, to move things around, when to use the scrolling the page. But there's a problem with scroll events in that they're asynchronous. Many browsers have discovered that if you try to run JavaScript at the same time the user is scrolling, that really impacts the users experience in scrolling, it really can make scrolling less than stellar. So a lot of browsers now send scroll events asynchronously to the page.
But there's one behavior that we saw that was really common where people were still using these scroll events which have this asynchronous problem to get what we call sticky behavior and I'll explain what that is. Here are a couple of examples. This is the Apple Store. And if you watch that box on the right hand side, when the page scrolls that box moves with the page for a bit and then it sticks to the top edge. And as the page scrolls back down it drops back into its old location.
Here's another example from Yelp. If you take a look at the map on the right hand side you'll see that when the page scrolls the map moves up with the page but it sticks to the top. And at a certain point the content underneath the map is pushing it up back to the viewport.
You may also have noticed a little jump at the beginning where the map disappeared under the edge slightly before it came back down and that's caused by the fact that scroll events are asynchronous so the page only hears about the scroll a little time after it's happened. So by using scroll events to get this behavior you're never going to get frame accurate really smooth scrolling. So we knew in Safari that we had to make a declarative way where authors could get this behavior in CSS without having to run any JavaScript. And that's what we call sticky position.
So this is a new value for the position property called sticky and because it's still in draw form it has the WebKit prefix. And to be a little more technical, sticky specifies that an element takes up space in the flow but its position is constrained by the viewport or some scrollable ancestor and the containing block. What does that mean? Well let me show you a few pictures. So here's a page with a sticky element, the red one, which is simply positioned or simply inflow after the green box. So at this point the element is behaving just like position relative would.
So when you scroll the page the sticky element just scrolls up with the page. But now the sticky element has hit the edge of the viewport so it starts behaving more like position fixed at this point. And so as the page scrolls it stays in the same place.
But if you scroll a bit more the sticky element hits the bottom edge of its containing block and so if you continue to scroll the bottom edge of the containing block is pushing the sticky item out of the page. And so this is exactly the behavior we saw with the Yelp map.
So how do you get this behavior now with this new property? Well it's actually just two lines of CSS. You specify that you want the sticky property for position and then you have to tell us which edge of the viewport the sticky thing is going to stick to so top right from the left and you have to tell us how far away from that edge you want to stick.
So in this case we're saying we want to stick 10 pixels from the top and that's all we need to get that behavior. So we've been pursuing position sticky in the CSS working group and they really loved it. They were really enthusiastic. And we hope to see it in a future version of the CSS positioning module.
And it's present in Safari 6.1 and 7 and on iOS 7 Mac. So what should you do to make sure that your pages are getting the best possible scrolling behavior in Safari? If you use fixed backgrounds you should only use them for the page background and preferably on the body element.
Now I talked about scroll events which is the most common scroll event handler that we see but there's one other scroll event that people register for which unfortunately does force us to drop out of our best scrolling mode and that's the mousewheel event. Now mousewheel doesn't have this asynchronous nature. People expect that mousewheel events are delivered synchronously with scrolling so if we see a mouse event header on the page we have to drop out of our fastest scrolling mode and get you know good scrolling but not great scrolling.
And of course as I said, if you're using scroll events and if you're using them just to get sticky behavior then you should start using position sticky. Finally this is another case where we optimize for the most common case that we saw which is that almost all pages scroll by just scrolling the main document but there are a few pages that scroll by putting a bunch of content into overflow scroll or iframes or frames.
And we didn't ultimize for those yet. So you should make sure that you simply use main document scroll then and that's how your users will have the best scrolling experiences on your pages. So at this point I'd like to invite Tim back on the stage to show us how we can take a page that doesn't scroll as well as it should and to scroll really well. [Applause]
[Tim Horton]
Alright so my friends who just got back from Bali sent me this website full of their photos and noted to me that scrolling didn't really seem as smooth as they expected it to be and they knew that I worked on WebKit so they wanted to see if there was anything that they could do to make it better. So here's that site. And let's just scroll it for a minute and see what it looks like.
So you might be able to see up here on the screen that scrolling is kind of chunky. It's not really super smooth and I can really, really feel it with my fingers down on the track pad. So let's see if we can figure out why this is happening. Let's go to the develop menu and show the web inspector. And let's record a layout remembering timeline while we're scrolling and just see what it looks like. So as we scroll along here we're painting constantly and that's not good. That consumes power and it obviously doesn't perform well.
So let's go back and look at the page and see if we can figure out why this is happening. You may remember, and Simon mentions to me quite often, that fixed backgrounds can often drop us out of, or can in some cases drop us out of fast scrolling. So you notice that this page back here has a fixed background so let's look at the code and see if that's what's happening.
[Noise] So as I look through the code I notice in fact they do have a fixed background on an element that is not a root element and not the body [inaudible] tells them to. So let's just move that up in the code, up to the body, and see what happens.
Woe, scrolling is perfectly smooth now. Let's go back into the web inspector and see what it looks like now. We'll record another timeline and take a look. And you'll notice now as we scroll there's really not much painting happening. That's excellent. So let's look at the JavaScript timeline, which we notice has lots of stuff going on in it for some reason and it looks like this page is using scroll events for some reason. You can see all of these scroll events. Let's go back and look at the page again and see if we can maybe determine why it would be doing that since waking up for JavaScript isn't great for power either.
So you'll notice here at the top of the page, scroll down, see the map and post section there, it's sticking to the top. You can see that, oops. So let's look at the page and see what they might be doing with JavaScript. And you see here they do actually have a bunch of JavaScript that's just being used to perform that sticky and that's not great because just as I mentioned we now have position sticky so we can actually just delete this JavaScript, get rid of all of the JavaScript on the page. Let's see what the page looks like now just for kicks. So you see now the sidebar does not stick.
It's alright because again we have sticky. We can go and with two lines we can apply to that same element, we can apply sticky positioning and tell it to stick to the top and now let's refresh and scroll and we get the same behavior we had before, right there very nice.
And let's look one more time back in the develop menu and show the web inspector and record another timeline while scrolling and you'll see there's nothing, no painting, no JavaScript events, nothing. This is fantastic for power. It's fantastic for performance. And so I hope this has shown you at least some small things that you can do to maybe make your website scroll better and use less power. Thank you. [Applause]
[Simon Fraser]
Thank you, Tim. Once you've feel the scrolling performance in Safari 7 in OS X Mavericks we think you'll really love it. And you should really make sure that your pages get the best possible scrolling because your uses will notice and they'll love your pages. So let me wrap up.
So I talked about the two new power saving features in Safari, App Nap for Safari Tabs and Power Saver, that give us these great power savings and make Safari super power efficient. And we've also talked about responses scrolling, which is not only a more efficient way of scrolling but really improves our scrolling performance in a way that you can feel.
But we also talked about how web content also needs to be ultimized. So we want you to go back and look at your web content and take this advice to heart so you should reduce the amount of work you do in background pages by using the page visibility API and also to use requestAnimationFrame for animations if you really need to use JavaScript for animations.
You should make sure that you don't fall into any of those common pitfalls that we talk about in terms of laying out too much and painting too much and use the web inspector to make sure that, as Tim showed, when you scroll your page there's really no work. The browser's just taking care of everything under the hood. And finally, as we said, responsive scrolling is really great, you'll love it. And we think your users will love it when they see it on your pages.
So if you need more information you contact John Geleynse or a look at the documentation on Safari.center. If you're an extension developer you should go and read the updates to the Safari extensions development guide because there are some important points about changes in Safari 7 that you should go and read about. And finally, of course, the Apple Developer forums are a great place to go and get help.
Now there were a lot of related sessions yesterday and I encourage you to go and watch the videos if you haven't seen those already. There were two on the web inspector. There was one on New Features in WebKit Safari and then there were a couple of more OS level sessions about battery life and actually coming up next I think is the App Nap talk which will give you the sort of system level view of the App Nap feature that I talked about. Thank you very much. [Applause] [Silence]