Skip to main content

Overview

Mis Compras implements a fully client-side shopping cart using localStorage for data persistence. The cart allows users to add products, manage quantities, view totals, and proceed to checkout.

Key Features

localStorage Persistence

Cart data persists across browser sessions

Dynamic Updates

Real-time cart updates without page refresh

Automatic Calculations

Subtotals and totals calculated automatically

Item Management

Add, remove, and update product quantities

Cart Implementation

Core Structure

The cart is implemented in carrito.js as an IIFE (Immediately Invoked Function Expression) that creates a global window.carrito API:
carrito.js
(function () {
  const STORAGE_KEY = 'miTienda_carrito_v1';

  function getCart() {
    const raw = localStorage.getItem(STORAGE_KEY);
    try {
      return raw ? JSON.parse(raw) : [];
    } catch (e) {
      console.error('Error parseando carrito:', e);
      return [];
    }
  }

  function saveCart(cart) {
    localStorage.setItem(STORAGE_KEY, JSON.stringify(cart));
  }

  function findIndex(cart, id) {
    return cart.findIndex(item => String(item.id) === String(id));
  }

  function parsePrice(v) {
    const n = Number(String(v).replace(/[^0-9.-]+/g, ''));
    return isNaN(n) ? 0 : n;
  }

  // Global API exposed on window.carrito
  window.carrito = {
    addItem(product) { /* ... */ },
    removeItem(id) { /* ... */ },
    clear() { /* ... */ },
    getItems: getCart,
    getTotal() { /* ... */ },
    checkout(extraData) { /* ... */ }
  };
})();
The cart uses localStorage key miTienda_carrito_v1 to store cart data as JSON.

Adding Items to Cart

Add Item Flow

1

Product data collected

Product ID, name, price, and image are gathered from button dataset
2

Check for existing item

Cart is searched for product with matching ID
3

Update or insert

If exists, quantity increases; if new, item is added
4

Save and render

Cart is saved to localStorage and UI updates

Implementation

carrito.js
window.carrito = {
  addItem(product) {
    const cart = getCart();
    const idx = findIndex(cart, product.id);
    const precio = parsePrice(product.precio);

    if (idx > -1) {
      // Product exists - increment quantity
      cart[idx].cantidad += 1;
      cart[idx].subtotal = +(cart[idx].cantidad * cart[idx].precio).toFixed(2);
    } else {
      // New product - add to cart
      cart.push({
        id: String(product.id),
        nombre: product.nombre,
        precio: +precio,
        imagen: product.imagen || '',
        cantidad: 1,
        subtotal: +precio
      });
    }
    
    saveCart(cart);
    if (typeof renderCart === 'function') renderCart();
  }
};

Button Click Handler

Products can be added to the cart by clicking buttons with class agregar-carrito:
carrito.js
document.addEventListener('click', (e) => {
  const btn = e.target.closest('.agregar-carrito');
  if (!btn) return;

  const { id, nombre, precio, imagen } = btn.dataset;
  window.carrito.addItem({ id, nombre, precio, imagen });
  
  btn.textContent = 'Añadido ✓';
  setTimeout(() => (btn.textContent = 'Agregar al Carrito'), 900);
});

HTML Button Example

<button 
  class="agregar-carrito"
  data-id="1"
  data-nombre="iPhone 15 Pro"
  data-precio="999.99"
  data-imagen="iphone15.jpg"
>
  Agregar al Carrito
</button>

Displaying the Cart

Cart Item Structure

Each cart item contains:
{
  id: "1",              // Product ID as string
  nombre: "iPhone 15 Pro",
  precio: 999.99,       // Unit price as number
  imagen: "iphone15.jpg",
  cantidad: 2,          // Quantity
  subtotal: 1999.98     // precio × cantidad
}

Rendering Cart Items

