繁体   English   中英

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

[英]A function called by a click event listener is being invoked without clicking, but only on second run through

这是一个谜题:

我正在尝试创建一个简单的选择你自己的路径游戏,它从本地 JSON 文件中获取其内容。 运行时,它会将每个场景(JSON 文件中的“部分”)分解为段落,并将第一段添加到 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);
};

更新:当我如下更改 updateParagraphs 函数时,它可以工作。 但是从 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});
    }
function updateParagraphs(){
    ...
    // container.addEventListener('click', readyToUpdate, {once: true});
    setTimeout(function () {
        container.addEventListener('click', readyToUpdate, {once: true});
    });
}

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



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

所以你有两个选择:第一个,正如@wth193 所建议的,是使用 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();
  // ...
}

这样,单击事件仅被选择元素捕获,而不是被容器捕获。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM