组件化的可编辑表格

        本文是基于一段购物车页面的 HTML 和 JavaScript 代码的解析。通过这段代码的解析,我们可以了解到该可编辑表格购物车页面的设计思路以及实现方式并学习到核心代码的编写方法。

  1. 设计思路

        这部分代码主要设计的是购物车页面的实现,其主要思路是通过获取后端提供的数据,循环生成表格行,并且根据用户的操作实现相关的功能,例如删除商品、更改商品数量、选择商品等。

在具体实现方面,我们需要先通过AJAX异步请求后端接口获取购物车列表中的商品数据,并将数据动态地渲染到页面中,使用户能够清晰地看到购物车中所有的商品信息。

然后,我们需要使用循环遍历的方式将商品数据逐一地生成表格行,将商品的名称、图片、数量、单价等信息显示出来。在生成表格行的同时,我们还需要根据商品的数量进行小计计算和总计计算。

接着,在实现购物车功能方面,我们需要为删除商品、更改商品数量、选择商品等操作绑定对应的事件监听器。当用户点击删除按钮或更改商品数量时,我们可以通过操作DOM元素来实现商品数值的变化,并重新计算小计和总计,从而完成相关的购物车功能。

最后,我们需要对代码进行一些优化,例如分页显示、按价格或数量排序等,以提高用户的体验。

  1. 实现方式

        我们针对上述的设计思路将代码分解成多个模块,首先是获取服务器上的数据;然后是遍历数据,生成表格行;接着我们需要实现全选复选框与删除所选按钮的功能;我们还需要实现数量输入框的内容更新和相关的小计计算;同时还需要实现删除商品和更新总价等功能。

  1. 核心代码讲解

在下面的代码中,我们将学习到实现上述功能的相关核心代码:

(1) 定义 fetch 请求方法获取服务器上的商品数据

​

fetch("http://localhost:3000/products")
  .then((response) => response.json()) // 将响应转换为 JSON 格式
  .then((products) => {
    // 处理获取到的数据
    // ...
  });

[点击并拖拽以移动]
​

        通过 fetch 方法访问指定的 URL 获取数据,并且将响应转换为 JSON 格式。其中我们需要注意的是,fetch 方法是一个异步方法,它的返回值是一个 Promise 对象。而在 Promise 对象中,我们需先通过 then 方法对结果进行处理,这里实现了将响应转换为 JSON 格式。

(2) 遍历数据,生成表格行

        通过遍历数据,生成每个商品的信息,并添加到 HTML 页面中。

​

// 遍历数据,生成表格行并添加到 tbody 中
for (const product of products) {
  const tr = document.createElement("tr"); // 创建 tr 元素
  tr.innerHTML = `
        <td><input type="checkbox"></td>
        <td><img src="${product.thumbnail}" alt="${
    product.name
  }"></td>
        <td>${product.id}</td>
        <td>${product.name}</td>
        <td>${product.price.toFixed(2)}</td>
        <td><input type="number" value="${product.quantity}" min="1" max="${
    product.quantity
  }"></td>
        <td>${(product.price * product.quantity).toFixed(2)}</td>
        <td><button class="delete">删除</button></td>`;
  tbody.appendChild(tr); // 将 tr 添加到 tbody 中
}

[点击并拖拽以移动]
​

        在这段代码中,我们使用了 for-of 循环遍历每一个商品信息,并根据商品信息生成 HTML 元素。在表格中,我们需要显示每一个商品的图片缩略图、商品名称、单价、数量、小计以及删除按钮等。最后,我们通过 appendChild 方法将生成的元素添加到表格的 tbody 中。

(3)实现全选复选框与删除所选按钮的功能

为全选复选框和删除所选按钮添加事件监听器。

​

//全选复选框的事件监听器
selectAll.addEventListener("change", () => {
  const checkboxes = document.querySelectorAll(
    '#cart tbody input[type="checkbox"]'
  );
  for (const checkbox of checkboxes) {
    checkbox.checked = selectAll.checked;
  }
  updateTotalPrice(); // 更新总价
});

//删除所选按钮的事件监听器
deleteSelected.addEventListener("click", () => {
  const checkboxes = document.querySelectorAll(
    '#cart tbody input[type="checkbox"]'
  );
  for (const checkbox of checkboxes) {
    if (checkbox.checked) {
      checkbox.closest("tr").remove(); // 删除被选中的行
    }
  }
  updateTotalPrice(); // 更新总价
});

[点击并拖拽以移动]
​

        在这里我们实现了全选复选框和删除所选按钮的功能。当我们点击全选复选框时,所有的子复选框(即 tbody 中的复选框)将会被选中/取消选中;当我们点击删除所选按钮时,所有被选中的表格行将会被删除。

(4)实现数量输入框的内容更新和相关的小计计算

        监听数量输入框的 input 事件,根据输入的数量值更新小计,并且更新总价。

​

