I was using confirm
in Javascript to get a user to, well, confirm an action, but it looks very ugly, and after a few confirmations, the browser starts to get obnoxious: would you like to block further popups from this site? Also, it’s limited to a very simple text string question, and I had in mind to provide further options, or more text with better explanations. So I thought to add an HTML popup that would function modally, and because it all linked with various API calls, I wanted the html_confirm
function to return a Promise
. The Promise will resolve when the user accepts the dialog:
// old code if (confirm(`Do you want to delete this?`)) { the_api.delete(postId); }
should become:
html_confirm(`Do you want to delete this?`).then( ()=>the_api.delete(postId) );
The popup HTML is simple enough:
<div id="html_confirm"> <div> <div class="titlebar">Confirm</div> <div class="content"> Our message will go here. </div> <div class="buttons"> <button class="button-cancel">No</button> <button class="button-continue">Yes</button> </div> </div> </div>
We will use that id
to find the form on the page, although in a final version of this, we would generate the HTML in the Javascript popup class. I would probably use a very simple template library (https://github.com/craigmj/dtemplate) to do that, but we’ll leave that aside for now.
The outer #html_confirm
fills the whole screen and provides a semi-transparent background. The inner div
is the actual popup prompt, containing a title bar, a message, and No and Yes buttons. Here’s the CSS, built with nested flexboxes.
#html_confirm { position: absolute; margin: 0; padding: 0; top: 0; left: 0; bottom: 0; right: 0; display: none; flex-direction: row; justify-content: center; align-items: center; background-color: rgba(0, 0, 0, 0.7); } #html_confirm.showing { display: flex; } #html_confirm>div { background-color: white; padding: 1em; border: 1px solid #ccc; display: flex; flex-direction: column; justify-content: flex-start; align-items: stretch; gap: 0.6em; } #html_confirm .titlebar { text-align: center; font-weight: bold; } #html_confirm .buttons { display: flex; flex-direction: row; justify-content: flex-end; align-items: baseline; gap: 1em; }
Let’s code the javascript for the popup. We’ll start with the code to display the popup. This is really easy:
- Find the popup html on the page,
- Set the message text, and
- Show the popup by adding the
showing
class.
let el = document.getElementById(`html_confirm`); el.querySelector(`.content`).innerHTML = message; el.classList.add(`showing`);
To get the Promise
working, consider the constructor of a Promise
:
new Promise(function(resolve, reject) {...});
A Promise
is constructed with a single argument: a function that receives two parameters, both of which are themselves functions. The resolve
function is called if the Promise is continues, and the reject
function is be called if the Promise is rejected. We will call resolve()
if the user clicks Yes, and reject()
if the user clicks No, and return the Promise
:
function html_confirm(message) { return new Promise( function(resolve, reject) { let el = document.getElementById(`html_confirm`); el.querySelector(`.content`).innerHTML = message; // wire the no and yes buttons let hide = function() { el.classList.remove(`showing`); } el.querySelector(`.button-cancel`).addEventListener(`click`, function() { hide(); reject(); }); el.querySelector(`.button-continue`).addEventListener(`click`, function() { hide(); resolve(); }); // show the popup el.classList.add(`showing`); }); };
Here it is in use:
document.addEventListener(`DOMContentLoaded`, function() { html_confirm(`Do you want to delete this?`) .then( ()=>alert(`you chose to continue`), ()=>console.log(`not continuing`) ) .catch( ()=>console.error(`caught an error`) ); });
You can get all the code on https://jsfiddle.net/craigmj/9dnro7L3/2/ .
Note that in our .then()
to html_confirm
, we provide both a resolve and a reject callback. We don’t want a cancel option to fall through as an error, which would happen if we don’t handle the reject in that then()
call.
That’s modal popups in 16 lines of Javascript code, and 34 lines of CSS. This used to require libraries in the past. Of course we could make it more complicated, could add a title bar, and a z-index
, but we’ve got an incredibly simple, very elegant popup. We could add a close button, which would have the same effect as clicking No, or handle pressing escape
as a keyboard shortcut. And we can style this exactly as we want.
We’ve also got a very basic confirmation popup that is incredibly easy to maintain because there’s almost nothing to it.
Eventually, we will have dialog
built into the browser (https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog) and this will make popups more of a first-class browser function, but it’s not difficult at the moment. Everytime I see a website using React to get a basic popup, I think of how resources are squandered in the modern world…