1️⃣

JavaScript 20 Projects - 1 Quote Generator

2023/08/15に公開

前言

這系列是課程 JavaScript Web Projects: 20 Projects to Build Your Portfolio 的筆記,學習利用Javascript 做出各種互動網站。

目標

要做一個名言產生器,利用提供的json檔,搭配按鈕隨機抽選句子,並實作分享到twitter的按鈕。

下面是這次要實作的畫面。範例的連結

Image.png

建立 html 內容與 CSS 設定

這邊只提供 HTML 的架構,CSS 就讓大家自由發揮XD。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Quote-generator</title>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <div class="page">
      <div class="header">
        <h1 class="title">Quote-Generator</h1>
      </div>
      <div class="quote-container" id="quote-container">
        <div class="quote-text">
          <span id="quote"></span>
        </div>
        <!-- Author -->
        <div class="quote-author">
          <span id="author"></span>
        </div>
        <!-- Button -->
        <div class="button-container">
          <button class="twitter-button" id="twitter" title="Tweet This!">
            Share to Twitter
          </button>
          <button id="new-quote">New Quote</button>
        </div>
      </div>
    </div>
    <div class="loader" id="loader"></div>
    <!-- Script -->
    <script src="script.js"></script>
  </body>
</html>

思考 javascript 所需要的動作

  1. 如何讀入 API?
  2. 如何隨機產生句子?
  3. 如何分享到 twitter ?
  4. 想增加畫面互動的細節,若要增加 loading 時的 Icon,那
    • 要如何設定他如何出現與消失?
    • 開始與消失要在哪一個 function 中執行?

動作解讀

如何讀入 API?

let apiQuote = [];

async function getQuote() {
  const apiUrl = "https://type.fit/api/quotes";
  try {
    const response = await fetch(apiUrl);
    apiQuote = await response.json();
    newQuote();
  } catch (error) {
    // do something
  }
}

getQuote();

newQuote() 是下一步驟會建立的 function。

如何隨機產生句子?

接續上步驟讀完資料後的動作,我們來隨機產生句子。

  1. 先設定來操作HTML元素的DOM。
  2. 利用Math函數來隨機抽出句子的Object quote。
  3. 分別設定到對應的HTML內容。
  4. 並把 newQuote 設定在點擊 newQuoteBtn 時觸發的動作。
const quoteContent = document.getElementById("quote");
const authorContent = document.getElementById("author");
const newQuoteBtn = document.getElementById("new-quote");

function newQuote() {
  const quote = apiQuote[Math.floor(Math.random() * apiQuote.length)];
  authorContent.textContent = quote.author;
  quoteContent.textContent = quote.text;
}

newQuoteBtn.addEventListener("click", getQuote);

但這邊有幾個問題要解決:

  1. 有些句子是沒有作者的,遇到這樣的情形要怎麼處理?
  2. 若句子的字數太多,可能會讓框框變化太大,可以怎麼處理?

解決方式:

  1. quote.authornull,我們就把它設定為"Unknown”。
  2. 若字數多於120,幫他添加 class 屬性 "long-quote”,並在CSS 把 "long-quote”的 font-size 屬性值設小一點。
function newQuote() {
  const quote = apiQuote[Math.floor(Math.random() * apiQuote.length)];
  // console.log(quote);
  if (!quote.author) {
    authorContent.textContent = "Unknown";
  } else {
    authorContent.textContent = quote.author;
  }

  if (quote.text.length > 120) {
    quoteContent.classList.add("long-quote");
  } else {
    quoteContent.classList.remove("long-quote");
  }
  // Set Quote, Hide Loader
  quoteContent.textContent = quote.text;
}

如何分享到 Twitter ?

  1. 設定來操作twitter button的DOM。
  2. 利用quoteContent以及authorContent設定要分享的句子與作者變數。
  3. 利用上述資料設定twitter url。
    • https://twitter.com/intent/tweet?text= 這部分是twitter 分享連結的基本 URL,表示用户正要開始一則新的推文。
  4. 利用 window.open(twitterUrl, "_blank") 方法開新網頁並分享到twitter去。
  5. 並把 tweetQuote 設定在點擊 twitterBtn 時觸發的動作。