//数量输入框的事件监听器
tbody.addEventListener("input", (event) => {
  if (event.target.matches('#cart tbody input[type="number"]')) {
    const quantity = parseInt(event.target.value);
    if (isNaN(quantity) || quantity < 1) {
      event.target.value = 1; // 这段代码使用事件委托的方式,监听 <tbody> 中所有输入框元素(type为number)的 input 事件。
      // 当输入框的值改变时,首先将输入的数量值转换为整数类型,如果转换后的值为NaN或小于1,则将输入框的值设为1。
    } else {
      const price = parseFloat(
        event.target.closest("tr").querySelector("td:nth-child(5)")
          .textContent
      );
      event.target
        .closest("tr")
        .querySelector("td:nth-child(7)").textContent = (
        price * quantity
      ).toFixed(2); // 更新小计
      updateTotalPrice(); // 更新总价
    }
  }
});

[点击并拖拽以移动]
​

        在这里,当用户在数量输入框中更改数量时,我们需要根据最新的数量值更新小计,并计算总价。我们可以使用事件委托,通过监听 tbody 元素的 input 事件,然后根据输入框的值和在该行中所显示的价格来更新小计。

(5)实现删除商品和更新总价等功能

监听删除按钮的点击事件,以及计算总价。

​

// 删除按钮的事件监听器
tbody.addEventListener("click", (event) => {
  if (event.target.matches("#cart tbody button.delete")) {
    event.target.closest("tr").remove(); // 删除行
    updateTotalPrice(); // 更新总价
  }
});

// 更新总价的函数
function updateTotalPrice() {
  const checkboxes = document.querySelectorAll(
    '#cart tbody input[type="checkbox"]'
  );
  let totalPriceValue = 0;
  for (const checkbox of checkboxes) {
    if (checkbox.checked) {
      totalPriceValue += parseFloat(
        checkbox.closest("tr").querySelector("td:nth-child(7)")
          .textContent
      ); // 计算总价
    }
  }
  totalPrice.textContent = totalPriceValue.toFixed(2); // 更新总价元素的文本内容
}

// 单独勾选时更新总价的方法
function updateSinglePrice() {
  const checkboxes = document.querySelectorAll(
    '#cart tbody input[type="checkbox"]'
  );
  for (const checkbox of checkboxes) {
    checkbox.addEventListener("change", () => {
      updateTotalPrice(); // 更新总价
    });
  }
}

// 调用更新单独勾选时总价的方法
updateSinglePrice();

[点击并拖拽以移动]
​

        在这里实现了删除商品和更新总价等功能。点击每一行的删除按钮时,当前行将被删除。在更新总价时,我们需要获取每一个被选中的商品行,在这些行中查询商品的小计并将他们加和得到总价。而对于每一行的单独勾选,我们还需要实现当单独勾选商品时更新总价的功能。这也是在这里调用的 updateSinglePrice() 函数的目的。

  1. 小结

通过这部分代码的解析,我们熟悉了购物车页面的设计思路以及实现过程,并学习了核心代码的编写方法。在购物车中,主要实现的是数据获取、表格数据的生成、全选和删除按钮、商品数量更新和删除商品、总价计算等几个功能。在实现这些功能中,我们学习了事件监听器的应用、DOM 操作的方法等技巧。总之,这段代码让我们更深入理解 JavaScript 语言和 Web 开发技术,并在实践中提高了我们的编程能力。

最后附上完整源代码:

