“ 지연되는 프로젝트에 인력을 더 투입하면 오히려 더 늦어진다. ”
- Frederick Philips Brooks
Mythical Man-Month 저자
자바스크립트 퀴즈 이펙트 7 OMR카드 형식 만들어보기
오늘은 수업시간에 한 자바스크립트
퀴즈 사이트 만들기
OMR형식에 대해 정리해보겠습니다.
💜 퀴즈 7번째 OMR
head
<!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>CBT카드 형식</title>
<meta charset="ISO-8859-4">
<title>Example Page</title>
<link rel="stylesheet" href="css/reset.css">
<link rel="stylesheet" href="css/quiz.css">
<link rel="shortcut icon" type="image/x-icon" href="../quiz/img/cake.png"/>
<link rel="apple-touch-icon" sizes="114x114" href="../quiz/img/cake.png"/>
<link rel="apple-touch-icon" href="../quiz/img/cake.png"/>
</head>
우선 header부분에 css를 연결해주고
파비콘도 만들어 넣어줬습니다.
body
<body>
<header id="header">
<h1><a href="../javascript14.html">Quiz <em>객관식 확인 OMR카드 형식</em></h1></a>
<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">7</a></li>
<li><a href="webd.html">web</a></li>
</ul>
</header>
<!-- //header -->
<main id="main">
<div class="quiz__wrap__cbt">
<div class="cbt__header">
<h2>2020년 1회 정보처리 기능사 기출문제</h2>
</div>
<!-- header -->
<div class="cbt__conts">
<div class="cbt__quiz">
<!-- <div class="cbt good">
<div class="cbt__question"><sapn>1</sapn>. 객체지향 프로그램에서 데이터를 추상화하는 단위는?</div>
<div class="cbt__question__img"><img src="../quiz/img/gineungsaJC2023_01_01.jpg" alt="기능사"></div>
<div class="cbt__selects">
<input type="radio" id="select1">
<label for="select1"><span>클래스</span></label>
<input type="radio" id="select2">
<label for="select2"><span>메소드</span></label>
<input type="radio" id="select3">
<label for="select3"><span>상속</span></label>
<input type="radio" id="select4">
<label for="select4"><span>메시지</span></label>
</div>
<div class="cbt__desc">객체지향언어는 __이다. 객체지향언어는 __이다. 객체지향언어는 __이다. 객체지향언어는 __이다.</div>
<div class="cbt__keyword">객체지향언어</div>
</div> -->
</div>
</div>
<!-- conts -->
<div id="cbt">
<div class="cbt__time">
<a href="#">59분 10초</a>
</div>
<div class="cbt__submit">제출하기</div>
</div>
<div class="cbt__aside">
<div class="cbt__info">
<div>
<div class="cbt__title">수험자 : <em>땡떙땡</em></div>
<div class="cbt__score">
<span>전체 문제수 : <em>60</em>문항</span>
<span>남은 문제수 : <em>59문항</em></span>
</div>
</div>
</div>
<div class="cbt__omr">
<!-- <div class="omr">
<strong>1</strong>
<input type="radio" id="omr0_1">
<label for="omr0_1">
<span class="label-inner">1</span>
</label>
<input type="radio" id="omr0_2">
<label for="omr0_2">
<span class="label-inner">2</span>
</label>
<input type="radio" id="omr0_3">
<label for="omr0_3">
<span class="label-inner">3</span>
</label>
<input type="radio" id="omr0_4">
<label for="omr0_4">
<span class="label-inner">4</span>
</label>
</div> -->
</div>
</div>
<!-- aside -->
</div>
</main>
<!-- //main -->
<!-- <footer id="footer">
<a href="mailto.hunmi961119@gmail.com">hunmi961119@gmail.com</a>
</footer> -->
<!-- //footer -->
그 다음 body안에
header, main, footer로
영역을 나누어 레이아웃작업을 해줬습니다.
원래작업했던 것들은
여기 안에서 문제정보와, OMR카드 작업을 해주었는데
이번에는 밑에 script부분에서 작업해줄거기때문에
일단 주석처리를 해놨습니다.
script
<script>
const cbtQuiz = document.querySelector(".cbt__quiz");
const cbtOmr = document.querySelector(".cbt__omr");
const cbtSubmit = document.querySelector(".cbt__submit");
let questionAll = [];//모든 퀴즈 정보
//데이터 가져오기
const dataQuestion = () => {
fetch("json/gineungsaWD2023_01.json")//이 json테이터 이름을 items로 만든것
.then(res => res.json()) //res 규칙 json파일을 가져오겠다는 규칙
.then(items => { //items 도 내가 지정한 이름
// console.log(items);
questionAll = items.map((item, index) => {
// console.log(item)
const formattedQuestion = {
question: item.question,
number: index + 1
// choice1: Builder,
// choice2: Prototype,
// choice3: Prototype,
// choice4: Visitor,
}
const answerChoices = [...item.incorrect_answers];//오답 불러오기
formattedQuestion.answer = Math.floor(Math.random()*answerChoices.length) +1;//정답 불러오기(랜덤)
// console.log(Math.floor(Math.random()*answerChoices.length)+1)
// console.log(formattedQuestion.Answer);
answerChoices.splice(formattedQuestion.answer-1,0,item.correct_answer);//정답을 랜덤으로 추가
//보기 추가
answerChoices.forEach((choice, index) => {
formattedQuestion["choice" + (index + 1)] = choice;
});
//문제에 대한 해설이 있으면 출력
if(item.hasOwnProperty("question_desc")){
formattedQuestion.questionDesc = item.question_desc;
}
//문제에 대한 이미지가 있으면 출력
if(item.hasOwnProperty("question_img")){
formattedQuestion.questionImg = item.question_img;
}
//해설이 있으면 출력
if(item.hasOwnProperty("desc")){
formattedQuestion.desc = item.desc;
}
//키워드가 번호가 있으면 출력
if(item.hasOwnProperty("keyword_num")){
formattedQuestion.keywordNum = item.keyword_num;
}
// console.log(formattedQuestion);
return formattedQuestion;
});
// console.log(questionAll);
newQuestion();//문제 만들기
})
.catch((err) => console.log(err));
const quizImg = document.querySelectorAll(".cbt__question__img");
if(quizImg[number].innerText == "img/undefined.png"){
quizImg[number].classList.add("hide");
}
}
//문제 만들기
const newQuestion = () => {
console.log(questionAll);
const exam = [];
const omr = [];
questionAll.forEach((question, number) => {
console.log(question.questionImg);
exam.push(`
<div class="cbt">
<div class="cbt__question"><sapn>${question.number}</sapn>. ${question.question}</div>
<div class="cbt__question__img">
<img src="img/${question.questionImg}.jpg" alt="">
</div>
<div class="cbt__selects">
<input type="radio" id="select${number}_1" name="select${number}" value="${number+1}_1" onclick="answerSelect(this)">
<label for="select${number}_1"><span>${question.choice1}</span></label>
<input type="radio" id="select${number}_2" name="select${number}" value="${number+2}_2" onclick="answerSelect(this)">
<label for="select${number}_2"><span>${question.choice2}</span></label>
<input type="radio" id="select${number}_3" name="select${number}" value="${number+3}_3" onclick="answerSelect(this)">
<label for="select${number}_3"><span>${question.choice3}</span></label>
<input type="radio" id="select${number}_4" name="select${number}" value="${number+4}_4" onclick="answerSelect(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}_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}_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}_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}_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('');
}
//정답 확인
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.slice(-1);
const numberAnswer = userAnswer ? userAnswer.slice(-1) : undefined;
// console.log(numberAnswer);
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 answerSelect = () => {
}
cbtSubmit.addEventListener("click", answerQuiz)
dataQuestion();
</script>
</body>
</html>
-----
fetch함수를 사용해 서버에서 json파일을 가져오기
.then(res => res.json()); 위에서 가져온 파일을 res.json()메서드를 사용해 JSON객체로 변환.
.then(items => {...}변환된 JSON객체를 items매개변수에 전달
map메서드를 사용해 각각의 문제 데이터를 가공,
이를 하나의 객체에 담아서 questionAll배열에 저장
cosnt formattedQusetion = {...}:map메서드에서 현재 문제 데이터를 가공
이때 formattedQuestion 객체를 선언.
이 객체는 가공된 문제 데이터를 저장할 객체
cosnt answerChoices = [...item.incorrect_answers] 현재 문제의 오답들을 배열로 저장
formattedQuestion.answer = Math.floor(Math.random()*answerChoices.length)+1
오답 중에서 랜덤으로 정답을 선택
formattedQuestion 속성에 정답의 인덱스를 저장.
answerChoices.splice(formattedQuestion.answer-1,0,item.correct_answer)
오답 중에서 랜덤으로 선택한 정답을 anwerChoices배열에 추가
return formattedQusetion map 메서드에서 현재 문제 데이터를 가공한 결과를 반환
newQuestion() questionAll 배열에 저장된 문제 데이터를 하나씩 화면에 출력하는 함수 newQuestion()을 호출
if(quizImg[number].innerText == "img/undefined.png") 출력된
문제 데이터에 이미지가 없는 경우, 해당 이미지를 숨겨줍니다.
남은 문제수 출력하기
<div class="cbt__info">
<div>
<div class="cbt__title">수험자 : <em>진현미</em></div>
<div class="cbt__score">
<span>전체 문제수 : <em class="num1">60</em>문항</span>
<span>남은 문제수 : <em class="num2">60</em>문항</span>
</div>
</div>
</div>
남은 문제수가 들어갈 자리에
class를 넣어 이름을 지정해줍니다.
const ScoreNum1 = document.querySelector(".num1");
const ScoreNum2 = document.querySelector(".num2");
선택자를 추가해주고
ScoreNum1.innerText - questionAll.length;
ScoreNum2.innerText - questionAll.length;
데이터 가져오기에서
문제수의 갯수를 불러옵니다.
const answerSelect = () => {
const answeredCount = document.querySelectorAll('input[type="radio"]:checked').length;
const remainingCount = questionAll.length - answeredCount;
ScoreNum2.innerText = remainingCount;
return remainingCount;
};
정답이 끝난 부분에서
객관식을 선택했을때 문제의 갯수가 하나빠지는 값을 Num2에 저장해주게 해줍니다.