質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

1回答

1785閲覧

【JavaScript】addEventListenerが重複して実行されるのを解決したいです。

akasira901

総合スコア12

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2019/07/08 12:12

編集2019/07/08 12:16

前提・実現したいこと

JavaScriptで、主人公(勇者)が一歩歩くごとに敵とのエンカウント判定を行い、エンカウントしたらたたかうボタンとにげるボタンが押せるようになり、それぞれのボタンを押した場合、敵によって一定の確率で勝てる、または逃げられるようになるという軽いゲーム(?)のようなものを実装しています。

それぞれのボタンをクリックすると一回だけ発火するようにしたいです。

発生している問題・エラーメッセージ

たたかうボタン(battle)と逃げるボタン(flee)をそれぞれaddEventListenerのclickで発火するようにコーディングしたのですが、例えば、たたかうボタンをクリックすると、1回目は1回の勝敗判定しかなされなくて正しいのですが、2,3,...n回目の戦闘でボタンを押すと2,3,...n回の勝敗判定がなされてしまっていました。
にげるボタンも同様でした。
この問題に関しては、addEventListenerの第三引数をonce:trueにしたところ、解決しました。

しかし、例えばにげるボタンをクリックした後の戦闘でたたかうボタンを押すと、上述したように2,3,...回と重複して判定が行われてしまいます。
この問題を解決したいです。

エラーメッセージ

該当のソースコード

HTML

1<!DOCTYPE html> 2<html lang="ja"> 3<head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 7 <link rel="stylesheet" href="./css/es6.css"> 8 <title>ES6</title> 9</head> 10<body> 11 <div id="main"> 12 <div id="box1"> 13 <div id="displayEnemy"> 14 <img id="enemyImg" src="" alt=""> 15 </div> 16 <div id="displayYusha"> 17 <button class="direction">前</button><br> 18 <button class="direction">左</button> 19 <img src="./images/yusya.png" alt=""> 20 <button class="direction">右</button><br> 21 <button class="direction">後</button> 22 </div> 23 <div id="resultWrapper"> 24 <div id="commands"> 25 <button id="battle">たたかう</button><br> 26 <button id="flee">にげる</button> 27 <p>ステージ: <span id="stage">平地</span></p> 28 </div> 29 <div id="messageWrapper"> 30 <p id="message">歩く方向を押してください</p> 31 </div> 32 </div> 33 </div> 34 <div id="box2"> 35 <p>LV: <span id="level"></span></p><br> 36 <p>[最終到達地点]</p> 37 <p>歩数Y: <span id="ymax"></span></p> 38 <p>歩数X: <span id="xmax"></span></p><br> 39 <p>倒した敵の数: <span id="dieNum"></span></p><br> 40 <p>敵出現平均歩数: <span id="enemyAv"></span></p><br> 41 <p>[敵出現ログ]</p> 42 <div id="enemyLog"> 43 </div> 44 <div id="buttonWrapper"> 45 <button id="start">スタート</button> 46 </div> 47 </div> 48 </div> 49 50 <script type="text/javascript" src="./js/es6.js"></script> 51</body> 52</html>

JavaScript