​

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>购物车</title>
    <style>
      /* 表格的样式 */
      #cart {
        border-collapse: collapse;
        width: 100%;
      }

      #cart th,
      #cart td {
        border: 1px solid #ddd;
        padding: 8px;
        text-align: center;
      }

      #cart th {
        background-color: #f2f2f2;
      }

      #cart tbody tr:nth-child(even) {
        background-color: #f2f2f2;
      }

      #cart tbody tr:hover {
        background-color: #ddd;
      }

      /* 按钮的样式 */
      button {
        background-color: #4caf50;
        border: none;
        color: white;
        padding: 8px 16px;
        text-align: center;
        text-decoration: none;
        display: inline-block;
        font-size: 14px;
        margin: 4px 2px;
        cursor: pointer;
        border-radius: 4px;
      }

      button:hover {
        background-color: #3e8e41;
      }

      /* 输入字段的样式 */
      input[type="checkbox"],
      input[type="number"] {
        width: 100%;
        padding: 8px;
        border: 1px solid #ccc;
        border-radius: 4px;
        box-sizing: border-box;
      }

      /* 全选复选框的样式 */
      #select-all {
        margin: 0;
      }

      /* 结算按钮的样式 */
      #checkout {
        margin-top: 16px;
      }

      /* 删除按钮的样式 */
      .delete {
        background-color: #f44336;
      }

      .delete:hover {
        background-color: #da190b;
      }

      /* 总价的样式 */
      #total-price {
        font-weight: bold;
      }
    </style>
  </head>
  <body>
    <table id="cart">
      <thead>
        <tr>
          <th><input type="checkbox" id="select-all" /></th>
          <th>图片</th>
          <th>商品ID</th>
          <th>商品名称</th>
          <th>价格</th>
          <th>数量</th>
          <th>小计</th>
          <th>删除</th>
        </tr>
      </thead>
      <tbody></tbody>
      <tfoot>
        <tr>
          <td colspan="6"></td>
          <td>总价:<span id="total-price"></span></td>
          <td><button id="delete-selected">删除选中商品</button></td>
        </tr>
      </tfoot>
    </table>
    <button id="checkout">结账</button>
    <script>
      // 通过 fetch 方法获取数据
      fetch("http://localhost:3000/products")
        .then((response) => response.json()) // 将响应转换为 JSON 格式
        .then((products) => {
          // 处理获取到的数据
          const tbody = document.querySelector("#cart tbody"); // 获取 tbody 元素

          // 遍历数据,生成表格行并添加到 tbody 中
          for (const product of products) {
            const tr = document.createElement("tr"); // 创建 tr 元素
            tr.innerHTML = `
          <td><input type="checkbox"></td>
          <td><img src="${product.thumbnail}" alt="${product.name}"></td>
          <td>${product.id}</td>
          <td>${product.name}</td>
          <td>${product.price.toFixed(2)}</td>
          <td><input type="number" value="${product.quantity}" min="1" max="${
              product.quantity
            }"></td>
          <td>${(product.price * product.quantity).toFixed(2)}</td>
          <td><button class="delete">删除</button></td>`;
            tbody.appendChild(tr); // 将 tr 添加到 tbody 中
          }

          // 获取全选复选框、删除所选按钮、总价元素
          const selectAll = document.querySelector("#select-all");
          const deleteSelected = document.querySelector("#delete-selected");
          const totalPrice = document.querySelector("#total-price");

          // 全选复选框的事件监听器
          selectAll.addEventListener("change", () => {
            const checkboxes = document.querySelectorAll(
              '#cart tbody input[type="checkbox"]'
            );
            for (const checkbox of checkboxes) {
              checkbox.checked = selectAll.checked;
            }
            updateTotalPrice(); // 更新总价
          });

          // 删除所选按钮的事件监听器
          deleteSelected.addEventListener("click", () => {
            const checkboxes = document.querySelectorAll(
              '#cart tbody input[type="checkbox"]'
            );
            for (const checkbox of checkboxes) {
              if (checkbox.checked) {
                checkbox.closest("tr").remove(); // 删除被选中的行
              }
            }
            updateTotalPrice(); // 更新总价
          });

          // 数量输入框的事件监听器
          tbody.addEventListener("input", (event) => {
            if (event.target.matches('#cart tbody input[type="number"]')) {
              const quantity = parseInt(event.target.value);
              if (isNaN(quantity) || quantity < 1) {
                event.target.value = 1; //这段代码使用事件委托的方式,监听 `<tbody>` 中所有输入框元素(type为number)的 input 事件。
                //当输入框的值改变时,首先将输入的数量值转换为整数类型,如果转换后的值为NaN或小于1,则将输入框的值设为1。
              } else {
                const price = parseFloat(
                  event.target.closest("tr").querySelector("td:nth-child(5)")
                    .textContent
                );
                event.target
                  .closest("tr")
                  .querySelector("td:nth-child(7)").textContent = (
                  price * quantity
                ).toFixed(2); // 更新小计
                updateTotalPrice(); // 更新总价
              } //否则,获取该行对应的商品单价(从第5个 `<td>` 中读取)并将其转换成浮点数类型,然后计算出该商品的小计(数量乘以单价),
              //将其格式化为字符串并设置到该行的第7个 `<td>` 元素中,用于显示商品小计。最后调用updateTotalPrice()函数更新购物车的总价。
            }
          });

          // 删除按钮的事件监听器
          tbody.addEventListener("click", (event) => {
            if (event.target.matches("#cart tbody button.delete")) {
              event.target.closest("tr").remove(); // 删除行
              updateTotalPrice(); // 更新总价
            }
          });

          // 更新总价的函数
          function updateTotalPrice() {
            const checkboxes = document.querySelectorAll(
              '#cart tbody input[type="checkbox"]'
            );
            let totalPriceValue = 0;
            for (const checkbox of checkboxes) {
              if (checkbox.checked) {
                totalPriceValue += parseFloat(
                  checkbox.closest("tr").querySelector("td:nth-child(7)")
                    .textContent
                ); // 计算总价
              }
            }
            totalPrice.textContent = totalPriceValue.toFixed(2); // 更新总价元素的文本内容
          }

          // 单独勾选时更新总价的方法
          function updateSinglePrice() {
            const checkboxes = document.querySelectorAll(
              '#cart tbody input[type="checkbox"]'
            );
            for (const checkbox of checkboxes) {
              checkbox.addEventListener("change", () => {
                updateTotalPrice(); // 更新总价
              });
            }
          }

          // 调用更新单独勾选时总价的方法
          updateSinglePrice();

          // 结算按钮的事件监听器
          document.querySelector("#checkout").addEventListener("click", () => {
            alert(`您需要支付 ${totalPrice.textContent} 元。`); // 弹出提示框
          });
        });
    </script>
  </body>
</html>

[点击并拖拽以移动]
​

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值