Created
April 1, 2025 18:01
-
-
Save cferdinandi/cf287f1c404872291b4120a17f6617e7 to your computer and use it in GitHub Desktop.
Watch the tutorial on YouTube: https://youtu.be/v-3O0MCl41k
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 lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<title>Toast Library</title> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<style> | |
body { | |
margin: 1em auto; | |
width: 88%; | |
max-width: 40em; | |
min-height: 100vh; | |
} | |
</style> | |
<link rel="stylesheet" type="text/css" href="toast.css"> | |
</head> | |
<body> | |
<h1>Toast Library</h1> | |
<button>Add a Thing</button> | |
<div class="notify-wrapper"></div> | |
<script src="toast.js"></script> | |
<script> | |
let btn = document.querySelector('button'); | |
btn.addEventListener('click', function () { | |
console.log('thing added!'); | |
let notification = new Notify('.notify-wrapper', 'Your thing was added!', { | |
type: 'info', // info || success || error || warning | |
noClose: false, // true || false | |
// duration: 0, // how long to persist in milliseconds (-1 for forever) | |
}); | |
notification.show(); | |
// notification.dismiss(); | |
notification.notification.addEventListener('notify:before-dismiss', (event) => { | |
if (!someOtherCondition) { | |
event.preventDefault(); | |
} | |
}); | |
}); | |
function log (event) { | |
console.log(event.type, event.target); | |
} | |
document.addEventListener('notify:before-show', log); | |
document.addEventListener('notify:show', log); | |
document.addEventListener('notify:before-dismiss', function (event) { | |
log(event); | |
event.preventDefault(); | |
}); | |
document.addEventListener('notify:dismiss', 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
/** | |
* @section Toast styles | |
*/ | |
.notify-wrapper { | |
position: fixed; | |
left: 0; | |
bottom: 0; | |
width: 100%; | |
} | |
.notify { | |
background-color: #272727; | |
border-radius: 1em; | |
color: #ffffff; | |
padding: 0.5em 1em; | |
margin: 0.25em auto; | |
width: 88%; | |
max-width: 30em; | |
} | |
.notify-info { | |
background-color: #007ab8; | |
} | |
.notify-success { | |
background-color: green; | |
} | |
.notify-error { | |
background-color: red; | |
} | |
.notify-close { | |
cursor: pointer; | |
float: right; | |
background: transparent; | |
border: none; | |
padding-left: 0.25em; | |
height: 1em; | |
width: 1em; | |
} | |
.notify-close:before { | |
content: "X"; | |
color: #ffffff; | |
} |
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
// Toast JS | |
class Notify { | |
/** | |
* Instantiate the library | |
* @param {String | Element} selector The target location to render the notification | |
* @param {String} message The message to render into the UI | |
* @param {Object} options The library options | |
*/ | |
constructor (selector, message, options = {}) { | |
// Get the target to render into | |
this.target = typeof selector === 'string' ? document.querySelector(selector) : selector; | |
if (!this.target) { | |
console.warn('NotifyJS: Target element not found'); | |
return; | |
} | |
if (!message) { | |
console.warn('NotifyJS: Please provide a message'); | |
return; | |
} | |
this.message = message; | |
this.type = options.type || 'info'; // info || success || error || warning | |
this.noClose = !!options.noClose; // true || false | |
this.duration = options.duration ?? 3000; // how long to persist in milliseconds (-1 for forever) | |
this.closeMessage = options.closeMessage || 'Close Notification'; | |
} | |
/** | |
* Show the notification | |
*/ | |
show () { | |
// Only run if target and message are provided | |
if (!this.target || !this.message) return; | |
// Create the notification message | |
this.notification = document.createElement('div'); | |
this.notification.className = `notify notify-${this.type}`; | |
this.notification.setAttribute('role', 'status'); | |
// Add it to the DOM | |
this.target.prepend(this.notification); | |
if (!this.emit('before-show')) return; | |
// Update the text | |
setTimeout(() => { | |
this.notification.textContent = this.message; | |
this.emit('show'); | |
}, 1); | |
// Automatically dismiss the notification | |
if (this.duration > 0) { | |
setTimeout(() => { | |
this.dismiss(); | |
}, this.duration); | |
} | |
// If user can't close, nothing else to do | |
if (this.noClose) return; | |
// Create close button | |
let btn = document.createElement('button'); | |
btn.className = 'notify-close'; | |
btn.setAttribute('aria-label', this.closeMessage); | |
setTimeout(() => { | |
this.notification.prepend(btn); | |
}, 2); | |
// Listen for button clicks, and dismiss the notification | |
btn.addEventListener('click', () => { | |
this.dismiss(); | |
}); | |
} | |
/** | |
* Dismiss the notification | |
*/ | |
dismiss () { | |
// Only run if target and message are provided | |
if (!this.target || !this.message || !this.notification) return; | |
if (!this.emit('before-dismiss')) return; | |
// Remove the notification from the DOM | |
this.emit('dismiss'); | |
this.notification.remove(); | |
} | |
/** | |
* Emit a custom event | |
* @param {String} type The event type | |
*/ | |
emit (type) { | |
// Make sure there's an event type | |
if (!type) return; | |
// Create a new event | |
let event = new CustomEvent(`notify:${type}`, { | |
bubbles: true, | |
cancelable: true | |
}); | |
// Dispatch the event | |
return this.notification.dispatchEvent(event); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment