آموزش ایجاد سبد خرید با جاوا اسکریپت

JavaScript Shopping Cart Tutorial

21 اسفند 1400
JavaScript-Shopping-Cart-Tutorial

در این مقاله آموزشی می خواهیم یک برنامه بسیار کاربردی را با یکدیگر بنویسیم. این برنامه سبد خرید با جاوا اسکریپت نام دارد. با این برنامه می توان محصولات خود را نمایش داد و آن ها را به سبد خرید افزود. در سبد خرید می توان تعداد محصولات را افزایش یا کاهش داد و یا حتی حذف کرد. همچنین در سبد خرید می توان قیمت نهایی محصولات را دید.

برای ساخت برنامه سبد خرید در جاوا اسکریپت به دانش javascript ,html و css نیاز داریم. همچنین به یک ویرایشگر کد و یک مرورگر نیاز داریم. تصویر این برنامه در زیر آمده است.

آموزش ایجاد سبد خرید با جاوا اسکریپت

ساختار برنامه

برای ساخت برنامه سبد خرید با جاوا اسکریپت به فایل های زیر نیاز داریم:

ویرایشگر متن خود را باز کنید و فایل های بالا در آن ایجاد کنید. عکس های موجود در این برنامه و کدهای آن را می توانید از نشانی دانلود کنید.

فایل index.html

فایل index.html برای چهارچوب یا استخوان بندی اولیه برنامه به کار می رود. فایل main.css و main.js در این فایل قرار می گیرند. کد آن در زیر آمده است:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Comfy House</title>
    <link
      rel="stylesheet"
      href="https://use.fontawesome.com/releases/v5.8.1/css/all.css"
      integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf"
      crossorigin="anonymous"
    />

    <link rel="stylesheet" href="main.css" />
  </head>
  <body>
    <nav class="navbar">
      <div class="navbar-center">
        <span class="nav-icon">
          <i class="fas fa-bars"></i>
        </span>
        <img src="./images/logo.svg" alt="store logo" />
        <div class="cart-btn">
          <span class="nav-icon">
            <i class="fas fa-cart-plus"></i>
          </span>
          <div class="cart-items">0</div>
        </div>
      </div>
    </nav>

    <header class="hero">
      <div class="banner">
        <h1 class="banner-title">furniture collection</h1>
        <button class="banner-btn">shop now</button>
      </div>
    </header>

    <section class="products">
      <div class="section-title">
        <h2>our products</h2>
      </div>
      <div class="products-center">
        <article class="product">
          <div class="img-container">
            <img
              src="./images/product-1.jpeg"
              alt="product"
              class="product-img"
            />
            <button class="bag-btn" data-id="1">
              <i class="fas fa-shopping-cart"></i>
              add to bag
            </button>
          </div>

          <h3>queen bed</h3>
          <h4>$16</h4>
        </article>
      </div>
    </section>

    <div class="cart-overlay">
      <div class="cart">
        <span class="close-cart"><i class="far fa-window-close"></i></span>
        <h2>your cart</h2>
        <div class="cart-content">
        
        </div>
        <div class="cart-footer">
          <h3>your total : $<span class="cart-total">0</span></h3>
          <button class="clear-cart banner-btn">clear cart</button>
        </div>
      </div>
    </div>

    <script src="main.js"></script>
  </body>
</html>



در کد بالا از fontawesome برای استفاده از آیکون های پروژه استفاده کرده ایم. این کتابخانه را می توان با تگ link در پروژه قرار داد. مانند زیر:

<link
  rel="stylesheet"
  href="https://use.fontawesome.com/releases/v5.8.1/css/all.css"
  integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf"
  crossorigin="anonymous"
/>

فایل main.css را نیز برای استایل دهی برنامه وارد index.html می کنیم. کد آن در زیر آمده است:

@import url("https://fonts.googleapis.com/css?family=Lato:400,700");

