はじめに
- これまで、HTMLとCSSを使って簡単なWebページを構築する方法、GitとGitHubを使ってコードの変更を追跡し、他の人と共同作業するための方法について説明してきました。また、Pythonプログラミング言語に慣れ、Djangoを使用してWebアプリケーションを作成し始め、Djangoモデルを使用してサイトに情報を保存する方法を学びました。次にJavaScriptを紹介し、それを使ってWebページをよりインタラクティブにする方法を学びました。
- 本日は、JavaScriptとCSSを使用してサイトをさらに使いやすくするユーザインターフェース設計の共通規則について説明します。
ユーザインターフェース
ユーザインターフェイスは、Webページへの訪問者がそのページと対話する方法です。Web開発者としての私たちの目標は、これらのインタラクションをユーザにとってできるだけ快適なものにすることであり、多くの手法があります。
シングルページアプリケーション
以前は、複数のページを持つWebサイトが必要な場合は、Djangoアプリケーションで異なるルートを使用して実現していましたが、今回から、1つのページのみをロードし、JavaScriptを使用してDOMを操作できるようになりました。これを行う主な利点の1つは、実際に変更されるページの部分のみを変更する必要があることです。たとえば、現在のページに基づいて変更されないナビゲーションバーがある場合、ページの新しい部分に切り替えるたびにそのナビゲーションバーをレンダリングし直す必要はありません。
JavaScriptでページ切り替えをシミュレートする例を見てみましょう。
<!DOCTYPE html>
<html lang="en">
<head>
<title>Single Page</title>
<style>
div {
display: none;
}
</style>
<script src="singlepage.js"></script>
</head>
<body>
<button data-page="page1">Page 1</button>
<button data-page="page2">Page 2</button>
<button data-page="page3">Page 3</button>
<div id="page1">
<h1>This is page 1</h1>
</div>
<div id="page2">
<h1>This is page 2</h1>
</div>
<div id="page3">
<h1>This is page 3</h1>
</div>
</body>
</html>
上記のHTMLには、3つのボタンと3つのdivがあることに注意してください。現時点では、divにはほんの少しのテキストしか含まれていませんが、各divには、このサイトの1ページのコンテンツが含まれていると考えることができます。次に、ボタンを使用してページを切り替えるJavaScriptを追加します。
// Shows one page and hides the other two
function showPage(page) {
// Hide all of the divs:
document.querySelectorAll('div').forEach(div => {
div.style.display = 'none';
});
// Show the div provided in the argument
document.querySelector(`#${page}`).style.display = 'block';
}
// Wait for page to loaded:
document.addEventListener('DOMContentLoaded', function() {
// Select all buttons
document.querySelectorAll('button').forEach(button => {
// When a button is clicked, switch to that page
button.onclick = function() {
showPage(this.dataset.page);
}
})
});
多くの場合、サイトに初めてアクセスするときに各ページのコンテンツ全体をロードするのは非効率的なため、新しいデータにアクセスするにはサーバを使用する必要があります。たとえば、ニュースサイトを初めて訪問したときに、利用可能なすべての記事を読み込む必要がある場合、サイトの読み込みに時間がかかりすぎます。この問題は、前の講義で為替レートをロードするときに使用したのと同様の方法を使用して回避できます。今回は、Djangoを使用して1ページのアプリケーションから情報を送受信する方法を見てみましょう。この仕組みを説明するために、単純なDjangoアプリケーションを見てみましょう。urls.py
には2つのURLパターンがあります。
urlpatterns = [
path("", views.index, name="index"),
path("sections/<int:num>", views.section, name="section")
]
views.py
に2つの対応するルートがあります。section
ルートは整数intを受け取り、その整数に基づいたテキスト文字列をHTTP応答として返すことに注意してください。
from django.http import Http404, HttpResponse
from django.shortcuts import render
# Create your views here.
def index(request):
return render(request, "singlepage/index.html")
# The texts are much longer in reality, but have
# been shortened here to save space
texts = ["Text 1", "Text 2", "Text 3"]
def section(request, num):
if 1 <= num <= 3:
return HttpResponse(texts[num - 1])
else:
raise Http404("No such section")
index.html
ファイルでは、前のレッスンで学習したAJAXを利用して、特定のセクションのテキストを取得して画面に表示するようサーバに要求します。
<!DOCTYPE html>
<html lang="en">
<head>
<title>Single Page</title>
<style>
</style>
<script>
// Shows given section
function showSection(section) {
// Find section text from server
fetch(`/sections/${section}`)
.then(response => response.text())
.then(text => {
// Log text and display on page
console.log(text);
document.querySelector('#content').innerHTML = text;
});
}
document.addEventListener('DOMContentLoaded', function() {
// Add button functionality
document.querySelectorAll('button').forEach(button => {
button.onclick = function() {
showSection(this.dataset.section);
};
});
});
</script>
</head>
<body>
<h1>Hello!</h1>
<button data-section="1">Section 1</button>
<button data-section="2">Section 2</button>
<button data-section="3">Section 3</button>
<div id="content">
</div>
</body>
</html>
これで、HTMLページ全体を再ロードすることなく、サーバから新しいデータをロードできるサイトを作成できました。
しかし、私たちのサイトの欠点の1つは、URLの情報が少なくなったことです。上のビデオを見ればわかるように、セクションを切り替えてもURLは変わりません。 JavaScriptHistory API を使用すると、この問題を解決できます。このAPIを使用すると、ブラウザ履歴に情報をプッシュし、URLを手動で更新できます。このAPIを使用する方法を見てみましょう。前のものと同じDjangoプロジェクトがありますが、今回はHistory APIを使用するようにスクリプトを変更したいと考えています。
// When back arrow is clicked, show previous section
window.onpopstate = function(event) {
console.log(event.state.section);
showSection(event.state.section);
}
function showSection(section) {
fetch(`/sections/${section}`)
.then(response => response.text())
.then(text => {
console.log(text);
document.querySelector('#content').innerHTML = text;
});
}
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('button').forEach(button => {
button.onclick = function() {
const section = this.dataset.section;
// Add the current state to the history
history.pushState({section: section}, "", `section${section}`);
showSection(section);
};
});
});
上記の showSection
関数では、history.pushState
関数を使用しています。この関数は、3つの引数に基づいて、ブラウズ履歴に新しい要素を追加します。
- 状態(state)に関連付けられたデータ
- ほとんどのWebブラウザで無視されるタイトルパラメータ
- URLの表示内容
上記のJavaScriptで行ったもう1つの変更は、onpopstate
パラメーターの設定です。このパラメーターは、ユーザが戻る矢印をクリックしたときに何を行うかを指定します。ここでは、ボタンを押したときに前のセクションを表示します。これでサイトは少し使いやすくなりました。
スクロール
ブラウザーの履歴を更新してアクセスするために、windowと呼ばれる重要なJavaScriptオブジェクトを使用しました。他にも、例えば、以下のような、サイトを見やすくすることができる、windowプロパティがあります。
window.innerWidth
:ウィンドウの幅 (ピクセル単位)window.innerHeight
:ウィンドウの高さ (ピクセル単位)
ウィンドウは現在ユーザに表示されている内容を表しますが、document はWebページ全体を参照します。多くの場合、ウィンドウよりもはるかに大きいため、ユーザはページのコンテンツを表示するために上下にスクロールする必要があります。スクロールを操作するには、他の変数にアクセスします。
window.scrollY
:ページの先頭からスクロールしたピクセル数document.body.offsetHeight
:ドキュメント全体の高さ (ピクセル単位)
これらの測定したサイズを次のように比較すると window.scrollY + window.innerHeight >= document.body.offsetHeight
、ユーザがページの最後までスクロールしたかどうかを判断できます。たとえば、次のページでは、ページの下部に達したときに背景色が緑色に変わります。
<!DOCTYPE html>
<html lang="en">
<head>
<title>Scroll</title>
<script>
// Event listener for scrolling
window.onscroll = () => {
// Check if we're at the bottom
if (window.innerHeight + window.scrollY >= document.body.offsetHeight) {
// Change color to green
document.querySelector('body').style.background = 'green';
} else {
// Change color to white
document.querySelector('body').style.background = 'white';
}
};
</script>
</head>
<body>
<p>1</p>
<p>2</p>
<!-- More paragraphs left out to save space -->
<p>99</p>
<p>100</p>
</body>
</html>
無限スクロール
ページの最後で背景色を変更できても、あまり使い道がありませんが、無限スクロールを実装する場合は、ページの最後であることを検出する必要があります。たとえば、ソーシャルメディアサイトで、すべての投稿を一度にロードする必要がない場合は、最初の10件をロードし、ユーザが一番下に達したら次の10件をロードします。これを実現するDjangoアプリケーションを見てみましょう。このアプリは urls.py
に2つのパスを持っています。
urlpatterns = [
path("", views.index, name="index"),
path("posts", views.posts, name="posts")
]
views.py
に2つの対応するビューがあります。
import time
from django.http import JsonResponse
from django.shortcuts import render
# Create your views here.
def index(request):
return render(request, "posts/index.html")
def posts(request):
# Get start and end points
start = int(request.GET.get("start") or 0)
end = int(request.GET.get("end") or (start + 9))
# Generate list of posts
data = []
for i in range(start, end + 1):
data.append(f"Post #{i}")
# Artificially delay speed of response
time.sleep(1)
# Return list of posts
return JsonResponse({
"posts": data
})
posts
ビューには、start
と end
の2つの引数が必要です。このビューでは、独自のAPIを作成し、URL localhost:8000/posts?start=10&end=15
にアクセスしてテストすることができます。次のJSONを返します。
{
"posts": [
"Post #10",
"Post #11",
"Post #12",
"Post #13",
"Post #14",
"Post #15"
]
}
ここで、サイトが読み込む index.html
テンプレートでは、本文に空の div
とスタイル設定だけから始めます。最初に静的ファイルを読み込み、次に static
フォルダー内のJavaScriptファイルを参照することに注意してください。
{% load static %}
<!DOCTYPE html>
<html>
<head>
<title>My Webpage</title>
<style>
.post {
background-color: #77dd11;
padding: 20px;
margin: 10px;
}
body {
padding-bottom: 50px;
}
</style>
<script scr="{% static 'posts/script.js' %}"></script>
</head>
<body>
<div id="posts">
</div>
</body>
</html>
JavaScriptでは、ユーザがページの最後までスクロールするのを待ってから、APIを使ってさらに投稿を読み込みます。
// Start with first post
let counter = 1;
// Load posts 20 at a time
const quantity = 20;
// When DOM loads, render the first 20 posts
document.addEventListener('DOMContentLoaded', load);
// If scrolled to bottom, load the next 20 posts
window.onscroll = () => {
if (window.innerHeight + window.scrollY >= document.body.offsetHeight) {
load();
}
};
// Load next set of posts
function load() {
// Set start and end post numbers, and update counter
const start = counter;
const end = start + quantity - 1;
counter = end + 1;
// Get new posts and add posts
fetch(`/posts?start=${start}&end=${end}`)
.then(response => response.json())
.then(data => {
data.posts.forEach(add_post);
})
};
// Add a new post with given contents to DOM
function add_post(contents) {
// Create new post
const post = document.createElement('div');
post.className = 'post';
post.innerHTML = contents;
// Add post to DOM
document.querySelector('#posts').append(post);
};
これで無限スクロールするサイトができました!
アニメーション
サイトをもう少し面白くする別の方法は、アニメーションを追加することです。CSSはスタイリングを提供するだけでなく、HTML要素をアニメーション化することも容易にします。
CSSでアニメーションを作成するには、次の形式を使用します。この形式では、アニメーション固有の開始スタイルと終了スタイル( to
と from
)、または継続時間内のさまざまな段階のスタイル( 0%
から100%
まで)を含めることができます。たとえば、次のようになります。
@keyframes animation_name {
from {
/* Some styling for the start */
}
to {
/* Some styling for the end */
}
}
または:
@keyframes animation_name {
0% {
/* Some styling for the start */
}
75% {
/* Some styling after 3/4 of animation */
}
100% {
/* Some styling for the end */
}
}
次に、アニメーションを要素に適用するために、animation-name
、animation-duration
(秒単位)、animation-fill-mode
(通常 forwards
)を含めます。たとえば、最初にページに入ったときにタイトルが大きくなるページを次に示します。
<!DOCTYPE html>
<html lang="en">
<head>
<title>Animate</title>
<style>
@keyframes grow {
from {
font-size: 20px;
}
to {
font-size: 100px;
}
}
h1 {
animation-name: grow;
animation-duration: 2s;
animation-fill-mode: forwards;
}
</style>
</head>
<body>
<h1>Welcome!</h1>
</body>
</html>
以下の例は、数行を変更するだけで見出しの位置を変更する方法を示しています。
<!DOCTYPE html>
<html lang="en">
<head>
<title>Animate</title>
<style>
@keyframes move {
from {
left: 0%;
}
to {
left: 50%;
}
}
h1 {
position: relative;
animation-name: move;
animation-duration: 2s;
animation-fill-mode: forwards;
}
</style>
</head>
<body>
<h1>Welcome!</h1>
</body>
</html>
次に、中間CSSプロパティの設定についても説明します。アニメーションの途中で任意の比率でスタイルを指定できます。下の例では、タイトルを左から右に移動した後、左に戻します。そのためには、上の例のアニメーションのみを変更します。
@keyframes move {
0% {
left: 0%;
}
50% {
left: 50%;
}
100% {
left: 0%;
}
}
アニメーションを複数回繰り返したい場合は、animation-iteration-count
を1より大きい数値 (無限アニメーションの場合は infinite
) に変更します。アニメーションのさまざまな設定を変更するためのアニメーションプロパティが多数あります。
CSSに加えて、JavaScriptを使用してアニメーションをさらに制御できます。移動ヘッダの例 (無限の繰り返し) を使用して、アニメーションを開始および停止するボタンを作成する方法を示します。すでにアニメーション、ボタン、見出しがある場合は、次のスクリプトを追加してアニメーションを開始および一時停止できます。
document.addEventListener('DOMContentLoaded', function() {
// Find heading
const h1 = document.querySelector('h1');
// Pause Animation by default
h1.style.animationPlayState = 'paused';
// Wait for button to be clicked
document.querySelector('button').onclick = () => {
// If animation is currently paused, begin playing it
if (h1.style.animationPlayState == 'paused') {
h1.style.animationPlayState = 'running';
}
// Otherwise, pause the animation
else {
h1.style.animationPlayState = 'paused';
}
}
})
では、アニメーションに関する新しい知識を、以前作成した投稿ページに適用する方法を見てみましょう。具体的には、投稿を読んだ後に非表示にしたいとします。先ほど作成したDjangoプロジェクトとまったく同じですが、HTMLとJavaScriptが少し異なっているとします。まず、add_post
関数を変更し、今度は投稿の右側にボタンを追加します。
// Add a new post with given contents to DOM
function add_post(contents) {
// Create new post
const post = document.createElement('div');
post.className = 'post';
post.innerHTML = `${contents} <button class="hide">Hide</button>`;
// Add post to DOM
document.querySelector('#posts').append(post);
};
ここでは、hide
ボタンをクリックしたときに投稿を非表示にする方法を説明します。これを行うには、ユーザがページの任意の場所をクリックするたびにトリガーされるイベントリスナを追加します。次に、引数としてevent
を取る関数を作成し、event.target
属性を使用してクリックされたものにアクセスできるようにします。parentElement
クラスを使用して、DOM内の特定の要素の親を取得することもできます。
// If hide button is clicked, delete the post
document.addEventListener('click', event => {
// Find what was clicked on
const element = event.target;
// Check if the user clicked on a hide button
if (element.className === 'hide') {
element.parentElement.remove()
}
});
これで、非表示ボタンを実装したことがわかりますが、これはあまり美しくありません。削除する前に、ポストをフェードアウトさせて縮小させることもできます。これを行うには、まずCSSアニメーションを作成します。下のアニメーションでは、75%の時間をかけて不透明度 opacity
を1から0に変更しています。これにより、ポストは徐々にフェードアウトします。その後、残りの時間はすべての height
関連の属性を0に移動し、投稿を事実上縮小して、見えなくします。
@keyframes hide {
0% {
opacity: 1;
height: 100%;
line-height: 100%;
padding: 20px;
margin-bottom: 10px;
}
75% {
opacity: 0;
height: 100%;
line-height: 100%;
padding: 20px;
margin-bottom: 10px;
}
100% {
opacity: 0;
height: 0px;
line-height: 0px;
padding: 0px;
margin-bottom: 0px;
}
}
次に、このアニメーションを投稿のCSSに追加します。最初にアニメーション再生状態 animation-play-state
を一時停止 paused
に設定します。これは、投稿が既定で非表示にならないことを意味します。
.post {
background-color: #77dd11;
padding: 20px;
margin-bottom: 10px;
animation-name: hide;
animation-duration: 2s;
animation-fill-mode: forwards;
animation-play-state: paused;
}
最後に、非表示 hide
ボタンをクリックしてアニメーションを開始し、投稿を削除します。これを行うには、上からJavaScriptを編集します。
// If hide button is clicked, delete the post
document.addEventListener('click', event => {
// Find what was clicked on
const element = event.target;
// Check if the user clicked on a hide button
if (element.className === 'hide') {
element.parentElement.style.animationPlayState = 'running';
element.parentElement.addEventList
}
});
このように、Hideボタンの機能が大幅に改善されました。
React
この時点で、より複雑なWebサイトにどれだけのJavaScriptコードを組み込む必要があるか想像できます。CSSフレームワークとしてBootstrapを採用し、実際に書かなければならないCSSの量を削減したように、JavaScriptフレームワークを採用することで、実際に書かなければならないコードの量を減らすことができます。最も一般的なJavaScriptフレームワークの1つはReactというライブラリーです。
このコースでは、これまで命令型プログラミング手法を使用してきました。この手法では、実行するステートメントのセットをコンピュータに与えます。たとえば、HTMLページのカウンタを更新するには、次のようなコードを使用します。
表示:
<h1>0</h1>
ロジック:
let num = parseInt(document.querySelector("h1").innerHTML);
num += 1;
document.querySelector("h1").innerHTML = num;
Reactを使えば、宣言型プログラミングを使うことができ、表示したいものを説明するコードを書くだけで、どのように表示するかを気にする必要がなくなります。Reactでは、カウンタは次のようになります。
表示:
<h1>{num}</h1>
ロジック:
num += 1;
Reactフレームワークは、それぞれが基礎となる状態(state)を持つことができるコンポーネントの概念に基づいて構築されています。コンポーネントは、投稿やナビゲーションバーなどのWebページに表示されるもので、状態はそのコンポーネントに関連付けられた変数のセットです。Reactの長所は、状態が変化すると、それに応じてReactが自動的にDOMを変更することです。
Reactを使う方法はいくつかあります (これにはFacebookが公開している人気の create-react-app コマンドも含まれます) が、今日はHTMLファイルで直接始めることに焦点を当てます。これを行うには、次の3つのJavaScriptパッケージをインポートする必要があります。
React
:コンポーネントとその動作を定義します。ReactDOM
:Reactコンポーネントを取得してDOMに挿入します。Babel
:これからReactで記述する言語であるJSX
から、ブラウザが解釈できるプレーンなJavaScriptに翻訳します。JSXはJavaScriptに非常に似ていますが、コード内でHTMLを表現する機能など、いくつかの追加機能があります。
最初のReactアプリケーションを作成します。
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://unpkg.com/react@17/umd/react.production.min.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<title>Hello</title>
</head>
<body>
<div id="app"></div>
<script type="text/babel">
function App() {
return (
<div>
Hello!
</div>
);
}
ReactDOM.render(<App />, document.querySelector("#app"));
</script>
</body>
</html>
これは私たちの最初のReactアプリなので、このコードの各部分が何をしているかを詳しく見てみましょう。
- titleの上の3行では、React、ReactDom、Babelの最新バージョンをインポートします。
- 本文には、
app
というid
を持つ1つのdiv
を含めます。ほとんどの場合、これを空のままにして、下のReactのコードに入力します。 type="text/babel"
を指定するscriptタグを含めます。これは、それ以降のスクリプトがBabelを使用して変換する必要があることをブラウザに通知します。- 次に、
App
というコンポーネントを作成します。ReactのコンポーネントはJavaScriptクラスとして表現されます。 - そのコンポーネントは、DOMにレンダリングしたい内容を返します。この場合は、
<div>Hello!</div>
を返します。 - スクリプトの最後の行では、2つの引数を取る
ReactDOM.render
関数を使用します。- レンダリングするコンポーネント
- コンポーネントが描画されるDOM内の要素
コードが何をしているのか理解できたので、結果のWebページを見てみましょう。
Reactの便利な機能の1つは、他のコンポーネント内のコンポーネントをレンダリングできることです。これを説明するために、Hello
という別のコンポーネントを作成します。
function Hello(props) {
return (
<h1>Hello</h1>
);
}
次に、App
コンポーネント内に3つの Hello
コンポーネントを描画します。
function App() {
return (
<div>
<Hello />
<Hello />
<Hello />
</div>
);
}
次のようなページが表示されます。
これまでのところ、コンポーネントはすべてまったく同じであるため、それほど興味深いものではありません。これらのコンポーネントにプロパティ(React用語でprops )を追加することで、コンポーネントの柔軟性を高めることができます。たとえば、3人の人に挨拶したいとします。HTMLタグに似たメソッドで、これらの人の名前を渡せます。
function App() {
return (
<div>
<Hello name="Harry" />
<Hello name="Ron" />
<Hello name="Hermione" />
</div>
);
}
その後、props.PROP_NAME
を使用してpropsにアクセスできます。次に、中括弧を使用してこれをJSXに挿入します。
function Hello(props) {
return (
<h1>Hello, {props.name}!</h1>
);
}
このページには、3つの名前が表示されます。
では、Reactを使って、JavaScriptを使って最初に作ったカウンター・ページを再実装する方法を見てみましょう。全体の構成は変わりませんが、App
コンポーネント内で、React のuseState
フックを利用して、コンポーネントのstate を追加します。useState
の引数は、state の初期値で、0
に設定します。この関数は、state を示す変数とstate を更新する関数を返します。
const [count, setCount] = React.useState(0);
次に、関数がレンダリングを行う箇所を説明します。ここでは、ヘッダーとボタンを定義します。ボタンをクリックした場合に、React がonclick
属性を使用して処理するイベントリスナーを以下のように追加します。
return (
<div>
<h1>{count}</h1>
<button onClick={updateCount}>Count</button>
</div>
);
最後にupdateCount
関数を定義します。その際、setCount
関数を利用し、引数にはstate の新しい値を指定します。
function updateCount() {
setCount(count + 1);
}
これで、機能するカウンターサイトができました!
足し算
Reactフレームワークの感触をつかんだので、ここで学んだことを利用して、ユーザが足し算の問題を解くことができるゲームのようなサイトを構築してみましょう。最初に、他のReactページと同じ設定で新しいファイルを作成します。このアプリケーションの構築を始めるにあたり、この状態で何を追跡しておきたいかを考えてみましょう。ユーザがページを開いている間に変更される可能性があるものはすべて含める必要があります。状態を次のように設定します。
num1
:足し算の最初の数値。num2
:足し算の2番目の数値。response
:ユーザが入力した内容。score
:ユーザが正しく回答した質問の数。
State は、次の情報を含むJavascript オブジェクトとすることができます。
const [state, setState] = React.useState({
num1: 1,
num2: 1,
response: "",
score: 0
});
ここで、state の値を用いて、基本的なユーザーインターフェースを表示します。
return (
<div>
<div>{state.num1} + {state.num2}</div>
<input value={state.response} />
<div>Score: {state.score}</div>
</div>
);
サイトの基本レイアウトは次のようになります。
この時点では、入力ボックスの値は現在空の文字列であるstate.response
として固定されているため、ユーザは入力ボックスに何も入力できません。これを修正するには、onChange
属性を入力ボックスに追加し、updateResponse
という関数を値に設定します。
onChange={this.updateResponse}
次に、updateResposne
関数を定義する必要があります。この関数は、関数をトリガしたイベント(event)を引数として取り込み、response
を入力の現在の値に設定します。この関数は、ユーザが入力した内容をstate
に保存します。
function updateResponse(event) {
setState({
...state,
response: event.target.value
});
}
次に、ユーザが問題を送信する機能を追加します。最初に別のイベントリスナーを追加し、次に記述する関数にリンクします。
onKeyPress={inputKeyPress}
次に、inputKeyPress
関数を定義します。この関数では、まず Enter
キーが押されたかどうかを確認してから、答えが正しいかどうかを確認します。ユーザが正しい場合は、スコアを1だけ増やし、次の問題の乱数を選択し、responseをクリアします。responseが間違っている場合は、スコアを1減らして回答をクリアします。
function inputKeyPress(event) {
if (event.key === "Enter") {
const answer = parseInt(state.response);
if (answer === state.num1 + state.num2) {
// User got question right
setState({
...state,
score: state.score + 1,
response: "",
num1: Math.ceil(Math.random() * 10),
num2: Math.ceil(Math.random() * 10)
});
} else {
// User got question wrong
setState({
...state,
score: state.score - 1,
response: ""
})
}
}
}
アプリケーションの仕上げとして、ページにスタイルを追加しましょう。アプリケーションのすべてを中央に配置し、問題を含む divに問題 problem
の id
を追加し、styleタグに次のCSSを追加することで、問題を大きくします。
#app {
text-align: center;
font-family: sans-serif;
}
#problem {
font-size: 72px;
}
最後に、10ポイントを獲得したら、ゲームに勝つ機能を追加しましょう。これを行うには、以下のように条件を追加し、ポイントが10になるとまったく異なるものを返します。
if (state.score === 10) {
return (
<div id="winner">You won!</div>
);
}
勝利をよりエキサイティングにするために、代替 div にもスタイルを追加します。
#winner {
font-size: 72px;
color: green;
}
では、アプリケーションを見てみましょう。
今日のレッスンはこれで終わりです!次回は、大規模なWebアプリケーションを構築するためのベストプラクティスについて説明します。