JSFiddle - React, Tailwind, and code Playground

HTML

<h1 style="text-align: center;">JavaScript 系列三:練習2 ── toast 吐司元件</h1>

<div class="box">
  <button onclick="toastSuccess()">成功訂購商品</button>
  <hr>
  <button onclick="toastError()">訂購商品失敗</button>
  <div class="toast-box"></div>
</div>

CSS

* {
  padding: 0;
  margin: 0;
}

.box {
  width: 500px;
  margin: auto;
  text-align: center;
}

.toast-box {
  position: fixed;
  right: 20px;
  bottom: 20px;
  width: 350px;
}

@keyframes slideIn {
  0% {
    /* 20px為fixed的水平偏移量 */
    transform: translateX(calc(100% + 20px));
    opacity: 0;
  }

  100% {
    transform: translateX(0%);
    opacity: 1;
  }
}

@keyframes slideOut {
  0% {
    transform: translateX(0%);
    opacity: 1;
  }

  100% {
    /* 20px為fixed的水平偏移量 */
    transform: translateX(calc(100% + 20px));
    opacity: 0;
  }
}

.toast {
  padding: 10px;
  margin-top: 10px;
  font-weight: 900;
  text-align: justify;
  animation: slideOut 0.5s;
  animation-fill-mode: forwards;
}

.toast.active {
  animation: slideIn 0.5s;
  animation-fill-mode: forwards;
}

.toast-success {
  background-color: #c6f6d5;
  border-left: 3px solid #266c4e;
  color: #266c4e;
}

.toast-error {
  background-color: #fed7d7;
  border-left: 3px solid #c81e1e;
  color: #c81e1e;
}

button {
  border: 2px solid #000;
  padding: 5px 10px;
  margin: 20px 0;
  border-radius: 5px;
  background-color: #fff;
  transition: 0.3s;
  cursor: pointer;
  font-size: 16px;
}

button:hover {
  transform: scale(1.25);
  background-color: #8429cf;
  border-color: #8429cf;
  color: #fff;
}

JavaScript

let toastBox = document.querySelector("div.toast-box");

function toastSuccess() {
  // 建立<div class="toast toast-success">然後給予文字訊息,放入toastBox內
  let toastMessage = document.createElement("div");
  toastMessage.className = "toast toast-success active";
  toastMessage.textContent = "訂購成功!可以查看 Email 確認訂單細節!";
  toastBox.append(toastMessage);

  // 定義吐司淡出函式
  function slideOut() {
    toastMessage.className = "toast toast-success";
  }
  // 定義刪除吐司函式
  function toastDelete() {
    toastMessage.remove();
  }

  setTimeout(slideOut, 5000);
  // 刪除吐司。至少淡出動畫開始執行以後的0.5s再完全刪除,因為@keyframes要0.5s。再此為了以防萬一,0.6s才刪除
  setTimeout(toastDelete, 5600);
  toastsLimit();
}

function toastError() {
  // 建立<div class="toast toast-error">然後給予文字訊息,放入toastBox內
  let toastMessage = document.createElement("div");
  toastMessage.className = "toast toast-error active";
  toastMessage.textContent = "抱歉,訂購失敗!請稍後重新嘗試";
  toastBox.append(toastMessage);

  // 定義吐司淡出函式
  function slideOut() {
    toastMessage.className = "toast toast-error";
  }
  // 定義刪除吐司函式
  function toastDelete() {
    toastMessage.remove();
  }

  setTimeout(slideOut, 5000);
  // 刪除吐司。至少淡出動畫開始執行以後的0.5s再完全刪除,因為@keyframes要0.5s。再此為了以防萬一,0.6s才刪除
  setTimeout(toastDelete, 5600);
  toastsLimit();
}

// 定義吐司數量偵測函式。吐司數量上限為10,太多時,刪除最舊的吐司
function toastsLimit() {
  let toasts = toastBox.querySelectorAll("div.toast");
  if (toasts.length > 10) {
    toasts[0].remove();
  }
}