The renderCart() function dynamically generates cart HTML:
carrito.js
window.renderCart = function () {
  const lista = document.querySelector('.carrito-lista');
  const totalSpan = document.querySelector('.carrito-total span');
  if (!lista) return;

  const cart = getCart();
  lista.innerHTML = '';

  if (!cart.length) {
    lista.innerHTML = '<p>Tu carrito está vacío.</p>';
    if (totalSpan) totalSpan.textContent = '$0.00';
    return;
  }

  cart.forEach(item => {
    const div = document.createElement('div');
    div.className = 'carrito-item';
    div.innerHTML = `
      <img 
        class="carrito-imagen" 
        src="${item.imagen}" 
        alt="${item.nombre}" 
        onerror="this.onerror=null;this.src='imagenes/default-product.svg'"
      >
      <div class="carrito-info">
        <h3>${item.nombre}</h3>
        <p>$${(+item.precio).toFixed(2)}</p>
        <p>Cantidad: <span class="cantidad">${item.cantidad}</span></p>
        <p>Subtotal: $${(+item.subtotal).toFixed(2)}</p>
      </div>
      <div class="carrito-acciones">
        <button 
          class="carrito-eliminar" 
          data-id="${item.id}" 
          title="Eliminar producto"
        >
          &times;
        </button>
      </div>
    `; 
    lista.appendChild(div);
  });

  if (totalSpan) totalSpan.textContent = `$${window.carrito.getTotal()}`;
};

Auto-render on Page Load

carrito.js
document.addEventListener('DOMContentLoaded', () => {
  renderCart();
});

Removing Items

Remove Item Implementation

carrito.js
window.carrito = {
  removeItem(id) {
    const cart = getCart().filter(p => String(p.id) !== String(id));
    saveCart(cart);
    if (typeof renderCart === 'function') renderCart();
  }
};

Delete Button Handler

carrito.js
document.addEventListener('click', (e) => {
  const btn = e.target.closest('.carrito-eliminar');
  if (!btn) return;

  const id = btn.dataset.id;
  if (!id) return;

  if (confirm('¿Eliminar este producto del carrito?')) {
    window.carrito.removeItem(id);
  }
});

Calculating Totals

Get Cart Total

carrito.js
window.carrito = {
  getTotal() {
    return getCart().reduce((sum, i) => sum + i.subtotal, 0).toFixed(2);
  }
};
The total is calculated by summing all item subtotals and formatting to 2 decimal places.

Usage Example

const total = window.carrito.getTotal();
console.log(`Total: $${total}`);
// Output: Total: $2999.97

Clearing the Cart

Clear Implementation

carrito.js
window.carrito = {
  clear() {
    localStorage.removeItem(STORAGE_KEY);
    if (typeof renderCart === 'function') renderCart();
  }
};
The cart is automatically cleared after successful checkout.

Checkout Integration

Checkout Flow

1

Validate cart

Ensure cart is not empty
2

Prepare payload

Collect cart items, total, and user information
3

Submit to server

POST request to checkout endpoint
4

Handle response

Clear cart on success and redirect to thank you page

Checkout Implementation

carrito.js
window.carrito = {
  async checkout(extraData = {}) {
    const cart = getCart();
    if (!cart.length) {
      alert('Tu carrito está vacío.');
      return;
    }

    const usuario_id = localStorage.getItem("usuario_id", 1) || null;
    const total = this.getTotal();

    const payload = {
      usuario_id,
      items: cart,
      total,
      direccion: extraData.direccion || null
    };

    try {
      const resp = await fetch('php/checkout.php', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(payload)
      });

      const data = await resp.json();

      if (data.success) {
        this.clear();

        // Success modal
        const modal = document.createElement('div');
        modal.innerHTML = `
          <div style="
            position:fixed;top:0;left:0;width:100%;height:100%;
            background:rgba(0,0,0,0.6);
            display:flex;align-items:center;justify-content:center;
            z-index:9999;">
            <div style="
              background:#fff;padding:40px 50px;border-radius:16px;
              text-align:center;box-shadow:0 5px 15px rgba(0,0,0,0.3);
              font-family:'Poppins',sans-serif;">
              <div style="font-size:60px;">🎉</div>
              <h2 style="color:#2e7d32;">¡Compra realizada con éxito!</h2>
              <p style="color:#444;">Tu pedido ha sido registrado correctamente.</p>
              <p style="font-size:14px;color:#666;">Serás redirigido en unos segundos...</p>
            </div>
          </div>`;
        document.body.appendChild(modal);

        setTimeout(() => {
          window.location.href = 'gracias.html';
        }, 3000);
      } else {
        alert('⚠️ No se pudo completar la compra: ' + (data.message || 'Error desconocido.'));
      }
    } catch (e) {
      console.error('Error en checkout:', e);
      alert('⚠️ Error de conexión con el servidor.');
    }
  }
};

