퀴즈 사이트 만들기
이번에는 문제 체크시 사이드의 오엠알 답안지도 함께 체크되며 문제 한개가 체크 될 때마다 전체 문항수의 남은 문항수가 하나씩 줄어들게 만들어 보겠습니다.
코드블럭
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>퀴즈 이펙트07</title>
<link rel="stylesheet" href="CSS/reset.css">
<link rel="stylesheet" href="CSS/quiz.css">
<link rel="shortcut icon" type="image/x-icon" href="img/favicon.png"/>
<link rel="apple-touch-icon" sizes="114x114" href="img/favicon.png"/>
<link rel="apple-touch-icon" href="img/favicon.png"/>
</head>
<body>
<header id="header">
<h1><a href="../javascript14.html">Quiz</a> <em>객관식 확인 CBT 유형</em></h1>
<ul>
<li><a href="quizEffect01.html">1</a></li>
<li><a href="quizEffect02.html">2</a></li>
<li><a href="quizEffect03.html">3</a></li>
<li><a href="quizEffect04.html">4</a></li>
<li><a href="quizEffect05.html">5</a></li>
<li><a href="quizEffect06.html">6</a></li>
<li class="active"><a href="quizEffect07.html">정보처리기능사 2009년 5회</a></li>
</ul>
</header>
<!-- //header -->
<main id="main">
<div class="quiz__wrap__cbt">
<div class="cbt__header">
<h2>2020년 1회 정보처리기능사 기출문제</h2>
</div>
<div class="cbt__conts">
<div class="cbt__quiz">
</div>
</div>
<div class="cbt__aside">
<div class="cbt__info">
<div>
<button class="cbt__submit">제출하기</button>
<span class="cbt__time">59분 10초</span>
</div>
<div>
<div class="cbt__title">수험자 : <em class="cbt__name">이은지</em></div>
<div class="cbt__score">
<span>전체 문제수 : <em class="cbt__length">0</em>문항</span>
<span>남은 문제수 : <em class="cbt__rest">0</em>문항</span>
</div>
</div>
</div>
<div class="cbt__omr">
</div>
</div>
<!-- <div class="cbt__start">
<div class="cbt__modal1">
<h2>기능사 시험 도전하기</h2>
<div class="cbt__choice">
<select name="cbtTime" id="cbtTime">
<option value="gineungsaJC2005_02">정보처리기능사 2005년 2회</option>
<option value="gineungsaJC2005_05">정보처리기능사 2005년 4회</option>
<option value="gineungsaJC2005_05">정보처리기능사 2005년 5회</option>
<option value="gineungsaJC2006_01">정보처리기능사 2006년 1회</option>
<option value="gineungsaJC2006_03">정보처리기능사 2006년 2회</option>
<option value="gineungsaJC2006_03">정보처리기능사 2006년 3회</option>
<option value="gineungsaJC2006_05">정보처리기능사 2006년 5회</option>
<option value="gineungsaJC2007_01">정보처리기능사 2007년 1회</option>
<option value="gineungsaJC2007_02">정보처리기능사 2007년 2회</option>
<option value="gineungsaJC2007_05">정보처리기능사 2007년 5회</option>
<option value="gineungsaJC2008_01">정보처리기능사 2008년 1회</option>
<option value="gineungsaJC2008_02">정보처리기능사 2008년 2회</option>
<option value="gineungsaJC2008_04">정보처리기능사 2008년 4회</option>
<option value="gineungsaJC2008_05">정보처리기능사 2008년 5회</option>
<option value="gineungsaJC2009_01">정보처리기능사 2009년 1회</option>
<option value="gineungsaJC2009_05">정보처리기능사 2009년 5회</option>
<option value="gineungsaJC2010_02">정보처리기능사 2010년 2회</option>
<option value="gineungsaJC2010_05">정보처리기능사 2010년 5회</option>
<option value="gineungsaJC2011_01">정보처리기능사 2011년 1회</option>
<option value="gineungsaJC2011_02">정보처리기능사 2011년 2회</option>
<option value="gineungsaJC2011_04">정보처리기능사 2011년 4회</option>
<option value="gineungsaJC2011_05">정보처리기능사 2011년 5회</option>
</select>
<select name="cbtTime" id="cbtTime">
<option value="gineungsaWD2009_05">웹디자인기능사 2009년 5회</option>
<option value="gineungsaWD2015_02">웹디자인기능사 2010년 1회</option>
<option value="gineungsaWD2015_02">웹디자인기능사 2010년 2회</option>
<option value="gineungsaWD2015_04">웹디자인기능사 2010년 4회</option>
<option value="gineungsaWD2015_04">웹디자인기능사 2010년 5회</option>
<option value="gineungsaWD2015_04">웹디자인기능사 2011년 1회</option>
<option value="gineungsaWD2015_04">웹디자인기능사 2011년 2회</option>
<option value="gineungsaWD2015_04">웹디자인기능사 2011년 4회</option>
<option value="gineungsaWD2015_05">웹디자인기능사 2011년 5회</option>
<option value="gineungsaWD2015_04">웹디자인기능사 2012년 2회</option>
<option value="gineungsaWD2015_04">웹디자인기능사 2012년 4회</option>
<option value="gineungsaWD2015_04">웹디자인기능사 2012년 5회</option>
<option value="gineungsaWD2015_04">웹디자인기능사 2013년 2회</option>
<option value="gineungsaWD2015_04">웹디자인기능사 2013년 4회</option>
<option value="gineungsaWD2015_04">웹디자인기능사 2013년 5회</option>
<option value="gineungsaWD2015_04">웹디자인기능사 2014년 1회</option>
<option value="gineungsaWD2015_04">웹디자인기능사 2014년 4회</option>
<option value="gineungsaWD2015_04">웹디자인기능사 2014년 5회</option>
<option value="gineungsaWD2015_04">웹디자인기능사 2015년 1회</option>
<option value="gineungsaWD2015_04">웹디자인기능사 2015년 3회</option>
<option value="gineungsaWD2015_04">웹디자인기능사 2015년 4회</option>
<option value="gineungsaWD2015_04">웹디자인기능사 2015년 5회</option>
<option value="gineungsaWD2015_04">웹디자인기능사 2016년 1회</option>
<option value="gineungsaWD2015_04">웹디자인기능사 2016년 4회</option>
</select>
</div>
<button></button>
<div data-lit-hue="20" data-lit-count="100" class="lit-container">
<button class="minimal">시작하기</button>
</div>
</div>
</div> -->
</div>
</main>
<!-- //main -->
<!-- <footer id="footer">
<a href="mailto:lee3ll@naver.com">lee3ll@naver.com</a>
</footer> -->
<!-- //footer -->
<script>
const cbt = document.querySelectorAll(".cbt");
const cbtQuiz = document.querySelector(".cbt__quiz");
const cbtOmr = document.querySelector(".cbt__omr");
const cbtSubmit = document.querySelector(".cbt__submit");
// const cbtName = document.querySelector(".cbt__title em");
const cbtRest = document.querySelector(".cbt__rest");
const cbtLength = document.querySelector(".cbt__length");
let questionAll = []; //모든 퀴즈 정보
let questionLength = 0; //전체 문제수
let questionRest = questionLength; //남은 문제수 //100개대신 변수를 넣어줌
//데이터 가져오기
const dataQuestion = () => {
fetch("json/gineungsaJC2009_05.json")
.then(res => res.json())
.then(items => {
questionAll = items.map((item, index) => {
const formattedQuestion = {
question: item.question,
number: index + 1
}
const answerChoices = [...item.incorrect_answers]; //오답 불러오기
formattedQuestion.answer = Math.floor(Math.random() * answerChoices.length) + 1;
answerChoices.splice(formattedQuestion.answer-1, 0, item.correct_answer);
//보기를 추가
answerChoices.forEach((choice, index) => {
formattedQuestion["choice" + (index+1)] = choice;
});
//문제에 대한 해설이 있으면 출력
if(item.hasOwnProperty("question_desc")){
formattedQuestion.question_desc = item.question_desc;
}
//문제에 대한 이미지가 있으면 출력
if(item.hasOwnProperty("question_img")){
formattedQuestion.question_img = item.question_img;
}
//해설이 있으면 출력
if(item.hasOwnProperty("desc")){
formattedQuestion.desc = item.desc;
}
//console.log(formattedQuestion);
return formattedQuestion;
});
newQuestion(); //문제 만들기
//전체 문제수
questionLength = questionAll.length;
cbtLength.innerHTML = questionLength;
cbtRest.innerHTML = questionLength;
})
.catch((err) => console.log(err));
}
//문제 만들기
const newQuestion = () => {
const exam = [];
const omr = [];
questionAll.forEach((question, number) => {
exam.push(`
<div class="cbt">
<div class="cbt__question"><span>${question.number}</span>. ${question.question}</div>
<div class="cbt__question__img">${question.question_img}</div>
<div class="cbt__question__desc">${question.question_desc}</div>
<div class="cbt__selects">
<input type="radio" id="select${number}_1" name="select${number}" value="${number}_1" onclick="answerSelect2(this)">
<label for="select${number}_1"><span>${question.choice1}</span></label>
<input type="radio" id="select${number}_2" name="select${number}" value="${number}_2" onclick="answerSelect2(this)">
<label for="select${number}_2"><span>${question.choice2}</span></label>
<input type="radio" id="select${number}_3" name="select${number}" value="${number}_3" onclick="answerSelect2(this)">
<label for="select${number}_3"><span>${question.choice3}</span></label>
<input type="radio" id="select${number}_4" name="select${number}" value="${number}_4" onclick="answerSelect2(this)">
<label for="select${number}_4"><span>${question.choice4}</span></label>
</div>
<div class="cbt__desc hide">${question.desc}</div>
</div>
`);
omr.push(`
<div class="omr">
<strong>${question.number}</strong>
<input type="radio" name="omr${number}" id="omr${number}_1" value="${number}_1" onclick="answerSelect(this)">
<label for="omr${number}_1"><span class="label-inner">1</span></label>
<input type="radio" name="omr${number}" id="omr${number}_2" value="${number}_2" onclick="answerSelect(this)">
<label for="omr${number}_2"><span class="label-inner">2</span></label>
<input type="radio" name="omr${number}" id="omr${number}_3" value="${number}_3" onclick="answerSelect(this)">
<label for="omr${number}_3"><span class="label-inner">3</span></label>
<input type="radio" name="omr${number}" id="omr${number}_4" value="${number}_4" onclick="answerSelect(this)">
<label for="omr${number}_4"><span class="label-inner">4</span></label>
</div>
`)
});
cbtQuiz.innerHTML = exam.join('');
cbtOmr.innerHTML = omr.join('');
// cbtName.innerHTML = prompt("수험자 성함을 적어주세요.");
}
//정답 확인
const answerQuiz = () => {
const cbtSelects = document.querySelectorAll(".cbt__selects");
questionAll.forEach((question, number) => {
const quizSelectsWrap = cbtSelects[number];
const userSelector = `input[name=select${number}]:checked`;
const userAnswer = (quizSelectsWrap.querySelector(userSelector) || {}).value;
const numberAnswer = userAnswer ? userAnswer.slice(-1) : undefined;
if(numberAnswer == question.answer){
console.log("정답입니다.");
cbtSelects[number].parentElement.classList.add("good");
} else {
console.log("오답입니다.")
cbtSelects[number].parentElement.classList.add("bad");
//오답 일 경우 정답 표시
const label = cbtSelects[number].querySelectorAll("label");
label[question.answer-1].classList.add("correct");
}
// 설명 숨기기
const quizDesc = document.querySelectorAll(".cbt__desc");
if(quizDesc[number].innerText == "undefined"){
quizDesc[number].classList.add("hide");
} else {
quizDesc[number].classList.remove("hide");
}
});
}
// 보기 체크
const answerSelect2 = (elem) => {
const answer = elem.value;
const answerNum = answer.split("_");
const select = document.querySelectorAll(".cbt__omr .omr"); //전체 문항 수 100개
const label = select[answerNum[0]].querySelectorAll("input"); //보기 4개
label[answerNum[1]-1].checked = true;
// console.log(answerNum[0]);
// console.log(answerNum[1]);
const answerInputs = document.querySelectorAll(".cbt__selects input:checked");
cbtRest.innerHTML = questionLength - answerInputs.length;
}
//보기체크2
const answerSelect = (elem) => {
const answer = elem.value;
const answerNum = answer.split("_");
const select = document.querySelectorAll(".cbt__quiz .cbt"); //전체 문항 수 100문제
const label = select[answerNum[0]].querySelectorAll("input"); //보기 4개
label[answerNum[1]-1].checked = true;
const answerInputs = document.querySelectorAll(".cbt__selects input:checked");
cbtRest.innerHTML = questionLength - answerInputs.length;
}
cbtSubmit.addEventListener("click", answerQuiz);
dataQuestion();
</script>
</body>
</html>
보충설명
👉 answerSelect2라는 이름의 함수가 정의되었습니다. 이 함수는 elem이라는 하나의 매개변수를 받습니다.
👉 answer라는 변수에 elem.value 값이 할당됩니다.
👉 select라는 변수에 ".cbt__omr .omr" CSS 선택자를 이용하여 문서 내에서 해당하는 모든 엘리먼트들의 목록이 선택됩니다.
👉 label이라는 변수에 select[answerNum[0]]으로 선택된 엘리먼트 내에서 "input" 태그들의 목록이 할당됩니다.
👉 label[answerNum[1]-1]이 체크되도록 설정됩니다. (answerNum[1]은 1부터 시작하는 인덱스이므로 1을 뺀 값으로 배열에서 해당하는 보기를 선택합니다)
👉 answerInputs라는 변수에 ".cbt__selects input:checked" CSS 선택자를 이용하여 문서 내에서 선택된(input 태그 중에서 체크된) 엘리먼트들의 목록이 할당됩니다.
👉 cbtRest.innerHTML에 questionLength에서 answerInputs.length를 뺀 값을 할당합니다. (이 코드에서 questionLength 변수는 선언되어 있지 않아서 외부에서 값을 전달받아야 사용할 수 있습니다)
👉 선택자를 사용해 .cbt__rest 클래스를 가진 엘리먼트를 cbtRest라는 변수에 입력해줍니다.
👉 마찬가지로 .cbt__length 클래스를 가진 엘리먼트를 cbtLength라는 변수에 입력해줍니다.
👉 questionAll이라는 빈 배열을 선언합니다.
👉 questionLength이라는 변수를 0으로 초기화합니다.
👉 questionRest라는 변수를 questionLength로 초기화합니다. (현재 questionLength의 값이 0이므로 questionRest도 0이 됩니다)