javascript

quiz 유형 사이트 만들기- 07 ver1 json기초

grovy 2023. 4. 3. 22:28
728x90

퀴즈 사이트 만들기 !  ( 객관식유형 * 60문제 한번에 만들기+json형식 )

이번엔 객관식 퀴즈 60문제가 있는 사이트를 만들어볼까해요 

60문제와 omr 카드가 같이 나오게 만들어 봤어요 

 전체 소스는 요기 

주요 소스를 볼게요 ! 

 

<div class="quiz__wrap__cbt">
    <div class="cbt__header">
        <h2>2012년 2회 웹디자인기능사 기출문제</h2>
    </div>
    <div class="cbt__conts">
        <div class="cbt__quiz">
            <!-- <div class="cbt good">
                <div class="cbt__question"><span>1.</span>객체지향프로그램에서 데이터를 추상화하는 단위는?</div>
                <div class="cbt__question__img"><img src="img/gineungsaJC2023_01_01.jpg" alt="기능사"></img></div>
                <div class="cbt__selects">
                    <input type="radio" id="select1">
                    <label for="select1"><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>
    <div class="cbt__time"> 59분 10초</div>
    <div class="cbt__submit">제출하기</div>
    <div class="cbt__aside">
        <div class="cbt__info">
                <div class="cbt__title">수험자 : <em></em></div>
                <div class="cbt__score">
                    <span>전체문제수 : <em></em></span>
                    <span class="cbt__remainder">남은문제수 : <em></em></span>
            </div>
        </div>
        <div class="cbt__omr">
            <!-- <div class="omr">
                <strong>1</strong>
                <input type="radio" id="omr0_1">
                ....
                <label for="omr0_4">
                    <span class="label-inner">4</span>
                </label>
            </div> -->

        </div>
    </div>
</div>

위처럼 body영역엔 소스가 없고 

 

스크립트에서 소스를 cbt_quiz, cbt_omr에 소스를 뿌려 줄거에요 

데이터 불러오는 부분

//데이터 불러오기
const dataQuestion = () => {
    fetch("json/gineungsaWD2012_02.json")  
    //이 json파일을 가져올거에요
    .then(res => res.json())        
    //res.send()는 send에 전해진 argument에 따라서 Content-type이 자동적으로 만들어줘요
    .then(items=> {                 
    //객체를 한번더 가공해서 출력할거에요 
        questionAll = items.map((item, index)=>{
            const formattedQuestion = {
                number : index+1,
                question: item.question
            };
            //펼침연산자로 각각의 배열에 보기 123을 넣어준다 
            const answerChoices = [...item.incorrect_answers];

            //랜덤함수
            formattedQuestion.Answer = Math.floor( Math.random()*answerChoices.length)+1; 

            // splice로 임의의 위치 formattedQuestion.Answer로 정답값을 넣어주는거에요 
            answerChoices.splice(formattedQuestion.Answer-1,0,item.correct_answer);

            //객관식보기 배열을 forEach문에 index를 넣고 값을 formattedQuestion에 넣어줘요
            //오브젝트 타입안에 배열로 보기를 넣어준다고 생각하면 편해요 
            answerChoices.forEach((choice, index)=>{
                formattedQuestion["choice"+ (index+1)]  = choice;
            })

            //hasOwnProperty는 값이 있을경우 즉 true일때 if문을 실행해요 
            //문제에 대한 해설이 있으면 출력해 줄거에요 
            if(item.hasOwnProperty("question_desc")){
                formattedQuestion.questionDesc = item.question_desc;
            }
            //문제에 대한 이미지가 있으면 출력해 줄거에요 
            if(item.hasOwnProperty("question_img")){
                formattedQuestion.questionImg = item.question_img;
            }
            //문제에 대한 desc 있으면 출력해 줄거에요 
            if(item.hasOwnProperty("desc")){
                formattedQuestion.desc = item.desc;
            }

            return formattedQuestion;
        });
        //문제 불러오기
        newQuestion(questionAll); 
        //시작시 모든문제갰수를 넣어준다
        document.querySelector(".cbt__score em").innerHTML = questionAll.length+"문항";
        document.querySelector(".cbt__remainder em").innerHTML = questionAll.length+"문항";

		
    })
    .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__selects">
                    <input type="radio" id="select${number}_1" name="select${number}" value="${number+1}_0" 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}_1" 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}_2" 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}_3" 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}_0" onclick="answerSelectOmr(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+1}_1" onclick="answerSelectOmr(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+1}_2" onclick="answerSelectOmr(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+1}_3" onclick="answerSelectOmr(this)"">
                <label for="omr${number}_4"><span class="label-inner">4</span>
                </label>
            </div>
        `)

    })
    cbtQuiz.innerHTML=exam.join('');
    cbtOmr.innerHTML=omr.join('');

모든정보를 가지고 있는 questionAll에 forEach문을 사용하여 

마지막에 값을 넣어줘요 

 cbtQuiz.innerHTML=exam.join('');    문제넣기
 cbtOmr.innerHTML=omr.join('');       객관식보기넣기

 

정답 확인하기

const answerQuiz = () =>{
    const cbtSelects = document.querySelectorAll(".cbt__selects");
	
    //모든 정보를 가져와서 forEach로 값을 비교할거에요
    questionAll.forEach((question, number) =>{
    	
        const quizSelectsWrap = cbtSelects[number];
        const userSelector = `input[name=select${number}]:checked`;
        
		//객관식 보기 문제에 체크유무 상관없이 값을 가져오기
        const userAnswer = (quizSelectsWrap.querySelector(userSelector)||{}).value;
		//userAnswer의 값유무에 따라 slice하여 값을 가져와요 없으면 undefined
        const numberAnswer = userAnswer ? userAnswer.slice(-1) : undefined;
        
        //정답을 가져와서 비교해요 
        if(Number(numberAnswer)+1 == question.Answer){
            cbtSelects[number].parentElement.classList.add("good");
        }else{
            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");
        }
    })
}

answerQuiz메서드는 제출할때 실행합니다 제출해서 체점해요 

 

이름을 적을때까지 while문이 실행하기

let inputString = prompt('이름을 입력하세요', '이름을 적어주세요');
while(inputString=='' || inputString ==undefined){
    inputString = prompt('이름을 입력하세요', '이름 왜 안적어ㅡㅡ');
}

document.querySelector(".cbt__title").innerHTML = inputString;

왼쪽, 오른쪽 객관식체크 동기화 & 남은문제출력

//quiz >> omr 체크 동기화
const answerSelect= ()=>{
    //cbtQuiz 모든 체크 값을 가져 오는거에요 
    let checkAllRadio = cbtQuiz.querySelectorAll('input[type=radio]:checked');
	//체크한값을 오른쪽 omr에 checked할거에요
    checkAllRadio.forEach((radioNum, index)=>{
        cbtOmr.querySelector("input[id=omr"+radioNum.id.slice(6)).checked = true;
    });
    chkQuizNumber(checkAllRadio);
}

//omr >> quiz 체크 동기화
const answerSelectOmr =()=>{
    //cbtOmr에서 모든 체크 값을 가져 오는거에요 
    let checkAllRadio1 = cbtOmr.querySelectorAll('input[type=radio]:checked');
	//체크한값을 왼쪽 객관식 보기에 checked할거에요
    checkAllRadio1.forEach((radioNum, index)=>{
        cbtQuiz.querySelector("input[id=select"+radioNum.id.slice(3)).checked = true;
    });
    chkQuizNumber(checkAllRadio1);
}
//남은문항 계산하기 
//매개변수로 checkAllRadio1 or checkAllRadio을 가져와서 몇문제 남았는지 체크해요 
const chkQuizNumber=(checkAll)=>{
    //값초기화
    totQuestion = questionAll.length;
    //cbt__remainder 는 상단에 cbt__info 부분에 추가 시켜 줬어요 
    document.querySelector(".cbt__remainder em").innerHTML = totQuestion-checkAll.length+"문항";
}

 

남은시간 & 이름적기(심화)는 다음에 같이 해요 !