单击事件侦听器调用的函数在没有单击的情况下被调用,但仅在第二次运行时被调用



这里有一个谜题:

我正在尝试创建一个简单的自选路径游戏,它从本地JSON文件中获取内容。运行时,它将每个场景(JSON文件中的"Section"(分解为段落,并将第一个段落添加到DOM中,并在其周围的div上附加一个事件侦听器。每次用户单击"容器"时,都会从该部分添加另一个段落,直到显示所有段落。

之后,如果用户再次单击"容器",将添加三个选项,每个选项都附加了自己的事件侦听器。单击其中一个选项时,场景(或"剖面"(将更改为选定的场景。HTML"容器"被清空,通过添加新场景(部分(的第一段,该过程再次开始。

问题是,它适用于第一部分和选项,但在用户单击选项后,它会加载新场景的两段,而不是一段。它应该加载一段并等待点击事件,但实际上它加载了第二段。第二段是通过readyToUpdate函数加载的,该函数只能由事件侦听器(尚未触发(调用。

{  
"Section_1": {
"Content": "Dark corner paragraph one<>Dark corner paragraph two<>Dark corner paragraph three",
"Choices": [
"Go to the garden<>24",
"Go to the terrace<>95",
"Go to the beach<>145"
]
},
"Section_24": {
"Content": "Garden paragraph one<>Garden paragraph two<>Garden paragraph three",
"Choices": [
"Go to the dark corner<>1",
"Go to the terrace<>95",
"Go to the beach<>145"
]
},
"Section_95": {
"Content": "Terrace paragraph one<>Terrace paragraph two<>Terrace paragraph three",
"Choices": [
"Go to the dark corner<>1",
"Go to the garden<>24",
"Go to the beach<>145"
]
},
"Section_145": {
"Content": "Beach paragraph one<>Beach paragraph two<>Beach paragraph three",
"Choices": [
"Go to the dark corner<>1",
"Go to the garden<>24",
"Go to the terrace<>95"
]
}
}
<div id="container"></div>
fetch("js/contentFile.json")
.then(response => response.json())
.then(json => initialSetup(json));
let narrativeText;
let gameData = {
section: "",
paraCount: 0,
choices: [],
choiceText: [],
choiceDest: [],
paragraphs: "",
paraSections: []
};
function updateParagraphs(){
console.log("Enter updateParagraphs");
let container = document.getElementById("container");
let node = document.createElement("p");             
let textnode = 
document.createTextNode(gameData.paraSections[gameData.paraCount]);  
node.appendChild(textnode);                    
container.appendChild(node);
container.addEventListener('click', readyToUpdate, {once: true});

console.log(gameData.paraCount + " is less than " + 
gameData.paraSections.length);
console.log("Exit updateParagraphs");
console.log(gameData);
}
function readyToUpdate(e) {
console.log("Enter readyToUpdate");
console.log(e);
gameData.paraCount ++;
let container = document.getElementById("container");   
update();
console.log("Exit readyToUpdate");
console.log(gameData);
}

function choiceUpdater(e){
console.log("Enter choiceUpdater");
let choiceNumber = e.target.id.split("_")[1];
gameData.section = "Section_" + gameData.choiceDest[choiceNumber];
document.getElementById("container").innerHTML = "";
gameData.paraCount = 0;
update();
console.log("Exit choiceUpdater");
console.log(gameData);
}
function addChoices() {
console.log("Enter addChoices");
gameData.choices = narrativeText[gameData.section].Choices;
console.log(gameData.choices);

for (let i=0; i<gameData.choices.length; i++){
let choice = document.createElement("h4");
let choicesSplit = gameData.choices[i].split("<>");
gameData.choiceText[i] = choicesSplit[0];
gameData.choiceDest[i] = choicesSplit[1];
let choiceTextNode = document.createTextNode(gameData.choiceText[i]);
choice.appendChild(choiceTextNode);
choice.setAttribute("id", "choice_" + i);
choice.addEventListener("click", choiceUpdater, {once: true});
document.getElementById("container").appendChild(choice);
}
console.log("Exit addChoices");
console.log(gameData);
}
function update() {
console.log("Enter update");
gameData.paragraphs = narrativeText[gameData.section].Content;
gameData.paraSections = gameData.paragraphs.split("<>");
if (gameData.paraCount < gameData.paraSections.length) {
updateParagraphs();
}
else {
addChoices();

}
console.log("Exit update");
console.log(gameData);
}
function initialSetup(data) {
console.log("Enter initial setup")
narrativeText = data;
gameData.section = "Section_1";
gameData.paraCount = 0;
update();
console.log("Exit initial setup");
console.log(gameData);
};

更新:当我更改updatePages函数如下时,它就起作用了。但从Chrome开发人员的性能来看,监听器的数量不断增加。我使用的是Chrome 80,所以它应该使用"once"属性来删除它们,但即使我使用removeEventListener((手动删除侦听器,我也遇到了同样的初始问题。

function updateParagraphs(){
console.log("Enter updateParagraphs");
let container = document.getElementById("container");
let node1 = document.createElement("div");
node1.setAttribute("id", "innerContainer");
document.getElementById("container").appendChild(node1);
let innerContainer = document.getElementById("innerContainer");
let node = document.createElement("p");             
let textnode = 
document.createTextNode(gameData.paraSections[gameData.paraCount]);  
node.appendChild(textnode);                    
innerContainer.appendChild(node);
innerContainer.addEventListener('click', readyToUpdate, {once: true});
}

我相信发生的事情是,当您单击一个选项,并调用该选项的click监听器时,您将执行update()updateParagraphs(),并向容器中添加一个新的click侦听器。不过,与此同时,单击事件正在传播到容器,并被您刚刚添加的新容器侦听器捕获。

因此,您有两个选项:第一个选项,如@wth193所建议的,是在下一个tick使用setTimeout((添加容器侦听器。

function updateParagraphs(){
// ...
// container.addEventListener('click', readyToUpdate, {once: true});
setTimeout(function () {
container.addEventListener('click', readyToUpdate, {once: true});
});
}

在我看来,一个更干净(更清晰(的解决方案是使用Event.stopPropagation((.

function choiceUpdater(e){
console.log("Enter choiceUpdater");
e.stopPropagation();
// ...
}

通过这种方式,单击事件仅由choice元素捕获,而不是由容器捕获。

function updateParagraphs(){
...
// container.addEventListener('click', readyToUpdate, {once: true});
setTimeout(function () {
container.addEventListener('click', readyToUpdate, {once: true});
});
}

我认为setTimeout破坏调用堆栈似乎有效,但我无法解释原因。



相关内容

  • 没有找到相关文章