石头剪刀布游戏中“再玩一次”后事件侦听器未重新连接

问题描述 投票:0回答:1

描述: 我正在使用 HTML、CSS 和 JavaScript 构建一个简单的剪刀石头布游戏。游戏最初运行良好,用户可以做出选择并与计算机对战。但是,在游戏结束并单击“再次玩”按钮后,游戏选择(石头、布、剪刀)的事件侦听器似乎无法正确重新连接。因此,用户点击“再次玩”后无法再玩一轮。

function getComputerChoice(){
    const choice = ['rock','paper','scissor']
    const length = choice.length
    return choice[Math.floor(Math.random() * length)]
}
const choicesImage = document.querySelectorAll('.choice img')
const choices = document.querySelectorAll('.choice')
const container = document.querySelector('.container')
const playerResult = document.querySelector('.playerChoice')
const computerResult = document.querySelector('.computerChoice')
const innerContent = container.innerHTML
const status = document.querySelector('.status')
let playerCurrentScore = 0 , computerCurrentScore = 0

function declareResult(){
    if(playerCurrentScore === 5 || computerCurrentScore === 5 ){
        container.classList.add('new')
        const newContent = document.querySelector('.new')
        newContent.textContent = playerCurrentScore === 5 ? 'Player Wins!':'Computer Wins!'
        newContent.style.fontSize = "35px"
        newContent.style.textAlign = "center"
        const btn = document.createElement('button')
        btn.textContent = "Play Again"
        newContent.append(btn)
        btn.addEventListener('click',changeContent)
    }  
}

function changeContent(){
    container.classList.remove('new')
    container.innerHTML = innerContent
    attachEvents();
}

function attachEvents(){
    choicesImage.forEach((choice) => {
        choice.addEventListener('click',game,true);
    });
}

attachEvents();

function game(e){
    let index = -Infinity
    if(e.target.alt === 'rock') index = 0
    else if(e.target.alt === 'paper') index = 1
    else if(e.target.alt === 'scissor') index = 2
    choices[index].classList.add('active')
        choicesImage.forEach((others,indices) => {
            if(index !== indices) choices[indices].classList.remove('active')
        });
        playerResult.src = 'images/rock.svg'
        computerResult.src = 'images/rock.svg'
        container.classList.add('start')
        status.textContent = "Loading.."
        setTimeout(() => {
            container.classList.remove('start')
            let user = e.target.alt
            let computer= getComputerChoice()
            if(user === 'scissor'){
                playerResult.src = `images/${user}.png`
            }else{
                playerResult.src = `images/${user}.svg`
            }
            if(computer === 'scissor'){
                computerResult.src = `images/${computer}.png`
            }else{
                computerResult.src = `images/${computer}.svg`
            }
            let playerScore = document.querySelector('.playerScore')
            let computerScore = document.querySelector('.computerScore')
            if(user === 'rock' && computer === 'scissor' || user === 'paper' && computer === 'rock' || 
            user === 'scissor' && computer === 'paper'){
                status.textContent = `You win! ${user} beats  ${computer}`
                playerCurrentScore++
                playerScore.textContent = playerCurrentScore
                computerScore.textContent = computerCurrentScore
            }else if(user === computer){
                status.textContent = `Draw Match...`
                playerScore.textContent = playerCurrentScore
                computerScore.textContent = computerCurrentScore
            }else{
                status.textContent = `You Lose! ${computer} beats ${user}`
                computerCurrentScore++
                playerScore.textContent = playerCurrentScore
                computerScore.textContent = computerCurrentScore
            }
            declareResult()
        },500)
}
@import url('https://fonts.googleapis.com/css2?family=Poppins&display=swap');
:root{
    --gradient: linear-gradient(
        to right,
        #8B008Ba1,
        #800080b2,
        slateblue,
        darkslateblue
    );
    --bodyFont:'Poppins', sans-serif;
}

