まるばつゲームを作成したいが、JavaScriptが読み込めない。
解決したいこと
Javascriptでまるばつゲームを作成中です。
コードは一通り記述したのですが、肝心の Javascriptが読み込めずゲームが作動しません。
現状は下の画像の通りです。
発生している問題・エラー(検証ツールのコンソールにて)
Uncaught TypeError: Cannot read property 'addEventListener' of null
at Object../app/javascript/app.js (app.js:95)
at __webpack_require__ (bootstrap:19)
at Object../app/javascript/packs/application.js (application.js:10)
at __webpack_require__ (bootstrap:19)
at bootstrap:83
at bootstrap:83
GET http://localhost:3000/app.js net::ERR_ABORTED 404 (Not Found) localhost/:71
GET http://localhost:3000/app.css net::ERR_ABORTED 404 (Not Found) localhost/:21
GET http://localhost:3000/app.js net::ERR_ABORTED 404 (Not Found) localhost/:71
app/assets/stylesheets/app.css
html {
box-sizing: border-box;
font-size: 16px;
}
*, *::before, *::after {
box-sizing: border-box;
}
li {
list-style: none;
}
body {
margin: 0;
line-height: normal;
}
h1 {
margin: 20px 0;
font-size: 2rem;
font-weight: bold;
}
.wrapper {
max-width: 1024px;
margin: 0 auto;
padding: 0 10px;
text-align: center;
}
.game-container {
padding: 60px 0;
}
.message-container {
margin-bottom: 20px;
font-size: 1.25rem;
font-weight: bold;
}
.batsu {
color: #00b0f0;
}
.maru {
color: #ff0000;
}
.js-hidden {
display: none;
}
.squares-container {
margin: 0 auto;
width: 202px;
}
.squares-box {
width: 202px;
height: 202px;
display: flex;
flex-wrap: wrap;
border: solid 2px #333;
}
.square {
position: relative;
width: calc(198px / 3);
height: calc(198px / 3);
border: solid 2px #333;
}
.js-maru-checked::before {
content: '';
width: 50px;
height: 50px;
border: solid 8px #ff0000;
border-radius: 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.js-batsu-checked::before {
content: '';
width: 50px;
height: 8px;
margin: auto;
background-color: #00b0f0;
border-radius: 4px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) rotate(45deg);
}
.js-batsu-checked::after {
content: '';
width: 8px;
height: 50px;
margin: auto;
background-color: #00b0f0;
border-radius: 4px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) rotate(45deg);
}
.btn-container {
padding-top: 40px;
}
.btn {
display: inline-block;
width: 150px;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
}
.btn-reset {
color: #fff;
background-color: #ffc000;
font-weight: bold;
}
.btn-reset:hover {
background-color: #ffd347;
transition-duration: 0.4s;
}
/* JS連動 */
.js-unclickable {
pointer-events: none;
}
.js-highLight {
background-color: #fff2cc;
}
app/views/layouts/index.html.erb
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>○×ゲーム</title>
<link href="app.css" rel="stylesheet" />
</head>
<body>
<div class="wrapper">
<div class="game-container">
<div class="message-container">
<ul class="message-list">
<li id="maru-turn" class="js-hidden">
<span class="maru">○</span>の番
</li>
<li id="batsu-turn">
<span class="batsu">×</span>の番
</li>
<li id="maru-win" class="js-hidden">
<span class="maru">○</span>の勝ち!
</li>
<li id="batsu-win" class="js-hidden">
<span class="batsu">×</span>の勝ち!
</li>
<li id="draw" class="js-hidden">
引き分け
</li>
</ul>
</div>
<div class="squares-container">
<div class="squares-box">
<!-- 1行め -->
<div id="1-1" class="square"></div>
<div id="1-2" class="square"></div>
<div id="1-3" class="square"></div>
<!-- 2行め -->
<div id="2-1" class="square"></div>
<div id="2-2" class="square"></div>
<div id="2-3" class="square"></div>
<!-- 3行め -->
<div id="3-1" class="square"></div>
<div id="3-2" class="square"></div>
<div id="3-3" class="square"></div>
</div>
</div>
<div class="btn-container">
<div class="js-hidden" id="reset-btn">
<span class="btn btn-reset">もう一回遊ぶ</span>
</div>
</div>
</div>
</div>
<script src="app.js"></script>
</body>
</html>
app/javascript/app.js
let flag = false;
let counter = 9;
let winningLine = null;
const squares = document.querySelectorAll('.square');
const squaresArray = [].slice.call(squares); // IE11対策
const messages = document.querySelectorAll('.message-list li');
const messagesArray = [].slice.call(messages); // IE11対策
const resetBtn = document.querySelector('#reset-btn');
// メッセージの切り替え関数
const setMessage = (id) => {
messagesArray.forEach((message) => {
if (message.id === id) {
message.classList.remove('js-hidden');
} else {
message.classList.add('js-hidden');
}
});
}
// 勝利判定のパターン関数
const filterById = (targetArray, idArray) => {
return targetArray.filter((e) => {
return (e.id === idArray[0] || e.id === idArray[1] || e.id === idArray[2]);
});
}
// 勝利判定パターン
const line1 = filterById(squaresArray, ['1-1', '1-2', '1-3']);
const line2 = filterById(squaresArray, ['2-1', '2-2', '2-3']);
const line3 = filterById(squaresArray, ['3-1', '3-2', '3-3']);
const line4 = filterById(squaresArray, ['1-1', '2-1', '3-1']);
const line5 = filterById(squaresArray, ['1-2', '2-2', '3-2']);
const line6 = filterById(squaresArray, ['1-3', '2-3', '3-3']);
const line7 = filterById(squaresArray, ['1-1', '2-2', '3-3']);
const line8 = filterById(squaresArray, ['1-3', '2-2', '3-1']);
const lineArray = [line1, line2, line3, line4, line5, line6, line7, line8];
// 勝利判定関数
const isWinner = (symbol) => {
// some: 1つでも条件を満たしていればTrueを返す
const result = lineArray.some((line) => {
// every: 全て条件を満たしていればTrueを返す
const subResult = line.every((square) => {
if (symbol === 'maru') {
return square.classList.contains('js-maru-checked');
} else
if (symbol === 'batsu') {
return square.classList.contains('js-batsu-checked');
}
});
if (subResult) { winningLine = line }
return subResult;
});
return result;
}
// ゲーム終了時の関数
const gameOver = () => {
// 全てのマスをクリック不可にする
squaresArray.forEach((square) => {
square.classList.add('js-unclickable');
});
// 勝った時のマス目をハイライトする
if (winningLine) {
winningLine.forEach((square) => {
square.classList.add('js-highLight');
});
}
// リセットボタン表示
resetBtn.classList.remove('js-hidden');
}
// ゲームの初期化の関数
const initGame = () => {
flag = false;
counter = 9;
winningLine = null;
squaresArray.forEach((square) => {
square.classList.remove('js-maru-checked');
square.classList.remove('js-batsu-checked');
square.classList.remove('js-unclickable');
square.classList.remove('js-highLight');
});
setMessage('batsu-turn');
resetBtn.classList.add('js-hidden');
}
resetBtn.addEventListener('click', function() {
initGame();
});
// マスをクリックした時のイベント発火
squaresArray.forEach((square) => {
square.addEventListener('click', () => {
if (flag === true) {
square.classList.add('js-maru-checked');
square.classList.add('js-unclickable');
if (isWinner('maru')) {
setMessage('maru-win');
gameOver();
return;
}
setMessage('batsu-turn');
flag = false;
} else {
square.classList.add('js-batsu-checked');
square.classList.add('js-unclickable');
if (isWinner('batsu')) {
setMessage('batsu-win');
gameOver();
return;
}
setMessage('maru-turn');
flag = true;
}
counter--;
// 引き分け判定
if (counter === 0) {
setMessage('draw');
gameOver();
}
});
});
app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>Marubatugame</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
<body>
<%= yield %>
</body>
</html>
app/javascript/packs/application.js
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.
require("@rails/ujs").start()
// require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
require("../app")
// Uncomment to copy all static images under ../images to the output folder and reference
// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
// or the `imagePath` JavaScript helper below.
//
// const images = require.context('../images', true)
// const imagePath = (name) => images(name, true)
自分で試したこと
『Rails6でJavaScriptを使う方法』や、『JavaScript 読み込めない』等、検索してその記事通り記述を編集しても
解決にはいたりませんでした。
未熟者で手詰まりなので教えていただけると助かります。
0