如何为多个商品分类动态生成独立表格,避免 AJAX 响应内容重复渲染

梦晨小哥_2401

梦晨小哥_2401

2026-07-25

193人浏览

原创

如何为多个商品分类动态生成独立表格,避免 AJAX 响应内容重复渲染

本文详解如何通过结构化 dom 操作与模板化克隆策略,为每个选中分类单独创建商品表格,彻底解决多分类下 ajax 返回数据交叉重复、dom 元素错位的问题。

本文详解如何通过结构化 dom 操作与模板化克隆策略,为每个选中分类单独创建商品表格,彻底解决多分类下 ajax 返回数据交叉重复、dom 元素错位的问题。

在基于 jQuery 的后台管理界面中,当用户选择多个商品分类(如 categories_id = [1, 2])并触发 AJAX 请求时,常见错误是:所有分类的响应数据被统一注入到同一个 .inventory_products_lines_values 模板实例中,导致后续克隆操作反复复用已渲染的 DOM 节点——结果就是第二分类的数据“叠加”在第一分类表格下方,甚至出现整块表格重复两次。

根本原因在于原逻辑中 clone() 和 insertAfter() 的使用方式未做作用域隔离:

  • 每次循环都调用 $('.inventory_products_lines.inventory_products_lines_values').last().clone(),但该选择器始终匹配全局最后一个可见/非 hidden 的行模板,而非当前分类专属副本;
  • $('.inventory_products_lines_values').closest('div').after(clone) 进一步将新克隆行插入到整个页面中最后一个 .inventory_products_lines_values 所在容器之后,完全脱离分类上下文;
  • 最终 $('#default_inventory_products .inventory_products_block').clone(true) 被重复执行多次,且每次都在 #inventory_products 同一位置后追加,造成多个相同结构的
    并列堆叠。

    ✅ 正确解法:为每个分类创建独立容器 + 使用静态 HTML 模板字符串克隆

    推荐采用「容器隔离 + 模板预定义」模式,避免依赖动态 DOM 状态进行克隆:

    AJAX技术—无刷新获取数据相关课件
    AJAX技术—无刷新获取数据相关课件

    AJAX技术—无刷新获取数据相关课件

    下载
    <!-- 隐藏的结构化模板(仅定义一次,不参与实际渲染) -->
    <div class="hidden" id="template-category-block">
      <div class="inventory_products_block">
        <div class="inventory_products_lines inventory_products_lines_head">
          <div class="search_category_name head"></div>
          <div class="combinations_head head">Comb</div>
          <div class="brand_head head">Brand</div>
          <div class="wholesale_price_head head">Wholesale Price</div>
          <div class="price_head head">Retail Price</div>
          <div class="now_quantity_head head">Now Qty</div>
          <div class="quantity_head head">New Qty</div>
          <div class="discrepancy_head head">Discrepancy</div>
          <div class="status_head head">Status</div>
        </div>
        <div class="inventory_products_lines_container"></div>
      </div>
    </div>
    
    <div class="hidden" id="template-product-row">
      <div class="inventory_products_lines inventory_products_lines_values inventory_products_lines_result">
        <div class="product_number"></div>
        <div class="id_product hidden"></div>
        <div class="id_product_attribute hidden">
          <input name="id_product_attribute[]" type="hidden" value="0">
    </div>
        <div class="name"></div>
        <div class="combinations"></div>
        <div class="brand"></div>
        <div class="wholesale_price"></div>
        <div class="price"></div>
        <div class="now_quantity"></div>
        <div class="inventory_quantity">
          <input name="inventory_quantity[]" type="text" value="">
    </div>
        <div class="discrepancy_block">
          <input name="discrepancy[]" type="hidden" value=""><span class="discrepancy"></span>
        </div>
        <div class="category_inventory_wholesale_deficit_block">
          <input name="category_inventory_wholesale_deficit_price[]" type="hidden" value="">
    </div>
        <div class="category_inventory_retail_deficit_price_block">
          <input name="category_inventory_retail_deficit_price[]" type="hidden" value="">
    </div>
        <div class="status"></div>
      </div>
    </div>

    对应 JavaScript 实现(含 Promise 并发控制,确保顺序):

    // 获取模板字符串(仅执行一次)
    const $blockTemplate = $('#template-category-block');
    const $rowTemplate = $('#template-product-row');
    const blockHTML = $blockTemplate.html();
    const rowHTML = $rowTemplate.html();
    
    // 使用 Promise.all 确保所有请求完成后再按序渲染(可选,提升用户体验)
    const ajaxPromises = categories_id.map(category_id =>
      $.ajax({
        type: 'GET',
        url: 'ajax-tab.php',
        data: {
          ajax: true,
          controller: 'AdminInventory',
          action: 'searchProductsByCategory',
          token: token,
          search_category_id: category_id,
          rand: Date.now()
        },
        dataType: 'json'
      })
    );
    
    Promise.all(ajaxPromises).then(responses => {
      responses.forEach((data, index) => {
        // 创建独立分类容器
        const $block = $(`<div class="inventory_products_block">${blockHTML}</div>`);
        $block.find('.search_category_name').append(`<h2>${data.category_name}</h2>`);
    
        // 渲染该分类下所有商品行
        const $container = $block.find('.inventory_products_lines_container');
        $.each(data.products, (key, val) => {
          const $row = $(rowHTML);
          $row.find('.name').html(`<span>${val.name}</span>`);
          $row.find('.brand').html(`<span>${val.manufacturer_name}</span>`);
          $row.find('.now_quantity').html(
            `<input type="hidden" name="now_quantity[]" value="${val.quantity}"><span>${val.quantity}</span>`
          ).attr('data-quantity', val.quantity);
          $row.find('.id_product').html(
            `<input name="id_product[]" type="hidden" value="${val.id_product}">`
          );
          $row.find('.status').html(
            val.active === 1 
              ? '<i style="color:#72C279;" class="icon-check"></i>' 
              : '<i style="color:#E08F95;" class="icon-remove"></i>'
          );
          $row.removeClass('hidden');
          $container.append($row);
        });
    
        // 插入到主容器末尾(如 #inventory_products)
        $('#inventory_products').after($block);
      });
    
      // 重置序号(全局重排)
      $('.inventory_products_lines_result').each((i, el) => {
        $(el).find('.product_number').text((i + 1) + '.');
      });
    });

    ⚠️ 关键注意事项:

    • 禁止跨分类复用 .last().clone():它极易受 DOM 当前状态干扰,应改用 $(templateHTML) 显式构造;
    • 避免多次 .after() 同一目标元素:$('#inventory_products').closest('div').after(...) 在循环中会不断在相同位置插入,导致层级混乱;
    • 推荐 Promise.all() 统一处理异步:既防止竞态导致的顺序错乱,也便于错误集中捕获;
    • 模板需保持纯净:#template-* 元素必须设为 .hidden 且不参与任何业务逻辑,仅作字符串源;
    • ID 属性需唯一性处理:示例中 id="inventory_id_product" 等静态 ID 应改为 class 或动态生成(如 id="inventory_id_product_${val.id_product}"),否则违反 HTML 规范。

    通过以上重构,每个分类都将拥有语义清晰、作用域隔离的独立表格区块,彻底消除数据混杂与重复渲染问题,同时提升代码可维护性与扩展性。

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

