[Proposal] Extension of window.prompt to allow other types of common inputs (<select>, date, color, password, range, file, ...)

I often find myself wanting a quick modal to accept some input, but I don’t want to include a big dependency, or manually code up something. And I’m obviously not alone here: I see window.prompt and window.confirm used quite often on the web - even in large applications.

I’m a huge fan of having “simple things be simple, and complex things be possible”, as the saying goes, and in the case where I just want to quickly grab some user input, I think it should be as simple as prompt(...).

An async version of this is “polyfill-able”. Here’s a very rough sketch:

async function prompt2(message, opts) {
  let ctn = document.createElement("div");
  let type = opts.type;
  let input;
  if(type == "select") {
    input = `<select style="width:100%;height:100%;background:white;color:inherit;font:inherit;box-sizing:border-box;">${opts.options.map(o => `<option value="${o.value}">${o.content}</option>`).join("")}</select>`;
  } else if(type == "buttons") {
    input = opts.buttons.map(o => `<button style="height:100%;margin-right:0.5rem;font:inherit;box-sizing:border-box;" data-value="${o.value}">${o.content}</button>`).join("");
  } else {
    input = `<input style="width:100%;height:100%;background:white;color:inherit;font:inherit;box-sizing:border-box;" type="${opts.type}">`;
  }
  ctn.innerHTML = `
    <div style="background:rgba(0,0,0,0.2); position:fixed; top:0; left:0; right:0; bottom:0; z-index:9999999; display:flex; justify-content:center; color:black; font-family: sans-serif;">
      <div style="width:400px; background:white; height: min-content; padding:1rem; border:1px solid #eaeaea; border-radius:3px; box-shadow: 0px 1px 10px 3px rgba(0,0,0,0.24); margin-top:0.5rem;">
        <div style="opacity:0.6;">${window.location.hostname} says:</div>
        <div style="margin:1rem 0;">${message}</div>
        <div style="display:flex; height:2.3rem;">
          <div style="flex-grow:1; padding-right:0.5rem;">${input}</div>
          ${type !== "buttons" ? `<button style="font:inherit;box-sizing:border-box;">Submit</button>` : ""}
        </div>
      </div>
    </div>
  `;
   document.body.appendChild(ctn);
   let value = await new Promise((resolve) => {
    if(type !== "buttons") {
      ctn.querySelector("button").onclick = () => {
        if(type == "file") {
          resolve(ctn.querySelector("input").files);
        } else {
          resolve(ctn.querySelector("input,select").value);
        }
      }
     } else {
        ctn.querySelectorAll("button").forEach(b => {
          b.onclick = () => resolve(b.dataset.value);
        });
     }
   });
   ctn.remove();
   return value;
}

Usage:

let name = await prompt2("Please type your name:", {type:"text"});
let pw = await prompt2("Please type your password:", {type:"password"});
let date = await prompt2("Please choose a date:", {type:"date"});
let files = await prompt2("Please choose a file:", {type:"file"});
let choice = await prompt2("Please choose an option:", {type:"select", options:[{content:"Thing 1", value:"1"}, {content:"Thing 2", value:"2"}]});
let choice = await prompt2("Please choose an option:", {type:"buttons", buttons:[{content:"Thing 1", value:"1"}, {content:"Thing 2", value:"2"}]});

// Demo:
(async function() {
  window.prompt2 = await import("https://deno.land/x/gh:josephrocca:prompt2@v0.0.2/mod.js").then(m => m.default);
  let date = await prompt2("Please choose a date:", {type:"date"});
})();

image

Obviously window.prompt can’t be made async, and the second param of window.prompt is already used for the default value, so perhaps it would be better to start over with a new function. Although it would be great to have a sync version of this API too - so that we don’t have to sprinkle async throughout a codebase that was completely sync up until that point.

I wonder if it would be safe enough to use the second param of window.prompt? I doubt anyone is actually passing an object to the second parameter on purpose, so that they can have [object Object] as the default value. And then a window.promptAsync could be added which has the same UI, but returns a promise. But I’m really just thinking out loud here.