:root {
  --primaryColor: #f09d51;
  --mainWhite: #fff;
  --mainBlack: #222;
  --mainGrey: #ececec;
  --mainSpacing: 0.1rem;
  --mainTransition: all 0.3s linear;
}
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
body {
  color: var(--mainBlack);
  background: var(--mainWhite);
  font-family: "Lato", sans-serif;
}
/* --------------- Navbar ---------------- */
.navbar {
  position: sticky;
  top: 0;
  height: 60px;
  width: 100%;
  display: flex;
  align-items: center;
  background: rgb(231, 226, 221);
  z-index: 1;
}
.navbar-center {
  width: 100%;
  max-width: 1170px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 1.5rem;
}
.nav-icon {
  font-size: 1.5rem;
}
.cart-btn {
  position: relative;
  cursor: pointer;
}
.cart-items {
  position: absolute;
  top: -8px;
  right: -8px;
  background: var(--primaryColor);
  padding: 0 5px;
  border-radius: 30%;
  color: var(--mainWhite);
}
/* --------------- End of Navbar ---------------- */
/* --------------- Hero ---------------- */
.hero {
  min-height: calc(100vh - 60px);
  background: url("./images/hero-bcg.jpeg") center/cover no-repeat;
  display: flex;
  align-items: center;
  justify-content: center;
}
.banner {
  text-align: center;
  background: rgba(255, 255, 255, 0.8);
  display: inline-block;
  padding: 2rem;
}
.banner-title {
  font-size: 3.4rem;
  text-transform: uppercase;
  letter-spacing: var(--mainSpacing);
  margin-bottom: 3rem;
}
.banner-btn {
  padding: 1rem 3rem;
  text-transform: uppercase;
  letter-spacing: var(--mainSpacing);
  font-size: 1rem;
  background: var(--primaryColor);
  color: var(--mainBlack);
  border: 1px solid var(--primaryColor);
  transition: var(--mainTransition);
  cursor: pointer;
}
.banner-btn:hover {
  background: transparent;
  color: var(--primaryColor);
}
/* --------------- End of Hero ---------------- */
/* --------------- Products ---------------- */

.products {
  padding: 4rem 0;
}
.section-title h2 {
  text-align: center;
  font-size: 2.5rem;
  margin-bottom: 5rem;
  text-transform: capitalize;
  letter-spacing: var(--mainSpacing);
}
.products-center {
  width: 90vw;
  margin: 0 auto;
  max-width: 1170px;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  grid-column-gap: 1.5rem;
  grid-row-gap: 2rem;
}
.img-container {
  position: relative;
  overflow: hidden;
}
.bag-btn {
  position: absolute;
  top: 70%;
  right: 0;
  background: var(--primaryColor);
  border: none;
  text-transform: uppercase;
  padding: 0.5rem 0.75rem;
  letter-spacing: var(--mainSpacing);
  font-weight: bold;
  transition: var(--mainTransition);
  transform: translateX(101%);
  cursor: pointer;
}
.bag-btn:hover {
  color: var(--mainWhite);
}
.fa-shopping-cart {
  margin-right: 0.5rem;
}
.img-container:hover .bag-btn {
  transform: translateX(0);
}
.product-img {
  display: block;
  width: 100%;
  min-height: 12rem;
  transition: var(--mainTransition);
}
.img-container:hover .product-img {
  opacity: 0.5;
}

.product h3 {
  text-transform: capitalize;
  font-size: 1.1rem;
  margin-top: 1rem;
  letter-spacing: var(--mainSpacing);
  text-align: center;
}

.product h4 {
  margin-top: 0.7rem;
  letter-spacing: var(--mainSpacing);
  color: var(--primaryColor);
  text-align: center;
}

/* ---------------End of Products ---------------- */
/* --------------- Cart ---------------- */
.cart-overlay {
  position: fixed;
  top: 0;
  right: 0;
  width: 100%;
  height: 100%;
  transition: var(--mainTransition);
  background: rgb(240, 157, 81, 0.5);
  z-index: 2;
  visibility: hidden;
}
.cart {
  position: fixed;
  top: 0;
  right: 0;
  width: 100%;
  height: 100%;
  overflow: scroll;
  z-index: 3;
  background: rgb(231, 226, 221);
  padding: 1.5rem;
  transition: var(--mainTransition);
  transform: translateX(100%);
}
.showCart {
  transform: translateX(0);
}
.transparentBcg {
  visibility: visible;
}
@media screen and (min-width: 768px) {
  .cart {
    width: 30vw;
    min-width: 450px;
  }
}

