描述: 我正在使用 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/
什么可能导致事件侦听器在单击“再次播放”后无法正确重新连接?是否有任何我可能忽略的潜在陷阱或常见错误?任何有关如何调试和解决此问题的见解或建议将不胜感激。
测试这个实际上让我受到 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,并仅显示/隐藏/修改与以下内容相同的整体标记:游戏状态发生变化。