Sitemap

You don’t need React or Angular or Svelte or frameworks. How Object Oriented UI rises with EventEmitter.

3 min readNov 4, 2025

--

Press enter or click to view image in full size

On part 1 we discussed webpack and OOP for a frameworkless User Interface system. On this article I’ll write about how you should use event emitter for UI class communication and will give some examples.

EventEmitter.js (implementation)

//     ______                 __      ______          _ __  __           
// / ____/ _____ ____ / /_ / ____/___ ___ (_) /_/ /____ _____
// / __/ | | / / _ \/ __ \/ __/ / __/ / __ `__ \/ / __/ __/ _ \/ ___/
// / /___ | |/ / __/ / / / /_ / /___/ / / / / / / /_/ /_/ __/ /
// /_____/ |___/\___/_/ /_/\__/ /_____/_/ /_/ /_/_/\__/\__/\___/_/

/*
PubSub
Singleton
*/

let eventEmitterInstance = null

class EventEmitter {
constructor() {
if (eventEmitterInstance) {
throw new Error("EventEmitter already exists. Can't be re-instantiated")
}

this.handlers = {}

eventEmitterInstance = this
}

on(event, fn, cb) {
let callback = cb

this.handlers[event] = {
handler: fn,
cb: function () {
if (callback) callback()
}
}
}

emit(event, data, cb) {
if (!this.handlers) {
return
}

let callback = cb

this.handlers[event].handler(data)

if(callback){
cb()
}
}
}

const eventEmitter = Object.freeze(new EventEmitter())

export default eventEmitter

EventEmitter seems complex huh? Pub/Sub(Publisher/Subscriber) pattern and Singleton patterns are used in the file. Don’t worry. I’m not telling lies and you’ll never go back to this implementation after playing it in your project directory.

Get Akif Volkan Ongun’s stories in your inbox

Join Medium for free to get updates from this writer.

MyUIClass is new in this code. MyApp class is the class which we wrote in the previous article.

MyApp.js

import EventEmitter from "./EventEmitter.js"

class MyApp {

constructor(){
this.myProp = 0
this.myString = "My String"
this.template

// As you know EventEmitter is singleton and should be instantiated
// without paranthesis. normally you should instantiate the class with
// open and close paranthesis.
this.eventEmitter = EventEmitter

this.initialize()
}

initialize(){
this.generateTemplate()
this.template.querySelector(".AppContainer").innerHTML = "Hello World"

/*
__ _ __
_______ __/ /_ _______________(_) /_ ___
/ ___/ / / / __ \/ ___/ ___/ ___/ / __ \/ _ \
(__ ) /_/ / /_/ (__ ) /__/ / / / /_/ / __/
/____/\__,_/_.___/____/\___/_/ /_/_.___/\___/

*/

this.eventEmitter.on("MyEvent", (data) => {
console.log(data)
// [1,2,3,4,5]
})
}

generateTemplate(){
let templateString = `<div class="myApp">
<div class="AppContainer">${this.myString}</div>
</div>`

let parser = new DOMParser()
this.template = parser.parseFromString(templateString, 'text/html')
this.template = this.template.body.childNodes[0]
}
}

class MyUIClass {
constructor(){

this.myData = [1,2,3,4,5]

// EventEmitter should exist in this class too.
this.eventEmitter = EventEmitter

this.initialize()
}

initialize(){

/*
__ ___ __
____ __ __/ /_ / (_)____/ /_
/ __ \/ / / / __ \/ / / ___/ __ \
/ /_/ / /_/ / /_/ / / (__ ) / / /
/ .___/\__,_/_.___/_/_/____/_/ /_/
/_/

*/

this.eventEmitter.emit("MyEvent", this.myData)
}
}

const myApp = new MyApp()

How should you interpret the top code? MyUIClass emits(publishes) the event, then MyApp class catches the events and does something in the callback. With this technique we can jump from one class to another and use callback to do things inside. “MyEvent” string can be anything. (“MyVeryExplanatoryEventName”) and you can pass class parameters.

What you need to know to understand this code is callbacks and class scopes. If you don’t understand just write a comment and i can help.

We used all those techniques in our upcoming OS, WebOS. Stay tuned for 2026.

Have a great day.

--

--