showModal is not what I’m looking for here, because it requires much more than a simple one-liner like prompt(...).

I noticed that there are over a million views on these questions simply asking how to customise the confirm prompt (either by adding an extra button or changing okay/cancel to yes/no):

So it looks like there’s decent demand for this sort of thing.

Does anyone have any thoughts here?

Thanks!

3 Likes

My first question would be, do websites use the prompt() function? If yes, in what scenarios? If no, why not?

In its current form it’s obviously only useful for accepting text input, so I tend to see it used in cases where a small bit of text input is needed - i.e. not in cases where some other data is needed at the same time. You could do that with sequential prompts, but it would be terrible UX. As an example, glitch.com uses it when renaming files in the file tree.

If you look at those stack overflow links, you can see that many hundreds of thousands of developers are wanting to use confirm() but struggling with the lack of customizability. Many just want to have two buttons: “yes” and “no”.

I’ve had that problem before too, when all I wanted to do was ask the user whether they wanted a high-quality render, or a low-quality/fast render. I could have asked “Would you like a high-quality render? It will take a bit longer.”, but okay/cancel doesn’t work because “cancel” sounds like it means “cancel the whole task”. Later, I added a third render method, and in that case a simple drop-down menu with three items (or a prompt with 3 buttons) would have been perfect.

The main use case for me though would be in being able to quickly whip up demos and non-user-facing tools. It’s super handy when I’m coding up some sort of simulation thing, and I don’t want to have to worry about the DOM at all. I just want to ask the user (usually me, in this case) something like “How many iterations?” or “Resolution?” and prompt() works well for that in a lot of cases. Having a drop-down menu would help in cases where the question is “Which algorithm? (foo, bar, blah)” and I’ve got to type in the one I want.

Also, it’s interesting that Deno is adding (or has added?) support for confirm and prompt. So if other input methods were added, that might be something worth thinking about. For example, adding a range slider might be interesting to implement via a command line interface. Although now that I think about it, you’d just use the arrow keys to slide along.

I think your example is both great and kinda answers your own question… in the sense that you can create the prompt you want using the form primitives already provided.

You can also use the tag to solve your proposed use case: Allowing you to create more complex pop-up UIs with forms.

you can create the prompt you want using the form primitives already provided

Hey Marc, it is of course possible for a developer to create a popup that accepts user input. I think it’s fairly clear that this isn’t meant as a “capabilities” proposal - it’s a UX/convenience one. I think that prompts are used often enough (going by the many hundreds of thousands of page views on those stack overflow links alone) that it would be valuable to revisit the idea of browser-provided input modals like this (in a new async form).

One could try to make the argument that browsers shouldn’t have provided color pickers, because it’s completely possible to implement it with the primitives that are already provided.

Of course, there are trade-offs in implementing features like this, but bets can be hedged by e.g. not providing guarantees on how the color picker looks, the position/size of the render, etc. This proposal is essentially just a convenient and programmatic way (i.e. no HTML/CSS) to accept user inputs of various types (i.e. other than just text). It’s abstract enough, and there is enough visible demand for a feature like this that I don’t think there’s any danger in it producing bad web “cruft” that will forever need to be kept around for backward-compatibility reasons.

A feature like this could also end up being implemented in runtimes like Deno (where confirm and prompt are already implemented).

You can also use the tag to solve your proposed use case

I mentioned the dialog tag in my original post. It’s a bit of a beast to work with compared to confirm and prompt and requires a bunch of HTML to declare it (not far off just coding up my own one from scratch at that point).

(Sorry for bumping this after so long! I am still very interested in a proposal like this though.)

I just noticed that Gmail uses the confirm function for warning about attachments mentioned in the email body text, but not attached:

Luckily the ok/cancel answers match their question well. If the convenience factor is strong enough for the Gmail team, it’s probably the case that these 403k devs aren’t just being lazy in wanting to use things like confirm and prompt, but with customized answer types like yes/no.

“Simple things should be simple, and complex things should be possible” - and taking some input from the user programmatically definitely falls into the “simple” class.

2 Likes