---
id: 68ee6b09fce1154556413f7e
title: Step 27
challengeType: 0
dashedName: step-27
---

# --description--

Inside the `initializeEventListeners` method, attach a `click` event listener to `singleCardBtn`. Within the event handler, call the `singleCardSelected` method. Remember to get the `singleCardBtn` from `elements`.

You will create this method in the coming steps.

# --hints--

You should attach a `click` event to the `singleCardBtn` element. Don't forget you need the `this` keyword here.

```js
assert.match(__helpers.removeJSComments(code), /(?:this\.elements|elements)\.singleCardBtn\s*\.\s*addEventListener\s*\(\s*['"]click['"]/);
```

Inside the event callback, you should call the `singleCardSelected` method.

```js
assert.match(__helpers.removeJSComments(code), /singleCardSelected\s*\(/);
```

# --seed--

## --seed-contents--

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Fortune Telling App</title>
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <main id="main_container">
      <header>
        <h1>Mystic Tarot</h1>
        <h6>
          Discover the wisdom of the cards and unveil the mysteries of your path
        </h6>
      </header>
      <div id="btn_title_container">
        <div class="title_wrapper">
          <h2 class="header_title">Choose Your Reading</h2>
        </div>
        <div class="btn_wrapper">
          <button class="btn btn_singleCard" id="btn-single-card">
            Single Card Reading
          </button>
          <button class="btn btn_threeCard" id="btn-multiple-cards">
            Three Card Spread
          </button>
        </div>
      </div>
      <section class="fortune_container hidden">
        <div class="single_card"></div>
        <h2 class="hidden text">Click the card and reveal the fortune</h2>
        <div class="multiple_card hidden"></div>
        <div class="fortune_description hidden">
          <h2 class="title"></h2>
          <h5 class="sub_title"></h5>
          <div class="description_wrapper">
            <span class="desc_title">Description:</span>
            <p class="description"></p>
          </div>
        </div>
        <button class="btn_reveal">New Reading</button>
      </section>
    </main>
    <script src="index.ts" type="module"></script>
  </body>
