我想在数组的每个索引处停下来,等待事件侦听器,然后再继续

I want to stop at each index of an array and wait for event listeners before moving on

提问人:Bryan 提问时间:11/10/2023 最后编辑:Peter SeligerBryan 更新时间:11/11/2023 访问量:63

问:

我想将现有的循环函数重用用于不同的数组,但要让它在索引之间停止并等待事件侦听器。

问题是这个函数只有在遍历整个数组时才会停止。我还想保持“逐个字符”的打字效果。我试过玩这个功能,但我无法弄清楚。

这是现有的打字效果/循环函数,我还想用它来遍历一个不同的数组,该数组称为,并让它在每个字符串中键入并在每个索引之间暂停,等待用户点击按钮,然后获得响应。然后,我将使用 键入一个响应,并触发下一个问题。questions[]buttonOne.addEventListenerquestions[]

const element =
  document.querySelector("div");

const messages =
  ["Hello, my operating name is Zurlan-42", "more stuff", "more things"];

const typingText = (elem, txt, done = () => {}) => {
  let i = 0;

  elem.textContent = ""; // Clear before typing

  const next = () => {
    elem.textContent += txt[i];

    if(i === txt.length - 1)  return done();

    i += 1;
    setTimeout(next, 30);
  };
  next(); // Start
};

const typingArray = (elem, arr, done = () => {}) => {
  let i = 0;

  const next = () => {
    typingText(elem, arr[i], () => {

      if(i === arr.length - 1) return done();

      i += 1;
      setTimeout(next, 2000);
    });    
  };
  next(); // Start
}
typingArray(element, messages, () => {
  console.log("All Done!");

  setTimeout(() => {
    element.textContent = "";
  }, 2000);
});
<div></div>

这是我目前所拥有的。我一直在使用 forEach 方法或为每个问题和响应写出每个函数之间来回跳动。但是,我觉得遍历数组是更好的方法。

const questionOne = () => {
    buttonOne.textContent = "No Arms";
    buttonTwo.textContent = "No Legs";

    buttonOne.addEventListener("click", () => {
        console.log("I was clicked!");
        
    })
    buttonTwo.addEventListener("click", () => {
        console.log("No, I was clicked!");
    })
}
questionOne();
JavaScript 循环 foreach addEventListener

评论

0赞 Peter Seliger 11/10/2023
不要使用该函数,但每次单击“对话”时,都会将数组中的下一个文本片段/句子传递给(例如,通过从中出字符串)。typingArraymessagestypingText
0赞 Bryan 11/11/2023
太棒了,谢谢。我将引用更多 Mmdn Web 文档。你可以用数组做很多事情,完全忘记了移位、排序、弹出等。

答:

1赞 Diego D 11/10/2023 #1

实现等待用户输入显示下一条消息的目标的一种方法可能是更改函数,这样,它不会等待一段时间后再转到下一条消息,而是动态创建包含这些答案(用户输入)的按钮,这些按钮具有单击事件处理程序处理下一个问题的逻辑。typingArray

在这里,我将该函数命名为 having as 参数,以便当答案按钮由其单击事件处理程序创建时,每次都会有一个带有指向下一个问题的索引的调用。processQuestionsindexcreateAnswerButtons

我还添加了一个非常基本的策略来确定给出的答案是否正确,并且还添加了一种更能取悦眼睛的风格。

const questions = [
  { q: 'Question1.. ?', a: ['Answer1', 'Answer2'], correct: 0 },
  { q: 'Question2.. ?', a: ['Answer1', 'Answer2', 'Answer3'], correct: 1 },
  { q: 'Question3.. ?', a: ['Answer1', 'Answer2', 'Answer3'], correct: 2 }
];

const questionElem = document.querySelector('.question');
const answersElem = document.querySelector('.answers');

//this is your original function to type text delaying each character
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();
};

//creates the buttons in the .answers area according to the passed answers
const createAnswerButtons = (answers, correct, callback) => {  
  answers.forEach( (answer, i) => {
    const button = document.createElement('button');
    button.textContent = answer;
    button.onclick = callback;
    //if the index of the current answer is the same as the correct one
    if(i==correct)
      //add the class correct to the answer button
      button.classList.add('correct');
    answersElem.appendChild(button);
  });
};

const processQuestions = (questions, index = 0) => {
  
  //if there are no more questions
  if (index >= questions.length) {
    console.log("All questions completed");
    return;
  }

  //get the current question according to the index
  const currentQuestion = questions[index];

  //instructs your typingText func to start typing the current question  
  typingText(questionElem, currentQuestion.q, () => {
    //and when the typing has been finished,
    //create and show the correspondent answer buttons
    createAnswerButtons(
      currentQuestion.a,
      currentQuestion.correct,
      //this is the callback that will be bound to the buttons click event
      //and when one of the answer button was clicked,
      (event) => {              
        //report result
        const isAnswerGivenCorrect =
          event.target.classList.contains('correct');
        printResult(index, isAnswerGivenCorrect);
        
        //then clear the area and pass to the next question
        questionElem.textContent = '';
        answersElem.innerHTML = '';
        processQuestions(questions, index + 1);
      });
  });
};

function printResult(index, wasCorrect){
  const output = document.getElementById('output');
  if(wasCorrect)
    output.textContent = `q${index+1} The answer given was correct!`;
  else
    output.textContent = `q${index+1} The answer given was wrong!`;
}

processQuestions(questions);
#output{
  margin-top: 1em;
  background: lightgray;
  padding: 0.5em;
}

#output:empty{
  display: none;
}

.container {
  border: dashed 2px lightgray;
  padding: 1em;
}

.container .question {
  margin-bottom: 1em;
}

.container .answers {
  display: flex;
  justify-content: space-evenly;
}

.container .answers button{
  cursor: pointer;
}
<div class="container">
  <div class="question"></div>
  <div class="answers"></div>
</div>

<div id="output"></div>

评论

0赞 Bryan 11/11/2023
哇,非常感谢您的详细回复和注释。首先,我不知道你可以把一个数组分成这样的部分。我喜欢它的简洁和简单:将两种数组可能性合二为一。对于我看到回调是传递给按钮的参数。然后将函数传入 。您可以访问您传递的 from the questions 参数,但回调是如何发挥作用的?对基本原理有点困惑。button.onclick = callback;createAnswerButtonsprocessQuestionscurrentQuestion.a
0赞 Diego D 11/11/2023
如果您停止从词法范围的角度思考,并想象这些函数将如何以及何时实际执行,可能会更容易阅读。 是入口点。它将开始一个链,该链每次都会以递增的索引不断调用自己,并且只有当该索引超过问题长度时才会最终返回。在执行的每个步骤中,它都会刷新问题区域,为属于它的每个答案创建一个按钮。此类操作由 .. 执行。processQuestioncreateAnswerButtons
0赞 Diego D 11/11/2023
这将把回调传递到的每个按钮上分配,作为它们的 Click 事件处理程序。当此类回调已知并按 1 递增传递时,在调用堆栈的上层定义。所以关键是,当按钮被点击时,绑定到事件的函数会意识到将再次调用的索引。所以想象一下序列:processQuestion(1) -> showQuestionAndAnswers(1) -> [用户点击按钮] -> processQuestion(2) -> showQuestionAndAnswers(2) -> [用户点击按钮] ...indexprocessQuestions(questions, index + 1)
0赞 Bryan 11/11/2023
肯定查了一下词汇范围,哈。我明白你的意思,这对我来说很清楚。这很有帮助,再次感谢。