2 minute read

学んだこと

  • 各項目を選択するとスコアが加算する
  • スコアが高いと健康、低いと健康ではない
  • ts
    • Food クラス
      • 各 Food のボタンのイベント関数を発行
    • Foods クラス
      • スコアの計算値を配列に格納
    • Score クラス
      • スコアの計算
import './style.css';

interface Scoreable {
  readonly totalScore: number;
  render(): void;
}

interface Foodable {
  element: HTMLDivElement;
  clickEventHandler(): void;
}

interface Foodsable {
  elements: NodeListOf<HTMLDivElement>;
  readonly activeElements: HTMLDivElement[];
  readonly activeElementsScore: number[];
}

class Score implements Scoreable {
  private static instance: Score;
  get totalScore() {
    const foods = Foods.getInstance();
    return foods.activeElementsScore.reduce((total, score) => total + score, 0);
  }

  render() {
    document.querySelector('.score__number')!.textContent = String(
      this.totalScore
    );
  }

  private constructor() {}
  static getInstance() {
    if (!Score.instance) {
      Score.instance = new Score();
    }
    return Score.instance;
  }
}
class Food implements Foodable {
  constructor(public element: HTMLDivElement) {
    // bindでthisを明示的に指し示す
    element.addEventListener('click', this.clickEventHandler.bind(this));
  }
  clickEventHandler() {
    this.element.classList.toggle('food--active');
    const score = Score.getInstance();
    score.render();
  }
}
class Foods implements Foodsable {
  private static instance: Foods;
  elements = document.querySelectorAll<HTMLDivElement>('.food');
  private _activeElements: HTMLDivElement[] = [];
  private _acriveElementsScore: number[] = [];
  get activeElements() {
    this._activeElements = [];
    this.elements.forEach((element) => {
      if (element.classList.contains('food--active')) {
        this._activeElements.push(element);
      }
    });

    return this._activeElements;
  }
  get activeElementsScore() {
    this._acriveElementsScore = [];
    this.activeElements.forEach((element) => {
      const foodScore = element.querySelector('.food__score');
      if (foodScore) {
        this._acriveElementsScore.push(Number(foodScore.textContent));
      }
    });

    return this._acriveElementsScore;
  }
  private constructor() {
    this.elements.forEach((element) => {
      new Food(element);
    });
  }
  static getInstance() {
    if (!Foods.instance) {
      Foods.instance = new Foods();
    }
    return Foods.instance;
  }
}

const foods = Foods.getInstance();

  • html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + TS</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.ts"></script>
    <div>
      <h1>Daily Food Score</h1>
      <br />
      <div>
        <h2>Today's Score</h2>
        <div class="score">
          <div class="score__circle">
            <div class="score__number">0</div>
          </div>
        </div>
      </div>
      <br />
      <div>
        <h2>Good</h2>
        <div class="foods-container">
          <div class="foods">
            <div class="food">
              <div class="food__name">Green, leafy vegetables</div>
              <div class="food__score">+5</div>
            </div>
            <div class="food">
              <div class="food__name">All other vegetables</div>
              <div class="food__score">+5</div>
            </div>
            <div class="food">
              <div class="food__name">Nuts</div>
              <div class="food__score">+2</div>
            </div>
            <div class="food">
              <div class="food__name">Whole grains</div>
              <div class="food__score">+1</div>
            </div>
            <div class="food">
              <div class="food__name">Fish</div>
              <div class="food__score">+4</div>
            </div>
            <div class="food">
              <div class="food__name">Beans</div>
              <div class="food__score">+3</div>
            </div>
            <div class="food">
              <div class="food__name">Chicken</div>
              <div class="food__score">+2</div>
            </div>
            <div class="food">
              <div class="food__name">
                Wine (no more than one glass per day)
              </div>
              <div class="food__score">+1</div>
            </div>
          </div>
        </div>
      </div>
      <br />
      <div>
        <h2>Bad</h2>
        <div class="foods-container">
          <div class="foods">
            <div class="food">
              <div class="food__name">Butter and margarine</div>
              <div class="food__score">-3</div>
            </div>
            <div class="food">
              <div class="food__name">Cheese</div>
              <div class="food__score">-1</div>
            </div>
            <div class="food">
              <div class="food__name">Red meat</div>
              <div class="food__score">-3</div>
            </div>
            <div class="food">
              <div class="food__name">Fried food</div>
              <div class="food__score">-5</div>
            </div>
            <div class="food">
              <div class="food__name">Sweets</div>
              <div class="food__score">-5</div>
            </div>
            <div class="food">
              <div class="food__name">Fast food</div>
              <div class="food__score">-5</div>
            </div>
            <div class="food">
              <div class="food__name">Eating out</div>
              <div class="food__score">-3</div>
            </div>
            <div class="food">
              <div class="food__name">Wine (more than one glass per day)</div>
              <div class="food__score">-3</div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>