You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

跨页面传递Total时出现Cannot read properties of null (reading '0')错误排查

问题描述

我已实现从商品页向结账页传递商品数据并计算总价,现在需要将结账页的total(总价)和subtotal(小计)传递至作为支付页的index页面,但支付页无法显示该数值,控制台报错:Uncaught TypeError: Cannot read properties of null (reading '0') at index.html:41:79。

商品页代码

<section id="prodetails" class="section-p1">
            <div class="productcontainer" id="enjoy">
                <div class="single-pro-image">
                  <img src="../data/high.png"  width="100%" id="product-image" class="product-img">
                </div>
            
                <div class="single-pro-details" action="C:\s\starter-ruby\client\checkout.html">
                  <h4 class="product-title" id="title">Enjoy</h4>
                  <h2 id="price" class="price">$20</h2>
                  <h3>Select Licence</h3>
                  <select id="select" class="licence">
                    <option>MP3</option>
                    <option>Tagged Wav</option>
                    <option>Un-Tagged Wav</option>
                    <option>Stems</option>
                    <option>Exlusive</option>
                  </select>
    
                    <script>
                      let select = document.getElementById('select');
                      let price = document.getElementById('price');
    
                      // Prices
                      let prices = {
                        "MP3": '$20',
                        "Tagged Wav": '$27',
                        "Un-Tagged Wav": '$30',
                        "Stems": '$120',
                        "Exlusive": '$300'
                      }
    
                      // When the value of select changes, this event listener fires and changes the text content of price to the coresponding value from the prices object
                      select.addEventListener('change', () => {
                        price.textContent = prices[select.value];
                      });
                    </script>
                    <button class="add-cart">Add To Cart <i class='bx bx-cart-add add-cart'></i></button>
                    <h4>Product Details</h4>
                    <span>Lil Tjay x Trap Type Beat - "Enjoy" in key D# Minor and 160 BPM,It has vibe of Lil Tjay , Pop Smoke and more , Prices are great for this type of beat;Tagged Mastered MP3 for $25,$27 for Tagged Unmastered Wav,$30 for Untagged unmastered Wav,$70 for Stems and $120 for Exlusive</span>
                </div>
                <div id="audio">
                    <audio controls style="width:100%" id="audio-clip">
                        <source src="Audio/Enjoy 160 x D sharp minor.mp3" type="audio/mp3">
                    </audio>
                </div>
              </section> 

              <script>
                var selectedItems = [];
  const addToCartButton = document.querySelector('.add-cart');

  if (addToCartButton) {
    addToCartButton.addEventListener('click', () => {
      const productTitle = document.querySelector('#title').textContent;
      const productPrice = document.querySelector('#price').textContent;
      const selectedLicense = document.querySelector('#select').value;
      const productImage = document.getElementById('product-image');
      const ImageSrc = productImage.src;
      const audioElement = document.getElementById('audio-clip');
      const audioSrc = audioElement.src;

      const cartItem = {
        title: productTitle,
        price: productPrice,
        license: selectedLicense,
        image: ImageSrc,
        audio: audioSrc
      };

      let selectedItems = JSON.parse(localStorage.getItem('selectedItems')) || [];

      // Check if the item already exists in the cart
      let existingItemIndex = selectedItems.findIndex(item => item.title === productTitle && item.license === selectedLicense);

      if (existingItemIndex > -1) {
        // If the item already exists, update the license
        selectedItems[existingItemIndex].license = selectedLicense;
      } else {
        // If the item doesn't exist, add it to the cart
        selectedItems.push(cartItem);
      }

      console.log(selectedItems);

      localStorage.setItem('selectedItems', JSON.stringify(selectedItems));
    });
  }
</script>

结账页代码

<h2 style="text-align:center;">Checkout</h2>
  <div id="product-items">
    <p style="text-align:center;"><span id="added-products"></p>
    <div class="product-container">
      <div class="cart-items"></div>
    </div>
    <button id="clear-button">CLEAR ITEMS - REDIRECT TO HOME</button>
