Dark patterns with the HTML 5.2 <dialog> tag and Chrome for fun and profit
Recently I was introduced to the <dialog>
tag and thought it was an interesting use of a web component pattern, but immediately I questioned the API and indeed after some minor digging, there really are some glaring issues regarding their use in terms of security, and as a major annoyance.
Update
I’ve added some additional comments to this post based on some feedback. As some have pointed out, you could really do this with any piece of the DOM and JavaScript — and they are correct. My suggestion is that however for something like a modal which is designed to focus user interaction you would have to ship something like jQuery or Bootstrap. This is now a feature in the platform and as such I think should be used responsibly (but I doubt it will — I’m sure we’ll soon see it being used for those annoying SIGN UP FOR MY NEWSLETTER popups). My main feature request would be to implement a global state like
isThereAlreadyAModalOpen
? and disallow any futureshowModal
calls to avoid flooding the browser with dialogs and to have a way to always guarantee their closing, but I have been working on a solution for that below.
The Hacks
I’ve put them together in a Codepen and there are 4 main hacks:
- Adding a listener over a close button to make it nearly impossible for the user to close it.
- Having a close button that when pressed closes the modal but also triggers an undesirable path.
- 2 Buttons that send you into an loop of death in creating modals.
- Hijacking the Escape (and Tab) button
The whole thing calls itself to get into a loop if you press the right buttons. Below are the issues I found:
Overriding the DOM for fun
One issue is that you pretty much have full control over the DOM element of the dialog. This is good in many ways, but with great power…
With our annoying button, we use this to control the transform
style of the modal itself and move it beyond the reach of the user. We add a second button that does close the modal using a call to modal.close()
but after that we can trigger any JavaScript we want, such as loading new tabs, running bitcoin miners, searching for aliens, etc.
Blocking the Escape Route
But of course most users might try press the Escape key in hopes that whatever is on the screen will disappear, but we have that particular route covered.
In this case we hijack when we detect the keycode for the Escape key, ASCII 27
and we use it to just keep clicking the open button for creating the modal.
However it’s worse than that! As an added bonus pointed out to me if you also check for ASCII 9, 13 and 32
as the Tab, Enter and Space characters so you can also punish the user for trying to use the keyboard to close the modal. The code that captures them, and the arrow keys is:
const eventKeys = [9, 13, 27, 32, 37, 38, 39, 40]; // Capture Escape, Enter, Tab, Space and arrow keys
Killing the Browser
It’s quite easy to create a stack overflow with the dialog by just calling a setInterval
on it:
Because this is quite easy to create from JavaScript alone, you have to be especially careful of 3rd-party scripts potentially using this to hijack pages using features built into the browser.
I’ve created another codepen with a web component that embeds the dialog in to a shadowroot and provides the global state so you cannot open more than one at a time.
Hopefully you enjoy the demo and this post, and this gives some pause for thought too in how we sometimes implement and use browser features. Users have already been taught bad habits for hitting buttons, especially on OSX these days.