</html>
```

```css
:root {
  /* Background Colors */
  --bg-main: #0b0f1a;
  --bg-card: #131622;
  --bg-secondary: #232633;

  /* Text Colors */
  --text-primary: #f7e580;
  --text-muted: #b8a880;
  --text-dark: #0b0f1a;

  /* Border Colors */
  --border-main: #2a2d3a;
  --border-input: #232633;

  /* Accent Colors */
  --accent-primary: #8080ff;
  --accent-secondary: #b8a866;
  --accent-mystic: #bf80ff;
  --accent-light-cosmic: #b3b3ff;
  --accent-light-gold: #f2d966;

  /* Interactive Colors */
  --focus-ring: #8080ff;
  --destructive: #e53e3e;

  /* Gradient Colors */
  --gradient-cosmic-from: #8080ff;
  --gradient-cosmic-to: #bf80ff;
  --gradient-gold-from: #b8a866;
  --gradient-gold-to: #f2d966;
  --gradient-card-from: #131622;
  --gradient-card-to: #1a1e2e;
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  color: var(--text-primary);
  text-align: center;
  min-height: 100vh;
  background-size: 100% 100%; /* forces stretch */
  background-repeat: no-repeat;
  background-image: linear-gradient(to bottom, #1e1b4b, #3b0764, #0f172a);
  font-family:
    ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
    "Segoe UI Symbol", "Noto Color Emoji";
}

header {
  background: rgb(0 0 0 / 0.2);
  padding: 1rem;
}

h1 {
  color: var(--accent-mystic);
  font-weight: 700;
  font-size: 3rem;
  padding-bottom: 12px;
}

h2 {
  color: var(--accent-light-gold);
  font-weight: 700;
  font-size: 1.75rem;
  margin: 2rem 0;
}

h6 {
  color: var(--accent-secondary);
  font-size: 1.8rem;
  font-weight: 1.75;
}

h5 {
  color: var(--text-muted);
  font-size: 1.1rem;
}

.fortune_description {
  width: 99%;
  border: 1px solid var(--accent-light-cosmic);
  border-radius: 0.5rem;
  display: flex;
  align-items: center;
  flex-direction: column;
  justify-content: center;
  margin: 1rem auto;
  padding: 1rem;
  h2 {
    margin: 0;
    padding-bottom: 1rem;
  }
  background: linear-gradient(
    to bottom,
    rgba(15, 23, 42, 0.9),
    rgba(30, 41, 59, 0.9)
  );
}

.description_wrapper {
  background-color: rgba(88, 28, 135, 0.2);
  border-radius: 0.5rem;
  border: 1px solid rgba(168, 85, 247, 0.2);
  color: var(--accent-secondary);
  padding: 2rem;
  margin: 1rem 0;
  display: flex;
  flex-direction: column;
  gap: 5px;
  align-items: center;
  justify-content: center;
}

.btn_wrapper {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.5rem;
  justify-content: center;
}
.btn {
  color: var(--text-dark);
  font-weight: 600;
  font-size: 1.125rem;
  padding: 1rem 3rem;
  cursor: pointer;
  white-space: nowrap;
  border-radius: 0.3em;
  outline: none;
  border: none;
}
.btn_singleCard {
  background-image: linear-gradient(
    to right,
    var(--gradient-cosmic-from),
    var(--gradient-cosmic-to)
  );
}

.btn_threeCard {
  color: var(--accent-light-gold);
  background-image: linear-gradient(
    to right,
    var(--gradient-cosmic-to),
    var(--gradient-cosmic-from)
  );
}

.btn:hover {
  background-image: linear-gradient(to right, var(--gradient-cosmic-to));
}

.btn_reveal {
  color: var(--text-dark);
  font-weight: 500;
  font-size: 0.875rem;
  line-height: 1.25rem;
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
  padding-left: 1rem;
  padding-right: 1rem;
  cursor: pointer;
  outline: none;
  border: none;
  background-color: var(--gradient-gold-to);
  border-radius: 0.3rem;
  margin: 1rem;
}

.btn_reveal:hover {
  background-color: var(--gradient-gold-from);
}

.hidden {
  display: none;
}

.card_container {
  position: relative;
  width: 10rem;
  height: 15rem;
  display: flex;
  align-items: center;
  justify-content: center;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  margin: 0 auto;
  overflow: hidden;
  border-radius: 0.5rem;
  cursor: pointer;
  border: 2px solid var(--accent-light-gold);
  box-shadow: 0 0 7px var(--accent-light-gold);
  img {
    width: 100%;
  }
}

.reversed-card {
  transform: rotate(180deg);
  transition: transform 0.4s ease;
}

.reversed-card:hover {
  transform: rotate(0deg) translateY(-4%);
}

.card_front span:nth-child(2) {
  font-size: 1.3rem;
}

.card_front span:nth-child(1) {
  font-size: 1.8rem;
}

.card_back {
  display: flex;
  align-items: center;
  width: 10rem;
  height: 15rem;
  border: 2px solid var(--gradient-cosmic-from);
  border-radius: 0.5rem;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  cursor: pointer;
}

.multiple_card {
  display: flex;
  gap: 15px;
  align-items: center;
  justify-content: center;
  flex-direction: column;
}

.card-shield span {
  opacity: 10;
  z-index: 3;
}

/* Small devices (phones in landscape, ≥ 600px) */
@media screen and (min-width: 600px) {
  h1 {
    font-size: 3rem;
  }

  .btn_wrapper {
    flex-direction: row;
  }

  .fortune_description {
    padding: 2rem;
    width: 60%;
  }

  .multiple_card {
    gap: 30px;
    flex-direction: row;
  }
}

/* Add to your CSS file (e.g., style.css) */
.img-loader {
  width: 80px;
  height: 120px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto;
}

.img-loader:after {
  content: "";
  display: block;
  width: 40px;
  height: 40px;
  border: 4px solid #ccc;
  border-top: 4px solid #333;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

.card-img.hidden {
  display: none;
}
```

```ts
interface Card {
  name: string;
  value: string | number;
  name_short: string;
  value_int: number;
  suit: string;
  type: string;
  img: string;
  meaning_up: string;
  meaning_rev: string;
  desc: string;
}

interface Deck {
  cards: Card[];
}

const CDN_URL = "https://cdn.freecodecamp.org/curriculum/typescript/tarot-app";
const defaultImg = new URL("default.svg", CDN_URL + "/");
const LOCAL_DEFAULT_IMG = defaultImg;

const getElement = <T extends HTMLElement>(selector: string): T => {
  const el = document.querySelector<T>(selector);
  if (!el) throw new Error(`Element not found: ${selector}`);
  return el;
};

const hideElements = (...elements: HTMLElement[]) =>
  elements.forEach((el) => el.classList.add("hidden"));

const showElements = (...elements: HTMLElement[]) =>
  elements.forEach((el) => el.classList.remove("hidden"));

const getRandomItem = <T,>(items: T[]): T => {
  const index = Math.floor(Math.random() * items.length);
  return items[index];
};

const renderCard = (drawingType: string, isReversed: boolean, shortName: string, img: string): string => `
  <div>
    <h2>${drawingType}</h2>
    <figure class="card_container ${isReversed ? "reversed-card" : ""}" data-id="${shortName}">
      <div class="img-loader"></div>
      <img
        src="${img ? `${CDN_URL}/${img}` : LOCAL_DEFAULT_IMG}"
        class="card-img hidden"
        onload="this.classList.remove('hidden');this.previousElementSibling.style.display='none';"
        onerror="
          if (!this.dataset.failed) {
            this.dataset.failed = '1';
            this.src='${LOCAL_DEFAULT_IMG}';
          } else {
            this.classList.remove('hidden');
            this.previousElementSibling.style.display='none';
          }
        "
      />
    </figure>
  </div>
`;

enum DrawingType {
  Past = "past",
  Present = "present",
  Future = "future",
}

class Game {
  cards: Card[] = [];
  
  private elements: {
    singleCardBtn: HTMLElement;
    singleCard: HTMLElement;
    multipleCardsBtn: HTMLElement;
    multipleCard: HTMLElement;
    title: HTMLElement;
    newReadingBtn: HTMLElement;
    fortuneContainer: HTMLElement;
    fortuneDescription: HTMLElement;
    headerTitle: HTMLElement;
    subTitle: HTMLElement;
    cardTitle: HTMLElement;
    description: HTMLElement;
    text: HTMLElement;
  }
  
  constructor() {
    this.elements = {
      singleCardBtn: getElement("#btn-single-card"),
      singleCard: getElement(".single_card"),
      multipleCardsBtn: getElement("#btn-multiple-cards"),
      multipleCard: getElement(".multiple_card"),
      title: getElement(".title"),
      newReadingBtn: getElement(".btn_reveal"),
      fortuneContainer: getElement(".fortune_container"),
      fortuneDescription: getElement(".fortune_description"),
      headerTitle: getElement(".header_title"),
      subTitle: getElement(".sub_title"),
      cardTitle: getElement(".desc_title"),
      description: getElement(".description"),
      text: getElement(".text"),
    }
    
    this.fetchCardsData();
    this.initializeEventListeners();
  }
  
  private async fetchCardsData() {
    try {
      const response = await fetch(`${CDN_URL}/card_data.json`);
      const data: Deck = await response.json();
      this.cards = data.cards;
    } catch (error) {
      console.error("Error fetching cards:", error);
    }
  }
  
  private initializeEventListeners(): void {
    --fcc-editable-region--
    
    --fcc-editable-region--
  }
}
```
