Delay Safari Extension

by on

Let Go

Recently, I caught myself spending too much time on websites like Hacker News and Reddit. You know the routine: do some work, then reward yourself with a picture of a cat in comically oversized tennis shoes. Running unit tests only takes about a minute, but I’d be lost on Reddit for a lot longer than that waiting for them to finish.

So I wanted to keep myself from doing that. Monitoring and outright blocking never worked for me. Randall Munroe’s distraction affliction correction extension looked promising. Unfortunately, my laptop boots too quickly for that. The comments there recommend several Firefox and Chrome extensions, but I use Safari and couldn’t find one like those. So I wrote my own.

Delay

Delay

Delay is a Safari extension that delays the loading of time-wasting websites. It’s very simple. Set up a list of hostnames to blacklist and they’ll be delayed for however long you want. When you visit a site that matches the blacklist, it will hide the whole page and show a countdown timer instead. The timer only runs when the window has focus, so you can’t open a bunch of tabs to sidestep it.

How It Works

Delaying a site proved to be harder than blocking it. To block a site, you can either empty location.href or call window.stop. I tried a bunch of different things for delaying and settled on styling the root element (<html>). The root element is always present, unlike <body>, which doesn’t show up until later. After my script identifies the site as a time-waster, it updates the page to look like this:

<html delay="30">
    <!-- the rest of the DOM -->
</html>

Styling is handled by adding an attribute to the root element and then matching it with the [attr] selector in CSS. The first time around, I set display: none, but that screwed up page layout so I went with visibility: hidden instead. Flash elements don’t obey visibility, so I had to set opacity: 0 for them.

The countdown timer is created in CSS using the :before pseudo-element and the attr() function.

[delay]:before {
    content: attr(delay);
    /* ... */
}
[delay] * {
    opacity: 0 !important;
    visibility: hidden !important;
}