ユーザが問題と選択肢から解答を導き出し、それを採点して結果を出力するスクリプトです。
HTML // まずは選択肢部分 部分的な文言については割愛 // 1問目 <label><input type="radio" name="q00">新潟</label><br> <label><input type="radio" name="q00">東京</label><br> <label><input type="radio" name="q00">大阪</label><br> <label><input type="radio" name="q00" checked="checked">未選択</label><br> // 2問目 <label><input type="radio" name="q01">新潟</label><br> <label><input type="radio" name="q01">東京</label><br> <label><input type="radio" name="q01">大阪</label><br> <label><input type="radio" name="q01" checked="checked">未選択</label><br> // 3問目 <label><input type="radio" name="q02">新潟</label><br> <label><input type="radio" name="q02">東京</label><br> <label><input type="radio" name="q02">大阪</label><br> <label><input type="radio" name="q02" checked="checked">未選択</label><br> // 4問目 // name の値をいじればいいということで以下略 // つづいて採点ボタンおよび採点結果出力部分 こちらも部分的な文言については割愛 // パターンその1 <input type="button" id="inst10" value="採点" onclick="quizInputCorrect(this, 5)"><br> <div id="anst10"> 採点結果はここに出力されます。 </div> // パターンその2 <input type="button" id="hoge" value="採点" onclick="quizInputCorrect(this, 5)"><br> <div id="hogeoutt"> 採点結果はここに出力されます。 </div>
Javascriptソース
// クイズスクリプト 配点表
function quizAnswerDataTable(){
return [[0,0,0,0,0]];
}
// クイズスクリプト
function quizInputCorrect(obj, quizNum){
const valueId = obj.id;
const point = quizScoreCorrect(0, quizNum);
const gFlg = (point >= 100) ? 0 : 1;
const htmlStringArray = [
"出直してこい!<br>\n",
"ちょっとちょっと、そんなんで本当に大丈夫!?<br>\n",
"待て待て待て、こんな簡単な問題がわからないとは。。。<br>\n",
"あともうちょっと……ってレベルじゃないでしょ! これぐらいちゃんと答えなさいよね!<br>\n",
"おーい、いい加減に全部正解しろよ。。<br>\n",
"そうです、答えは全部「新潟」です。新潟県民だから新潟びいきなんだろ?<br>\n" +
"うーん、少し違うな。よく見てみなさいな、そもそも選択肢に”県”が他にないんだよ。<br>\n"
];
const messageIndex = point / 20;
const htmlString = `
<p class='fr_tx'>
${point >= 100 ? 'おめでとうございます、' : '残念ですが、'}採点結果は
<span class='pkup3 lg_at'>${point}点</span>でした。<br>
${htmlStringArray[messageIndex]}<br>
</p>
`;
quizNtbrSet(valueId, htmlString, gFlg);
}
// クイズスクリプト 配点
function quizScoreMerge(page, questionNum) {
// 5問クイズなので、anstが5以上(配点要求)なら一律20点を返す
if (questionNum >= 5) return 20;
return quizAnswerDataTable()[pagepg][questionNum];
}
// クイズスクリプト 判定箇所共通
function quizScoreCorrect(list, quizNum) {
let point = 0;
const scoreOffset = Math.ceil(quizNum / 5) * 5;
for (let i = 0; i < quizNum; i++) {
const quizNumId = document.getElementsByName("q" + zeroPad(i, 2));
const checkedIndex = Array.from(quizNumId).findIndex(radio => radio.checked);
if (checkedIndex !== -1 && checkedIndex === quizScoreMerge(list, i)) {
point += quizScoreMerge(list, i + scoreOffset);
}
}
return point;
}
// クイズスクリプト 表示処理共通
function quizNtbrSet(valueId, str, gFlg) {
let elementIdI, elementIdX;
if (valueId.slice(0, 4) === "inst") {
elementIdI = "anst" + valueId.slice(4);
elementIdX = "ansx" + valueId.slice(4);
} else {
elementIdI = valueId + "outt";
elementIdX = valueId + "outx";
}
const targetIn = document.getElementById(elementIdI);
if (targetIn) targetIn.innerHTML = str;
const element = document.getElementById(elementIdX);
if (element) {
if (gFlg === 1) {
// クラス側の display: none を力づくで上書きするため、
// タグ名が SPAN なら "inline"、それ以外(DIV等)なら "block" を明示的に指定する
element.style.display = (element.tagName === "SPAN") ? "inline" : "block";
} else {
element.style.display = "none";
}
}
}
// クイズスクリプト 選択リセット処理共通
function quizSelectReset(rFlg, quizNum) {
/*
rFlg : 選択肢の初期位置の設定
-1 選択肢の一番下 / -2 選択肢を消す
*/
for (let i = 0; i < quizNum; i++) {
const quizNum = document.getElementsByName("q" + zeroPad(i, 2));
if (quizNum.length === 0) continue;
if (ced >= 0 && quizNum[rFlg]) {
quizNum[rFlg].checked = true;
} else if (rFlg === -1) {
quizNum[rFlg.length - 1].checked = true;
} else if (rFlg === -2) {
quizNum.forEach(radio => radio.checked = false);
}
}
}
// オマケ機能:ゼロパディング(実装しているので同梱)
function zeroPad(num, len, decimalLen = 0) {
// num.toFixed(len); 機能も同時実装!
// 使い方:
// zeroPad(5, 2) -> "05" (整数部2桁)
// zeroPad(1.2, 4, 2) -> "0001.20" (整数部4桁、小数部2桁でパディング)
// 小数点以下の指定があれば先に固定
let str = decimalLen > 0 ? num.toFixed(decimalLen) : num.toString();
// 整数部のパディング(小数点の左側だけをターゲットにする)
const parts = str.split('.');
parts[0] = parts[0].padStart(len, '0');
return parts.join('.');
}
名前とフォームを見た通り、選択形式のクイズスクリプトで、内部で計算を実行し、その結果を出力しているだけです。
選択はラジオボタンのため、1つの問題の選択肢の「name」の値を同じものにする必要があります。
この際の「name」の値にはルールがあり、1問目から順番に「q00」、「q01」と、”q +番号”の形で欠番なく、
0から順番に記述していく必要があります。
また、採点ボタン(<input type="button" id="inst10" value="採点" onclick="quizInputCorrect(this, 5)">)の関数呼び出しの引数が2つありますが、
第2引数(「5」の部分)には、問題数を入力する必要があります。
当然、JS内部へ直接値を記述してもいいのですが、そこはお好みで。(個人的に、ただサイトへの汎用性を高めるべくしていろいろやっていたらこうなってしまっただけですので。。。)
採点ボタンと出力結果を表示する箇所のid名のルールは2種類あり、他で紹介しているネタバレスクリプトのものと同じ感じにしています。
まず、採点ボタン(<input type="button" id="inst10" value="採点" onclick="quizInputCorrect(this, 5)">)のidが”inst”で始まる場合は「inst00」などのように、
”inst +番号”の形で番号を添えて書きます。番号は何桁でも構いません(というか、実際には番号である必要はありません)。
”inst”系のid名の場合は、採点の出力結果を表示する箇所のid名を「anst00」などのように、”anst +番号”の形で番号を添えて書きます。
ここで使用する番号は採点ボタンのid名と同じ番号にしてください。
次に、採点ボタン(<input type="button" id="hoge" value="採点" onclick="quizInputCorrect(this, 5)">)のidが”inst”で始まらない場合、
要は、”hoge”のような任意の文字列も設定可能です。
この場合、採点の出力結果を表示する箇所のid名を、「hogeoutt」などのように、”採点ボタンのid名 +outt”の形で、末尾に”outt”を添えて書きます。
次に、JS部分について。関数が多いので、関数ごとに説明しますね。
quizInputCorrect(obj, qnum) は、HTMLのハンドラのイベントによって最初に動く関数です。
先にも言った通り、第2引数で問題数を受け取っています。
quizScoreCorrect(qnum) は、回答の採点をする関数です。
この関数本体は成否の判定をしながら、純粋に得点を加算しているだけです。
そして、quizScoreMerge(anst) が、各問題の成否を判定するための答えが書いてある関数です。
そのため、ユーザーはこの関数の内容を変更する必要があります。
「0」の記載があるところは解答番号となっていて、つまり、上記の問題の「新潟」に対応するところで、それが5つ並んでいますね。
つまり、5問とも答えは「新潟」になるようになっているということです。
ラジオボタンは同じ name の値の選択肢が並んでいる場合、上から順に0、1、2......となっている(要するに配列となっている)ため、
つまり、答えを「東京」にしたければ「1」、「大阪」にしたければ「2」にすればいいだけです。
ボタンを増やした場合も3、4、5……と、答えの選択肢が単に増えるだけです。
そして、20の値が書かれているのは配点です。20が5つ、つまり、5問全問正解でちょうど100点になるような配点ですね。
配点を記載する箇所の法則を説明するのが面倒ですが、要はこういうことです。
・問題数が1~5の場合→6番目から記載する
・問題数が6~10の場合→11番目から記載する
・問題数が11~20の場合→21番目から記載する
というか、「Math.ceil(問題数 / 5) * 5」が意味していることがわかれば大丈夫かと思います。。。
Excel では Ceiling(問題数,5) と同じ意味です。
最後に、qurs(ced, qnum) は、選択肢のリセット処理をしています。
満点(100点)に満たなかった場合にチェック位置を初期化しています。
初期化しない場合は quizInputCorrect(obj, qnum) で qurs(ced, qnum) を呼び出している部分(と周りのif関数)と共に削除してしまえばいいと思います。
初期化する場合は quizInputCorrect(obj, qnum) で書いたように qurs(-1, qnum) などと記載します。
第2引数は問題数が代入されているのでともかく、問題は第1引数のルールですね。
第1引数には、初期化する場合のルールを記載します。
0、1、2 などの数値の場合、quizScoreMerge(anst) の解答番号を記載するルールと同じで、
たとえば、「0」にすれば初期化した場合は必ず全部一番上の「新潟」に、「2」なら「大阪」にチェックが入る状態となります。
あとは「-1」と「-2」があり、「-1」の場合は選択肢の数に関係なく、選択肢の一番下の選択肢(上記では「未選択」)に、
「-2」の場合は選択肢のチェックがなくなります。
なお、実際の上のスクリプトではちょっとした改造が入っています。
1つ関数を追加して、出力する文言を追加しています。
別ページではネタバレスクリプトを応用して、満点だった場合にさらに内容を追加する関数を設置しています。