JSFiddle - React, Tailwind, and code Playground

by mepcott

HTML

<section id="searchArea">
  <header>
    <form>
      <label for="breeds">Breeds:</label>
      <select name="breed" id="breedList" disabled>
        <!-- populate with fetch -->
      </select>
      <input id="fetchImagesButton" type="button" value="Fetch Images" disabled>
    </form>
  </header>
  <div id="imageList">
    <!-- populate with fetch -->
  </div>
  <footer>
    <small>
      Powered by the
      <a href="https://dog.ceo/dog-api/">Dog API</a>.
    </small>
  </footer>
</section>

CSS

#searchArea {
  font-family: sans-serif;
  display: flex;
  flex-flow: column nowrap;
  align-items: center;
  gap: 1em;
}

#searchArea > header > form {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 0.25rem;
}

#searchArea > #imageList > img {
  width: 64px;
  height: 64px;
}

JavaScript

class RestApi {

  constructor(endpoint) {
    this.endpoint = endpoint;
  } // constructor

  okOrThrow(response) {
    if (response.ok) {
      return response;
    } else {
      throw new Error(`HTTP ${response.status}`)
    } // if
  } // check

  fetch(...args) {
    let method = args.join("/");
    return fetch(this.endpoint + method)
      .then(this.okOrThrow)
  } // fetch

} // RestApi

const dogApi = new RestApi("https://dog.ceo/api/");
const breedList = document.querySelector("#breedList");
const imageList = document.querySelector("#imageList");
const fetchImagesButton = document.querySelector("#fetchImagesButton");

function option(value, text) {
  element = document.createElement("option");
  element.value = value;
  element.textContent = text;
  return element;
} // option

function image(src, alt) {
  element = document.createElement("img");
  element.src = src;
  element.alt = alt;
  return element;
} // image

function initBreeds(breeds) {
  if (breeds.length > 0) {
    breeds.forEach(breed => breedList.appendChild(option(breed, breed)));
    breedList.disabled = false;
    fetchImagesButton.disabled = false;
  } // if
} // initBreeds

function updateImages(urls) {
  if (urls.length > 0) {
    imageList.innerHTML = "";
    urls.forEach(url => imageList.appendChild(image(url)));
  } else {
    imageList.innerHTML = "No results...";
  } // if
  fetchImagesButton.disabled = false;
} // updateImages

function fetchImages() {
  let name = breedList.options[breedList.selectedIndex].textContent;
  imageList.innerHTML = "Loading...";
  fetchImagesButton.disabled = true;
  dogApi.fetch("breed", name, "images")
    .then(response => response.json())
    .then(data => data.message)
    .then(urls => updateImages(urls));
} // fetchImages

dogApi.fetch("breeds", "list", "all")
  .then(response => response.json())
  .then(data => Object.keys(data.message))
  .then(breeds => initBreeds(breeds))

fetchImagesButton.addEventListener("click", fetchImages, false);