Exporting a Browser Event Listener Class in TypeScript

Use Case: To write code efficiently and comply with the 'single responsibility' coding principle

If you're writing code in TypeScript then I'll assume that you've put a focus on writing code efficiently by leveraging static typing and identifying potential bugs at compile time instead of dealing with them later on in development.

The single responsibility principle should then be on your radar as well. It states that

a computer programming principle that states that every module, class, or function should have responsibility over a single part of the functionality provided by the software, and that responsibility should be entirely encapsulated by the class, module or function. All its services should be narrowly aligned with that responsibility. (Wikipedia).

You could implement this principle by creating and using classes and functions within the same file, but then our coding efficiency would eventually be lost when trying to scroll through a long file. Its best to create individual files that hold our modules and include them when necessary.

There are many ways to do this (depending on your setup) including export, import,include, require, and others. However, these might not work if you're writing front-end TypeScript code to be used in the browser.

In this post, I'm going to share with you how to write modular code leveraging TypeScript Namespaces that will be compiled into a single file and used in the browser.

Getting Started

You might not need this exact setup, but I'll be using Node.js with the TypeScript NPM module. I'll also be leveraging TypeScript's AMD module.

Here's my tsconfig.json file:

JSON

{
	"compilerOptions": {
		"noImplicitAny": true,
		"removeComments": true,
		"preserveConstEnums": true,
		"forceConsistentCasingInFileNames": true,
		"sourceMap": true,
		"module": "amd",
		"outFile": "public/js/app.js"
	},
	"include": [
		"dev/ts/**/*.ts",
		"dev/ts/*.ts"
	]
}

Having declared everything in the tsconfig file, I can initiate the compilation by simply running tsc in the terminal.

Creating the class

In this particular instance, we want to create a class called Alert that will allow us to close an alert popup and perform an Ajax call to initialize a cookie in Node.js.

TypeScript

export class Alert {
	closeBtn: HTMLElement
	container: HTMLElement
	url: string
	constructor (closeBtn: HTMLElement, container: HTMLElement, url: string) {
		this.closeBtn = closeBtn
		this.container = container
		this.url = url
	}
}

To start, we'll declare our class and the 3 include properties:

  • closeBtn - the HTMLElement that, when clicked on, will close our container
  • container - the alert container (popup) HTMLElement
  • url - a string containing the URL needed for our Ajax call

Then we'll set their valued with the constructor() function.

Next, we need to add an event listener to the closeBtn, if it exists.

TypeScript

export class Alert {
	...
	constructor (closeBtn: HTMLElement, container: HTMLElement, url: string) {
		...
		// If element exists, add event listener
		if (typeof(this.closeBtn) != 'undefined' && this.closeBtn != null) {
			this.closeBtn.addEventListener("click", (e: Event) => this.setCookie())
		}
	}
	
	setCookie() { ... }
}

We'll perform a check that the element exists and then add a 'click' event listener that, when clicked, we'll call our setCookie() function.

Here's what that function will do:

TypeScript

	...
	setCookie() {
		let container = this.container
		let url = this.url
		$.ajax({
			url: url,
			success: function(data){
				container.style.display = 'none'
			}
		})
	}
}

Here, we're setting container and url equal to the properties of the class because the this keyword's lexical environment will be altered when inside of the Ajax call.

Inside the Ajax call we're performing a GET request with the URL and hiding the container if the call is successful.

If you've created a browser eventListener before using vanilla JavaScript then this should be mostly recognizable. The main difference is that we want to export the class, not just declare it.

Adding the class to a namespace

Where do TypeScript's Namespaces come in? We're going to put that entire class inside of a namespace called clickEvents:

TypeScript

namespace clickEvents {
	export class Alert {
		...
	}
}

Namespaces, previously called internal modules, allow us to group functionalities together logically.

Here, we're calling our namespace clickEvents because we may decide to add other click-type events other than just alerts.

Using the namespace in our front-end code

Now we can use this namespace and all included functionality in our front-end scripts.

TypeScript

/// <reference path="namespaces/clickEvents.ts" />
new clickEvents.Alert(
	<HTMLElement>document.querySelector(`.alert-close`),
	<HTMLElement>document.querySelector(`.alert`),
	'/sessions/alerts'
)

We'll include our namespace by using a triple-slash directive to declare this file's dependency on the 'clickEvents.ts' file. Then we'll create a new alert.

To do this, we first need to use the name of our namespace and then the class within the namespace. Then we'll include our arguments that the class will use as the closeBtn, container and url.

Now you just need to run tsc in your terminal to compile the files and test them in the browser.

Linux

tsc

Tweet me @tylerewillis

Or send an email:

And support me on Patreon