ajax

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
OpenJDK JVM参数怎么调优
OpenJDK JVM参数怎么调优

Java应用运行缓慢,如何调整JVM参数?本专题介绍OpenJDK JVM性能调优的基本流程,涵盖性能指标确认、运行数据采集、内存配置和垃圾回收分析,帮助你先定位瓶颈,再通过对照测试验证效果,避免直接套用参数而影响应用稳定性。

2026.10.16

20

10

OpenJDK怎么配置Java开发环境
OpenJDK怎么配置Java开发环境

怎样用OpenJDK搭建开发环境?本专题介绍版本选择、下载安装、环境变量配置和编译运行验证,帮助你理清JDK的作用及基本配置流程,并通过简单程序检查环境是否可用,为后续项目开发做好准备。

2026.10.16

20

10

OpenJDK怎么部署Java应用
OpenJDK怎么部署Java应用

Java项目开发完成后,怎样部署到目标环境并正常运行?本专题介绍OpenJDK应用部署流程,涵盖运行环境准备、版本兼容检查、应用打包、启动参数配置和日志查看,帮助你理清部署前后的操作顺序,减少环境差异造成的运行问题。

2026.10.16

0

10

Revel框架模板渲染与表单验证
Revel框架模板渲染与表单验证

本专题围绕Revel框架视图层开发,介绍模板语法、变量输出、布局复用、表单提交、参数绑定与验证规则,帮助开发者完成安全规范的页面交互。

2026.10.16

20

15

Revel框架MVC路由与控制器
Revel框架MVC路由与控制器

本专题聚焦Revel框架MVC核心机制,讲解路由定义、控制器Action、参数绑定、请求响应和反向路由,并结合常见场景梳理接口请求处理流程。

2026.10.16

20

15

Revel框架入门与安装配置
Revel框架入门与安装配置

本专题从Revel框架环境搭建讲起,系统介绍命令行工具安装、项目创建、目录结构、配置文件及热代码重载,帮助开发者快速完成首个Go Web应用。

2026.10.16

20

15

Linux 新手先学哪些命令
Linux 新手先学哪些命令

Linux新手命令专题汇集终端基础、文件目录操作、文本查看、软件安装和权限管理等入门文章,帮助按实际任务练习常用命令并建立命令行操作习惯。

2026.10.15

40

20

MySQL 忘记密码怎么办
MySQL 忘记密码怎么办

MySQL忘记密码专题汇集不同版本与系统下的root密码重置方法,覆盖MySQL 5.7、8.0、Mac、Linux及本地集成环境,并提示恢复权限与重新登录的检查步骤。

2026.10.15

20

18

Vue.js 中 ref 和 reactive 的区别
Vue.js 中 ref 和 reactive 的区别

Vue.js中ref和reactive的区别专题汇集两种响应式API的定义、访问方式、对象解构、侦听与性能取舍等文章,帮助结合实际状态类型选择合适写法。

2026.10.15

20

20

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
jQuery官方API文档
jQuery官方API文档

共0课时 | 0人学习

Symfony教程(入门篇+基础篇)
Symfony教程(入门篇+基础篇)

共18课时 | 1.8万人学习