The mobile landscape today is all but monopolized by WebKit, as a result, most of the tooling and infrastructure to support mobile web development on the frontend is taking place in the WebKit Inspector, so I’ll focus on it, and take a deep dive into its entire feature-set and how and when to use it.
Google and the Chrome team have been pumping a ton of resources into the WebKit Inspector. The changes have enabled a whole new class of complex and ambitious applications that would have otherwise collapsed on their own weight. This is great news, of course, but as I talk to more and more web developers about their process and tooling, it became clear to me that many of them haven’t caught up with the changes or aren’t making effective use of the tooling available. This blog post attempts to remedy that, not only by walking you through the inspector’s feature set, but also highlighting certain techniques for bug hunting and feature development that I’ve found to be indispensable. The post is meant to be scannable and shareable. You can click on any header to share a URL to a specific tip/technique or feature to your friends.
Before we start, I want to make it clear that I don’t claim to have infallible knowledge on tooling and process. If you find any of this information to be incorrect, out-of-date, or inefficient, please reach out to me and let me know, I’d love to hear your thoughts.
If you’re new to the inspector or have a passing knowledge of it, do me a favor and play with it as you read this post. Trying things out are the best ways to learn it! “But Majd, I don’t want to keep creating new files and loading them and editing them to test these things out! I have kids to feed!” I hear you, and I agree with you, that’s where the data: URLscheme comes in!
Try it out: Open a new tab of Chrome, and paste this into the address bar and hit enter:
data:text/html,<b>ZOMG I AM BOLD!?!!?</b>
This is one of the easiest ways to get some HTML into the page and start inspecting it and playing around/investing ideas. Anything after the comma is interpreted as html and once loaded, you can open up the inspector and start playing with it!
There isn’t a single WebKit Inspector. In fact, there are 5 at any given time that you can use:
- Safari Stable
- Chrome Stable
- WebKit Nightlies
- Chrome Canary
I’ve tried to sort them from oldest to newest. Chrome Canary gets updated with new features all the time. After they bake for a while with the early adopters, they make their way slowly to Chromium, WebKit Nightlies, Chrome, then Safari. At any given time, Safari’s Inspector is about a year’s worth of development behind Chrome Canary. That is to say, use Chrome Canary for your development.
Awesomesauce. You’ve downloaded Chrome Canary, you have it open, you have your site up, you’ve cracked your knuckles and rolled up your sleeves. Hit CMD+J to open up the Inspector or right click on a specific element and click “Inspect Element”.
Now let’s set up the environment so you’re comfortable. The first thing to do is familiarize yourself with the UI. Take a minute to click on every button you see, hover over it to see the tooltip, and find out what everything does.
The first thing you’ll want to do is click on the gear icon in the bottom right to bring up the settings. Ignoring the horrendously ugly HUDdisplay, I highly recommend checking the “Dock to Right” setting. What this does is stick the inspector to the right side of your window, but keeps it attached. If you decide to pop it out into its own window, then opening multiple inspectors at the same time can become confusing. If you dock it to the bottom, you get a lot of horizontal space, but vertically you’re very constricted. Docking to the right is the best of both worlds. Here’s what it should look like:
If you’re working on a mobile application, this setting (in the settings HUD) is essential.
Google, if you’re listening: Default the docked setting to “Dock to Right”.
Another quick tip to help speed up your work with the inspector is to get very familiar with the keyboard shortcuts. Go to the Elements Panel (some of the other panels eat the keystroke), and type “?”. This should bring up another horrendously ugly HUD display that contains a list of keyboard shortcuts. The most helpful shortcuts are the ones to navigate the Scripts panel, which we’ll talk about in a bit.
Google, if you’re listening: Make the keyboard shortcuts accessible from the settings panel. Most people don’t know the secret key. While we’re on the topic, why does the HUD have a different light source than the rest of the UI? Why is the border radius so big? Why is the “X” button not vertically centered in the header? Why are some of the shortcuts so small you can’t see them?
Debugging iFrames has long eluded web developers. NO MORE! You can now set which iframe the inspector is inspecting through the popup in the bottom center of the inspector that says “”. This panel is interestingly only visible when you have the console expanded (hit escape from any panel).
Google, if you’re listening: Make the iframe drop down accessible everywhere.
Sweetums. You’re all set up, you’ve cranked up your music, and you’re hacking your heart away. Now you need to actually do something: Run a command, check the output of a function, check for the existence of a method, or see any logs/errors/warnings. This is where console comes in to help. The console is such a badass, in fact, it not only gets its own panel, it’s also accessible from any other panel by hitting the “>=” icon in the bottom left, or by hitting the Escape key.
It also happens to be one of the more intuitive features: Type something, hit enter, see the output of the expression. Any logs are visible chronologically, click the file name on the right and get taken there in the Scripts panel. What you may not have known though, is that you can type multi-line expressions in the console by hitting “shift + enter”. This is really helpful for writing out long commands that involve anonymous functions or complex statements. You can also type “ctrl + L” to clear the console when the logs get overwhelming.
From a DOM-debugging perspective, one of the cooler features of the console is its integration with the Elements panel (which we’ll discuss in the next section). Any element that’s selected in the Elements panel can be quickly accessed using “$0”. That will reference the element and allow you to interact with it. Inversely, whenever you see a DOM node printed in the console, you can right click on it and click “Reveal in Elements Panel” to quickly find it in the DOM.
Google, if you’re listening: Add syntax highlighting to the console command, it’ll help simple parse errors. It wouldn’t hurt to auto-insert braces and parenthesis and quotes, too.
Because its live, I find myself doing a lot of experimentation in this panel. Drag this node here, does it fix that z-index ordering issue? That div is invisible, is it being hidden by another layer?
At the bottom of the inspector, you’ll find a magnifying glass. When you click [that, you can hover over your application itself and it will highlight the node you have selected. This comes in handy when you need a quick way to go to a deeply-nested element.
While you’re playing around with the Elements panel, try right clicking on one of the nodes and see the options that come up. One of these of note, is the “Break on Subtree Modifications” option. When you enable that, any changes that you make to the DOMhierarchy under that node, it will automatically pause, and let you debug it.
Moving on to the right, the CSS editor is up-there in the list of most-helpful features of the inspector. Superficially, it allows you to live-edit your CSS. However, it reduces the barrier to experimentation so much, I find myself moving things around and playing around with ideas I normally would’ve dismissed as stupid, which is a good thing, as Bret Victor says.
The first thing in the list you see is the “Computed Style” section. Check the “Show inherited” checkbox, and you will see a list of every single style property and its corresponding value that’s applied to the selected node. Even if you didn’t explicitly set a property, it will show you the inherited default. This helps you not only understand what is making up the styling of the node, but it also helps you find out what properties you can set yourself to modify it. Take a quick look at it and see if there are any CSSproperties you didn’t know about, then try them out and see what they do!
Next in the list is the “Styles” section. This shows you the list of properties, grouped by selector, that have been applied to the node. The first sub-section here says “element.style”. This shows the properties set through the style="" attribute in the HTML. The next section titled “Matched CSS Rules” shows you the selectors that matched the selected node, their properties, and their values, along with the file name and the line number on the right.
To add a new selector, click the “+” button to the right of the “Styles” header, here you can define the selector, hit tab, then start writing properties and values. You’ll notice Chrome offer auto-complete suggestions (this is another great way to investigate which properties you can set). The version of Canary as of this writing requires you to hit the right arrow key to fill in the autocompletion, then you can hit tab to set the value. Hitting tab again will let you set the next property. If you wanted to set a new property to an existing selector, click once on the closing bracket. Clicking on the button to the right of the “+” new selector button will allow you to specify a pseudo selector. Handy!
Colors behave slightly differently. Clicking once on a color value will toggle through a list of representations (hex, rgb, rgba, etc), and clicking on the little colored square will open up the color picker.
All this is great, but let’s say you’ve just spent 10 minutes editingCSS. Now what? How do you save those changes? To do that, click on the file name of the selector (or go to the file yourself in the Resources panel), then right click on the file (which will be updated to reflect those changes), and you can right click to “Save As” and overwrite your existing files. Personally, I find selecting the text and pasting it into my editor to be faster and less error-prone.
One final node on CSS Editing: If you write a new selector (by hitting the “+” key), it won’t persist into a file because it doesn’t know which CSS file to put the selector in.
Google, if you’re listening: Prompt me which file to put a new selector in when I create one. Also, make “tab” fill in the selected auto-completion. Finally, find a way to make persisting changes inCSS to my code easier. This last one is more of a high-level wish, I don’t really know whether or not it’s possible nor how it would work, I just want the process streamlined :P.
Phew, that was a handful! Take a minute to play with all this. When you’re done, let’s move down to the next item in the list: Metrics. If you’re not familiar with the CSS Box Model, take a minute to readthis awesome guide.
The Metrics section basically shows you how the browser is rendering a node from the perspective of content size, padding, border, and margin. Use this tool to debug positioning/dimension problems.
A nice companion tip to the Metrics panel, is that the semi-transparent blue box that overlays the selected node will actually show you the padding and the margin, so you can understand how a box is affecting the flow
Blue is for content, green is for padding, and orange is for margin.
We’ll skip Properties and DOM Breakpoints for now. I haven’t found a use for the Properties section yet, and DOM Breakpoints shows you any “Break on …” actions you’ve taken by right clicking on a node. The last section we’ll talk about in the Elements panel is the Event Listeners section.
Clicking on the little funnel icon on the right allows you to see either the listeners on the selected node or in the document. This comes in handy if you have listeners attached on things you can’t select in the main list (like window, document, etc).
Of note is the first item in the list called “Frames”. It includes a sub-item for every frame in your application, and groups resources such as Images, Scripts, and Stylesheets. Really the only reasons I’ve found to dig through this list is to find a Stylesheet file I edited so I can copy its content and paste it into my editor to save.
The Network Panel comes in handy when you’re debugging, well, network issues. Is your image asset not updating? The network panel will tell you whether or not it’s being served from a cache. Is your XHR not responding? The network panel will tell you if it’s stuck or error-ing out.
Clicking the “Record” button (the black circle) will maintain a Network session across reloads, handy if you want to see how a change in your codebase affects the network performance.
If you find yourself debugging performance issues, the Network Panel is also very helpful. On the right, you see a “cascading waterfall” of your network requests. If your page is taking a really long time to load, your first step in debugging it would be to look at the waterfall. The blue and red vertical lines show you when the DOMContentLoaded event fired, which means that before then, your use is sitting around waiting for something to happen. You want to reduce that time as much as you can.
Why do I keep calling it a waterfall, you ask? Well, it looks like one, but more importantly, the reason it looks like a waterfall is because every network interaction takes time both in network latency, and in download time, and the browser can parallelize only so many of them. If your waterfall is wide, you need to go out of your way to cut HTTP requests.
By the way, you can click on the “Timeline” header and you get a drop down (!) to change the sort order. Particularly interesting is the “Latency” sorting, which shows you which of your requests took the longest to establish.
The half-transparent left-side of the capsule shows the latency, and the dark part on the right shows the download time.
On the bottom of the Network panel, click “XHR” to filter the network connections just to XMLHttpRequests. Then click one of them. You see that the right side now changes to show details of that specific request. The first tab shows you all the HTTP Headers sent with the request, and those of the response. This helps when debugging server bugs or CORS bugs.
Preview is a pretty-formatted version of Response. If you’re getting back a lot of JSON, the Preview tab will format it into a collapsable list for you, whereas the Response tab will show it in plain text.
Cookies show you the cookies that were sent as part of the request, and Timing shows more details regarding the time profile of the request. Again, helpful if you have requests taking a long time.
Looking around, two buttons worth mentioning are the “Pretty Print” button (looks like a pair of curly braces), which will properly format a minified file. This comes in handy if you get a bug in a third-party minified package and you want to have an even remote chance of figuring out what is going on.
The other button worth mentioning is the “Break on Exception” button (Looks like a pause button). When gray, it’s disabled. Clicking it once will cause your script to pause if it hits any thrown exception. Clicking it again puts it in the “Break on Uncaught Exception” mode, which only pauses your script if you (or something else you use), throw an exception that doesn’t get caught. This is indispensable when you want to track an exception that’s being thrown, since it preserves the call stack and the current state of the application.
The addition of tabs in the main workspace and a full file browser, scoped by origin has dramatically improved the efficiency of moving around your project.
To open the file browser, click the weird icon in the top left (it looks like two folders with a right angle between them). By default, the file browser will “float” on top of your workspace, so click the weird icon on the top right of the browser (it looks like a half-white, half-black rectangle) to dock it. Try typing while you’re in the file inspector, it will do a fuzzy-match of the files in your project!
While we’re on the topic of moving around your project, try hitting “CMD+O”, this will open up a TextMate-style “Go-to-File” popover that lets you quickly jump to a file. “CMD + Shift+ O” will open up a “Go-to-Symbol” popover that lets you quickly jump to a symbol in the current file, and “CMD + L” will let you jump to a specific line.
Google, if you’re listening: Add the path to the file in the Go-to-File popover, in case there are duplicates.
Occasionally, you’ll want to debug a problem in hot code (code that gets run over and over). Putting a breakpoint here becomes tedious, since you’d have to hit “Continue” over and over. What I usually do in that situation is wrap the function call I want to debug with “window.foo = true” and “window.foo = false”, then right click on the breakpoint, click on “Edit Breakpoint”, then in the textfield I type “window.foo”. What this tells the inspector is that unless window.foo evaluates to true, don’t stop.
So, you’ve added a breakpoint, you refreshed the page, and now your script is paused. Now, the fun begins.
The first thing of interest in the sidebar is “Watched Expressions”. If you care about the value of some expression (Say “MyApp.someController.property === ‘some value’”), then adding that as a watch expression will let you keep an eye on it without having to type it over and over in the console.
Below that, the “Call Stack” section shows you every function call that the system went through to end up where it did. You can navigate this list using “CTRL + .” to go down and “CTRL + ,” to go up in the stack. I usually navigate this list when I care about how a variable came to have its value and how it traveled through the system.
Next, “Scope Variables” lists the local variables and their variables. If you’re using closures to close over variables, then those will be grouped together. The “Global” group shows you every variable on the “window” object (a huge list). As you move through a function, this list gets updated automatically.
Now that you’ve inspected the state of the app on a breakpoint, you’ll probably want to move around. “Continue”, “Step Over”, “Step Into”, and “Step Out” are your friends here. In fact, you’ll be using them so frequently, it’s worth bringing up the keyboard shortcut list again and learning the shortcuts for them. It’s much more efficient to navigate through the list with the keyboard than the mouse. If you’ve used a debugger before, these terms are already familiar to you. For the web developer though, these are new concepts that we didn’t have before.
Continue resumes the execution of the program. If execution hits the same breakpoint again, it’ll pause again. Step over will skip function invocations and stay within the current function. Step into will enter the new function if it’s being called. If you’re calling multiple functions in the same line, you can step into, and out of each one in order. Step Out will leave the current function and go back up one level in the call stack.
These tools are essential in walking through your code and pin-pointing a bug, or finding out which path through your codebase is being followed.
Another handy tool lives under the “XHR Breakpoints” section. As the name suggests, it sets up breakpoints related to XMLHttpRequest objects. The way you specify the breakpoint, is by typing in a substring of the URL you want to inspect. Handy!
By the way, in the “Scope Variables” section, you can right click on a function and choose “Jump to Definition” to jump to the part of the file that defines that function.
What do I mean by “browser performance”? I’m referring to things that are generally out of your control, but which affect the performance of your application. Understanding what’s happening under the hood is essential to supporting the growing complexity of an application.
The first section of interest is the Timelines section. To activate it, hit the Record button (the black circle in the bottom left of the panel). When it’s glowing red, perform a task in your application you like to inspect. If you’re wondering why your scrolling performance is slow, you would try a short scroll. If you’re wondering why loading a modal panel is slow, you would do that. Don’t worry about doing too much and getting overloaded by data in the panel, there are tools to help!
So, you’ve managed to get a nice little timeline of what’s happening, but you’re only interested in a little section of the data. To “Zoom in”, you can click and drag on the timeline graph. You’ll see a couple of draggable thumbs so you can adjust the visible section. Moving down into the waterfall graph, you’ll notice little arrows next to some of the orange capsules. Expand those, and what you’ll see is the function invocation that triggered the browser to do work. In the example below, we can see that a scroll event had fired, invoked a handler, which caused a “Recalculate Style” to fire. If you hover over the item in the sidebar, you see even more detail.
You may be wondering at this point “What are these purple events that are taking up time? Repaint, Recalculate Style, and Layout?”. These are browser-events that are usually invoked by in response to visual changes. For example, If your resize or scroll, the browser needs to do a bunch of work to make sure everything looks as you’d expect it to. You could also invoke these events yourself, so it’s worth understanding what the inspector is telling you and not taking DOM changes for granted.
This occurs when you modify CSS properties. In the above screenshot, you see I’m running the same command twice, but seeing different results. The first time I run it, the browser has to Recalculate Style, Layout, and Paint. The next time, it just has to Recalculate style. What’s happening?
Because this is on a fresh page load with nothing visible, giving an unstyle div a height causes its contents to change layout (Layout), causes the visual display to change (Paint), and invalidates the styles (Recalculate Style). The next time I run the same command with the same value, it gets to skip the Paint and the Layout, since they didn’t change. Notice the order: Recalculate Style informs the later processes whether or not they need to get triggered.
Back to our empty page, let’s give our div a width, height, and background color so it actually changes visibly on the screen:
Now, it has to repaint both times, but it only needs to calculate layout once. That’s because the Recalculate Style step found that the positioning/dimensions haven’t changed, but the background has, so it skipped that step. Anything that cause a visual change, causes a repaint.
Often referred to as “Reflows”, a Layout event causes the browser to recalculate the position of elements. For example, say you have a picture that’s floated, so the text wraps around it. If you remove the float, the browser has to “re-flow” the text around it. Paul Irish has a great video where he describes reflows and how to avoid them.
Google, if you’re listening: Please fix the resize behavior. On small Macbook Air screen, it’s common to hit multiple UI bugs when you arrange the inspector/application side by side.
For more detail on how this works under the hood, refer to this blog post.
Now we’ve looked at the timeline, optimized it, but the application is still crashing every now and then after you use it for a while, sometimes it slows down, and it stutters a lot. You’ve seen a few “GC” (Stands for Garbage Collection, which runs every now and then to clean up unused objects) events in your timeline, the Memory tool helps you debug those issues.
Tony Gentilcore has a great writeup on finding and fixing memory leaks, you should definitely give it a read. I’ll give you a quick overview here.
Click on Memory in the sidebar, then click record. Perform a task in your app, and observe the memory profile. Here’s the memory profile for an app that simply creates an object and throws it away every mousemove event:
Below the graph you’ll see some more useful metrics to track the number of DOM nodes, and event listeners, they’re pretty self-explanatory.
Finally, let’s say you deployed your app in production, you found a bug in production, located it in the inspector. How do you tell your coworker/developer? Well, instead of taking a static screenshot, you can actually right click on “Timelines” and “Memory”, and save your session so that you can share the actual debug data! Handy when you’re working as part of a team.
So, you’re now at a point in your application’s development where it looks the way you want it to look, HTTP requests are minimized, and you’re causing the browser to do more work than it needs to. Something’s not right though. Everything seems to be snappy, except for one small part. You’ve tried debugging it with the Timeline panel, but it doesn’t give you enough depth and detail. It’s not the browser that’s slow, it’s your own code.
That’s where the Profiles panel comes in.
The first type of profiling you can do is JS CPU profile. The description outlines at a high level what it measures: What functions your app spends the most time in.
Select it, then click “Start”. Every action you take now, will be logged until you hit the glowing red “Record” button to stop recording (Or click on the “Stop” button). Once stopped, the profiler will show you an ordered list of all the functions that had executed, and the time it spent in each. You’ll probably see something like “(program)” at the top of your list. That’s webkit taking time to do stuff. You can’t inspect it, and you can’t do much about it (but the Timelines tab should help locate some of those bugs!). Apart from that, here’s what a typical profile looks like:
This is the profile for my blog when I open the photo portfolio and open/close a photo a few times. Looks like the most expensive part of that interaction is setting the curtain’s opacity and retrieving the bounding client rect. Just the info I needed to optimize that part of my app.
Google, if you’re listening: There’s a lot of work you can do here to surface useful information and hide useless information. I often get stuck in infinitely-deep lists, or 99.99% of my time spent in (Program), distorting everything else.
CSS doesn’t come for free. Complex stylesheets take time to parse, calculate, and apply. The CSS Selector profiler shows you how many times each selector was matched to a DOM node, and how long the browser spends applying those selectors. In a complex application with many divs of the same type, applying styles to them might take a substantial amount of your app’s startup time.
This is one of the most obtuse views in the inspector. So much so, I haven’t actually found it useful. Take this for example:
The amount of useful information there as far as I can discern is zero. I’ll defer once more to Tony Gentilcore’s writeup, may he light your way.
We’re almost there! The last panel is the Audits Panel, and it’s basically the last sanity-check of performance you do before going to production. Essentially, it will inspect the resources and the HTTP requests and suggests improvements based on current industry best practices. For example, while writing this, running the Audits panel told me that I should specify the image dimensions in the HTML to make layout faster. Good to know!
All the tips here are well-documented, and you should at least take a quick look through them.
I hope you’ve found at least some of this information useful. I’ve been writing complex web applications on the desktop and mobile for 2 years now, and I’ve had to use each of these panels to solve a different class of problems.
If I’ve mis-interpreted something, missed something, or made any errors, please let me know.
How Browsers Work – A Fantastic, and in-depth look at the processes and plumbing that we build on.