<script>
  document.getElementById("clear-button").addEventListener("click", function() {
  const cartItemsDiv = document.querySelector(".cart-items");
  cartItemsDiv.innerHTML = ""; // clear the HTML content of the cart-items div
  const subtotalSpan = document.getElementById("subtotal");
  const totalSpan = document.getElementById("total");
  subtotalSpan.innerHTML = "";
  totalSpan.innerHTML = "$0"; // set the total price to $0
  
  localStorage.removeItem("selectedItems"); // remove the selected items from localStorage
  clearCartItems();
});
</script>
  <!--fix the error that how much items in one cart is,e.g if add 2 mp3 and that it does not display that both 2 items
  
  for (var i = 0; i < cartProducts.length; i++) {
        if (cartProducts[i].title === title && cartProducts[i].option === licence) {
            alert('You have already added this product to your cart with the same option.');
            return;
        }
    }
  -->
  </div>

  <div class="total">
    <form action="index.html" class="total">
    <h3 style="text-align:center; display: none;">Subtotal: <br><span id="subtotal"></span></h3>
    <h3 style="text-align:center;">Total: <br><span id="total">$0</span></h3>
    <a href="index.html"><button type="submit" class="buy-btn" style="background-color: rgb(29, 4, 216);">Continue to purchase</button></a>
    <a href="index.html" onclick="alert('You will be redirected to the home page')"><button style="background-color: rgb(216, 4, 4);">Cancel</button></a>
  </div>
</form>
  <!--It does clear but wonder , make that you cant click on button if that function is done-->
</body>
<script>
  const cartItemsDiv = document.querySelector(".cart-items");
  const storedCartItem = localStorage.getItem("selectedItems");  
  let subtotal = 0;

  const continueButton = document.getElementById("buy-btn");
  const clearButton = document.getElementById("clear-button");

  function clearCartItems() {
  // Clear the HTML content of the cart-items div
  const cartItemsDiv = document.querySelector(".cart-items");
  cartItemsDiv.innerHTML = "";

  // Set the subtotal and total to $0
  const subtotalSpan = document.getElementById("subtotal");
  const totalSpan = document.getElementById("total");
  subtotalSpan.innerHTML = "";
  totalSpan.innerHTML = "$0";

  // Remove the selected items from localStorage
  localStorage.removeItem("selectedItems");

  alert("Added items will be cleared.");
  // Change the URL to the index page and prevent going to the checkout page
  window.location.href = "sproduct6.html";
  history.replaceState(null, "", window.location.href);

  // Update the UI
  document.getElementById("added-products").innerHTML = "You have no items in your cart!";
  document.getElementById("buy-btn").style.display = "none";
  document.getElementById("clear-button").style.display = "none";

   if (cartItemCount == 0) {
    setTimeout(function() {
      alert("You have no items in your cart. Let's get back to product page.");
      window.location.href = ('sproduct6.html');
    }, 50); // 3 second delay 
    document.getElementById("added-products").innerHTML = "You have no items in your cart!";
    continueButton.style.display = "none";
    clearButton.style.display = "none";
    // Save the visibility state of the buttons
    localStorage.setItem("buttonsVisible", "false");
  } else {
    document.getElementById("added-products").innerHTML = "YOU ADDED THIS PRODUCTS";
  } 
}

if (storedCartItem) {
  const cartItem = JSON.parse(storedCartItem); // Parse the storedCartItem string into an array

  for (let i = 0; i < cartItem.length; i++) {
    const cartItemElement = document.createElement("div");  

    
    cartItemElement.innerHTML = `
        <hr />
        <h3>${cartItem[i].title}</h3>  
        <p>Price: ${cartItem[i].price}</p>
        <p>License: ${cartItem[i].license}</p>
        <img src="${cartItem[i].image}" width='30%'/><br>
        <audio controls style="width:50%;">
          <source src="${cartItem[i].audio}" type="audio/mp3"> 
        </audio>
        `;
    // ADD DELETE OPTION 

    cartItemsDiv.appendChild(cartItemElement);

    const price = Number(cartItem[i].price.replace("$", "")); // Parse price as a number and remove dollar sign
    subtotal += price;
    // MAKE REPLACE TITLE TO YOU HAVE NO ITEMS IN YOUR CART
  }

  const subtotalElement = document.querySelector("#subtotal");
  subtotalElement.textContent = `$${subtotal.toFixed(2)}`;

  const totalElement = document.querySelector("#total");
  const total = subtotal;
  totalElement.textContent = `$${total.toFixed(2)}`;
  const totalPriceInput = document.querySelector("#total_price");
  totalPriceInput.value = total.toFixed(2);
}

function passtotal(){
  var total = document.getElementById('total').value;
  var subtotal = document.getElementById('subtotal').value;
  localStorage.setItem("values", JSON.stringify({total: total, subtotal: subtotal}));
  return false;
}
</script>
</html>

支付页(index.html)代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Stripe Sample</title>
    <meta name="description" content="A demo of Stripe" />

    <link rel="icon" href="favicon.ico" type="image/x-icon" />
    <link rel="stylesheet" href="css/normalize.css" />
    <link rel="stylesheet" href="css/global.css" />
    <script src="https://js.stripe.com/v3/"></script>
    <script src="./payment.js"></script>
  </head>

  <body>
    <div class="sr-root">
      <span id="total"></span>
      <span id="subtotal"></span>
      <div class="sr-main">
        <h1>Pay with a bunch of payment option</h1>

        <form id="payment-form" method="POST">
          <input type="text" name="email" id="email" placeholder="E-Mail" required>
          <input type="text" name="name" id="name" placeholder="Full Name" required>
    
          <div id="payment-element"></div>
    
          <button>Pay</button><!--Make functional-->
          <!--Make total price hidden and that total of this is same as in checkout and that its connected to ruby-->
          <div id="error-messages"></div>
        </form>        
      </div>
    </div>
    <script>
      var value = localStorage.getItem("value");
if (value !== null && value !== undefined) {
  document.getElementById("total").innerHTML = value[0];
} else {
  alert('something went wrong...Can you please try again')
}
      document.getElementById("total").innerHTML=localStorage.getItem("value")[0];
    </script>
  </body>
</html>

错误原因分析

  1. localStorage键名不匹配:结账页存的是"values",但支付页取的是"value",导致获取结果为null,再访问[0]触发类型错误。
  2. DOM元素值获取错误:结账页的total和subtotal是<span>标签,用.value无法获取内容,应该用.textContent。
  3. 数据解析逻辑错误:存入localStorage的是JSON对象,支付页直接把字符串当数组访问[0],未做JSON解析。
  4. 存储函数未触发:结账页的passtotal函数没有被调用,导致数据根本没存入localStorage。

修复方案

1. 修复结账页代码

修改passtotal函数

function passtotal(){
  // 用textContent获取span的文本内容
  var total = document.getElementById('total').textContent;
  var subtotal = document.getElementById('subtotal').textContent;
  localStorage.setItem("values", JSON.stringify({total: total, subtotal: subtotal}));
  return true; // 允许表单正常跳转
}

修改提交按钮

移除外层<a>标签,给按钮绑定函数触发事件:

<button type="submit" class="buy-btn" style="background-color: rgb(29, 4, 216);" onclick="return passtotal();">Continue to purchase</button>

2. 修复支付页代码

替换原有的script代码,正确解析并展示数据:

<script>
  const storedValues = localStorage.getItem("values");
  if (storedValues) {
    const { total, subtotal } = JSON.parse(storedValues);
    document.getElementById("total").textContent = `总价: ${total}`;
    document.getElementById("subtotal").textContent = `小计: ${subtotal}`;
  } else {
    alert('数据传递失败,请返回结账页重试');
  }
</script>

额外优化建议

  • 结账页可判断购物车为空时禁用支付按钮,避免空数据跳转
  • 支付页完成支付后,清理localStorage中的"values"和"selectedItems",防止残留数据干扰后续操作
  • 增加数据格式验证,确保总价和小计为有效数值后再展示

内容的提问来源于stack exchange,提问作者Danijel Krnic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 15:12:00