Making a ‘like’ button with vanilla javascript… optimistically!

Carla Stickler
From the Stage to the (Computer) Screen
6 min readJul 15, 2019

Everything on the internet has a social component these days and that means like buttons. Users liking other user’s stories, posts, comments, images… the like button is literally everywhere.

So, let’s learn how to make one. I’m going to talk about making one optimistically instead of pessimistically. That basically means that we are going to update what you see on the DOM before it is saved to the database. Getting the like button to work before you attempt to save it to the database is the first step.

So let’s get our basic set up. In your HTML you’re going to need a span or a p tag or a div that will store how many likes something has. If it doesn’t exist in your HTML before you do your fetch, you will probably want to add it when you add your element to the DOM after your fetch. For instance, if you are displaying an image from an API that has a “url”, “name” and a “like_count” attached to each object in your JSON, you would want to do something like this with your fetch:

fetch (imageURL)
.then (res => res.json())
.then (showImg)
const showImg = (jsonImg) => {
const imageCard = document.querySelector("#image_card")
imageCard.dataset.id = jsonImg.id
imageCard.innerHTML = `
<img src="${jsonImg.url}" id="image" data-id="${jsonImg.id}"/>
<h4 id="name">${jsonImg.name}</h4>
<span>Likes:
<span id="likes">${jsonImg.like_count}</span>
</span>
<button id="like_button">Like</button>
`
}

In the above javascript, we are fetching to our API and grabbing the images or image, in this case. The “imageURL” is a variable that holds the actual link to the the API. After we receive our information from our Fetch, we send it the showImg function. This function grabs the DIV from our HTML with an ID of “image_card” and stores in in the variable imageCard. Once we have access to this div we can create all of our HTML that will display our information from our API (which is currently called jsonImg).

Notice we have a span that we are creating that is holding the text “Likes:” and pointing to the like_count for this specific image. jsonImg.like_count is currently a string with a number in. We will come back to why it’s important to know that it is a string and not an integer. Just for reference, here’s what you’ve got in your HTML that we are attaching the above code to:

<div id="image_card" class="card"><!-- Your new div with the JSON info goes here --></div>

Great, so we’ve got our Javascript and our page is showing an image and a like count of 0 next to it, assuming that is how many likes our json object has attached to this image. It is our job to update the DOM with likes as the like button is pressed. So we are going to need an event listener on our newly created like button. We need to create this event listener at the same time that the button is created so we can have access to it. If we try to make the event listener outside of the scope of our showImg function, it will try to attach and event listener to something before that something exists and will throw us an undefined error when we try to access it. We are going to add our event listener into the bottom of our function showImg. It could look something like this:

const showImg = (jsonImg) => {
const imageCard = document.querySelector("#image_card")
imageCard.dataset.id = jsonImg.id
imageCard.innerHTML = `
<img src="${jsonImg.url}" id="image" data-id="${jsonImg.id}"/>
<h4 id="name">${jsonImg.name}</h4>
<span>Likes:
<span id="likes">${jsonImg.like_count}</span>
</span>
<button id="like_button">Like</button>
`
const button = document.querySelector("#like_button")
button.addEventListener("click", likeButton)
}

First we grab our newly created button with an id of “like_button” that we gave it above. And then we add our event listener to it. I am passing in the event which in this case is a “click” and a function called “likeButton”. Are you having fun yet??

Let’s take a look now at our function “likeButton”:

const likeButton = (event) => {
id = parseInt(event.target.parentElement.dataset.id)
let likes = document.querySelector("#likes")
num = parseInt(likes.innerText)
num += 1
likes.innerText = num
fetch (likeURL, {
method: "POST",
headers: {
"Accept": 'application/json',
"Content-Type": 'application/json'
},
body: JSON.stringify ({
image_id: id
})
})
}

Let’s break down what is going on in the above code. We have a likeButton function that is taking in the event that we are passing to it from our above event listener. If you throw in a debugger after the first line you can go into your console and find out what the event is that is being passed in. We are first going to store the dataset id into the variable id. We want access to the id of the image so we can create a new like instance. This new “like” will hold onto the id of the new like as well as the image id.

Ok now let’s talk about updating the DOM with the amount of likes. Before we even add in the fetch, we should be able to update the DOM with the above code. So if you happen to be coding along, go ahead and comment out the fetch in the above code and just run the top half. This is called optimistically updating the DOM. We are changing the like count before the actual ‘like’ has been created in the database.

First we grab the span that is storing how many likes this image has and saving it to the variable “likes”. Remember, that number is actually a string in our code so we need to make it into an integer before we can work with it. So, we parseInt the string and save it to a new variable, num. We want to add a like every time this button is pressed, so we are going to num += 1. You could also write this as num++. Same thing. Then we are going to take our new number and add that into the innerText of our span that holds the like count.

The page should update the number now every time you click the button. However, if we don’t actually update the database, when we refresh the page our number will show us that we have zero likes… no fun!

That’s where our fetch comes in. We are going to create a new like that attaches to our image by passing in the image_id when it’s created. Also, note that since we are optimistically rendering, we don’t actually need any of our “.then” statements after our fetch since we don’t need to do anything after the fetch. The update happened before the fetch! CRAZY!

The new like gets stored and the image will have a like count of how ever many like’s are attached to it. You can refresh the page all you want it the number will stay the same.

This is only one way to create a like button. Javascript is the wild west so there are many other ways to do this. You can render the change pessimistically and do it after you create the new like. Most of your decisions will based on your own deliverables. So this is merely a guideline for a basic like button, but by no means the way it has to work every time!! Good luck on your like buttons!!! You got this!

--

--