如何用jQuery遍历多份JSON文件构建在线测验页面
解决异步AJAX顺序问题及数据关联渲染的方案
看起来你遇到的核心问题有两个:异步请求的执行顺序不可控,导致DOM渲染时机不对;数据没有通过关联ID正确匹配,导致内容乱套。别担心,我们一步步来解决:
问题根源拆解
- 三个
$.ajax是并行发起的,谁先返回谁先执行回调,可能出现答案先渲染但题目DOM还没生成,或者题目先渲染但分类容器还没创建的情况。 - 当前代码中,添加问题时会把同一个题目插入到所有
.section-bottom容器中,添加答案时也会把同一个选项插入到所有answerContainer里,完全没有按分类-题目-选项的层级匹配。
完整解决方案
我们先一次性获取所有三个数据源,等全部拿到后再按分类→题目→选项的顺序关联数据并渲染DOM,既保证顺序,又能精准匹配内容。
HTML部分(保持不变)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>quiz</title> </head> <body> <div id="bigMain" class="hide"> </div> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <script src="js/refactor3.js"></script> </body> </html>
JavaScript修改版
// 先发起三个请求,暂不处理回调 var request1 = $.ajax({ url: 'https://api.myjson.com/bins/nilct', dataType: 'json', type: 'get', cache: false }); var request2 = $.ajax({ url: 'https://api.myjson.com/bins/1178p1', dataType: 'json', type: 'get', cache: false }); var request3 = $.ajax({ url: 'https://api.myjson.com/bins/qhft1', dataType: 'json', type: 'get', cache: false }); // 等待三个请求全部完成后再统一处理 $.when(request1, request2, request3).done(function(categoriesRes, questionsRes, answersRes) { // 提取每个请求的返回数据 const categories = categoriesRes[0]; const questions = questionsRes[0]; const answers = answersRes[0]; const bigMain = $('#bigMain'); bigMain.removeClass('hide'); // 显示主容器 // 遍历每个分类,逐层渲染 categories.forEach(category => { const categoryId = category.id; // 创建分类section容器 const section = $(` <section id="${categoryId}"> <div class="section-top"> <p class="title optionsTitle ${category.category} titleFor_${categoryId}"> ${category.category} </p> <hr class="hideOnMobile"> <h1 class="top-title">${category.header}</h1> </div> <div class="section-bottom"></div> </section> `); bigMain.append(section); // 筛选当前分类下的所有题目 const categoryQuestions = questions.filter(q => q.category_id === categoryId); const sectionBottom = section.find('.section-bottom'); // 遍历当前分类的题目 categoryQuestions.forEach(question => { const questionId = question.id; // 创建题目元素 const questionElement = $(` <div class="question question${categoryId}"> <p class="questionP questionId_${questionId}">${question.question}</p> <ul id="questionId-${questionId}" class="answerContainer"></ul> </div> `); sectionBottom.append(questionElement); // 筛选当前题目对应的选项(按orderNum匹配,可根据实际JSON结构调整) const questionAnswers = answers.filter(a => a.orderNum === question.orderNum); const answerContainer = questionElement.find('.answerContainer'); // 遍历并添加选项 questionAnswers.forEach(answer => { const answerElement = $(` <li> <input type="radio" name="question-${questionId}" value="${answer.answer}"> ${answer.answer} </li> `); answerContainer.append(answerElement); }); }); }); }).fail(function(err) { console.error('请求失败:', err); });
关键改进说明
- 用
$.when控制异步顺序:确保所有数据都获取完成后再开始渲染,彻底避免异步回调顺序混乱的问题。 - 层级化数据匹配:
- 每个分类下筛选
category_id匹配的题目 - 每个题目下筛选关联字段匹配的选项(这里用
orderNum,如果你的JSON里有question_id字段,用它匹配会更准确)
- 每个分类下筛选
- 精准DOM插入:创建分类后直接在当前分类的底部插入题目,创建题目后直接在当前题目选项容器插入选项,完全避免了之前“一对多”插入的错误。
- 修复单选按钮逻辑:把radio的
name属性改为题目ID(name="question-${questionId}"),保证同一题的选项互斥,之前的answerOrderNum会导致不同题的选项互斥,这是个隐性bug。
内容的提问来源于stack exchange,提问作者fropt
相关产品推荐
相关产品推荐

