在构建在线订餐或购物应用时,一个常见的需求是允许用户动态添加多个商品条目,并实时显示这些条目的总价。这涉及到前端动态元素的生成、数据获取以及实时的数学计算。本教程将以一个典型的订单详情页面为例,详细讲解如何使用jquery来优雅地解决这一问题。
问题的核心在于两个方面:
由于商品行是动态添加的,传统的直接事件绑定方式可能无法覆盖到新生成的元素。因此,我们需要采用一种能够处理动态元素的事件绑定策略,并在每次数量变化时,遍历所有商品行进行计算。
我们将分步解析HTML结构、PHP后端逻辑以及jQuery前端实现。
订单详情部分通常包含一个容器,用于存放所有商品条目。每个条目包含:
<div class="form-group"> <label class="control-label col-lg-4"></label> <div id="append" class="col-lg-4"> <!-- 初始的商品条目 --> <div id="diva" class="box"> <header> <h5>تفاصيل الطلب</h5> </header> <div class="body getval"> <div class="form-group"> <label class="control-label col-lg-2">الصنف</label> <div class="col-lg-10"> <select id="itemname" name="itemname[]" class="form-control"> <option disabled selected>اختر الصنف</option> <?php echo getValues($pdo); ?> </select> </div> </div> <div class="form-group"> <label class="control-label col-lg-2">السعر</label> <div class="col-lg-10"> <input type="text" id="price" name="price[]" placeholder="-" readonly class="form-control price"> </div> </div> <div class="form-group"> <label class="control-label col-lg-2">الكمية</label> <div class="col-lg-10"> <input class="form-control quantity" type="number" name="quantity[]" value="0" min="1"> </div> </div> </div> </div> </div> <!-- 总价显示框 --> <input type="text" id="total" placeholder="-" readonly class="form-control"> </div> <!-- 添加新行的按钮 (假设有一个ID为'add'的按钮) --> <button id="add">添加商品</button>
注意,为了方便jQuery选择和操作,我们为价格输入框添加了 price 类,为数量输入框添加了 quantity 类,为总价输入框添加了 total ID。
PHP后端(GetPrice.php)负责根据前端传递的商品名称(或ID)从数据库中查询并返回对应的价格。
<?php require 'DBConnection.php'; // 数据库连接 $code=''; if(isset($_POST["code"])){ $id = $_POST["code"]; // 假设 all_menu 表中包含 item_name 和 price 字段 $get_c = $pdo->prepare("SELECT price FROM all_menu WHERE `item_name` = :item_name"); $get_c->bindParam(':item_name', $id); // 使用参数绑定防止SQL注入 $get_c->execute(); while ($row = $get_c->fetch()) { $code .= $row['price']; } echo $code.'.00'; // 返回价格,格式化为 .00 } ?>
重要提示: 在实际生产环境中,请务必使用预处理语句和参数绑定来防止SQL注入,如上述代码所示。
jQuery代码是实现动态功能的核心。
当用户选择商品名称时,通过AJAX请求后端获取价格并填充到对应的价格输入框。对于动态添加的行,需要为每个新行单独绑定 change 事件。
// 初始行的事件绑定 $('#itemname').change(function(){ var code = $(this).val(); $.ajax({ type: 'POST', url: 'pages/GetPrice.php', data:{code:code}, success: function(data){ $('#price').val(data); // 使用jQuery选择器更简洁 handleQuantityChange(); // 价格更新后也可能影响总价,触发计算 }, error: function (jqXHR, textStatus, errorThrown){ alert(errorThrown); } }); });
点击“添加商品”按钮时,复制现有HTML结构并插入到页面中,同时为新行的元素赋予唯一的ID(如果需要)。
var nextRowID = 0; $('#add').click(function(e){ e.preventDefault(); var id = ++nextRowID; // 构建新的行HTML,注意ID的动态生成 var newRowHtml = '<div class="form-group"><label class="control-label col-lg-4"></label><div id ="diva'+id+'" class="col-lg-4"><div class="box"><header><h5>تفاصيل الطلب</h5></header><div class="body getval"><div class="form-group"><label class="control-label col-lg-2">الصنف</label><div class="col-lg-10"><select id="itemname'+id+'" name="itemname[]" class="form-control"><option disabled selected>اختر الصنف</option><?php echo getValues($pdo); ?></select></div></div><div class="form-group"><label class="control-label col-lg-2">السعر</label><div class="col-lg-10"><input type="text" id="price'+id+'" name="price[]" placeholder="-" readonly class="form-control price"></div></div><div class="form-group"><label class="control-label col-lg-2">الكمية</label><div class="col-lg-10"><input class="form-control quantity" type="number" name="quantity[]" value="0" min="1"></div></div></div></div></div></div>'; $('#append').append(newRowHtml); // 为新添加的商品名称选择框绑定事件 $('#itemname'+id+'').change(function(){ var code = $(this).val(); var currentRowId = $(this).attr('id').replace('itemname', ''); // 获取当前行的ID后缀 $.ajax({ type: 'POST', url: 'pages/GetPrice.php', data:{code:code}, success: function(data){ $('#price'+currentRowId+'').val(data); handleQuantityChange(); // 价格更新后,重新计算总价 }, error: function (jqXHR, textStatus, errorThrown){ alert(errorThrown); } }); }); // 每次添加新行后,重新绑定或触发数量变化事件 handleQuantityChange(); });
这是解决问题的关键。我们需要一个函数来遍历所有商品行,获取它们的单价和数量,然后计算总和。
function handleQuantityChange(){ // 解除旧的change事件绑定,然后重新绑定,确保只执行一次 // 对于动态添加的元素,更推荐使用事件委托(见注意事项) $('.quantity').unbind('change').change(function(){ let totalAmount = 0.0; // 遍历所有具有 'price' 类的输入框 $('.price').each(function(){ const priceElement = $(this); // 找到与当前价格输入框在同一商品行内的数量输入框 // 这里通过 DOM 结构进行查找: // priceElement.parent() -> <div class="col-lg-10"> (价格输入框的父级) // .parent() -> <div class="form-group"> (价格输入框的form-group) // .next() -> <div class="form-group"> (数量输入框的form-group) // .find('.quantity') -> 找到其中的数量输入框 const quantityElement = priceElement.closest('.form-group').next('.form-group').find('.quantity'); const price = parseFloat(priceElement.val()) || 0; // 确保是数字,默认为0 const quantity = parseFloat(quantityElement.val()) || 0; // 确保是数字,默认为0 totalAmount += price * quantity; }); // 更新总价显示 $('#total').val(totalAmount.toFixed(2)); // 保留两位小数 }); // 首次加载或添加新行后,立即触发一次计算,确保总价是正确的 // 模拟一次数量变化,以便在页面加载时或新行添加后立即计算总价 $('.quantity').first().trigger('change'); }
DOM遍历解释:priceElement.closest('.form-group') 向上查找最近的 .form-group 父元素,这是包含价格输入框的整个行容器。 next('.form-group') 找到这个 form-group 的下一个兄弟元素,这通常是包含数量输入框的 form-group。 find('.quantity') 在这个数量的 form-group 中查找 quantity 类。
在文档加载完成后,以及每次动态添加新行后,都需要调用 handleQuantityChange() 函数,以确保所有当前存在的数量输入框都能正确绑定事件,并且总价能够被实时更新。
$(document).ready(function(){ // 页面加载完成后,初始化总价计算 handleQuantityChange(); // 初始行的商品名称选择事件 $('#itemname').change(function(){ var code = $(this).val(); $.ajax({ type: 'POST', url: 'pages/GetPrice.php', data:{code:code}, success: function(data){ $('#price').val(data); handleQuantityChange(); // 价格更新后,重新计算总价 }, error: function (jqXHR, textStatus, errorThrown){ alert(errorThrown); } }); }); // 添加新行的按钮点击事件 var nextRowID = 0; $('#add').click(function(e){ e.preventDefault(); var id = ++nextRowID; // 构建新的行HTML,注意ID的动态生成 var newRowHtml = '<div class="form-group"><label class="control-label col-lg-4"></label><div id ="diva'+id+'" class="col-lg-4"><div class="box"><header><h5>تفاصيل الطلب</h5></header><div class="body getval"><div class="form-group"><label class="control-label col-lg-2">الصنف</label><div class="col-lg-10"><select id="itemname'+id+'" name="itemname[]" class="form-control"><option disabled selected>اختر الصulo</option><?php echo getValues($pdo); ?></select></div></div><div class="form-group"><label class="control-label col-lg-2">السعر</label><div class="col-lg-10"><input type="text" id="price'+id+'" name="price[]" placeholder="-" readonly class="form-control price"></div></div><div class="form-group"><label class="control-label col-lg-2">الكمية</label><div class="col-lg-10"><input class="form-control quantity" type="number" name="quantity[]" value="0" min="1"></div></div></div></div></div></div>'; $('#append').append(newRowHtml); // 为新添加的商品名称选择框绑定事件 $('#itemname'+id+'').change(function(){ var code = $(this).val(); var currentRowId = $(this).attr('id').replace('itemname', ''); $.ajax({ type: 'POST', url: 'pages/GetPrice.php', data:{code:code}, success: function(data){ $('#price'+currentRowId+'').val(data); handleQuantityChange(); // 价格更新后,重新计算总价 }, error: function (jqXHR, textStatus, errorThrown){ alert(errorThrown); } }); }); // 每次添加新行后,重新绑定或触发数量变化事件 handleQuantityChange(); }); });
事件委托 (Event Delegation): 在 handleQuantityChange 函数中,我们使用了 $('.quantity').unbind('change').change(...) 来重新绑定事件。这种方法在每次添加新行时都会解除并重新绑定所有 .quantity 元素的 change 事件。对于频繁添加新行的场景,这可能导致性能问题。 更推荐的做法是使用事件委托,将事件绑定到父元素上,利用事件冒泡来监听动态子元素的事件。这样,无论何时添加新的 .quantity 元素,事件监听器都无需重新绑定。
// 优化后的 handleQuantityChange 函数 function handleQuantityChange() { let totalAmount = 0.0; // 遍历所有具有 'price' 类的输入框 $('.price').each(function(){ const priceElement = $(this); const quantityElement = priceElement.closest('.form-group').next('.form-group').find('.quantity'); const price = parseFloat(priceElement.val()) || 0; const quantity = parseFloat(quantityElement.val()) || 0; totalAmount += price * quantity; }); $('#total').val(totalAmount.toFixed(2)); } // 在 document ready 中使用事件委托 $(document).ready(function(){ // 绑定数量输入框的change事件,使用事件委托 // 这里的'body'可以替换为更具体的、不会动态变化的父容器 $('body').on('change', '.quantity', handleQuantityChange); // 绑定商品名称选择框的change事件,同样使用事件委托 $('body').on('change', '[name="itemname[]"]', function(){ // 使用name属性选择器更通用 var code = $(this).val(); var currentItemSelect = $(this); // 获取当前触发事件的select元素 $.ajax({ type: 'POST', url: 'pages/GetPrice.php', data:{code:code}, success: function(data){ // 找到当前select元素所在行的价格输入框 currentItemSelect.closest('.form-group').nextAll('.form-group').find('.price').val(data); handleQuantityChange(); // 价格更新后,重新计算总价 }, error: function (jqXHR, textStatus, errorThrown){ alert(errorThrown); } }); }); // 添加新行的按钮点击事件 var nextRowID = 0; $('#add').click(function(e){ e.preventDefault(); var id = ++nextRowID; // 构建新的行HTML,注意ID的动态生成 // 确保新生成的select和input有正确的class和name属性 var newRowHtml = '<div class="form-group"><label class="control-label col-lg-4"></label><div id ="diva'+id+'" class="col-lg-4"><div class="box"><header><h5>تفاصيل الطلب</h5></header><div class="body getval"><div class="form-group"><label class="control-label col-lg-2">الصنف</label><div class="col-lg-10"><select id="itemname'+id+'" name="itemname[]" class="form-control"><option disabled selected>اختر الصنف</option><?php echo getValues($pdo); ?></select></div></div><div class="form-group"><label class="control-label col-lg-2">السعر</label><div class="col-lg-10"><input type="text" id="price'+id+'" name="price[]" placeholder="-" readonly class="form-control price"></div></div><div class="form-group"><label class="control-label col-lg-2">الكمية</label><div class="col-lg-10"><input class="form-control quantity" type="number" name="quantity[]" value="0" min="1"></div></div></div></div></div></div>'; $('#append').append(newRowHtml); // 添加新行后,触发一次总价计算,以防新行有默认值 handleQuantityChange(); }); // 页面加载时,首次触发总价计算 handleQuantityChange(); });
使用事件委托后,handleQuantityChange 函数本身不再需要负责绑定事件,它只负责计算逻辑。
数据类型转换与验证: 在使用 parseFloat() 进行计算时,务必确保输入框的值是有效的数字。如果用户输入了非数字字符,parseFloat() 可能会返回 NaN。在计算前,可以使用 isNaN() 或逻辑或 || 0 来处理这种情况,确保计算结果的正确性。例如:parseFloat(value) || 0。
用户体验:
通过本教程,我们学习了如何利用jQuery处理动态生成的HTML元素,并实现复杂的实时计算逻辑。关键在于理解事件绑定的策略(尤其是事件委托),以及如何在DOM结构中准确地定位相关元素。掌握这些技术,将使您能够构建更加动态、响应式的Web应用。在实际开发中,始终考虑代码的可维护性、性能和用户体验,选择最适合当前场景的实现方式。
以上就是使用jQuery实现动态输入框的价格与数量联动计算教程的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号