.close-cart {
  font-size: 1.7rem;
  cursor: pointer;
}
.cart h2 {
  text-transform: capitalize;
  text-align: center;
  letter-spacing: var(--mainSpacing);
  margin-bottom: 2rem;
}
/*---------- Cart Item -------------------- */
.cart-item {
  display: grid;
  align-items: center;
  grid-template-columns: auto 1fr auto;
  grid-column-gap: 1.5rem;
  margin: 1.5rem 0;
}
.cart-item img {
  width: 75px;
  height: 75px;
}
.cart-item h4 {
  font-size: 0.85rem;
  text-transform: capitalize;
  letter-spacing: var(--mainSpacing);
}
.cart-item h5 {
  margin: 0.5rem 0;
  letter-spacing: var(--mainSpacing);
}
.item-amount {
  text-align: center;
}
.remove-item {
  color: grey;
  cursor: pointer;
}
.fa-chevron-up,
.fa-chevron-down {
  color: var(--primaryColor);
  cursor: pointer;
}
/*---------- End of Cart Item -------------------- */

.cart-footer {
  margin-top: 2rem;
  letter-spacing: var(--mainSpacing);
  text-align: center;
}
.cart-footer h3 {
  text-transform: capitalize;
  margin-bottom: 1rem;
}

/* --------------- End of Cart ---------------- */

تمزکر این مقاله بیش تر بر روی javascript است پس زیاد به index.html و main.css نمی پردازیم.

قسمت های مهم index.html به شرح زیر هستند:

  1. تگ nav با کلاس navbar (تعداد محصولاتی که در سبد هستند در بالای آیکون سبد خرید قرار می گیرد. آیکون سبد خرید در تگ div با کلاس cart-btn قرار می گیرد. در این تگ تعداد محصولات تنظیم می شود در تگ div با کلاس cart-items قرار می گیرد.)
<nav class="navbar">
     <div class="navbar-center">
       <span class="nav-icon">
         <i class="fas fa-bars"></i>
       </span>
       <img src="./images/logo.svg" alt="store logo" />
       <div class="cart-btn">
         <span class="nav-icon">
           <i class="fas fa-cart-plus"></i>
         </span>
         <div class="cart-items">0</div>
       </div>
     </div>
   </nav>

  1. تگ section با کلاس products (محصولات در این جا قرار می گیرند).
<section class="products">
  <div class="section-title">
    <h2>our products</h2>
  </div>
  <div class="products-center">
    <!-- <article class="product">
      <div class="img-container">
        <img
          src="./images/product-1.jpeg"
          alt="product"
          class="product-img"
        />
        <button class="bag-btn" data-id="1">
          <i class="fas fa-shopping-cart"></i>
          add to bag
        </button>
      </div>

      <h3>queen bed</h3>
      <h4>$16</h4>
    </article> -->
  </div>
</section>

تگ div با کلاس products-center (این تگ داخل section است) تگ article (برای هر محصول یک تگ article داریم. تصویر محصول، نام محصول و قیمت محصول در این قسمت قرار می گیرند.)

  1. تگ div با کلاس cart-overlay (این تگ برای کارت است که به صورت پرده ای کنار و جلو می آید)
<div class="cart-overlay">
      <div class="cart">
        <span class="close-cart"><i class="far fa-window-close"></i></span>
        <h2>your cart</h2>
        <div class="cart-content">
        
        </div>
        <div class="cart-footer">
          <h3>your total : $<span class="cart-total">0</span></h3>
          <button class="clear-cart banner-btn">clear cart</button>
        </div>
      </div>
    </div>

تگ div با کلاس cart (در داخل تگ div با کلاس cart-overlay است). تگ div با کلاس cart-content (در داخل تگ div با کلاس cart-overlay است. محتویات کارت در این قسمت قرار می گیرند. تصویر محصول، نام محصول، قیمت محصول، دو دکمه برای افزایش و کاهش محصول، و تگ span برای حذف محصول در این جا می آیند.برای بستن کارت از یک span با کلاس close-cart استفاده می کنیم).

تگ div با کلاس cart-footer (قیمت نهایی در این جا قرار می گیرد. قیمت تک تک محصولات با هم جمع می شوند و سپس قیمت نهایی به دست می آید.یک دکمه برای پاک کردن همه محصولات کارت در این جا درست می کنیم.)

فایل main.js

منطق برنامه و مغز آن در این فایل پیاده سازی می شود. در این فایل محصولات را از products.json می خوانیم و آن ها در DOM قرار می دهیم. افزودن محصول به کارت، کاهش و افزایش محصول، حذف محصول، نمایش کارت، پنهان کردن کارت و پاک کردن کارت همگی در این جا انجام می شوند. فایل products.json در زیر آمده است:

{
  "items": [
    {
      "sys": { "id": "1" },
      "fields": {
        "title": "queen panel bed",
        "price": 10.99,
        "image": { "fields": { "file": { "url": "./images/product-1.jpeg" } } }
      }
    },
    {
      "sys": { "id": "2" },
      "fields": {
        "title": "king panel bed",
        "price": 12.99,
        "image": { "fields": { "file": { "url": "./images/product-2.jpeg" } } }
      }
    },
    {
      "sys": { "id": "3" },
      "fields": {
        "title": "single panel bed",
        "price": 12.99,
        "image": { "fields": { "file": { "url": "./images/product-3.jpeg" } } }
      }
    },
    {
      "sys": { "id": "4" },
      "fields": {
        "title": "twin panel bed",
        "price": 22.99,
        "image": { "fields": { "file": { "url": "./images/product-4.jpeg" } } }
      }
    },
    {
      "sys": { "id": "5" },
      "fields": {
        "title": "fridge",
        "price": 88.99,
        "image": { "fields": { "file": { "url": "./images/product-5.jpeg" } } }
      }
    },
    {
      "sys": { "id": "6" },
      "fields": {
        "title": "dresser",
        "price": 32.99,
        "image": { "fields": { "file": { "url": "./images/product-6.jpeg" } } }
      }
    },
    {
      "sys": { "id": "7" },
      "fields": {
        "title": "couch",
        "price": 45.99,
        "image": { "fields": { "file": { "url": "./images/product-7.jpeg" } } }
      }
    },
    {
      "sys": { "id": "8" },
      "fields": {
        "title": "table",
        "price": 33.99,
        "image": { "fields": { "file": { "url": "./images/product-8.jpeg" } } }
      }
    }
  ]
}

فایل main.js در زیر آمده است:

// variables
const cartBtn = document.querySelector(".cart-btn");
const closeCartBtn = document.querySelector(".close-cart");
const clearCartBtn = document.querySelector(".clear-cart");
const cartDOM = document.querySelector(".cart");
const cartOverlay = document.querySelector(".cart-overlay");
const cartItems = document.querySelector(".cart-items");
const cartTotal = document.querySelector(".cart-total");
const cartContent = document.querySelector(".cart-content");
const productsDOM = document.querySelector(".products-center");

let cart = [];
let buttonsDOM = [];


//syntactical sugar of writing constructor function

// products
class Products {
  async getProducts() {
    try {
      let result = await fetch("products.json");
      let data = await result.json();
      
      let products = data.items;
      products = products.map(item => {
        const { title, price } = item.fields;
        const { id } = item.sys;
        const image = item.fields.image.fields.file.url;
        return { title, price, id, image };
      });

      return products;
    } catch (error) {
      console.log(error);
    }
  }
}

// ui
class UI {
  displayProducts(products) {
    let result = "";
    products.forEach(product => {
      result += `
   <!-- single product -->
        <article class="product">
          <div class="img-container">
            <img
              src=${product.image}
              alt="product"
              class="product-img"
            />
            <button class="bag-btn" data-id=${product.id}>
              <i class="fas fa-shopping-cart"></i>
              add to bag
            </button>
          </div>
          <h3>${product.title}</h3>
          <h4>$${product.price}</h4>
        </article>
        <!-- end of single product -->
   `;
    });
    productsDOM.innerHTML = result;
  }
  getBagButtons() {
    let buttons = [...document.querySelectorAll(".bag-btn")];
    buttonsDOM = buttons;
    buttons.forEach(button => {
      let id = button.dataset.id;
      let inCart = cart.find(item => item.id === id);

      if (inCart) {
        button.innerText = "In Cart";
        button.disabled = true;
      }

      button.addEventListener("click", event => {
        // disable button
        event.target.innerText = "In Cart";
        event.target.disabled = true;
        // add to cart
        let cartItem = { ...Storage.getProduct(id), amount: 1 };
        cart = [...cart, cartItem];
        Storage.saveCart(cart);
        // add to DOM
        this.setCartValues(cart);
        this.addCartItem(cartItem);
        this.showCart();
      });
    });
  }

  setCartValues(cart) {
    let tempTotal = 0;
    let itemsTotal = 0;
    cart.map(item => {
      tempTotal += item.price * item.amount;
      itemsTotal += item.amount;
    });
    cartTotal.innerText = parseFloat(tempTotal.toFixed(2));
    cartItems.innerText = itemsTotal;
  }

  addCartItem(item) {
    const div = document.createElement("div");
    div.classList.add("cart-item");
    div.innerHTML = `<!-- cart item -->
            <!-- item image -->
            <img src=${item.image} alt="product" />
            <!-- item info -->
            <div>
              <h4>${item.title}</h4>
              <h5>$${item.price}</h5>
              <span class="remove-item" data-id=${item.id}>remove</span>
            </div>
            <!-- item functionality -->
            <div>
                <i class="fas fa-chevron-up" data-id=${item.id}></i>
              <p class="item-amount">
                ${item.amount}
              </p>
                <i class="fas fa-chevron-down" data-id=${item.id}></i>
            </div>
          <!-- cart item -->
    `;
    cartContent.appendChild(div);
  }
  showCart() {
    cartOverlay.classList.add("transparentBcg");
    cartDOM.classList.add("showCart");
  }

  setupAPP() {
    cart = Storage.getCart();
    this.setCartValues(cart);
    this.populateCart(cart);
    cartBtn.addEventListener("click", this.showCart);
    closeCartBtn.addEventListener("click", this.hideCart);
  }

  populateCart(cart) {
    cart.forEach(item => this.addCartItem(item));
  }

  hideCart() {
    cartOverlay.classList.remove("transparentBcg");
    cartDOM.classList.remove("showCart");
  }
  
  cartLogic() {
    clearCartBtn.addEventListener("click", () => {
      this.clearCart();
    });
    cartContent.addEventListener("click", event => {
      if (event.target.classList.contains("remove-item")) {
        let removeItem = event.target;
        let id = removeItem.dataset.id;
        console.log(cartContent);
        cartContent.removeChild(removeItem.parentElement.parentElement);
        // remove item
        this.removeItem(id);
      } else if (event.target.classList.contains("fa-chevron-up")) {
        let addAmount = event.target;
        let id = addAmount.dataset.id;
        let tempItem = cart.find(item => item.id === id);
        tempItem.amount = tempItem.amount + 1;
        Storage.saveCart(cart);
        this.setCartValues(cart);
        addAmount.nextElementSibling.innerText = tempItem.amount;
      } else if (event.target.classList.contains("fa-chevron-down")) {
        let lowerAmount = event.target;
        let id = lowerAmount.dataset.id;
        let tempItem = cart.find(item => item.id === id);
        tempItem.amount = tempItem.amount - 1;
        if (tempItem.amount > 0) {
          Storage.saveCart(cart);
          this.setCartValues(cart);
          lowerAmount.previousElementSibling.innerText = tempItem.amount;
        } else {
          cartContent.removeChild(lowerAmount.parentElement.parentElement);
          this.removeItem(id);
        }
      }
    });
  }