Triggering Checkout

carrito.js
document.addEventListener('DOMContentLoaded', () => {
  const btnFinalizar = document.querySelector('.carrito-btn');
  if (btnFinalizar) {
    btnFinalizar.addEventListener('click', () => carrito.checkout());
  }
});

Checkout Button HTML

<button class="carrito-btn">Finalizar Compra</button>

Cart Data Format

localStorage Structure

The cart is stored as a JSON array:
[
  {
    "id": "1",
    "nombre": "iPhone 15 Pro",
    "precio": 999.99,
    "imagen": "iphone15.jpg",
    "cantidad": 2,
    "subtotal": 1999.98
  },
  {
    "id": "2",
    "nombre": "MacBook Pro M3",
    "precio": 1599.99,
    "imagen": "macbook.jpg",
    "cantidad": 1,
    "subtotal": 1599.99
  }
]

Checkout Payload

When sent to the server:
{
  "usuario_id": 17,
  "total": "3599.97",
  "direccion": null,
  "items": [
    {
      "id": "1",
      "nombre": "iPhone 15 Pro",
      "precio": 999.99,
      "cantidad": 2,
      "subtotal": 1999.98,
      "imagen": "iphone15.jpg"
    }
  ]
}

Price Parsing

Handling Various Price Formats

The parsePrice() function normalizes price strings:
carrito.js
function parsePrice(v) {
  const n = Number(String(v).replace(/[^0-9.-]+/g, ''));
  return isNaN(n) ? 0 : n;
}
Examples:
  • "$999.99"999.99
  • "1,599.99"1599.99
  • "999"999
  • "invalid"0
All prices are stored as numbers in the cart for accurate calculations. String conversion only happens during display.

API Reference

window.carrito.addItem(product)

Add a product to the cart or increment its quantity. Parameters:
  • product (object): Product data
    • id (string|number): Product ID
    • nombre (string): Product name
    • precio (string|number): Product price
    • imagen (string): Image filename
Example:
window.carrito.addItem({
  id: 1,
  nombre: "iPhone 15 Pro",
  precio: "999.99",
  imagen: "iphone15.jpg"
});

window.carrito.removeItem(id)

Remove a product from the cart by ID. Parameters:
  • id (string|number): Product ID
Example:
window.carrito.removeItem(1);

window.carrito.getItems()

Get all items currently in the cart. Returns: Array of cart item objects Example:
const items = window.carrito.getItems();
console.log(`Cart has ${items.length} items`);

window.carrito.getTotal()

Calculate the total price of all items in cart. Returns: String with 2 decimal places Example:
const total = window.carrito.getTotal();
document.querySelector('.total').textContent = `$${total}`;

window.carrito.clear()

Remove all items from the cart. Example:
if (confirm('Clear entire cart?')) {
  window.carrito.clear();
}

window.carrito.checkout(extraData)

Process checkout and create order. Parameters:
  • extraData (object, optional): Additional order data
    • direccion (string): Shipping address
Returns: Promise that resolves when checkout completes Example:
await window.carrito.checkout({
  direccion: "123 Main St, City, State 12345"
});

Best Practices

Error Handling

Always handle JSON parse errors when reading from localStorage

Price Formatting

Use .toFixed(2) for all price displays to ensure consistency

Image Fallbacks

Provide default images for missing product photos

User Feedback

Show visual confirmation when items are added or removed

Next Steps

Order Processing

Learn how orders are processed on the backend

Product Management

Understand how products are created and managed

Build docs developers (and LLMs) love