Welcome to the JavaScript for Everyone newsletter, a behind-the-scenes look at my work on a course designed to take your JavaScript skills from junior- to senior-level.


Invoking the Right to Remain Silent

Black Friday deal. Save £60 on all courses and get a 50% coupon code to use any time

I'm not gonna do it. I won't. So help me, if I get one more email with "save" in the subject line, I'm gonna lose it. It's— what's today, Thursday? That means we've all endured a week of me sounding like a television commercial — very literally — and I'm starting to worry that I may never return to normal. So welcome back to the JavaScript for Everyone newsletter, where we're just gonna talk for a little while. Is there still a big sale on? Yes, absolutely there is; through the end of the day Tuesday, in fact. Will I be discussing it here? I will not! Do not ask about the sale, nor speak to me of values, incredible though said values may be! No, we're going to talk about... Hm. Well, what JavaScript topics didn't I cover in the course? Not a lot, honestly. You see, it's just that it's so expansive! Why, with more than fifty lessons spanning a dozen modules, JavaScript for Everyo No! No, I'm here; I'm good.

No, even talking about JavaScript itself is too risky, it seems. We'd better stick to non-JavaScript topics, despite today being JavaScript’s birthday, kind of. In fact, hey, have you been following all the developments with invokers? They allow you to add interaction to a page without using any JavaScript at all. How weird is that?

Invokers hinge on the addition of two new HTML attributes: command and commandfor, which align really nicely with the way I've always used data- attributes for my custom interactions. Say I were creating an interaction where a button shows a dialog — knowing me, odds are I'd mark up the toggle something like this:

<button data-showdialog data-target="#the-dialog">Show Dialog</button>

Makes sense at a glance, yeah? A data-showdialog attribute ties the "show" functionality to the element itself, and the selector for the target element is the value of a data-target attribute. Sure, we could use a single attribute to do both if this were the only custom interaction we had planned, but that’s rare, and there's value in consistency when you're building a system of interactions — it stands to reason that we’d want to use that data-target pattern for other interactions as well:

<button data-showdialog data-target="#the-dialog">Show Dialog</button> <button data-dootherthing data-target="#the-otherthing">Do something else to something else</button>

Then, y'know, there's the whole part where we make that button do something, with— well now, I'm not gonna talk about JavaScript. In fact, we're not gonna talk about JavaScript at all; we're gonna keep it out of this. If we use an invoker, we've simply no need of the stuff:

<button command="show-modal" commandfor="the-dialog">Show Dialog</button>

Not at all unfamiliar, this pattern. There's a fixed set of command values that can be used on a button element to determine its behavior, and a commandfor attribute that determines the target element (by id). As used here — with command="show-modal" — interacting with this button is equivalent to calling the HTMLDialogElement.showModal() method on a dialog element with the id of the-dialog, but without, y’know, you needing to do all that. Pretty rad, huh? Done and dusted; not a single line of JavaScript required.

Granted, invokers don't have nothing to do with JavaScript. These commands dispatch "command events" that we can listen for over on the script side of things:

dialog.addEventListener('command', function (e) {
  if (e.command === 'close') {
    console.log('Closed!');
  }
});

That doesn't do a hell of a lot for us here, sure — I mean, we can already listen for a close event on a dialog element:

dialog.addEventListener('close', () => {
  console.log('Closed!');
});

But we're also able to use custom invoker commands to dispatch events on target elements, which will be really handy for building custom interactions in a way that dovetails with our invoker-powered interactions:

<button command="--airhorns" commandfor="player">Show Dialog</button>
const player = document.querySelector('audio');

player.addEventListener('command', function (e) {
  if (e.command === '--airhorns') {
    this.play();
  }
});

Notice the -- prefix there? That way we don't run the risk of our custom commands overlapping with any built-in ones that that get added to the web platform as time goes on.

Speaking of things being added to the platform: this is all pretty new, so it may not be quite ready for non-polyfilled prime-time use just yet. Still, no reason you can't give it a try right now to start getting the hang of it. That CodePen should work across all of the usual "modern browser" suspects — fork it and try it out for yourself, yeah?

Wait. Hey. That was JavaScript! Look at that. I made it through without a single sales pitch, despite the fact that there are only a few days left to save £60 on JavaScript for Everyon OH NO QUICK CLOSE THE EMAIL

If you enjoyed reading this post, sign up to get these (plus news about the course) delivered to your inbox.