  clearCart() {
    // console.log(this);
    let cartItems = cart.map(item => item.id);
    cartItems.forEach(id => this.removeItem(id));
    while (cartContent.children.length > 0) {
      cartContent.removeChild(cartContent.children[0]);
    }
    this.hideCart();
  }


  removeItem(id) {
    cart = cart.filter(item => item.id !== id);
    this.setCartValues(cart);
    Storage.saveCart(cart);
    let button = this.getSingleButton(id);
    button.disabled = false;
    button.innerHTML = `<i class="fas fa-shopping-cart"></i>add to bag`;
  }
  
  getSingleButton(id) {
    return buttonsDOM.find(button => button.dataset.id === id);
  }
}

class Storage {
  static saveProducts(products) {
    localStorage.setItem("products", JSON.stringify(products));
  }
  static getProduct(id) {
    let products = JSON.parse(localStorage.getItem("products"));
    return products.find(product => product.id === id);
  }
  static saveCart(cart) {
    localStorage.setItem("cart", JSON.stringify(cart));
  }
  static getCart() {
    return localStorage.getItem("cart")
      ? JSON.parse(localStorage.getItem("cart"))
      : [];
  }
}

document.addEventListener("DOMContentLoaded", () => {
  const ui = new UI();
  const products = new Products();
  ui.setupAPP();

  // get all products
  products
    .getProducts()
    .then(products => {
      ui.displayProducts(products);
      Storage.saveProducts(products);
    })
    .then(() => {
      ui.getBagButtons();
      ui.cartLogic();
    });
});

برای ادامه کار با استفاده از جاوااسکریپت به المنت های Html دستیابی پیدا می کنیم. المنت های به دست آمده در یک متغیر قرار می دهیم. از متغیر cart برای ذخیره محتویات کارت از متغیر buttonsDOM برای ذخیره اطلاعات دکمه های افزودن به کارت استفاده می کنیم.

برای ادامه کار به سه کلاس نیاز داریم:

  • کلاس Products برای واکشی محصول ها
  • کلاس UI برای نمایش محصولات و ظاهر و منطق برنامه
  • کلاس Storage برای ذخیره داده ها در localStorage

کلاس Products

 همان طور که قبلا گفتم کلاس Products برای واکشی محصول ها از فایل products.json به کار می رود. تابع getProducts را در این کلاس تعریف می کنیم.

class Products {
    async getProducts() {
      try {
        let result = await fetch("products.json");
        let data = await result.json();
        
        let products = data.items;
        products = products.map(item => {
          const { title, price } = item.fields;
          const { id } = item.sys;
          const image = item.fields.image.fields.file.url;
          return { title, price, id, image };
        });
  
        return products;
      } catch (error) {
        console.log(error);
      }
    }
}

سپس این تابع را در هنگامی DOM به طور کامل باگذاری می شود فراخوانی می کنیم. برای نمایش سریع محصولات در console تابع displayProducts را در کلاس UI ایجاد می کنیم و کد زیر را در آن قرار می دهیم:

class UI{
  displayProducts(products){
    console.log(products);
  }
}

سپس کد زیر را در آخر فایل main.script قرار می دهیم. این کد زمانی اجرا می شود که DOM به طور کامل بارگذاری می شود.

  document.addEventListener("DOMContentLoaded", () => {
      const ui=new UI()
      const products=new Products()
  
      products.getProducts().then(products=>{
          ui.displayProducts(products)
      })
});

اگر مرورگر را باز کنیم و console را نگاه کنیم باید تصویر زیر را ببینیم.

کلاس UI

برای قرار دادن محصولات در صفحه تابع displayProducts را به صورت زیر ویرایش می کنیم.

displayProducts(products){
  let result = "";
  products.forEach(product => {
    result += `
 <!-- single product -->
      <article class="product">
        <div class="img-container">
          <img
            src=${product.image}
            alt="product"
            class="product-img"
          />
          <button class="bag-btn" data-id=${product.id}>
            <i class="fas fa-shopping-cart"></i>
            add to bag
          </button>
        </div>
        <h3>${product.title}</h3>
        <h4>$${product.price}</h4>
      </article>
      <!-- end of single product -->
 `;
  });
  productsDOM.innerHTML = result;
}

اگر مرورگر را باز کنیم باید تصویر محصولات را ببینیم. کد بالا المنت های html لازم را عنصر ایجاد می کند و سپس آن ها را وارد productsDOM می کند.

هر کدام از این محصولات بالا یک دکمه به نام ADD TO BAG دارند. تصویر و قیمت محصول نیز برای هر یک از محصولات نمایش داده می شود.

گام بعدی مدیریت دکمه های  ADD TO BAG است. می خواهیم هر دکمه ADD TO BAG که کلیک می شود محصول آن به سبد خرید افزوده شود. برای این منظور باید بدانیم کدام دکمه کلیک شده است. برای فهمیدن این موضوع از id دکمه های ADD TO BAG استفاده می کنیم.

همه دکمه های ADD TO BAG را با تابع getBagButtons می گیریم. پس از زدن یک دکمه باید بررسی کنیم آیا محصول در سبد هست یا نه. اگر محصول در سبد باشد نباید دوباره به آن اضافه شود و اگر نباشد باید اضافه شود.

اگر محصول در سبد باشد باید متن دکمه ADD TO BAG به “IN CART” تغییر کند. پس از این کارت محتویات کارت باید تغییر کند و محصول در آن قرار گیرد و تعداد محصولات هم باید تغییر کند. داده ها باید در localStorage ذخیره شوند تا پس از refresh کردن محتویات سبد تغییر نکند.

cart باید پس از کلیک کردن به روز شود و محتوی پیشین آن نیز تغییر نکند. (این مهم است) پس از این کارها کارت باید نمایش داده شود و مقادیر آن تنظیم شوند.

  getBagButtons() {
    let buttons = [...document.querySelectorAll(".bag-btn")];
    buttonsDOM = buttons;
    buttons.forEach(button => {
      let id = button.dataset.id;
      let inCart = cart.find(item => item.id === id);

      if (inCart) {
        button.innerText = "In Cart";
        button.disabled = true;
      }

      button.addEventListener("click", event => {
        // disable button
        event.target.innerText = "In Cart";
        event.target.disabled = true;
        // add to cart
        let cartItem = { ...Storage.getProduct(id), amount: 1 };
        cart = [...cart, cartItem];
        Storage.saveCart(cart);
        // add to DOM
        this.setCartValues(cart);
        this.addCartItem(cartItem);
        this.showCart();
      });
  });
}

در گام بعدی باید مقدارهایی که در کارت قرار می گیرند مانند قیمت نهایی و تعداد محصولات را بدست آوریم و سپس آن ها را نمایش دهیم. سپس باید مقادیر به دست آمده را در متغیرهای مربوط به آن ها قرار بدهیم. سپس باید کارت را نمایش دهیم.

برای تثبیت اطلاعات در localStorage و تغییر نکردن آن ها پس از کلیک کردن دکمه های ADD TO BAG باید UI را populate یا تجمیع (انبوهش) کنیم و مقداردهی متغیرها را نیز داشته باشیم.

    setupAPP() {
  cart = Storage.getCart();
  this.setCartValues(cart);
  this.populateCart(cart);
  cartBtn.addEventListener("click", this.showCart);
  closeCartBtn.addEventListener("click", this.hideCart);
}

populateCart(cart) {
  cart.forEach(item => this.addCartItem(item));
}

می خواهیم محصولاتی که به کارت افزوده می شوند قابل افزایش و کاهش باشند. همچنین می خواهیم اگر به محصول نیاز نداشتیم آن را حذف کنیم.و در آخر می خواهیم قیمت نهایی محصول پس از افزودن به کارت و افزایش و کاهش تعداد محصول ها محاسبه شود. برای انجام این کارها تابع cartLogic را در کلاس UI ایجاد می کنیم.

