-
-
Save cferdinandi/ce2b63ed6ca9acaed0d4706c170b06d6 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Modals</title> | |
<style type="text/css"> | |
body { | |
margin: 1em auto; | |
max-width: 30em; | |
width: 88%; | |
} | |
dialog::backdrop { | |
background-color: rgba(0, 0, 0, 0.7); | |
} | |
</style> | |
</head> | |
<body> | |
<h1>Modals</h1> | |
<modal-toggle target="#wizards" hidden><button>Wizards</button></modal-toggle> | |
<modal-toggle target="#apes" hidden><button>Apes</button></modal-toggle> | |
<modal-toggle target="#confirm" hidden><button>Confirm</button></modal-toggle> | |
<modal-content id="wizards"> | |
<dialog> | |
<h2>Wizards</h2> | |
<form method="dialog"> | |
<label for="wizard">Who is your favorite wizard?</label> | |
<input type="text" id="wizard" name="wizard" modal-value> | |
<button close>Cancel</button> | |
<button>Submit</button> | |
</form> | |
</dialog> | |
</modal-content> | |
<modal-content id="apes"> | |
<dialog> | |
<h2>Apes</h2> | |
<form method="dialog"> | |
<label for="best-ape">What's the best ape?</label> | |
<select id="best-ape" modal-value> | |
<option value="">--</option> | |
<option value="Chimp">Chimp</option> | |
<option value="Orangatan">Orangatan</option> | |
<option value="Gorilla">Gorilla</option> | |
</select> | |
<button close>Cancel</button> | |
<button>Submit</button> | |
</form> | |
</dialog> | |
</modal-content> | |
<modal-content id="confirm"> | |
<dialog> | |
<h2>Stuff is happening</h2> | |
<form method="dialog"> | |
<button close>Cancel</button> | |
<button value="ok">Ok</button> | |
</form> | |
</dialog> | |
</modal-content> | |
<script src="modals.js"></script> | |
<script> | |
function log (event) { | |
console.log(event.type, event.target, event.detail); | |
} | |
document.addEventListener('modal:open', log); | |
document.addEventListener('modal:submit', log); | |
document.addEventListener('modal:cancel', log); | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
customElements.define('modal-toggle', class extends HTMLElement { | |
/** | |
* Instantiate the component | |
*/ | |
constructor () { | |
// Inherit parent class properties | |
super(); | |
// Set properties | |
this.modal = document.querySelector(this.getAttribute('target')); | |
this.toggle = this.querySelector('button, a'); | |
// Show button | |
this.removeAttribute('hidden'); | |
// If toggle is a link, add proper role | |
if (this.toggle.matches('a')) { | |
this.toggle.setAttribute('role', 'button'); | |
} | |
// Listen for click events | |
this.toggle.addEventListener('click', this); | |
} | |
/** | |
* Handle events | |
* @param {Event} event The event object | |
*/ | |
handleEvent (event) { | |
event.preventDefault(); | |
if (!this.modal) return; | |
this.modal.open(); | |
} | |
}); | |
customElements.define('modal-content', class extends HTMLElement { | |
/** | |
* Instantiate the component | |
*/ | |
constructor () { | |
// Inherit parent class properties | |
super(); | |
// Set properties | |
this.modal = this.querySelector('dialog'); | |
this.form = this.querySelector('[method="dialog"]'); | |
this.cancelling = false; | |
// Setup the DOM | |
this.setup(); | |
} | |
/** | |
* Handle events | |
* @param {Event} event The event object | |
*/ | |
handleEvent (event) { | |
this[`on${event.type}`](event); | |
} | |
/** | |
* Handle click events | |
* @param {Event} event The event object | |
*/ | |
onclick (event) { | |
if (!event.target.closest('[close]')) return; | |
this.modal.returnValue = ''; | |
this.close('cancel', true); | |
} | |
/** | |
* Handle close events | |
* @param {Event} event The event object | |
*/ | |
onclose (event) { | |
if (this.cancelling) { | |
this.cancelling = false; | |
return; | |
} | |
this.close(this.modal.returnValue ? 'submit' : 'cancel'); | |
} | |
/** | |
* Handle cancel events | |
* @param {Event} event The event object | |
*/ | |
oncancel (event) { | |
this.close('cancel', true); | |
} | |
/** | |
* Handle input events | |
* @param {Event} event The event object | |
*/ | |
oninput (event) { | |
if (!event.target.matches('[modal-value]')) return; | |
this.modal.returnValue = event.target.value || ''; | |
} | |
/** | |
* Prevent form buttons from submitting | |
*/ | |
setup () { | |
// Stop cancel buttons from submitting the form | |
let btns = this.querySelectorAll('form [close]'); | |
for (let btn of btns) { | |
btn.setAttribute('type', 'button'); | |
} | |
// Listen for events | |
if (!this.modal) return; | |
this.modal.addEventListener('click', this); | |
this.modal.addEventListener('close', this); | |
this.modal.addEventListener('cancel', this); | |
if (!this.form) return; | |
this.modal.addEventListener('input', this); | |
} | |
/** | |
* Emit a custom event | |
* @param {String} type The event type name | |
*/ | |
emit (type) { | |
// Set the event detail | |
let detail = type === 'submit' ? this.modal.returnValue : null; | |
// Create a new event | |
let event = new CustomEvent(`modal:${type}`, { | |
bubbles: true, | |
detail | |
}); | |
// Dispatch the event | |
return this.dispatchEvent(event); | |
} | |
/** | |
* Open the modal | |
*/ | |
open () { | |
// If there's no modal, bail | |
if (!this.modal) return; | |
// Show the modal | |
this.modal.showModal(); | |
// Add styling hook to body | |
document.body.setAttribute('modal-open', ''); | |
// Emit an open event | |
this.emit('open'); | |
} | |
/** | |
* Close the modal | |
* @param {string} type The close type | |
* @param {boolean} cancel If true, temporary set cancel state | |
*/ | |
close (type, cancel = false) { | |
// If there's no modal, bail | |
if (!this.modal) return; | |
// Remove the style hook from the body | |
document.body.removeAttribute('modal-open'); | |
// Temporarily set cancel state | |
this.cancelling = cancel; | |
// Close the modal | |
this.modal.close(); | |
// Emit a close event | |
this.emit(type); | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment