Javascript Event Listeners

Luis Martinez
5 min readJun 27, 2021

Javascript event listeners are an important component in the user experience with a webpage. Event listeners allow to dynamically display lots of information and provide rich functionality to a webpage without page reload. In this article I would like to discuss my experience with Javascript event listeners while working on my Javascript project, StoQuotes, as part of Flatiron school software engineering 4th milestone project.

Event Listeners and API Calls

The project required to use Rails as an API for the backend (the server side) implementing RESTful routes and the MVC framework, and object-oriented Javascript for the frontend (the client side). In addition, all functionality needed to occur within a single page using AJAX calls (that is, no page reload). For this project, I decided to extend my previous Ruby command line interface (CLI) project, QuotesApp, and make it live in a webpage using Javascript, while adding additional functionalities. In QuotesApp, a user can select to get a random quote, a quote from a list of ten random authors, or a quote from a list of five categories (you can find an article on QuotesApp here, and the link to the repo here). For my javascript project, I included all features from QuotesApp and added the functionality for a user to search for their desired author, and the ability to write a story, edit a story, or delete a story for any given quote; hence the name for the app, StoQuotes.

Developing this project was very exciting to me as I was going to transform my very first app, QuotesApp, accessible only in the command line, and make it available in a webpage. Let us take a look at a snapshot of the welcome interface for each of the applications:

Figure 1. QuotesApp welcome interface
Figure 2. StoQuotes welcome interface

Along with event listeners, I would also like to discuss how my approach to the code changed as I was guided by the project workflow. I wanted a new set of ten authors to be displayed each time the Authors tab was clicked. So, at first, I added an event listener to the Authors tab, with a callback function that triggered a get request to the /authors endpoint of my API:

In index.js:const authorsTab = document.getElementById('nav-authors-tab')authorsTab.addEventListener('click', () => {
authorService.getAuthors();
})

In authorService.js:
getAuthors(){
fetch(`${this.endpoint}/authors`)
.then (resp => resp.json())
.then(authors => {
for (const author of authors){
const a = new Author(author)
a.addToDom();
}
})
}

Then I configured my API to render just ten random authors rather than all authors:

In authors_controller.rb in API:def index
authors = Author.all.sample(10)`
end

However, at a later stage, I realized that I would eventually need all authors– when the Search Author tab was clicked. With such a setting, I would have an API call for each click on the Authors tab and the Search Author tab, respectively. I was not happy with this setting and came up with a solution: load all the authors upon initialization of the app, and manipulate my javascriptAuthor.all array to get the desired number of authors I wanted to display depending on the scenario I was working on. Below is the code that shows this new approach:

  • First, reconfigure the backend to send all authors:
In authors_controller.rb in API:def index
authors = Author.all
end
  • Then, load (instantiate) the authors in the frontend (one API call):
In index.js:authorService.loadAuthors()
In authorService.js:loadAuthors(){
fetch(`${this.endpoint}/authors`)
.then(resp => resp.json())
.then(authors => {
for(const author of authors){
const a = new Author(author)
}
})
}
  • Last, refactor the getAuthors()method from the AuthorService class to retrieve ten random authors from the Author.all array generated in author.js (no API calls):
In index.js:const authorsTab = document.getElementById('nav-authors-tab')authorsTab.addEventListener('click', () => {
authorService.getAuthors();
})
In authorService.js:getAuthors(){
// Empty the authors container
Author.authorsContainer.innerHTML = ""
// Make a copy of the Author.all array, shuffle the array, get 10
authors, and sort by author name
const authors =
shuffleArray(Author.all.slice(0)).slice(0,10).sort((a,b) => {
if (a.name > b.name){
return 1;
}
if (a.name < b.name){
return -1
}
return 0;
});
// Add each author to the DOM
authors.forEach(author => author.addToDom())
}

The result? Only one API call is made to the /authors endpoint; after that, authors are retrieved from the javascript Author class . Below are two pictures that partially show the functionality of the three previous code snippets:

Figure 3. Clicking the Authors tab one time renders ten random authors.
Figure 4. Clicking the Authors tab another time renders a new set of ten random authors.

Object Instantiation and Event Listeners

Another important component of my project consisted in populating a datalist with all the authors from my API. A datalist is a HTML element that can be linked to an input field and provides a list of available options as the user types in the input field. The displayed options are appended to the datalist element. Find an example below:

<input type="text" list="author-name">
<datalist id="author-name">
</datalist>

The datalist is linked to the input field via its id attribute, matching the input’s list attribute. The datalist takes option elements:

<input type="text" list="author-name">
<datalist id="author-name>
<option value="Isaac Newton">
<option value="Charles Dickens">
</datalist>

The above datalist contains two options (two authors); I needed to populate the datalist for my project with all the authors from my API: more than 700. Obviously, populating the datalist needed to be done dynamically. After a big fight with nonworking static methods, I arrived at a solution via object instantiation. Bear with me on the next steps to end this article:

  • Fire an instance method upon initialization of the app:
In index.js:authorService.loadAuthors()
  • The instance method instantiates all authors on the frontend:
In authorService.js:loadAuthors(){
fetch(`${this.endpoint}/authors`)
.then(resp => resp.json())
.then(authors => {
for(const author of authors){
const a = new Author(author)
a.addToDatalist()
}
})
}
  • Create an option HTML element for each author object upon instantiation:
class Author {

static all = []
static authorsContainer = document.getElementById('authors-container')
static datalist = document.getElementById('author-name')
constructor({id, name}) {
this.id = id,
this.name = name

// 'this.element' is used for rendering authors
this.element = document.createElement('li')
this.element.dataset.id = this.id
this.element.id = `author-${this.id}`
this.element.addEventListener('click', this.handleClick)
// 'this.option' is used for author-name datalist
this.option = document.createElement('option')
this.option.value = this.name


Author.all.push(this)
}
addToDatalist(){
Author.datalist.appendChild(this.option)
}
... more code
  • Finally, each author is appended to the datalist via the addToDatalist method.

You can find a link to StoQuotes frontend repo here, and its backend repo here.

--

--