Learning web development: Plain objects in JavaScript

[2025-08-28] dev, javascript, learning web dev
(Ad, please don’t block)

This blog post is part of the series “Learning web development” – which teaches people who have never programmed how to create web apps with JavaScript.

To download the projects, go to the GitHub repository learning-web-dev-code and follow the instructions there.

I’m interested in feedback! If there is something you don’t understand, please write a comment at the end of this page.


In this chapter, we learn how to create plain objects with properties. We use them to create a simple flash card app.

Creating and using plain objects  

The following statement creates a plain object and assigns it to the variable purchase:

const purchase = {
  product: 'toothbrush',
  quantity: 1,
};

The comma at the end (a trailing comma) is optional.

In a way, a plain object is a group of variables – it groups together related pieces of data that we can access via names. In contrast, in an array, we access related pieces of data via numberic indices.

This is what we can do with purchase:

We can read a property such as .quantity (we get that property):

> purchase.quantity
4

And we can write to a property (we set that property):

> purchase.quantity += 1;
> purchase
{ product: 'carrot', quantity: 5 }

Project: toggle-content.html  

The project toggle-content.html is a preparation for the next project: We learn how to show and hide content. This project can be used via a file: URL.

This is the HTML:

<p>
  <a href="">Toggle the content</a>
</p>
<p id="content" class="hidden">
  [...]
</p>

The following CSS hides the HTML element #content if it has the class hidden:

#content.hidden {
  display: none;
}

Therefore, initially, the content inside <p> is hidden. The link toggles the display:

  • If the content is currently hidden, clicking the link shows it.
  • If we can currently see the content, clicking the link hides it.

This is the JavaScript code that does the toggling:

const content = document.querySelector('#content');
document.querySelector('a')
  .addEventListener(
    'click',
    (event) => {
      event.preventDefault();
      content.classList.toggle('hidden'); // (A)
    }
  );

What is going on in line A? .classList gives us a “list” (a data structure loosely similar to an array) with the classes of content. Method .toggle() of the list adds or removes class hidden as necessary. Line A is equivalent to the following code (which demonstrates more methods of class lists):

if (content.classList.contains('hidden')) {
  content.classList.remove('hidden');
} else {
  content.classList.add('hidden');
}

If we remove or add class hidden, CSS immediately reacts to the change and shows or hides #content.

Project: flash-cards/  

Project flash-cards shows us flash cards, randomly:

  • The front of a flash card contains a Spanish word.
  • The back of the flash card contains the English translation. It is initially hidden but can be revealed.

Running this project  

This project uses JavaScript modules – which is why we need to run a web server:

cd learning-web-dev-code/projects/
npx http-server

After that, the project is available at:

http://127.0.0.1:8080/flash-cards/flash-cards.html

From now on, I will not explain how to use http-server anymore. When it doubt, use it to run a web app.

data.js: the flash card data  

The flash cards are defined in their own module, data.js:

export const data = [
  { front: 'perro', back: 'dog' },
  // ...
];

flash-cards.html: HTML  

This is the user interface, defined in HTML:

<p>
  <button id="nextCard">Next Flashcard</button>
</p>

<p>
  <a href="" id="toggleBack">Toggle Back</a>
</p>

<div id="flashCard">
  <p id="front"></p>
  <hr>
  <p id="back"></p>
</div>

<p>
  <span id="cardNumber"></span> of <span id="cardTotal"></span>
</p>

The last <p> displays texts such as “2 of 10” – “cards” is implied.

flash-cards.html: CSS  

We need CSS so that we can easily reveal and conceal the back of a flash card:

#back {
  display: none;
}
#back.revealed {
  display: initial;
}

By default, the back is hidden. If we add the class .revealed, we can see it.

The following CSS centers the user interface horizontally and gives it a maximum width of 40 rem (a CSS unit that is useful for specifying lengths in CSS):

html {
  /* top right bottom left */
  margin: 2rem auto 2rem auto;
  max-width: 40rem;
}

We surround the flash card with border that has rounded corners (thanks to border-radius):

#flashCard {
  border: solid thin gray;
  border-radius: 0.5rem;
  padding-left: 1rem;
  padding-right: 1rem;
  padding-bottom: 1rem;
}

flash-cards.html: JavaScript  

We start by importing the tools and data we need:

import { shuffleArray } from './utils.js';
import { data } from './data.js';

shuffleArray() is a function that, unsurprisingly, shuffles the elements of an array. We’ll take a look at its code later. Shuffling guarantees that each time we go through the cards, we (generally) see them in a different order. An alternative would have been to simply pick a random card each time but then we are much more likely to see the same card twice in a row.

Variable currentIndex records where we currently are inside the array data:

let currentIndex = 0;

Function showCard() displays an element of array data in the user interface:

const showCard = (index) => {
  const front = document.querySelector('#front');
  const back = document.querySelector('#back');
  const cardNumber = document.querySelector('#cardNumber');
  
  // Make sure the back is hidden
  back.classList.remove('revealed');

  const card = data[index];
  front.innerText = card.front;
  back.innerText = card.back;
  cardNumber.innerText = index + 1;
};

The following function starts a new round, “restarting” the game if you will. It is the first function we call when the web app starts because it sets up everything correctly.

const startNewRound = () => {
  const cardTotal = document.querySelector('#cardTotal');

  shuffleArray(data);
  cardTotal.innerText = data.length;

  currentIndex = 0;
  showCard(currentIndex);
};

This function goes to the next card:

const goToNextCard = () => {
  currentIndex = currentIndex + 1;
  if (currentIndex >= data.length) {
    startNewRound(); // sets currentIndex to 0
  } else {
    showCard(currentIndex);
  }
}

It first increments currentIndex:

  • If the new index is equal to the length of data then we are past the last card (whose index is data.length - 1) and have to start a new round.
  • Otherwise, we can show the card at the new index.

The following function toggles whether the back is revealed or not:

const toggleBack = () => {
  const back = document.querySelector('#back');
  back.classList.toggle('revealed');
};

utils.js  

The following utility function uses the Fisher–Yates algorithm to shuffle an array:

export const shuffleArray = (arr) => {
  for (let current = arr.length - 1; current >= 1; current--) {
    const randomPick = getRandomInteger(current);
    // Swap elements at the indices `current` and `randomPick`
    const tmp = arr[current];
    arr[current] = arr[randomPick];
    arr[randomPick] = tmp;
  }
  return arr;
};

That algorithm is actually remarkably simple:

  • We go through the array elements from the end to the beginning.
  • First, we pick a random array index that is smaller than the index of the last element.
  • Then we swap the element at that index with the last element. We need the temporary variable tmp to do that.
  • Then we continue with the second-to-last element.
  • Etc.

If we did the same with playing cards, it would look like this:

  • Input: a deck of unshuffled cards that we hold in one hand.
  • Output: a deck of shuffled cards that lies on the table.
  • First step: We pick a random card from the deck in our hand and put it on the table.
  • Second step: We pick another random card and put it on top of the card that’s already on the table.
  • We continue until there are no more cards in our hand.

Terminology: model vs. view  

In project flash-cards, there was in interesting phenomenon:

  • On one hand, we had the data that described the flash cards – it was stored in the array data. And we had an index for the current flash card.
  • On the other hand, that data was displayed in the user interface (on the HTML page).

The former is sometimes called a model. The latter is sometimes called a view for the model. Handling events and updating model and view is sometimes called a controller, but that term is not as common as the other two.

The following two functions “transport” the model to the view – they update the view so that it reflects the model:

  • showCard()
  • startNewRound()