const twitterBtn = document.getElementById("twitter");

// Tweet Quote
function tweetQuote() {
  const quote = quoteContent.innerText;
  const author = authorContent.innerText;
  const twitterUrl = `https://twitter.com/intent/tweet?text=${quote} - ${author}`;
  window.open(twitterUrl, "_blank");
}

twitterBtn.addEventListener("click", tweetQuote);

增加畫面互動的細節

終於來到最後一步,來看看若要增加 loading 時的Icon,那

  1. 要如何設定他如何出現與消失?
  2. 開始與消失要在哪一個function中執行?

解決方式:

要如何設定他如何出現與消失?

  1. 設定來操作 quoteContainer 以及 loader 的 DOM。
    • 要操作 quoteContainer 是因為當 loader 出現時,會希望除了 loader 的其他東西可以先隱藏起來。
  2. 設定兩個 function,一個是 loading 時,一個是 loading 完成時。分別設定好兩個模式需要隱藏跟顯示的部分。
const quoteContainer = document.getElementById("quote-container");
const loader = document.getElementById("loader");

// Loading Spinner Shown
function loading() {
  loader.hidden = false;
  quoteContainer.hidden = true;
}

// Remove Loading Spinner
function complete() {
  quoteContainer.hidden = false;
  loader.hidden = true;
}

開始與消失要在哪一個 function 中執行?

我們選擇的是 newQuote(),因為在他隨機產生句子的過程中,我們還需要針對句子的狀況做畫面跟內容的調整,而我們希望這部分是可以忽略掉的,所以在這個function開始與結束的地方,分別插入 loading()complete() 來達成loading的效果。

function newQuote() {
  loading();
  const quote = apiQuote[Math.floor(Math.random() * apiQuote.length)];
  // console.log(quote);
  if (!quote.author) {
    authorContent.textContent = "Unknown";
  } else {
    authorContent.textContent = quote.author;
  }

  if (quote.text.length > 120) {
    quoteContent.classList.add("long-quote");
  } else {
    quoteContent.classList.remove("long-quote");
  }
  // Set Quote, Hide Loader
  quoteContent.textContent = quote.text;
  complete();
}

最終 Javascript 的程式碼

const quoteContainer = document.getElementById("quote-container");
const quoteContent = document.getElementById("quote");
const authorContent = document.getElementById("author");
const newQuoteBtn = document.getElementById("new-quote");
const loader = document.getElementById("loader");
const twitterBtn = document.getElementById("twitter");

let apiQuote = [];

// Loading Spinner Shown
function loading() {
  loader.hidden = false;
  quoteContainer.hidden = true;
}

// Remove Loading Spinner
function complete() {
  quoteContainer.hidden = false;
  loader.hidden = true;
}

function newQuote() {
  loading();
  const quote = apiQuote[Math.floor(Math.random() * apiQuote.length)];
  // console.log(quote);
  if (!quote.author) {
    authorContent.textContent = "Unknown";
  } else {
    authorContent.textContent = quote.author;
  }

  if (quote.text.length > 120) {
    quoteContent.classList.add("long-quote");
  } else {
    quoteContent.classList.remove("long-quote");
  }
  // Set Quote, Hide Loader
  quoteContent.textContent = quote.text;
  complete();
}

async function getQuote() {
  const apiUrl = "https://type.fit/api/quotes";
  try {
    const response = await fetch(apiUrl);
    apiQuote = await response.json();
    newQuote();
  } catch (error) {
    // do something
  }
}

// Tweet Quote
function tweetQuote() {
  const quote = quoteContent.innerText;
  const author = authorContent.innerText;
  const twitterUrl = `https://twitter.com/intent/tweet?text=${quote} - ${author}`;
  window.open(twitterUrl, "_blank");
}

newQuoteBtn.addEventListener("click", getQuote);
twitterBtn.addEventListener("click", tweetQuote);

getQuote();

Discussion