如何复用打字效果循环函数,实现数组索引间等待按钮事件触发
解决方案:基于递归+Promise实现带用户交互的逐题打字流程
核心思路是把每个问题的展示、用户交互、响应展示拆分为异步步骤,用Promise包裹用户点击事件,通过递归逐个处理questions数组,替代原有的自动循环逻辑。
步骤1:定义结构化问题数据
先把问题、选项和对应响应整理成数组,方便统一遍历:
const questions = [ { question: "What's your biggest flaw?", options: [ { text: "No Arms", response: "Fair enough, I can't lift heavy objects at all." }, { text: "No Legs", response: "Yep, I'm stuck in one spot forever." } ] }, { question: "What do you like to do in your free time?", options: [ { text: "Stare at walls", response: "They're surprisingly interesting, trust me." }, { text: "Count pixels", response: "1, 2, 3... Wait, did I miss one?" } ] } ];
步骤2:改造流程控制逻辑
保留原有typingText打字函数,新增Promise封装用户点击事件,再用递归函数逐题处理:
const element = document.querySelector("div"); const buttonOne = document.querySelector("#buttonOne"); const buttonTwo = document.querySelector("#buttonTwo"); // 保留原有的打字效果函数 const typingText = (elem, txt, done = () => {}) => { let i = 0; elem.textContent = ""; const next = () => { elem.textContent += txt[i]; if (i === txt.length - 1) return done(); i += 1; setTimeout(next, 30); }; next(); }; // 把按钮点击转为Promise,等待用户选择 const waitForButtonClick = (buttons) => { return new Promise((resolve) => { const handleClick = (response) => { // 移除所有按钮事件监听,避免重复触发 buttons.forEach(btn => btn.removeEventListener("click", btn._clickHandler)); resolve(response); }; buttons.forEach(({ btn, response }) => { btn._clickHandler = () => handleClick(response); btn.addEventListener("click", btn._clickHandler); }); }); }; // 递归处理下一个问题 const processNextQuestion = (index = 0) => { if (index >= questions.length) { typingText(element, "That's all for now!", () => { console.log("All interactions done!"); }); return; } const currentQ = questions[index]; // 1. 打字显示当前问题 typingText(element, currentQ.question, async () => { // 2. 设置按钮文本 buttonOne.textContent = currentQ.options[0].text; buttonTwo.textContent = currentQ.options[1].text; // 3. 等待用户点击,获取响应文本 const responseText = await waitForButtonClick([ { btn: buttonOne, response: currentQ.options[0].response }, { btn: buttonTwo, response: currentQ.options[1].response } ]); // 4. 打字显示响应,完成后进入下一题 typingText(element, responseText, () => { setTimeout(() => { processNextQuestion(index + 1); }, 1000); }); }); }; // 启动流程 processNextQuestion();
步骤3:补充HTML结构
需要添加对应按钮元素:
<div></div> <button id="buttonOne"></button> <button id="buttonTwo"></button>
关键改进点
- 用Promise封装按钮点击,将同步循环转为异步分步执行,解决原函数一次性遍历数组的问题
- 递归调用
processNextQuestion替代自动循环,确保每完成一次交互才进入下一题 - 每次点击后移除按钮事件监听,避免重复绑定导致的多次触发
- 完全复用原有
typingText打字函数,无需重新实现核心动画逻辑
内容的提问来源于stack exchange,提问作者Bryan
相关产品推荐
相关产品推荐

