讲师中心 微信公众号
AI工具推荐 视频效率加速

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

梦晨小哥_2401

梦晨小哥_2401

发布时间:2026-07-25 18:10:07

|

193人浏览过

|

来源于php中文网

原创

如何为多个商品分类动态生成独立表格,避免 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 同一位置后追加,造成多个相同结构的 <div class="inventory_products_block"> 并列堆叠。

✅ 正确解法:为每个分类创建独立容器 + 使用静态 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 规范。

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

热门AI工具

更多
Atoms
Atoms Hot

Atoms是一款AI智能体工具,第一支自动构建真实业务的 AI 团队。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

PixTV
PixTV Hot

PixTV是一款面向AIGC内容创作的AI视频生成工具。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

VibeKnow
VibeKnow Hot

一款AI视频创作工具,主要用于全球首个AI知识视频创作平台,文档、文章、网页,一键生成视频,适合需要提升相关任务效率的用户。

UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

相关专题

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

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

60

2026.10.16

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

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

40

2026.10.16

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

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

40

2026.10.16

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

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

20

2026.10.16

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

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

20

2026.10.16

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

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

20

2026.10.16

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

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

40

2026.10.15

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

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

20

2026.10.15

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

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

20

2026.10.15

热门下载

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

精品课程

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

共0课时 | 0人学习

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

共18课时 | 1.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn