SimplyDialogs

This is demonstration / test page for github.com/davidkonrad/simplydialogs. Latest release v1.1.0 (10.05.2025).

SimplyDialogs offers a range of common dialogs, all highly customizeable. Comes with no dependencies and a really small footprint.

alert() sample info() sample confirm() sample error() sample bell() sample wait() sample progress() sample input() sample

The input dialog can be used to create rather large forms with validation, see the Forms section.

Basic usage, assuming you have installed / included SimplyDialogs :

const Dlg = SimplyDialogs

Dlg.alert('Lorem ipsum ..')
Dlg.information('Lorem ipsum ..')
Dlg.bell('Lorem ipsum ..')
Dlg.error('Lorem ipsum ..')

Dlg.confirm('Lorem ipsum ..').then(answer => {
  console.log(answer)
})

const wait = Dlg.wait('Lorem ipsum ..')
setTimeout(() => { wait.close() }, 1500)

Dlg.input('Lorem ipsum ..').then(input => {
  console.log('input', input)
})

SimplyDialogs can be stacked upon each other endlessly

You can customise the dialog by passing an extra options argument

Dlg.confirm('Lorem ipsum ..', options).then(answer => {...})

options should follow the same structure as the DEFAULTS literal discussed below.

DEFAULTS

Simplydialogs uses internally a defaults literal, which is accessible through the .DEFAULTS property :

let defaults = {
  return: true,
  escape: true,
  backdrop: undefined,
  classes: '',
  headers: {
    alert: 'Alert', 
    error: 'Error',
    confirm: 'Confirm',
    information: 'Information',
    bell: 'Notice',
    input: ''
  },
  icons: {
    alert: '⚠', 
    error: '⛔',
    confirm: '✔️',
    information: '💡',
    bell: '🔔',
    wait: '⚙️',
    input: '✏️'
  },
  buttons: {
    captions: {
      ok: 'Ok',
      cancel: 'Cancel',
      yes: 'Yes',
      no: 'No'
    },
    classes: {
      ok: '',
      cancel: '',
      yes: '',
      no: ''
    }
  },
  progress: {
    max: undefined,
    value: undefined
  },
  input: {
    formLayout: 'left full-width',
    classes: {
      label: '',
      input: ''
    },
    inputs: [
      { type: 'input', inputType: 'text', label: 'Input ', name: 'input', placeholder: 'Enter text ..' },
    ],
    callback: function(state) {
      return state.input && state.input.length > 1
    },
    promise: undefined
  }
}

You can alter those defaults by simple assignment, i.e <instance>.DEFAULTS.icons.alert = '<img src="path/to/image">' . Changes to DEFAULTS are global, but you can still pass alternative options to each dialog (see later).

Any of the DEFAULTS settings can be overruled by passing an options literal as second parameter. The structure of the options argument must follow the same format as DEFAULTS.

For convenience you can use the shorthand aliases header and icon instead of repeating the convoluted literal :

Dlg.alert('Lorem ipsum dolor sit amet ...', { icon: '🔥', header: 'Danger!' })

Captions, language-settings

There is no such thing as "language" in SimplyDialogs, but most people would like to adjust their basic dialogs to the lingo or language they use. You can use DEFAULTS to change the dialogs into "spanish" as default by :

const Dlg = SimplyDialogs
Dlg.DEFAULTS.buttons.captions = {
  ok: 'Ok',	
  yes: 'Aceptar',
  no: 'Salir',
  cancel: 'Cancelar'
}
Dlg.DEFAULTS.headers = {
  alert: 'Alerta', 
  error: 'Error',
  confirm: 'Confirmar',
  information: 'Información',
  bell: 'Nota',
  input: 'Input'
}

//change placeholder for the default input dialog as well
Dlg.DEFAULTS.input.inputs[0].placeholder = 'Introducir texto ..'

If a button.caption is '', undefined or similar, the button will be hidden.

Icons

Icons can be replaced the same way; either individually or as a whole. For example, use another standard unicode emoji :

Dlg.DEFAULTS.icons.alert = '💥' //U+1F4A5, collision

See also the integration section. Here an example using Font Awesome icons :

