使用Handlebars填充jQuery手风琴时嵌套Div仅首行生效的问题
解决手风琴组件嵌套内容仅第一项生效的问题
这种动态生成的嵌套组件只在第一个项生效的坑我之前踩过好几次,大概率是选择器冲突、事件绑定方式不对,或者模板数据上下文错误导致的,给你几个针对性的排查和解决思路:
1. 立刻检查:是否用了重复的ID
ID在DOM里是唯一的,如果你的嵌套Div用了类似#nested-content这种固定ID,不管渲染多少个手风琴项,JS只会找到第一个匹配的元素。
解决办法:把嵌套容器的ID改成class,比如.nested-content,然后在Handlebars模板里统一使用class选择器:
{{#each accordionItems}} <div class="accordion-item"> <div class="accordion-header">{{title}}</div> <div class="accordion-body"> <p>{{mainDetail}}</p> <!-- 用class代替ID --> <div class="nested-content"> {{#each subItems}} <div class="nested-item">{{subTitle}}</div> {{/each}} </div> </div> </div> {{/each}}
2. 事件绑定要改用「事件委托」
如果你的手风琴展开事件是直接绑定在初始DOM元素上的(比如$('.accordion-header').click()),那动态生成的后续手风琴项不会触发这个事件,自然也不会加载嵌套内容。
解决办法:把事件绑定到父容器(或者document)上,通过事件委托处理动态元素:
// 错误写法:只绑定初始存在的元素 // $('.accordion-header').click(function() { ... }) // 正确写法:事件委托,覆盖所有动态生成的元素 $('.accordion-container').on('click', '.accordion-header', function() { // 基于当前点击的header,找到对应的body和嵌套容器 const $targetBody = $(this).next('.accordion-body'); const $nestedContent = $targetBody.find('.nested-content'); // 切换展开/收起状态 $targetBody.toggleClass('active'); // 如果是动态加载嵌套数据,这里可以根据当前项的标识去请求数据 // 比如 const itemId = $(this).data('item-id'); // 然后把数据渲染到$nestedContent里 });
3. 确保模板数据是「项级独立」的
如果你的嵌套数据是全局的,没有和每个手风琴项绑定,那所有项的嵌套内容都会用同一份数据,甚至可能只有第一个项能正确渲染。
解决办法:保证每个手风琴项的数据源里包含自己的嵌套数据,比如:
const accordionData = { accordionItems: [ { title: "手风琴项1", mainDetail: "主内容1", subItems: [{subTitle: "嵌套内容1-1"}, {subTitle: "嵌套内容1-2"}] }, { title: "手风琴项2", mainDetail: "主内容2", subItems: [{subTitle: "嵌套内容2-1"}, {subTitle: "嵌套内容2-2"}] } ] };
然后在Handlebars模板里通过{{#each subItems}}遍历当前项的嵌套数据,而不是全局的数组。
4. 定位元素时要基于「当前项上下文」
如果你的代码里是直接用$('.nested-content')来操作嵌套容器,那会选中所有匹配的元素,导致只有第一个被正确处理。
解决办法:永远从当前点击的手风琴header出发,向上/向下查找对应的嵌套容器,比如:
// 错误写法:操作所有嵌套容器 // $('.nested-content').html(xxx); // 正确写法:只操作当前展开项的嵌套容器 $(this).next('.accordion-body').find('.nested-content').html(yourNestedTemplateData);
完整示例代码
给你一个能跑通的极简示例,你可以对照调整自己的代码:
Handlebars模板
<script id="accordion-tpl" type="text/x-handlebars-template"> {{#each items}} <div class="accordion-item"> <button class="accordion-header" data-item-id="{{id}}">{{title}}</button> <div class="accordion-body"> <p>{{mainText}}</p> <div class="nested-box"> {{#each nestedList}} <span class="nested-item">{{text}}</span> {{/each}} </div> </div> </div> {{/each}} </script>
JS逻辑
// 1. 编译模板并渲染初始内容 const tpl = Handlebars.compile($('#accordion-tpl').html()); const data = { items: [ {id: 1, title: "项1", mainText: "主内容1", nestedList: [{text: "嵌套1"}, {text: "嵌套2"}]}, {id: 2, title: "项2", mainText: "主内容2", nestedList: [{text: "嵌套3"}, {text: "嵌套4"}]}, {id: 3, title: "项3", mainText: "主内容3", nestedList: [{text: "嵌套5"}, {text: "嵌套6"}]} ] }; $('.accordion-wrap').html(tpl(data)); // 2. 事件委托实现手风琴展开/收起 $('.accordion-wrap').on('click', '.accordion-header', function() { const $body = $(this).next('.accordion-body'); $body.toggleClass('show'); });
基础CSS
.accordion-body { display: none; padding: 10px; border: 1px solid #eee; } .accordion-body.show { display: block; } .nested-box { margin-top: 10px; padding: 5px; background: #f5f5f5; }
内容的提问来源于stack exchange,提问作者wcpulling
相关产品推荐
相关产品推荐

