はじめに
Udemyの【最新ver対応済】モダンJavaScriptの基礎から始める挫折しないためのReact入門 を受講しながら、JacaScriptの基本を学んでいます。セクション5、「素のJavaScriptだけでToDoアプリを作成してみよう」で詰まってしまったことがあるのでそのエラーについて投稿します。
作ろうとしているもの
入力欄にテキストを入力すると未完了のTODOとして表示され、完了を押すと完了したTODOとなる画面
コード
Udemyの講座の課題のコード
<!DOCTYPE html>
<html>
<head>
<title>TODO(JS)</title>
<meta charset="UTF-8" />
</head>
<body>
<div class="input-area">
<input id="add-text" placeholder="TODOを入力" />
<button id="add-button">追加</button>
</div>
<div class="incomplete-area">
<p class="title">未完了のTODO</p>
<ul id="incomplete-list">
<!-- <li>
<div class="list-row">
<p class="todo-item">TODOです</p>
<button>完了</button>
<button>削除</button>
</div>
</li>
<li>
<div class="list-row">
<p class="todo-item">TODOです</p>
<button>完了</button>
<button>削除</button>
</div>
</li> -->
</ul>
</div>
<div class="complete-area">
<p class="title">完了したTODO</p>
<ul id="complete-list">
<!-- <li>
<div class="list-row">
<p class="todo-item">TODOでした</p>
<button>戻す</button>
</div>
</li> -->
</ul>
</div>
<script src="src/index.js"></script>
</body>
</html>
import "./styles.css";
const onClickAdd = () => {
// テキストボックスの値を取得し、初期化する
const inputText = document.getElementById("add-text").value;
document.getElementById("add-text").value = "";
// 未完了リストに追加
createIncompleteTodo(inputText);
};
// 渡された引数を基に未完了のTODOを作成する関数
const createIncompleteTodo = (todo) => {
// li生成
const li = document.createElement("li");
// div生成
const div = document.createElement("div");
div.className = "list-row";
// p生成
const p = document.createElement("p");
p.className = "todo-item";
p.innerText = todo;
// button(完了)タグ生成
const completeButton = document.createElement("button");
completeButton.innerText = "完了";
completeButton.addEventListener("click", () => {
// 押された削除ボタンの親にあるliタグ配下の完了ボタンと削除ボタンを削除
const moveTarget = completeButton.closest("li");
completeButton.nextElementSibling.remove();
completeButton.remove();
// 戻すボタンを生成してdivタグ配下に設定
const backButton = document.createElement("button");
backButton.innerText = "戻す";
backButton.addEventListener("click", () => {
// TODOの内容を取得し、未完了リストに追加
const todoText = backButton.previousElementSibling.innerText;
createIncompleteTodo(todoText);
// 押された戻すボタンの親にあるliタグを削除
backButton.closest("li").remove();
});
moveTarget.firstElementChild.appendChild(backButton);
// 完了リストに移動
document.getElementById("complete-list").appendChild(moveTarget);
});
// button(削除)タグ生成
const deleteButton = document.createElement("button");
deleteButton.innerText = "削除";
deleteButton.addEventListener("click", () => {
// 押された削除ボタンの親にあるliタグを未完了リストから削除
const deleteTarget = deleteButton.closest("li");
document.getElementById("incomplete-list").removeChild(deleteTarget);
});
// liタグの子要素に各要素を設定
div.appendChild(p);
div.appendChild(completeButton);
div.appendChild(deleteButton);
li.appendChild(div);
// 未完了リストに追加
document.getElementById("incomplete-list").appendChild(li);
};
document.getElementById("add-button").addEventListener("click", onClickAdd);
body {
font-family: sans-serif;
color: #666;
}
input {
border-radius: 8px;
border: none;
padding: 6px 16px;
}
button {
border-radius: 8px;
border: none;
padding: 4px 16px;
margin: 0px 2px;
}
button:hover {
background-color: #79a8a9;
color: #fff;
cursor: pointer;
}
.input-area {
background-color: #c6e5d9;
width: 400px;
height: 30px;
padding: 8px;
margin: 8px;
border-radius: 8px;
}
.incomplete-area {
border: 2px solid #aacfd0;
width: 400px;
min-height: 200px;
padding: 8px;
margin: 8px;
border-radius: 8px;
}
.complete-area {
border: 2px solid #aacfd0;
width: 400px;
min-height: 200px;
padding: 8px;
margin: 8px;
border-radius: 8px;
background-color: #c9dede;
}
.title {
text-align: center;
margin-top: 0;
font-weight: bold;
}
.list-row {
display: flex;
align-items: center;
}
.todo-item {
margin: 6px;
}
{
"name": "react-beginner-section5",
"version": "1.0.0",
"description": "",
"main": "index.html",
"scripts": {
"start": "parcel index.html",
"build": "parcel build index.html"
},
"dependencies": {
"parcel-bundler": "^1.6.1"
},
"devDependencies": {
"@babel/core": "7.2.0",
"typescript": "4.4.4"
},
"resolutions": {
"@babel/preset-env": "7.13.8"
},
"keywords": []
}
発生した問題
Udemyの講座を参考に、オンラインIDEのstackblitzで全く同じコードを再現したところ同様の挙動のプログラムを再現することができた。ローカルでも同じことをやってみようとしたところ、エラーで詰まってしまい、前に進まなくなってしまった。
ローカルで発生したエラー
CSSもJSも読み込まれず、HTMLしか表示されない。
解決方法
claudeに相談
・単純ミスを疑い、stackblitzからダウンロードしたコードの一部を自分で間違って操作した可能性が指摘される →きちんと動いているコードを再度DLしてローカルで開く →解決せず
・index.htmlから、CSSやJSファイルの参照を間違っている可能性が指摘される→チェック →問題なし
・node.jsが必要とか、npm installがうまくいっていないかもしれないと指摘→チェック、実行 →解決せず
Copilotに相談相手を変更
なかなか解決しないので、IDEをPycharmからVS codeにしてCopilot Chatに相談(Copilot Chatはpycharmでも使えるが、/workspace は今のところVS codeでしか使えず、これを使いたかったので)
Q.@workspace index.html,styles.css,index.jpを調べて問題ないか教えてください。特に相対パスが間違っていないか
Q.index.htmlを実行してもjsとcssが読み込まれていない様で、色や挙動がchromeで見ることができません
→cssをHTMLから読み込む様に指示(変更前はJSを読んで、JSからCSSをインポートしていたが、変えたほうがいいらしい)
→1は問題ないことは確認できていたので、chromeの開発者ツールでコンソールを確認しにいいく
Q.Uncaught SyntaxError: Cannot use import statement outside a module というエラーがでています。
→StackBlitzとJSの読み込み方の記述が違うのか?よくわからないけど言われた通りにコードを変更。
→まだエラーがコンソールにでる
Q.Access to script at 'file:///<自分のファイルパス>/udemy/index.js' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, isolated-app, chrome-extension, chrome, https, chrome-untrusted. index.html:44
→まだ、コンソールでエラー発生
→引き続きエラーがでる<中略>
→解決!!
おわりに
まだ学習の始めなのでいまいちよくわかっていないのですが
・index.htmlから直接CSSを読み込む
・jsファイルを読み込むscriptタグを修正
・ブラウザで普通に表示するだけだとコンソールでエラーになるのでローカルサーバーを立ち上げて表示して解決。
なんか遠回りしているだけな気がするのでJSの理解を深めてこの手のエラーを早く解決できるようにしたいと思います。
フロントエンド初挑戦の身からすると、何が原因でどこでエラーがおきているのかPythonでスクリプト書いているときよりも、原因が分かりづらい(コードの問題なのか、依存関係なのか)ので、さっそく???になりました。
複数のファイルを見てエラーの原因がないか探るのに、/workspaceはめちゃ便利なので、JSの開発はVS codeでやっていこうと思います。