cartLogic() {
      clearCartBtn.addEventListener("click", () => {
        this.clearCart();
      });
      cartContent.addEventListener("click", event => {
        if (event.target.classList.contains("remove-item")) {
          let removeItem = event.target;
          let id = removeItem.dataset.id;
          console.log(cartContent);
          cartContent.removeChild(removeItem.parentElement.parentElement);
          // remove item
          this.removeItem(id);
        } else if (event.target.classList.contains("fa-chevron-up")) {
          let addAmount = event.target;
          let id = addAmount.dataset.id;
          let tempItem = cart.find(item => item.id === id);
          tempItem.amount = tempItem.amount + 1;
          Storage.saveCart(cart);
          this.setCartValues(cart);
          addAmount.nextElementSibling.innerText = tempItem.amount;
        } else if (event.target.classList.contains("fa-chevron-down")) {
          let lowerAmount = event.target;
          let id = lowerAmount.dataset.id;
          let tempItem = cart.find(item => item.id === id);
          tempItem.amount = tempItem.amount - 1;
          if (tempItem.amount > 0) {
            Storage.saveCart(cart);
            this.setCartValues(cart);
            lowerAmount.previousElementSibling.innerText = tempItem.amount;
          } else {
            cartContent.removeChild(lowerAmount.parentElement.parentElement);
            this.removeItem(id);
          }
        }
      });
    }

دکمه CLEAR CART برای پاک کردن همه محصولات موجود در کارت است. برای پاک کردن کارت تابع clearCart را  به صورت زیر تعریف می کنیم:

clearCart() {
  // console.log(this);
  let cartItems = cart.map(item => item.id);
  cartItems.forEach(id => this.removeItem(id));
  while (cartContent.children.length > 0) {
    cartContent.removeChild(cartContent.children[0]);
  }
  this.hideCart();
}

برای پاک یک محصول تابع removeItem را  به صورت زیر تعریف می کنیم:

removeItem(id) {
  cart = cart.filter(item => item.id !== id);
  this.setCartValues(cart);
  Storage.saveCart(cart);
  let button = this.getSingleButton(id);
  button.disabled = false;
  button.innerHTML = `<i class="fas fa-shopping-cart"></i>add to bag`;
}

برای به دست یک دکمه با یک id خاص از تابع getSingleButton استفاده می کنیم.

getSingleButton(id) {
  return buttonsDOM.find(button => button.dataset.id === id);
}

از این تابع برای فعال کردن دوباره دکمه پس از حذف محصول استفاده می کنیم. این تابع در تابع دیگری به نام removeItem استفاده شده است.

کلاس Storage

این کلاس برای ذخیره سازی داده ها در localStorage به کار می رود. محصولات و اطلاعات کارت در localStorage می شوند. همچنین از این کلاس می توان برای به دست آوردن یک محصول و خواندن اطلاعات کارت استفاده کرد.این کلاس در زیر آمده است:

class Storage {
   static saveProducts(products) {
     localStorage.setItem("products", JSON.stringify(products));
   }
   static getProduct(id) {
     let products = JSON.parse(localStorage.getItem("products"));
     return products.find(product => product.id === id);
   }
   static saveCart(cart) {
     localStorage.setItem("cart", JSON.stringify(cart));
   }
   static getCart() {
     return localStorage.getItem("cart")
       ? JSON.parse(localStorage.getItem("cart"))
       : [];
   }
 }

در آخر کد زیر را ویرایش می کنیم:

document.addEventListener("DOMContentLoaded", () => {
  const ui = new UI();
  const products = new Products();
  ui.setupAPP();

  // get all products
  products
    .getProducts()
    .then(products => {
      ui.displayProducts(products);
      Storage.saveProducts(products);
    })
    .then(() => {
      ui.getBagButtons();
      ui.cartLogic();
    });
});

اگر مرورگر را باز یک برنامه سبد خرید با جاوا اسکریپت خواهیم دید که همه توانایی های لازم را دارد.

برنامه سبد خرید با جاوا اسکریپت

نویسنده شوید

دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.