はじめに
こんにちは!前回は、文中にあるワードをクリックしたら、そのワードの記事を生成する機能を追加しました。前の記事からかなり時間が経過してしまいましたね・・・。自分でコードを見返したら「これなんで動いてんの?」状態になっちゃってました笑
Qiitaに自分である程度解説書いておいてよかった~
というわけで、今回は、「スタートとなるワード」と「ゴールとなるワード」を取得し、ゴールに辿りついたら「ゴール!」みたいなのを表示させるようにしましょう。
前回の記事はこちらです👇
作業開始
1.スタートとなるワードを取得する
wikipediaに収納されている記事のタイトルをランダムに取得し、それをスタートにしましょう。
では、その関数を作ります。
def select_word():
import requests
#wikipediaからランダムに記事を受け取るAPI
S = requests.Session()
URL = "https://ja.wikipedia.org/w/api.php"
PARAMS = {
"action": "query",
"format": "json",
"list": "random",
"rnlimit": "1",
"rnnamespace": "0",
}
#要求したデータを受け取る
R = S.get(url=URL, params=PARAMS)
DATA = R.json()
#データをランダムに受け取る
RANDOMS = DATA["query"]["random"]
selected_word = RANDOMS[0]["title"]
return selected_word
この関数を使えばwikipediaからランダムな記事のタイトルを取得できます。
WikipediaのAPIについては#1👇で詳しく解説しているので割愛します。
select_first_word()で生成したselected_wordを使って、スタートのページを表示させることができます。
2.ゴールとなるワードを取得する
こちらも、スタートとなるワードを取得した時と全く同じようにすればいいのですが、ゴールをwikipediaにあるすべての記事を対象にしてしまうと、めちゃくちゃマイナーな用語の記事が出てきて、ゲームが難しすぎるので、タイトル画面でモード選択できるようにします。#タイトル画面
@csrf_exempt
def title(request):
#モード選択用
if 'checkbox' in request.POST:
#チェックボックスのデータを受け取る
checkedbox = request.POST.get('checkbox')
#モードに応じて目標の文字を受け取る
if checkedbox == "1":
#別モード
#今回は実装しない
elif checkedbox == "2":
#wikiモード
goal_word = select_word()
else:
print("エラーです")
# セッションにgoal_wordを保存
request.session['goal_word'] = goal_word
return render(request, 'main/title.html')
// スタートボタン
function submitForm(event) {
event.preventDefault(); // デフォルトのフォーム送信をキャンセル
var form = document.getElementById("start");
var formData = new FormData(form);
// チェックボックスの値を取得
var checkbox = document.querySelector('input[name="checkbox"]:checked');
var checkboxValue = checkbox ? checkbox.value : null
//チェックボックスの値が取得されなかったら
if (checkboxValue === null) {
console.error('チェックボックスが選択されていません');
alert('ゲームモードを選択してください');
return;
}
//formDataにチェックボックスの値を追加
formData.append('checkbox', checkboxValue);
// フォームデータを送信
fetch(form.action, {
method: form.method,
body: formData,
}).then(function(response) {
// ページ遷移
window.location.href = "/maingame/";
console.log("ページ遷移・・・完了")
}).catch(function(error) {
console.error('エラー:', error);
});
}
// 排他的モード選択
function toggleCheckboxes(clickedCheckbox) {
var checkboxes = document.getElementsByName("checkbox");
checkboxes.forEach(function(checkbox) {
if (checkbox !== clickedCheckbox) {
checkbox.checked = false;
}
});
}
{% extends "main/base.html" %}
{% load static %}
{% block header %}
<link rel="stylesheet" type="text/css" href="{% static 'css/title.css' %}">
<script src="{% static 'js/title.js' %}"></script>
{% endblock %}
{% block content %}
<div class="title_panel">
<form id="start" action="" method="POST">
<div class="start_button btn-shine">
<a href="#" onclick="submitForm(event)">ゲーム開始</a>
</div>
<label>
<input type="checkbox" id="countrymode" name="checkbox" value="1" onclick="toggleCheckboxes(this)">別モード
</label>
<label>
<input type="checkbox" id="wikimode" name="checkbox" value="2" onclick="toggleCheckboxes(this)">wikiモード
</label>
</form>
</div>
{% endblock %}
まず、javascriptとhtmlの方で、モード選択の情報を受け取るチェックボックスを作成します。
//htmlから「start」のIDのformを受け取る
var form = document.getElementById("start");
//formのデータをformDataとして宣言する
var formData = new FormData(form);
// チェックされたチェックボックスの値を追加する。
// 今回はhtmlでそれぞれのvalueに1や2を設定しているので、それを登録する。
var checkbox = document.querySelector('input[name="checkbox"]:checked');
var checkboxValue = checkbox ? checkbox.value : null
//チェックボックスの値が取得されなかったらエラーメッセージを表示する
if (checkboxValue === null) {
console.error('チェックボックスが選択されていません');
alert('ゲームモードを選択してください');
return;
}
//formDataに値を追加
formData.append('checkbox', checkboxValue);
この時、チェックボックスが複数選択できてしまうと、モード選択ができないので、1つ選択したら、それ以外の選択を無効化する関数を作ります。
// 排他的モード選択
function toggleCheckboxes(clickedCheckbox) {
//htmlでname属性がcheckboxと定義されている要素を取得する。
var checkboxes = document.getElementsByName("checkbox");
//各チェックボックスを調査し、clickedCheckbox(クリックしたチェックボックス)でなければ、そのチェックボックスをfalseにする。
checkboxes.forEach(function(checkbox) {
if (checkbox !== clickedCheckbox) {
checkbox.checked = false;
}
});
}
javascriptはこんな感じです。
次はpython側の処理です。
受け取ったモードデータに基づいてゴールのワードを設定します。
チェックされたチェックボックスの値に応じて、ゴールに設定するワードを決めます。
今回は、wikipedia内にあるランダムなワードを取得します。
#モード選択用
if 'checkbox' in request.POST:
#チェックボックスのデータを受け取る
checkedbox = request.POST.get('checkbox')
#モードに応じて目標の文字を受け取る
if checkedbox == "1":
#別モード
#今回は実装しない
elif checkedbox == "2":
#wikiモード
goal_word = select_word()
else:
print("エラーです")
さて、ここで決定したゴールのワードをメインのhtmlで表示したいので、メイン画面を定義しているmain()に送ります。
ただ、main()でtitle()を呼び出すわけにはいかないので、間接的にデータを送らなければなりません。
これを実現するには、request.session
というDjangoフレームワーク内でセッション管理を行うためのオブジェクトを利用して、無理やり送ります。
もっとスマートなやり方を知っている方は是非教えてください・・・。
# セッションにgoal_wordを保存
request.session['goal_word'] = goal_word
これでmain()でtitle()で定義したゴールのワードを受け取ることができます。
#3で作成したmain()にゴールのワードを受け取って、htmlに渡せるようにしましょう。
#メインの画面
@csrf_exempt
def main(request):
#ワードクリック用
if 'word' in request.POST:
#ここは変化なし
#スタートゲーム用
else:
# セッションからgoal_wordを取得
goal_word = request.session.get('goal_word')#←追加
#省略
#main.htmlに情報を返す
return render(request, 'main/main.html', {
'page_text': page_text,
'page_title': page_title,
'res': res,
'goal_word':goal_word,#←追加
})
{% extends "main/base.html" %}
{% load static %}
{% block header %}
<title>Result</title>
<script src="{% static 'js/main.js' %}"></script>
{% endblock %}
{% block content %}
<!-- ここにゴールのワードを表示させる -->
<div id="goal-word" data-goal-word="{{ goal_word }}">
{{ goal_word }}
</div>
<div class="main-text" id="main-text">
<h1>{{ page_title }}</h1>
<p>{{ res|safe }}</p>
</div>
{% endblock %}
これで無事、メインの画面にゴールのワードを表示させられるようになりました。
3.ゴールの処理
最後に、ゴールのワードにたどり着いたら、何らかのアクションが発生するようにしておきましょう。
今回は、表示されている記事のタイトルとゴールのワードが一致した時に「ゴール!」と画面上に表示させましょう。
まず、一致の判断をjavascriptでさせるためにjavascriptでゴールのワードを受け取ります。
{{ goal_word }}
の中にある、正しいゴールのワードを取得するには、htmlでdata-goal-word
に{{ goal_word }}
の中のデータを入れることで、受け取ることができるようになります。
// IDがgoal-wordで、data-goal-wordのデータを受け取る
let goalWord = document.getElementById('goal-word').getAttribute('data-goal-word');
ここでデータを入れたgoalWord
と、ページを作るときにviews.pyから受け取るpage_title
を比較して、一致したらhtmlに「ゴール!」と書き込みます。
//省略
.then(data => {
let goalWord = document.getElementById('goal-word').getAttribute('data-goal-word');
// 受け取ったデータを使用してHTMLを再構築する
let html = '';
html += `
<h1>${data.page_title}</h1>
<p>${data.res}</p>
`;
//下のif文を追加
//ゴールにたどり着いたとき
if (goalWord === data.page_title) {
html += "<p>正しい</p>";
}
document.getElementById('main-text').innerHTML = html;
})
すごくシンプルですが、一応ゴールした時に何らかのアクションを起こすことができました。
おわりに
以上、「スタートとなるワード」と「ゴールとなるワード」を取得し、ゴールに辿りついたら「ゴール!」と表示させる所まで作成しました。
一応、ゲームとして遊べるようにはなりましたね!
ちょっと時間を空けてしまったので、説明が難しかったな・・・。
もし分かりにくいところや、改善点があれば、是非教えてください!!
次回は、まだモードが一個のままなので、新しいモードを追加していきたいと思います。