You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何复用打字效果循环函数,实现数组索引间等待按钮事件触发

解决方案:基于递归+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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 19:17:05