I've been doing web development for a long time, and like a lot of developers, much of what I know about Chrome DevTools came from simply using it.
Need to figure something out? Open DevTools. Poke around. Google something. Figure out a trick. Add it to the mental toolbox. Repeat for a couple of decades.
That approach works, but there's a downside to teaching yourself this way: you tend to get very good at the things you already know are possible while completely missing features that could make you more efficient.
I recently discovered that the filter field in the Chrome Console accepts regular expressions. Instead of filtering for one string, I can do:
/foo|bar|baz/i
And just like that, I can filter the Console for messages containing foo, bar, or baz, case-insensitively.
Simple. Obvious in retrospect. And somehow I'd never thought to try it.
That made me wonder: What else have I been missing?
Quite a bit, apparently.
Here are some of the most useful Chrome DevTools Console features I've either picked up over the years or wish I'd learned sooner.
1. $0 References the Element You Currently Have Selected
This is one of the most useful shortcuts in DevTools.
Select an element in the Elements panel, then switch to the Console:
$0
That's the selected DOM element.
You can immediately start working with it:
$0.innerText
$0.getBoundingClientRect()
$0.classList
If jQuery is loaded on the page:
$($0).hide()
Even better, Chrome remembers your previously selected elements as $1, $2, $3, and $4.
That makes comparing elements incredibly easy:
$0.getBoundingClientRect()
$1.getBoundingClientRect()
No selectors required.
2. $() and $$() Are Built In
I've spent a lot of my career working with jQuery, so typing $() into a Console comes naturally.
But Chrome DevTools provides its own selector shortcuts even when jQuery isn't present.
$('.product-form')
gets the first matching element.
And:
$$('.product-card')
gets all matching elements.
That means you can quickly do things like:
$$('a').map(a => a.href)
or:
$$('.product-card').map(el => el.innerText)
For quick Console work, there's often no reason to reach for jQuery at all.
3. $_ Gives You the Previous Result
Suppose you run:
$$('.product-card')
and Chrome spits out a giant array.
You don't have to run the query again to continue working with it. $_ contains the result of the last evaluated expression:
$_.length
or:
$_.map(el => el.innerText)
It's a small convenience, but that's exactly the kind of thing that makes interactive debugging faster.
4. copy() Copies Data Directly to Your Clipboard
This might be one of my favorites.
Want the text from the currently selected element?
copy($0.innerText)
Done. It's on your clipboard.
Want every link on the page?
copy($$('a').map(a => a.href).join('\n'))
Want a nicely formatted JSON object?
copy(JSON.stringify(myObject, null, 2))
No selecting Console output. No temporary textarea. No Clipboard API.
Just copy().
5. Use console.table() for Structured Data
Logging an array of objects can quickly become unreadable:
console.log(products)
Try this instead:
console.table(products)
Chrome renders the data as a table.
You can even specify which properties you care about:
console.table(products, ['title', 'sku', 'price'])
If you're working with product catalogs, API responses, variants, orders, or any other repetitive structured data, this is dramatically easier to scan.
6. monitorEvents() Shows You What an Element Is Doing
This is a great tool when you're reverse-engineering an unfamiliar interface.
Select an element in the Elements panel and run:
monitorEvents($0)
Now interact with it.
Chrome will start logging the events being fired by that element.
You can narrow it down:
monitorEvents($0, 'click')
or:
monitorEvents($0, ['click', 'change', 'input'])
When you're finished:
unmonitorEvents($0)
This can save a lot of guessing when you're trying to understand how an existing UI works.
7. Inspect Event Listeners with getEventListeners()
Sometimes the question isn't what event fired?
It's what JavaScript is listening for it?
Select an element and run:
getEventListeners($0)
Or inspect a particular event:
getEventListeners($0).click
This gives you visibility into event handlers attached to the element and can provide a much faster route into unfamiliar JavaScript.
8. Break When a Function Runs with debug()
If you've found a function and want to understand what's happening when it's called:
debug(someFunction)
The next time that function executes, Chrome's debugger pauses inside it.
When you're done:
undebug(someFunction)
Instead of trying to work backward through a codebase to determine when something runs, you can simply tell Chrome:
Stop when this happens.
9. Watch Function Calls with monitor()
Sometimes you don't want execution to stop. You just want to know when a function is being called.
Use:
monitor(someFunction)
Chrome will log calls to that function and their arguments.
Stop monitoring with:
unmonitor(someFunction)
This is particularly useful for understanding the behavior of code you didn't write.
10. Use keys() and values() to Explore Objects
If you've got some giant object you're investigating:
keys(window.Shopify)
can be much easier than dumping the entire thing into the Console.
Likewise:
values(someObject)
They're essentially convenient DevTools shortcuts for exploring an object's contents interactively.
11. Store Console Objects as Global Variables
This one doesn't require a command.
When Chrome logs an object, right-click it and choose:
Store as global variable
Chrome will create something like:
temp1
Now you can manipulate it however you want:
Object.keys(temp1)
console.table(temp1)
copy(JSON.stringify(temp1, null, 2))
This is incredibly useful when an application has logged some complicated object that you want to investigate further.
12. console.trace() Tells You How Execution Got Somewhere
Sometimes knowing that something happened isn't enough.
You need to know why the code got there in the first place.
That's where:
console.trace()
comes in.
It prints the current call stack, showing the chain of function calls that led to that point.
It's especially useful in temporary debugging code:
if (somethingUnexpected) {
console.trace('Why is this happening?');
}
Now you're not just seeing the unexpected condition. You're seeing how execution arrived there.
13. Console Filtering Supports Regex
And finally, the feature that sent me down this rabbit hole.
The Console filter isn't limited to simple text.
You can use a regular expression:
/foo|bar/
to show messages containing either value.
Add i for case-insensitive matching:
/foo|bar/i
Or combine several things you're watching:
/product|variant|cart|error/i
It's such a small feature, but when you're working on a noisy site with scripts, analytics, apps, and browser extensions all dumping messages into the Console, it can make debugging much more pleasant.
Bonus: Don't Ignore the DevTools Command Menu
This technically goes beyond the Console, but it's worth mentioning.
Press:
Command + Shift + P
on macOS to open the DevTools Command Menu.
Think of it like Spotlight for DevTools.
Instead of remembering where Chrome buried some obscure feature, start typing what you want to do and see what's available.
It's also a great way to discover DevTools features you didn't know existed.
The Bigger Lesson
None of these features is revolutionary by itself.
That's precisely the point.
Saving five seconds selecting an element doesn't seem important. Neither does saving ten seconds copying some data, finding an event listener, or filtering Console output.
But developers perform these little operations hundreds or thousands of times.
The accumulated friction matters.
And if you're largely self-taught, it's worth occasionally questioning the workflows you've developed over the years.
Just because you've gotten fast at doing something one way doesn't mean Chrome hasn't been quietly sitting there for ten years with a button that does it faster.
I apparently have some catching up to do.