*{
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

body{
    background-image: var(--gradient);
    background-repeat: no-repeat;
    background-attachment: fixed;
    animation: bg-animation 20s ease infinite 2s alternate;
    background-size: 300%;
    font-size: 62.5%;
    font-family: var(--bodyFont);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    overflow-y: hidden;
}

@keyframes bg-animation{
    0%{
        background-position: left;
    }

    50%{
        background-position: right;
    }

    100%{
        background-position: left;
    }
}

.container{
    background-color: white;
    padding: 2.5rem;
    width: 90%;
    max-width: 500px;
    border-radius: 1.5rem;
    box-shadow: 2px 2px 30px 4px rgba(60, 103, 108, 0.593);
}

.container.start .choice{
    pointer-events: none;
}

.game-container{
    display: flex;
    justify-content: space-around;
}

.playerChoice,.computerChoice{
    width: 6rem;
    height: 6rem;
    transform: rotate(90deg);
}

.container.start .playerChoice{
    animation: userShake .5s ease infinite;
    
}

@keyframes userShake{
    0%{
        transform: rotate(85deg);
    }

    50%{
        transform: rotate(99deg);
    }
}

.container.start .computerChoice{
    animation: computerShake .5s ease infinite;
}


@keyframes computerShake{
    0%{
        transform: rotate(265deg) rotateY(175deg);
    }

    50%{
        transform: rotate(279deg) rotateY(190deg);
    }

}

.computerChoice{
    transform: rotate(270deg) rotateY(180deg);
}

.status{
    text-align: center;
    margin: 2.5rem auto;
    font-size: 1.5rem;
}

.choice-container{
    display: flex;
    justify-content: space-around;
    align-items: center;
}

.choice{
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    opacity: 0.5;
    transition: opacity 0.1s;
}

.choice:hover{
    cursor:pointer;
    opacity: 1;
}

.active{
    opacity: 1;
}

.choice img{
    width: 4rem;
    height: 4rem;
}

.desc{
    font-size: 1.2rem;
    font-weight: bold;
}

.score{
    display:flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
}

.player,.computer{
    font-size: 1.2rem;
    margin-top: 2.0rem;
}

.playerScore,.computerScore{
    font-size: 1.5rem;
    vertical-align: middle;
    font-weight: bold;
    margin-left: 1.0rem;
}

.container.new p{
    font-size: 35px;
    text-align: center;
}

.container.new button{
    display: block;
    width: 250px;
    height: 50px;
    border-radius: 10px;
    font-size: 15px;
    margin: auto;
}
<div class="container">
    <div class="game-container">
        <img src="images/rock.svg" alt="player" class="playerChoice">
        <img src="images/rock.svg" alt="computer" class="computerChoice">
    </div>
    <p class="status"></p>
    <div class="choice-container">
        <span class="choice">
            <img src="images/rock.svg" alt="rock">
            <p class="desc">Rock</p>
        </span>
        <span class="choice">
            <img src="images/paper.svg" alt="paper">
            <p class="desc">Paper</p>
        </span>
        <span class="choice">
            <img src="images/scissor.png" alt="scissor">
            <p class="desc">Scissor</p>
        </span>
    </div>
    <div class="results">
        <div class="score">
            <p class="player">Player Score: <span class="playerScore">0</span></p>
            <p class="computer">Computer Score: <span class="computerScore">0</span></p>
        </div>
        <p class="announce"></p>
    </div>
</div>

链接:https://ms-softwareengineer.github.io/odin-RPS-Game/

  • 我尝试删除并重新附加事件侦听器,但它似乎没有按预期工作。
  • 游戏 UI 元素确实已正确重置,但选项上的单击事件未重新附加。
  • 我检查了浏览器控制台是否有错误,但没有显示错误。
  • 我已经检查了 HTML 结构、CSS 类,并确保事件侦听器代码正在执行。

什么可能导致事件侦听器在单击“再次播放”后无法正确重新连接?是否有任何我可能忽略的潜在陷阱或常见错误?任何有关如何调试和解决此问题的见解或建议将不胜感激。

javascript html css addeventlistener event-listener
1个回答
0
投票

测试这个实际上让我受到 Stack Overflow 的速率限制,所以这会向服务器发出很多请求。

这里可能需要进行各种各样的改进。但特别是关于点击事件处理程序......

代码将替换此处的整个 HTML:

container.innerHTML = innerContent

但它更新任何这些变量:

const choicesImage = document.querySelectorAll('.choice img')

因此,即使当它尝试重新附加单击处理程序时,它也会将它们附加到过时的元素引用。您可以通过更新这一参考来测试这一点。首先使用

let
,以便可以重新分配:

let choicesImage = document.querySelectorAll('.choice img')

然后在这里重新分配:

function attachEvents(){
    choicesImage = document.querySelectorAll('.choice img');
    choicesImage.forEach((choice) => {
        choice.addEventListener('click',game,true);
    });
}

这些特定的点击事件将再次开始工作。

退一步看大局,我强烈建议根本不要替换 HTML。这似乎是一种以这种方式“重置”游戏的简单捷径,但它造成的问题比它解决的问题还要多。从长远来看,您可能会更好地返回并重新进行一些工作(甚至从头开始),以在页面上保留相同的 HTML,并仅显示/隐藏/修改与以下内容相同的整体标记:游戏状态发生变化。

© www.soinside.com 2019 - 2024. All rights reserved.