🛠️ Dota Heroes: Listing heroes

Hey ,

I'm thrilled to help you learn JavaScript. Unfortunately, you've landed on a page where you cannot access with your current purchase.

Please upgrade (use this link) access this content.

I'm super eager to help you learn more!

🛠️ Dota Heroes: Listing heroes

Dota (Defense of the Ancients) is a popular game where up to 10 people (five on each team) battle out against each other. The goal of the game is to take down the enemy’s fortress.

When you play Dota, you get to select a character (a hero) from a list of heroes. At the time of writing, Dota has 117 heroes.

The app we’re building together is a Dota Heroes explorer. It lets you filter Dota’s heroes according to three categories:

  1. Attack Type
  2. Primary Attribute
  3. Role

Here’s it looks like:

Picture of the Dota Heroes application

If you’re unfamiliar with Dota, you can treat this application like an e-commerce platform where you can filter search results by specific attributes.

HTML and CSS for the heroes

Since we’re building a list of heroes, each hero should be a <li> element. And they should be placed in a <ul> element collectively.

<ul class="heroes-list">
  <li class="hero"> ... </li>
  <li class="hero"> ... </li>
  <li class="hero"> ... </li>
</ul>

We want to expand this Dota Heroes app later to allow users to find out more information about individual heroes. The best way to do it is through a link. So each hero is wrapped with a link.

<li class="hero">
  <a href="#"> ... </a>
</li>

Each hero has two things:

  1. A name
  2. An image

We’re going to place these two things inside the <a> element.

<li class="hero">
  <span class="hero__name"> ... </span>
  <img src="..." alt="Hero image">
</li>

Here’s an example of a hero:

<li>
  <a href="#">
    <span class="hero__name">Anti-Mage</span>
    <img src="https://api.opendota.com/apps/dota2/images/heroes/antimage_full.png" alt="Anti-Mage image">
  </a>
</li>
Image of Antimage.

Fetching the heroes

Dota has an API. It’s called Open Dota API. You can find the documentation here. We’ll use this API to get information about heroes. (Try to explore the API and see what you find!)

Were you able to find a list of heroes from the API?

After digging around, I noticed you can get a list of heroes from two endpoints:

  1. The /heroStats endpoint
  2. The constants repository

The heroStats endpoint

You can send a GET request to the /heroStats endpoint to get a list of heroes.

const dotaApi = 'https://api.opendota.com/api'

zlFetch(`${dotaApi}/heroStats`)
  .then(response => console.log(response.body))
  .catch(error => console.log(error))

The server responds with an array of objects. Each object contains information about one hero.

Array of heroes in the response

You see 117 in the picture above because there are 117 heroes when I took the picture. The number of heroes in Dota has risen since then.

Each entry in this data contains a lot of information:

Each entry in the heroStats endpoint.

Although the heroStats endpoint gives us a list of heroes, it isn’t the most reliable source for hero information. There was a point where information about one hero was missing from this resource.

Hero information missing from one entry

The difference in data can cause errors down the road. One way to handle this difference is to filter out this entry. But in this case we have a better alternative – we can use the constants repository.

The constants repository

The constants repository contains information that’s considered to be constant in Dota. Information about heroes can be found in this repository.

The Constants API

If you follow the link above, you’ll get to a Github repository with lots of files. We can get hero information from the heroes.json file.

The heroes.json file.

Data from this file can be fetched with the following URL.

const dotaApi = 'https://api.opendota.com/api'

zlFetch(`${dotaApi}/constants/heroes`)
  .then(response => console.log(response.body))
  .catch(error => console.log(error))

This data returns an array-like object.

Data from the heroes endpoint

Each entry is a hero with the following information:

Each entry from the heroes endpoint.

The constants/heroes endpoint is superior compared to the heroStats endpoint because of two reasons:

  1. It contains lesser data than heroStats, and this data is sufficient for our use case. When it comes to asynchronous data, less is better since it makes the request slightly faster. (Don’t take this “less is better” too literally though).
  2. The constants/heroes data is updated faster than the heroStats data.

We’ll use constants/heroes since it’s better than heroStats.

I’ll call constants/heroes the heroes endpoint from here on.

Creating the HTML for each hero

Here’s the HTML we need for each hero.

<li class="hero">
  <a href="#">
    <span class="hero__name"> HERO_NAME </span>
    <img src="HERO_IMAGE" alt="HERO_NAME">
  </a>
</li>

To build this HTML, we need to loop through the data from the heroes Ëť endpoint. Since the data is an object, we need to convert it into an array.

In this case, we can use the Object.values method.

zlFetch(`${dotaApi}/constants/heroes`)
  .then(response => {
    const heroes = Object.values(response.body)
    console.log(heroes)
})
Converted the data from the constants hero endpoint into an array.

We can create a <li> element for each hero.

zlFetch(/*...*/)
  .then(response => {
    const heroes = Object.values(response.body)
    heroes.forEach(hero => {
      const li = document.createElement('li')
      li.classList.add('hero')
      li.innerHTML = `
        <a href="#">
          <span class="hero__name"> HERO NAME </span>
          <img src="PATH TO HERO IMAGE" alt="Hero image">
        </a>
      `
    })
  })

We can get the hero’s name through the localized_name property and the hero’s image through the img property.

Information about each hero.

The image link begins with /app. The / here tells us it’s an absolute URL. After some trial and error, I discovered the absolute URL needs to point back to https://api.opendota.com.

So the JavaScript to create each hero looks like this:

zlFetch(/*...*/)
  .then(response => {
    // ...
    heroes.forEach(hero => {
      // ...
      li.innerHTML = `
        <a href="#">
          <span class="hero__name"> ${hero.localized_name} </span>
          <img src="https://api.opendota.com${hero.img}" alt="${hero.localized_name} image">
        </a>
      `
    })
  })

We’ll add each hero to the DOM by appending the into the DOM.

const heroesList = document.querySelector('.heroes-list')

zlFetch(/*...*/)
  .then(response => {
    // ...
    heroes.forEach(hero => {
      // ...
      heroesList.appendChild(li)
    })
  })

That’s how you fetch the list of heroes from Dota!

Picture of the Dota Heroes application