Dlg.DEFAULTS.icons = {
  alert: '<i class="fa fa-warning" style="color:fuchsia;"></i>',
  information: '<i class="fa fa-info-circle" style="color:royalblue;"></i>',
  confirm: '<i class="fa fa-question-circle" style="color:forestgreen;"></i>',
  bell: '<i class="fa fa-bell" style="color:orange;"></i>',
  error: '<i class="fa fa-times-circle-o" style="color:maroon;"></i>',
  input: '<i class="fa fa-pencil" style="color:navy;"></i>',
  wait: '<i class="fa fa-gear" style="color:gray;"></i>'
}

classes

You can specify custom classes for the <dialog element itself. Either static through .DEFAULTS or on the fly via options :

Dlg.DEFAULTS.classes = 'my dialog classes'

Dlg.alert('Lorem ipsum ..', { classes: 'my dialog classes'}).then(answer => 

However, the main purpose with classes is to use predefined classes for specifying size and position for the dialog, like top right medium which is discussed in separate sections below. A few additional builtin classes might occasionally be useful :

no-paddingThe dialog has no interior padding
no-shadowThe dialog has no shadow
no-backdropThe dialog has no backdrop
dark-backdropThe dialog has a darker backdrop

Sizing

You can use predefined class names to set dialog default size

Dlg.DEFAULTS.classes = 'small'
xs / xsmall18vw / 15vhxs-width / xs-height
sm / small22vw / 18vhsm-width / sm-height
md / medium34vw / 31vhmd-width / md-height
lg / large55vw / 52vhlg-width / lg-height
fullsize100vw / 100vh

You can override the default using options

Dlg.info('some text ...', { classes: 'xs' }).then(..)

Note: The examples show dimensions, they are not meant to fit

Positioning

Per default all dialogs are shown in the center of the viewscreen, but vertically centered between middle and top. Both the default centered position and position middle are leaning to the top for aesthetic reasons. The default middle is 9vh from the top. The left, right margin is 4vw; top, bottom has 5vh margin.

You can control the dialog position by using the predefined classes top, middle, bottom>, left, center, right in any meaningful combination. Remember: center is vertical, middle is horizontal.


This snippet show all combinations stacked upon each other :

['top left', 'top center', 'top right', 
 'middle left', 'middle center', 'middle right', 
 'bottom left', 'bottom center', 'bottom right'].forEach(function(position) {
  Dlg.info(position, { classes: position })
})

Keyboard

Specify how to react on keyboard strokes, return and escape. You can set both true or false for all type of dialogs, except wait and progress, but is mostly useful for the input-dialog. Note: Return / enter are ignored in textareas.

const options = {
  return: (true | false),
  escape: (true | false),
  input: {
    inputs: [
      { type: 'input', inputType: 'text', label: 'Input', name: 'input', required: true, minlength: 3 }, 
      { type: 'textarea', label: 'Textarea', name: 'textarea', spellcheck: false }
    ]
  }
}

This produces a form where you need to enter at least 3 characters to the <input> field, and also have an optional <textarea>. In the first sample you can hit enter and submit the form, in the second you'll need to click on OK (and so on).

progress

The progress dialog cannot be closed or cancelled by the user, but must be closed programmatically by using the close() method. By default the <progress> element is shown in indeterminate state (ongoing with no indication)

const progress = Dlg.progress('Lorem ipsum ...')
setTimeout(() => progress.close(), 2000)

To make a progressive progress, specify a max value and use setValue to update the progress

const progress = Dlg.progress('Lorem ipsum ...', { progress: { value: 0, max: 100 }})

//mimick a progressive task
let interval, value = 0
const update = function() {
  value++
  progress.setValue(value)
  progress.setText(`progress ${value}%`)
  if (value > 100) {
    clearInterval(interval)
    progress.close()
  }
}
interval = setInterval(update, 20)

wait

When using the wait-dialog, it can be convenient to update the dialog for details about progress or whatever there is going on. wait has two special methods for updating the message, setText and addText. Both takes a string as argument, that can be plain text or HTML.

setText

setText replaces the message with new content. Consider this mockup :

let counter = 0
let interval = undefined
const wait = Dlg.wait('Lorem ipsum ...')
const replace = function() {
  counter ++
  wait.setText(`text or message changed ${counter} time(s)`)
  if (counter > 5) {
    clearInterval(interval)
    wait.close()
  }
}
interval = setInterval(replace, 1000)

addText

addText appends something to the already existing text/message. The content can be plain text or HTML. Consider this mockup :

let counter = 0
let interval = undefined
const wait = Dlg.wait('Lorem ipsum ...')
const append = function() {
  counter ++
  wait.addText(`<br>${counter} line(s) added to message`)
  if (counter > 5) {
    clearInterval(interval)
    wait.close()
  }
}
interval = setInterval(append, 1000)

backdrop

Across the browsers, their computed default ::backdrop CSS more or less look like this :

dialog::backdrop {
  position: fixed;
  top: 0px;
  right: 0px;
  bottom: 0px;
  left: 0px;
  background: rgba(0, 0, 0, 0.1);
}

You can change DEFAULTS.backdrop in order to change the background of the dialogs. You can specify the background style itself by passing a valid CSS class-definition. Here are some examples:

Dlg.DEFAULTS.backdrop = 'background: rgba(0, 0, 0, 0);'
Dlg.DEFAULTS.backdrop = 'background: rgba(0, 0, 0, 0.5);'

Or get more inspiration at https://projects.verou.me/css3patterns/, where the ideas below are taken from :

Dlg.DEFAULTS.backdrop = `background: linear-gradient(27deg, #151515 5px, transparent 5px) 0 5px,
  linear-gradient(207deg, #151515 5px, transparent 5px) 10px 0px,
  linear-gradient(27deg, #222 5px, transparent 5px) 0px 10px,
  linear-gradient(207deg, #222 5px, transparent 5px) 10px 5px,
  linear-gradient(90deg, #1b1b1b 10px, transparent 10px),
  linear-gradient(#1d1d1d 25%, #1a1a1a 25%, #1a1a1a 50%, transparent 50%, transparent 75%, #242424 75%, #242424);
  background-color: #131313;
  opacity: 0.4;
  background-size: 20px 20px;`

Dlg.DEFAULTS.backdrop = `background: linear-gradient(63deg, #999 23%, transparent 23%) 7px 0,
  linear-gradient(63deg, transparent 74%, #999 78%),
  linear-gradient(63deg, transparent 34%, #999 38%, #999 58%, transparent 62%), #444;
  opacity: 0.4;
  background-size: 16px 48px;`

Forms

SimplyDialogs are designed for handling advanced inputs (or forms). With built-in form validation, customiseable by callbacks (or promises), and taking care of spellcheck and autocomplete, you can show rather complex forms just by a few lines of code. First remember the input section of the DEFAULTS literal :

const options = {
  ...
  input: { 
    formLayout: '',
    classes: {
      label: '',
      input: ''
    },
    inputs: [
      { .. }, 
      { .. }, //<- define form controls here
    ],
    callback: undefined,
    promise: undefined
  }
}

Basically you show a form by passing a list to input.inputs, specifying which inputs/form controls that should go into the form, along with their properties/settings.

Here is an example with a simple login form, demonstrating some of the basic features :

const options = {
  header: 'Login',
  icon: '🔑',
  input: {
    inputs: [{ 
      type: 'input', 
      inputType: 'text', 
      label: 'Username', 
      name: 'username',
      required: true,
      minlength: 3,
      //autocomplete: true, 
      //spellcheck: true,
      //classes: 'required'
    }, { 
      type: 'input', 
      inputType: 'password', 
      label: 'Password', 
      name: 'password',
      required: true,
      minlength: 3,
      //autocomplete: true, 
      //classes: 'required'
    }]
  }
}
Dlg.input('', options).then(input => console.log('login', input))

Notice the use of required and minlength. The submit button is disabled until all required criterias are fulfilled. Also notice the absence of browser-enforced spellcheck and autocomplete, those features are opt-in. If the predefined required class is added to classes, the form control is outlined according to its validation status.

Form controls

Use the type property to specify the type of form control. You should also at least specify a name for each control, if not the control is given an anonymous name like select_1.

const options = {
  input: {
    inputs: [
      { 
        type: 'input' || 'textarea' || 'select' || 'radio',
        name: 'name',
        ... //control specific properties
      },
    ]
  }
}

Each type is discussed below :

input

Set inputType to specify the type of the <input>. See MDN's <input> types for a complete reference.

const options = {
  input: {
    formLayout: 'left clear-left',
    inputs: [
      { type: 'input', inputType: 'text', label: 'Text', name: 'input', placeholder: 'Type some text ...' },
      { type: 'input', inputType: 'checkbox', label: 'Checkbox', name: 'checkbox', checked: true },
      { type: 'input', inputType: 'color', label: 'Color', name: 'color', value: '#123456' },
      { type: 'input', inputType: 'number', label: 'Number', name: 'number', value: 42 },
      { type: 'input', inputType: 'password', label: 'Password', name: 'password', style: 'color:maroon;' },
      { type: 'input', inputType: 'date', label: 'Date', name: 'date' },
      { type: 'input', inputType: 'datetime-local', label: 'Datetime-local', name: 'datetime-local' },
      { type: 'input', inputType: 'week', label: 'Week', name: 'week' },
      { type: 'input', inputType: 'file', label: 'File', name: 'file' },
      { type: 'input', inputType: 'url', label: 'Url', name: 'url' },
      { type: 'input', inputType: 'tel', label: 'Tel', name: 'tel' },
      { type: 'input', inputType: 'email', label: 'Email', name: 'email' },
      { type: 'input', inputType: 'range', label: 'Range', name: 'range', value: 25, max: 100 },
      { type: 'input', inputType: 'hidden', label: '', name: 'hidden', value: 'Pass extra values to the form via hidden inputs' }
    ]
  }
}
Dlg.input('Lorem ipsum ...', options).then(input => console.log('result', input))

You can pass any <input>-specific attribute and it will blindly be rendered, even style. input takes care of the following properties :

inputType as described above
value empty by default
classes CSS classes important predefined classes are password, required
required false by default not supported by inputType color, range, hidden
spellcheck false by default only relevant for inputType text

select

type: 'select produces a <select> control. Use options to define the selects' options :

const options = { 
  input: { 
    inputs [{ 
      type: 'select', label: 'select', name: 'select', value: 'option2',
        options: [
          { label: 'option1', value: 'option1' },
          { label: 'option2', value: 'option2' },
          { label: 'option3', value: 'option3' }
        ]
      }
    ]
  }
}	

radio

type: radio renders a group of radio buttons. Use options to define the radio group items :

const options = { 
  input: { 
    inputs: [{ 
      type: 'radio', label: 'radio', name: 'radio', value: 'option2', //inline: true, 
        options: [
          { label: 'option1', value: 'option1' },
          { label: 'option2', value: 'option2' },
          { label: 'option3', value: 'option3' }
        ]
      }
    ]
  }
}	

radio takes special care of the following properties :

options as described above
inline false by default continuous rendering of the inputs
classes CSS classes
required false by default Will reset any value

textarea

type: 'select produces a <select> control. Use options to define the selects' options :

const options = { 
  input: { 
    inputs [{ 
      type: 'select', label: 'select', name: 'select', value: 'option2',
        options: [
          { label: 'option1', value: 'option1' },
          { label: 'option2', value: 'option2' },
          { label: 'option3', value: 'option3' }
        ]
      }
    ]
  }
}	

formLayout

A few classes that let you specify the layout of the form

left, top labels to the left or on top of form controls, default is left
full-width form controls 100% width, default
form-clear by default the form floats inline after icon and message (if set), form-left clears the form to the left

const options = {
  input: {
    formLayout: 'top | left | full-width | form-clear',
    inputs: [
      { type: 'input', inputType: 'text', label: 'Input', name: 'input', placeholder: 'Input required' },
      { type: 'textarea', label: 'Textarea', name: 'textarea', placeholder: 'Additional text', rows: 4 }
    ]
  }
}

autofocus

If you set autofocus: true, then the input (or textarea, select) will be focused when the dialog is shown

spellcheck

Browser spellcheck is disabled per default and therefore opt-in; Use spellcheck: true to enable spellcheck

autocomplete

Built-in browser autocomplete is opt-in as well; Use autocomplete: 'on' to enable autocomplete (input elements only)

Form validation

You can choose between 'native' declarative form validation by specifying form controls as required, or programmatically by passing a callback, that can be either a function or a promise.

required

In your options, mark one or more form controls as required and specify their constraints (if needed), just as you would have done in HTML markup. SimplyDialogs support the same limits and patterns you can build by using required and HTML5 properties. Here is a very basic example where the user is prompted to enter a valid email address :

const options = {
  header: 'Enter valid email',
  icon: '📧',
  input: {
    inputs: [{ 
      type: 'input', 
      inputType: 'email', 
      name: 'email', 
      spellcheck: false,
      required: true //<-- mark required as true
    }],
  }
}
Dlg.input('', options).then(input => {
  console.log('email', input)
})

The "magic" works, you dont even have to specify some constraints, because an input of type email always will be invalid if the email address is invalid - thus the submit button is not enabled before a valid email address is entered. Here is another very basic example where we are asking for a danish-style phone number, 8 digits :

const options = {
  header: 'Phone number',
  icon: null,
  input: {
    inputs: [{ 
      type: 'input', 
      inputType: 'text', 
      name: 'phone', 
      label: '+45',
      required: true,
      pattern: '[0-9]{8}'
    }],
  }
}
Dlg.input('Enter 8 digit number, regional code implied', options).then(input => {
  console.log('phone', input)
})

btn-validation-required-danish-phonenumber Mark any All you need to do is to mark the form control as required using declarative inputs, You can There are two ways to validate form input: input.callback and input.promise.

If (and only if) you specify a callback (or a promise), the submit-button is disabled and the callback will determine when it should be enabled. Callbacks are triggered whenever any of the inputs changes state, and the result will be handled as a boolean; i.e if you return true the submit-button will be enabled. The callback passes two params :

callback: function(state, dialog) {
  return true || false
}
  • state is an object literal containing { name: value } for all inputs.
  • dialog is the dialog's actual HTMLDialogElement, which can be used to manipulate input fields on the form (or anything else).

An example of using both, here the submit button is enabled only when the user type 42

callback

Example: You are not allowed to submit before you have entered the secret number 47 :

const options = {
  headers: { input: 'What is 42 with inflation?' },
  icons: { input: '🖖' },
  input: {
    inputs: [	
      { type: 'input', inputType: 'password', label: '', name: 'star-trek-number' }
    ],
    callback: function(state) {
      return parseInt(state['star-trek-number']) === 47
    }
  }
}
Dlg.input(null, options).then(input => {
  console.log(input)
})

promise

A promise should be used when you have more complex / time consuming validation schemes. For example, if you need to lookup an username in your backend, to test if it is already in use. In the below we just mimick a server-side call by using a timeout :

const options = {
  headers: { input: 'Enter user name' },
  icons: { input: '' },
  input: {
    inputs: [	
      { type: 'input', inputType: 'text', label: '', name: 'username' }
    ],
    promise: function(state) {
      return new Promise(function(resolve) {
        if (state.username.length < 5) return resolve(false)
        const letters = /^[0-9a-zA-Z]+$/
        if (!letters.test(state.username)) return resolve(false)
        setTimeout(function() {
          resolve(true)
        }, 1000)
      })
    }
  }
}
Dlg.input('Must be at least 5 characters long and may only contain letters and numbers', options).then(input => {
  console.log(input)
})

Login Dialog

Here is an example of how to show a modal login-dialog. Notice the use of autocomplete: off which turns off annoying auto suggestions :

const options = {
  headers: { input: 'Login' },
  icons: { input: '🔑' },
  input: {
    inputs: [	
      { type: 'input', inputType: 'text', label: 'Username', name: 'username', autocomplete: 'off' },
      { type: 'input', inputType: 'password', label: 'Password', name: 'password' }  
    ],
    callback: function(state) {
      return state.username.length > 0 && state.password.length > 0
    }
  }
}
Dlg.input('', options).then(input => {
  console.log('login', input)
  // { username: '...', password: '...' }
})

File input with preview

You kan combine a image type input with a file type input, and by that create an image-upload-dialog with preview. It takes a little more options-tweaks than the prior examples:

const options = {
  headers: { input: 'Upload Image' },
  icons: { input: '🖼' },
  input: {
    inputs: [
      { type: 'input', 
        inputType: 'image', 
        label: 'Preview', 
        name: 'preview', 
        alt: 'No image yet', 
        disabled: 'disabled',
        src: 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"/>',
        style: 'min-height: 30vh; max-height: 50vh; border: 1px solid #dadada; object-fit: contain;' 
      }, { 
        type: 'input', 
        inputType: 'file', 
        label: 'Image', 
        name: 'file', 
        accept: 'image/png, image/gif, image/jpeg' 
      }
    ],
    callback: function(state, dialog) {
      if (state.file) {
        dialog.querySelector('input[type="image"]').src = URL.createObjectURL(state.file)
        return true
      }
    }
  }
}
Dlg.input('Select image file <br><sup>(PNG, GIF, JPG supported)</sup>', options).then(function(input) {
  console.log('image upload', input)
})

Further examples

As example, define the following CSS-class and pass the literal :

dialog.nasa-earth {
  background: url(https://explorer1.jpl.nasa.gov/assets/images/galleries/1972_BlueMarble_115334main_image_feature_329_ys_full.jpg);
  background-size: cover;
  color: yellow;
  min-height: 50vh;
  font-size: x-large;
}
const options = {
  classes: 'nasa-earth lg-height sm-width',
  icons: { confirm: '👽' },
  headers: { confirm: null },
  buttons: { captions: { yes: 'No', no: 'Defently not!' }}
}
Dlg.confirm('Are There Any Chance of Intelligent Life in The Universe, anywhere?', options)

;- it is hard to figure out examples, or maybe I am just stoned.

Image Viewer

Reset button-captions and the header :

const msg = '<img src="https://explorer1.jpl.nasa.gov/assets/images/galleries/1972_BlueMarble_115334main_image_feature_329_ys_full.jpg" style="max-height:75vh;">'
const options = {
  headers: { bell: '' },
  icons: { bell: '' },
  buttons: { captions: { ok: null }}
}
Dlg.bell(msg, options)

Maps example

You can use HTML in headers, buttons and the dialog message itself. Here the informtion() dialog showing a Google Maps destination :

const msg = 'The Great Pyramid of Giza is the largest Egyptian pyramid and tomb of<br> Fourth Dynasty pharaoh Khufu. Built in the 26th century BC during a<br> period of around 27 years. '
const html = '<div class="mapouter"><div class="gmap_canvas"><iframe width="600" height="500" id="gmap_canvas" src="https://maps.google.com/maps?q=the%20great%20pyramids&t=k&z=13&ie=UTF8&iwloc=&output=embed" frameborder="0" scrolling="no" marginheight="0" marginwidth="0"></iframe><a href="https://www.whatismyip-address.com"></a><br><style>.mapouter{position:relative;text-align:right;height:500px;width:600px;}</style><a href="https://www.embedgooglemap.net">embedgooglemap.net</a><style>.gmap_canvas {overflow:hidden;background:none!important;height:500px;width:600px;}</style></div></div>'
const options = {
  headers: { information: 'The Great Pyramid of Giza' }
}
Dlg.information(msg + html, options)

X-mas

More suitable if you want to style a certain dialog differently. Here is an example with a kind of "Merry X-mas dialog", that overrule many of the default styles :

dialog.x-mas {
  border-radius: 0;
  border: 1mm ridge #34a65f !important;
  background: linear-gradient(45deg, #bf953f, #fcf6ba, #b38728, #fbf5b7, #aa771c);
}
dialog.x-mas h4 {
  font-size: xxx-large;
  font-family: 'Mountains of Christmas';
  font-weight: 900;
  color: #cc231e;
  text-shadow: 1px 1px #34A65F;
}
dialog.x-mas .dialog-message {
  font-size: medium;
  font-family: cursive;
  color: #cc231e;
  text-shadow: -1px -1px gold;
}
dialog.x-mas button {
  background-color: #0f8a5f;
  font-size: x-large;
  font-weight: bold;
  padding: 7px;
  color: gold;
  border-color: gold;
  padding-left: 20px;
  padding-right: 20px;
}

Then activate the dialog in code like this :

const options = {
  classes: 'x-mas',
  headers: { alert: 'Merry Christmas ...' },
  icons: { alert : '🎄' },
  buttons: { captions: { ok: '✨ Ok ✨' }}
}
const msg = '🎀 🎁 Frohe Weihnachten, メリークリスマス, Joyeux Noël, Feliz Navidad, 聖誕快樂, Buon Natale, Glædelig Jul ...!'
Dlg.alert(msg, options)

Integration

Example, Bootstrap 5

Example, Materialize CSS

Credits

This site was easy to produce, thanks to :