1const enemyImg=document.getElementById("enemyImg"); 2const direction=document.getElementsByClassName("direction"); 3const battle=document.getElementById("battle"); 4const flee=document.getElementById("flee"); 5const stage=document.getElementById("stage"); 6const message=document.getElementById("message"); 7const level=document.getElementById("level"); 8const ymax=document.getElementById("ymax"); 9const xmax=document.getElementById("xmax"); 10const dieNum=document.getElementById("dieNum"); 11const enemyAv=document.getElementById("enemyAv"); 12const enemyLog=document.getElementById("enemyLog"); 13const start=document.getElementById("start"); 14const enemyImages=new Array("./images/enemy3.png","./images/enemy2.jpg","./images/enemy1.jpg","./images/enemydie.jpg"); 15let countEnemyDie=0;//倒した敵の数 16let steps= new Array("mystepf","mystepl","mystepr","mystepb");//クラス作成に用いる 17let countAll=0;//歩いた歩数のカウント 18let tmp1=0; //countAllを一時的に格納 19let tmp2=0; //敵出現歩数 20let tmp3=0; //敵出現歩数の総数 21let countEnc=0; //遭遇回数のカウント 22let countf=0; //前のカウント 23let countl=0; //左のカウント 24let countr=0; //右のカウント 25let countb=0; //後のカウント 26let countDirection=new Array(4); //各方向の移動距離を格納(前、左、右、後) 27let i=0; 28let j=0; 29for(i=0;i<countDirection.length;++i){ //配列の初期化 30 countDirection[i]=0; 31} 32battle.disabled=true; 33flee.disabled=true; 34 35class Yusha { 36 constructor(lev){ 37 this.lev=lev; 38 } 39 set mylev(valuelev){ 40 this.lev=valuelev; 41 } 42 get mylev(){ 43 return this.lev; 44 } 45} 46 47class Enemy{ 48 constructor(win,flee,winflee){ 49 this.win=win; 50 this.flee=flee; 51 this.winflee=winflee; 52 } 53 get mywin(){ 54 return this.win; 55 } 56 get myflee(){ 57 return this.flee; 58 } 59 get mywinflee(){ 60 return this.winflee; 61 } 62} 63 64let yusha = new Yusha(1); 65level.innerHTML=yusha.mylev; //初期値の出力 66ymax.innerHTML=0; //初期値の出力 67xmax.innerHTML=0; //初期値の出力 68dieNum.innerHTML=countEnemyDie; //初期値の出力 69 70class Green extends Enemy{ 71 constructor(){ 72 super(19+yusha.mylev,19+yusha.mylev,19+yusha.mylev); 73 } 74} 75class Blue extends Enemy{ 76 constructor(){ 77 super(18+yusha.mylev,18+yusha.mylev,18+yusha.mylev); 78 } 79} 80class Red extends Enemy{ 81 constructor(){ 82 super(17+yusha.mylev,17+yusha.mylev,17+yusha.mylev); 83 } 84} 85let green = new Green(); 86let blue = new Blue(); 87let red = new Red(); 88 89const isBattle = (yushaWin,yushaFlee,yushaFleeWin)=>{ 90 battle.addEventListener("click",()=> { 91 let encNum3 = Math.floor(Math.random()*(21+(yusha.mylev)-1)+1);//敵に勝つかの乱数(1~20+勇者のレベル) 92 console.log(encNum3); 93 if((encNum3>=1)&&(encNum3<=yushaWin)){ //勝ち 94 enemyImg.src=enemyImages[enemyImages.length-1]; 95 message.innerHTML="勝った!<br>歩く方向を押してください"; 96 for(j=0;j<direction.length;++j){ 97 direction[j].disabled=false; 98 } 99 battle.disabled=true; 100 flee.disabled=true; 101 ++countEnemyDie; 102 dieNum.innerHTML=countEnemyDie; 103 if(countEnemyDie%10===0){ //10匹倒すごとにレベルアップ 104 ++yusha.mylev; 105 level.innerHTML=yusha.mylev; 106 } 107 } 108 else{ //負け 109 message.innerHTML="勇者は死んでしまった"; 110 for(j=0;j<direction.length;++j){ 111 direction[j].disabled=true; 112 } 113 battle.disabled=true; 114 flee.disabled=true; 115 return; 116 } 117 },{once: true}); 118 flee.addEventListener("click",()=>{ 119 let encNum4 = Math.floor(Math.random()*(21+(yusha.mylev)-1)+1);//敵から逃げられるかの乱数 120 console.log(encNum4); 121 if((encNum4>=1)&&(encNum4<=yushaFlee)){ 122 enemyImg.src=""; 123 message.innerHTML="逃げた!<br>歩く方向を押してください"; 124 for(j=0;j<direction.length;++j){ 125 direction[j].disabled=false; 126 } 127 battle.disabled=true; 128 flee.disabled=true; 129 } 130 else{ 131 let encNum5 = Math.floor(Math.random()*(21+(yusha.mylev)-1)+1);//敵に勝つかの乱数 132 if((encNum5>=1)&&(encNum5<=yushaFleeWin)){ 133 message.innerHTML="逃げられない!"; 134 for(j=0;j<direction.length;++j){ 135 direction[j].disabled=true; 136 } 137 } 138 else{ 139 message.innerHTML="勇者は死んでしまった"; 140 for(j=0;j<direction.length;++j){ 141 direction[j].disabled=true; 142 } 143 battle.disabled=true; 144 flee.disabled=true; 145 return; 146 } 147 } 148 },{once: true}); 149} 150 151const isEnemy = (enemyImage,enemyName) =>{ 152 enemyImg.src=enemyImage; 153 message.innerHTML="敵が現れた!"; 154 for(j=0;j<direction.length;++j){ 155 direction[j].disabled=true; 156 } 157 battle.disabled=false; //たたかう解禁 158 flee.disabled=false; //にげる解禁 159 isBattle(enemyName.mywin,enemyName.myflee,enemyName.mywinflee); 160 return; 161} 162 163 164const isEnc = (stageName,pEnc) =>{ 165 stage.innerHTML= stageName; 166 let encNum1 = Math.floor(Math.random()*(4-1)+1); //遭遇するかしないかの乱数 167 if(encNum1<=pEnc){ //遭遇 168 ++countEnc; 169 tmp2=countAll-tmp1; 170 tmp3+=tmp2; 171 enemyAv.innerHTML=tmp3/countEnc+"歩"; 172 enemyLog.insertAdjacentHTML("beforeend", `<p>${countAll}歩目</p>`); 173 tmp1=countAll; 174 let encNum2 = Math.floor(Math.random()*(11-1)+1);//どの敵に遭遇するかの乱数 175 if((encNum2>=1)&&(encNum2<=6)){ //緑モンスターに遭遇 176 isEnemy(enemyImages[0],green); 177 return; 178 } 179 else if((encNum2>=7)&&(encNum2<=9)){ //青モンスターに遭遇 180 isEnemy(enemyImages[1],blue); 181 return; 182 } 183 else{ //赤モンスターに遭遇 184 isEnemy(enemyImages[2],red); 185 return; 186 } 187 } 188 else{ 189 enemyImg.src=""; 190 message.innerHTML="歩く方向を押してください" 191 } 192} 193let tmpy=0;//countf-countbを一時的に格納 194let tmpx=0;//countr-countlを一時的に格納 195let isStep = (direct,countDirect) =>{ 196 direct.addEventListener("click", ()=> { 197 ++countAll; 198 switch(direct){ //各方向の歩いた歩数のカウント 199 case direction[0]: 200 ++countf; 201 break; 202 case direction[1]: 203 ++countl; 204 break; 205 case direction[2]: 206 ++countr; 207 break; 208 case direction[3]: 209 ++countb; 210 break; 211 default: 212 break; 213 } 214 if(Math.abs(tmpy)<Math.abs(countf-countb)){ //最終到達点の表示 215 ymax.innerHTML=countf-countb; 216 tmpy=countf-countb; 217 } 218 if(Math.abs(tmpx)<Math.abs(countr-countl)){ //最終到達点の表示 219 xmax.innerHTML=countr-countl; 220 tmpx=countr-countl; 221 } 222 if(((countf-countb>=-10)&&(countf-countb<=10))&&((countr-countl>=-10)&&(countr-countl<=10))){ //平地 223 isEnc("平地",1); 224 } 225 else if((countr-countl>=0)){ //森 226 isEnc("森",2); 227 } 228 else{ //沼 229 isEnc("沼",3); 230 } 231 },false); 232} 233 234isStep(direction[0],countDirection[0]); 235isStep(direction[1],countDirection[1]); 236isStep(direction[2],countDirection[2]); 237isStep(direction[3],countDirection[3]);

