用戶在購物車頁面修改輸入框數量
↓
前端 JS 監聽 change/blur 事件,獲取 (商品ID, 新數量)
↓
AJAX (fetch/axios/jQuery) 異步提交到後台 Servlet/Controller
↓
後台從 Session 獲取購物車 List/Map,更新對應商品數量
↓
後台返回 JSON 結果 {success: true, newTotal: 99.00}
↓
前端 JS 更新頁面顯示 (小計、總價),無需刷新整頁
<!-- 購物車列表項模板 -->
<c:forEach items="${cartItems}" var="item">
<tr data-id="${item.productId}">
<td>${item.name}</td>
<td>¥${item.price}</td>
<td>
<input type="number" class="qty-input" value="${item.quantity}" min="1" max="99"
data-id="${item.productId}" style="width: 60px;" />
</td>
<td class="subtotal">¥${item.price * item.quantity}</td>
</tr>
</c:forEach>
<script>
document.querySelectorAll('.qty-input').forEach(input => {
input.addEventListener('change', function() {
const productId = this.dataset.id;
const newQty = parseInt(this.value, 10);
if (isNaN(newQty) || newQty < 1) {
alert('請輸入有效數量');
this.value = this.defaultValue; // 恢復原值
return;
}
updateQuantity(productId, newQty, this);
});
});
async function updateQuantity(productId, quantity, inputEl) {
try {
const response = await fetch('/CartServlet', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: `action=update&productId=${productId}&quantity=${quantity}`
});
const result = await response.json();
if (result.success) {
// 更新小計
const row = inputEl.closest('tr');
const price = parseFloat(row.querySelector('td:nth-child(2)').textContent.replace('¥',''));
row.querySelector('.subtotal').textContent = '¥' + (price * quantity).toFixed(2);
// 更新總價 (假設後台返回)
document.getElementById('totalPrice').textContent = '¥' + result.newTotal.toFixed(2);
} else {
alert('更新失敗: ' + result.msg);
inputEl.value = inputEl.defaultValue;
}
} catch (e) {
console.error(e);
alert('網絡錯誤');
inputEl.value = inputEl.defaultValue;
}
}
</script>
@WebServlet("/CartServlet")
public class CartServlet extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setContentType("application/json;charset=UTF-8");
PrintWriter out = resp.getWriter();
String action = req.getParameter("action");
if ("update".equals(action)) {
String productId = req.getParameter("productId");
int quantity = Integer.parseInt(req.getParameter("quantity"));
HttpSession session = req.getSession();
// 假設購物車存為 Map key=productId
@SuppressWarnings("unchecked")
Map cart = (Map) session.getAttribute("cart");
if (cart != null && cart.containsKey(productId)) {
CartItem item = cart.get(productId);
if (quantity > 0) {
item.setQuantity(quantity);
} else {
cart.remove(productId); // 數量 <=0 視為刪除
}
// 重新計算總價
double total = cart.values().stream()
.mapToDouble(i -> i.getPrice() * i.getQuantity())
.sum();
out.write("{\"success\":true,\"newTotal\":" + total + "}");
} else {
out.write("{\"success\":false,\"msg\":\"商品不存在\"}");
}
} else {
out.write("{\"success\":false,\"msg\":\"未知操作\"}");
}
out.flush();
}
}
public class CartItem implements Serializable {
private String productId;
private String name;
private double price;
private int quantity;
// Getter/Setter/構造函數...
}
「前端 JS 獲取輸入值 → AJAX 提交 → 後台更新 Session Map → 返回 JSON → 前端局部刷新」是現代 JavaWeb 購物車修改數量的標準模式。核心在於**前後端職責分離**:後台只管數據邏輯,前台只管交互渲染。