Skip to content

Instantly share code, notes, and snippets.

@cferdinandi
Created March 12, 2025 15:56
Show Gist options
  • Save cferdinandi/71317b4881104e15d6a7b70f97c7f093 to your computer and use it in GitHub Desktop.
Save cferdinandi/71317b4881104e15d6a7b70f97c7f093 to your computer and use it in GitHub Desktop.
Watch the tutorial for this code: https://youtu.be/Ap26IwDVOV8
customElements.define('count-up-display', class extends HTMLElement {
/**
* The class constructor object
*/
constructor () {
// Gives element access to the parent class properties
super();
// Define properties
this.count = 0;
this.max = parseFloat(this.getAttribute('max-count')) ?? false;
// Create HTML
this.ui = document.createElement('div');
this.ui.setAttribute('aria-live', 'polite');
this.render();
// Inject HTML
this.append(this.ui);
// Listen for countup events
let parent = this.closest('count-up');
parent.addEventListener('countuptrigger', this);
parent.addEventListener('countupreset', this);
}
/**
* Handle events on the Web Component
* @param {Event} event The event object
*/
handleEvent (event) {
this[`${event.type}Handler`](event);
}
/**
* Handle trigger events
* @param {Event} event The event object
*/
countuptriggerHandler (event) {
// Make sure we're not at the max count
if (this.max && this.count === this.max) return;
// Increase the count
this.count = this.count + event.detail.interval;
this.render();
}
/**
* Handle reset events
* @param {Event} event The event object
*/
countupresetHandler (event) {
this.count = 0;
this.render();
}
/**
* Render the updated UI
*/
render () {
this.ui.textContent = `Clicked ${this.count} times`;
}
});
customElements.define('count-up-reset', class extends HTMLElement {
/**
* The class constructor object
*/
constructor () {
// Gives element access to the parent class properties
super();
// Define properties
this.confirmMessage = this.getAttribute('reset-confirm') ?? 'Are you sure? This cannot be undone.';
// Create HTML
this.btn = document.createElement('button');
this.btn.textContent = 'Reset the Count';
// Inject HTML
this.append(this.btn);
// Listen for click events on the button
this.btn.addEventListener('click', this);
}
/**
* Handle events on the Web Component
* @param {Event} event The event object
*/
handleEvent (event) {
let doubleCheck = confirm(this.confirmMessage);
if (!doubleCheck) return;
this.emit();
}
/**
* Emit a custom event
*/
emit () {
// Create a new event
let event = new CustomEvent('countupreset', {
bubbles: true
});
// Dispatch the event
return this.dispatchEvent(event);
}
});
customElements.define('count-up-trigger', class extends HTMLElement {
/**
* The class constructor object
*/
constructor () {
// Gives element access to the parent class properties
super();
// Define properties
let interval = parseFloat(this.getAttribute('intervals'));
this.intervals = Number.isNaN(interval) ? 1 : interval;
// Create HTML
this.btn = document.createElement('button');
this.btn.textContent = 'Click Me';
// Inject HTML
this.append(this.btn);
// Listen for click events on the button
this.btn.addEventListener('click', this);
}
/**
* Handle events on the Web Component
* @param {Event} event The event object
*/
handleEvent (event) {
this.emit({
interval: this.intervals
});
}
/**
* Emit a custom event
* @param {Object} detail Any details to pass along with the event
*/
emit (detail = {}) {
// Create a new event
let event = new CustomEvent('countuptrigger', {
bubbles: true,
detail: detail
});
// Dispatch the event
return this.dispatchEvent(event);
}
});
count-up-display {
display: block;
width: 100%;
margin-bottom: 1rem;
}
customElements.define('count-up', class extends HTMLElement {
/**
* The class constructor object
*/
constructor () {
// Gives element access to the parent class properties
super();
// get options and settings
let maxCount = this.getAttribute('max-count');
let intervals = this.getAttribute('intervals');
let resetConfirm = this.getAttribute('reset-confirm');
// Inject some HTML
this.innerHTML =
`<count-up-display ${maxCount ? `max-count="${maxCount}"` : ''}></count-up-display>
<count-up-trigger ${intervals ? `intervals="${intervals}"` : ''}></count-up-trigger>
<count-up-reset ${resetConfirm ? `reset-confirm="${resetConfirm}"` : ''}></count-up-reset>`;
}
});
<!DOCTYPE html>
<html>
<head>
<title>Count-Up</title>
<style type="text/css">
body {
margin: 1em auto;
max-width: 30em;
width: 88%;
}
</style>
<link rel="stylesheet" type="text/css" href="count-up.css">
</head>
<body>
<h1>Count-Up</h1>
<count-up max-count="21" intervals="3" reset-confirm="You sure yo? Bad idea!"></count-up>
<script src="count-up.js"></script>
<script src="count-up-display.js"></script>
<script src="count-up-trigger.js"></script>
<script src="count-up-reset.js"></script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment