Lumpy Space Princess - Adventure Time

JAVASCRIPT

Quiz Effect 07 - 객관식 확인하기 CBT 카드 유형에 대해 알아보자 2

jongyung 2023. 4. 5. 01:00

“ 지연되는 프로젝트에 인력을 더 투입하면 오히려 더 늦어진다. ”

- Frederick Philips Brooks
Mythical Man-Month 저자
728x90

지난 게시글에서 추가한 내용이 script에 있으므로 이번에는 script 만 보여드리면서 설명하겠습니다.

script

<script>

    const cbtQuiz = document.querySelector(".cbt__quiz");
    const cbtOmr = document.querySelector(".cbt__omr");
    const cbtSubmit = document.querySelector(".cbt__submit");
    
    const cbtLength = document.querySelector(".cbt__length");
    const cbtRest = document.querySelector(".cbt__rest");

    let questionAll = []; //모든 퀴즈 정보
    
    let questionLength = 0; //전체 문제 수
    let questionRest = questionLength; //남은 문제 수

    //데이터 불러오기
    const dataQuestion = () => {
        fetch("json/gineungsaWD2023_01.json")
        .then(res => res.json())
        .then(items => {                
            questionAll = items.map((item, index) => {
                const formattedQuestion = {
                    question: item.question,
                    number: index + 1                            
                }
                const answerChoices = [...item.incorrect_answer]; //오답 불러오기
                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;
                }

                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"></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+1}_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+1}_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+1}_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}_0">
                <label for="omr${number}_1"><span class="label-inner">1</span></label>
                <input type="radio" name="omr${number}" id="omr${number}_2" value="${number}_1">
                <label for="omr${number}_2"><span class="label-inner">2</span></label>
                <input type="radio" name="omr${number}" id="omr${number}_3" value="${number}_2">
                <label for="omr${number}_3"><span class="label-inner">3</span></label>
                <input type="radio" name="omr${number}" id="omr${number}_4" value="${number}_3">
                <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 ? 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].innerHTML == "undefined"){
                quizDesc[number].classList.add("hide");
            } else {
                quizDesc[number].classList.remove("hide");
            }
        });
    }

 	//보기 체크
    const answerSelect2 = (el) => { 
        const answer = el.value; //value="${number}_1"
        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;

        const answerInputs = document.querySelectorAll(".cbt__selects input:checked")
        cbtRest.innerHTML = questionLength - answerInputs.length;
    }

    //보기 체크2
    const answerSelect = (el) => {     
        const answer = el.value; //value="${number}_1"
        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>

전체 문제 수와 전체 문제 수에서 푼 문제 수를 뺀 남은 문제 수를 표현하기 위해서 추가한 부분들이 있습니다.

const cbtLength = document.querySelector(".cbt__length);
const cbtRest = document.querySelector(".cbt__rest);

전체 문제 수는 변수 cbtLength 에,
남은 문제 수는 변수 cbtRest 에 저장한 선택자를 만들었습니다.

questionLength = questionAll.length;
cbtLength.innerHTML = questionLength;
cbtRest.innerHTML = questionLength;

전체 문제 수는 퀴즈의 모든 정보를 가진 questionAll 변수의 길이를 세주는 .length를 사용한 값이므로, 

questionLength 라는 변수를 정하고, 만들어 둔 선택자로 그 변수를 불러옵니다.

let questionLength = 0;
let questionRest = questionLength;

전체 문제 수에 0이라는 초기값을 주고, 
남은 문제 수는 전체 문제 수라는 초기값을 줬습니다.

 

그리고 보기 두 가지의 보기 체크 부분들은 문제에서 객관식을 선택하거나 omr 답안지에서 객관식을 선택한 경우에 답이 선택되게 하기 위해서 추가한 부분들입니다.

 

split() 메서드는 문자열을 구분자로 구분하고, 여러 개의 문자열(배열)을 반환합니다.

"문자열".split(구분자, [제한])