<button id="add-counter">Add counter</button>
<button id="increment">Increment</button>
<div class="counters-container"></div>
.counters-container {
display: flex;
gap: 1rem;
flex-wrap: wrap;
}
.counter {
padding: 4px 8px;
display: flex;
align-items: center;
gap: 1rem;
}
.counter:nth-child(even) {
background: lightgreen;
}
.counter:nth-child(odd) {
background: lightpink;
}
class EventBus {
// List of all subscribed components
subscribers = new Set();
// Add subscriber
subscribe(subscriberCallback) {
this.subscribers.add(subscriberCallback);
subscriberCallback(reactiveData.counter);
}
// Remove subscriber
unsubscribe(subscriberCallback) {
this.subscribers.delete(subscriberCallback);
}
// Letting the subscribers know about new change
publish() {
this.subscribers.forEach(subscriberCallback => subscriberCallback(reactiveData.counter));
}
}
const eventBus = new EventBus();
// Initial DOM update
eventBus.publish();
function renderCounterComponent() {
// Rendering a counter component that shows the counter number and also a button to remove itself.
const counter = document.createElement('div');
counter.classList.add('counter');
const counterText = document.createElement('span');
counter.appendChild(counterText);
function updateCounter(count) {
// Updating the counter text.
counterText.innerText = count;
}
const removeCounterBtn = document.createElement('button');
removeCounterBtn.innerText = '❌';
removeCounterBtn.addEventListener('click', () => {
eventBus.unsubscribe(updateCounter);
counter.remove();
})
counter.appendChild(removeCounterBtn);
eventBus.subscribe(updateCounter);
document.body.querySelector('.counters-container').appendChild(counter);
}
const addCounter = document.getElementById('add-counter');
addCounter.addEventListener('click', renderCounterComponent);
// Creating a simple object that holds a state. Will be used as the target object.
const data = {
counter: 0
};
// Configure the handler object and how we are going to follow changes on state and update the DOM.
const handler = {
get(target, property) {
// Get the value of a specific property
return target[property];
},
set(target, property, value) {
// Set the value of a specific property
target[property] = value;
// Update the DOM
eventBus.publish();
}
};
// Creating the Proxy
const reactiveData = new Proxy(data, handler);
// Listen to click event to set the new counter value
const incrementButton = document.getElementById('increment');
if (incrementButton) {
incrementButton.addEventListener('click', () => {
reactiveData.counter++;
});
}
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.