試したこと

addEventListener内の関数を名前をつけて定義して、removeEventListnerを用いたりとremoveEventListnerを用いてごちゃごちゃやりました。
また、e.stopPropagation()も試しました。

計10時間くらい費やしましたが、上手くいきませんでした。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yasutomi

2019/07/08 13:22

このプログラムにはバグがあって「たたかう」と「にげる」ボタンを押せないため バグを修正してから再度質問をお願いします。
guest

回答1

0

フローをすっきりさせパートを関数化させればよいのでは
(元ソース全く参考にしてません、あしからず)

javascript

1<script> 2window.addEventListener('DOMContentLoaded', function(e){ 3 document.addEventListener('click',function(e){ 4 var t=e.target; 5 if(t.id=="aruku") walk(); 6 if(t.id=="nigeru") escape(); 7 if(t.id=="tatakau") battle(); 8 judge(); 9 }); 10}); 11function walk(){ 12 var hosuu=parseInt(document.querySelector('#hosuu').textContent); 13 hosuu++; 14 document.querySelector('#hosuu').textContent=hosuu; 15 var sogu=Math.random(); 16 if(sogu>0.8){ 17 document.querySelector('#aruku').disabled=true; 18 document.querySelector('#tatakau').disabled=false; 19 document.querySelector('#nigeru').disabled=false; 20 document.querySelector('#sento').textContent="敵が現れた"; 21 } 22} 23function escape(){ 24 document.querySelector('#sento').textContent="逃げた"; 25 var hp=parseInt(document.querySelector('#hp').textContent); 26 hp-=5; 27 if(hp<0) hp=0; 28 document.querySelector('#hp').textContent=hp; 29 document.querySelector('#aruku').disabled=false; 30 document.querySelector('#tatakau').disabled=true; 31 document.querySelector('#nigeru').disabled=true; 32} 33function battle(){ 34 var flg=Math.random(); 35 if(flg>0.8){ 36 document.querySelector('#sento').textContent="勝った"; 37 document.querySelector('#aruku').disabled=false; 38 document.querySelector('#tatakau').disabled=true; 39 document.querySelector('#nigeru').disabled=true; 40 }else{ 41 document.querySelector('#sento').textContent="倒せない"; 42 var hp=parseInt(document.querySelector('#hp').textContent); 43 hp-=10; 44 if(hp<0) hp=0; 45 document.querySelector('#hp').textContent=hp; 46 } 47} 48 49function judge(){ 50 var hp=parseInt(document.querySelector('#hp').textContent); 51 if(hp<=0){ 52 document.querySelector('#sento').textContent="死んでしまった gameover"; 53 document.querySelector('#tatakau').disabled=true; 54 document.querySelector('#nigeru').disabled=true; 55 } 56} 57</script> 58<input type="button" value="あるく" id="aruku"> 59<input type="button" value="たたかう" id="tatakau" disabled> 60<input type="button" value="にげる" id="nigeru" disabled> 61<div id="view"> 62<div>HP:<span id="hp">100</span></div> 63<div>歩数:<span id="hosuu">0</span></div> 64<div>バトル:<span id="sento"></span></div> 65</div>

投稿2019/07/09 01:01

yambejp

総合スコア114833

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問