JAVASCRIPT

자바스크립트 퀴즈이펙트 07 추가 부분

ture403 2023. 4. 4. 20:23

- Frederick Philips Brooks
Mythical Man-Month 저자
728x90
반응형

완성된화면입니다.

 

HTML 코드입니다.

<!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">7</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 class="cbt good">
                        <div class="cbt__question"><span>1</span>. 객체지향 프로그램에서 데이터를 추상화하는 단위는?</div>
                        <div class="cbt__question__img"><img src="img/gineungsaWD2023_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>
            <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">60</em>문항</span>
                            <span>남은 문제수 : <em class="cbt_rest">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>
            <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_04">정보처리기능사 2005년 4회</option>
                            <option value="gineungsaJC2005_05">정보처리기능사 2005년 5회</option>
                            <option value="gineungsaJC2006_01">정보처리기능사 2006년 1회</option>
                            <option value="gineungsaJC2006_02">정보처리기능사 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="gineungsaWD2010_00">웹디자인기능사 2010년 1회</option>
                            <option value="gineungsaWD2010_02">웹디자인기능사 2010년 2회</option>
                            <option value="gineungsaWD2010_04">웹디자인기능사 2010년 4회</option>
                            <option value="gineungsaWD2010_05">웹디자인기능사 2010년 5회</option>
                            <option value="gineungsaWD2011_01">웹디자인기능사 2011년 1회</option>
                            <option value="gineungsaWD2011_02">웹디자인기능사 2011년 2회</option>
                            <option value="gineungsaWD2011_04">웹디자인기능사 2011년 4회</option>
                            <option value="gineungsaWD2011_05">웹디자인기능사 2011년 5회</option>
                            <option value="gineungsaWD2012_02">웹디자인기능사 2012년 2회</option>
                            <option value="gineungsaWD2012_04">웹디자인기능사 2012년 4회</option>
                            <option value="gineungsaWD2012_05">웹디자인기능사 2012년 5회</option>
                            <option value="gineungsaWD2013_02">웹디자인기능사 2013년 2회</option>
                            <option value="gineungsaWD2013_04">웹디자인기능사 2013년 4회</option>
                            <option value="gineungsaWD2013_05">웹디자인기능사 2013년 5회</option>
                            <option value="gineungsaWD2014_01">웹디자인기능사 2014년 1회</option>
                            <option value="gineungsaWD2014_04">웹디자인기능사 2014년 4회</option>
                            <option value="gineungsaWD2014_05">웹디자인기능사 2014년 5회</option>
                            <option value="gineungsaWD2015_01">웹디자인기능사 2015년 1회</option>
                            <option value="gineungsaWD2015_03">웹디자인기능사 2015년 3회</option>
                            <option value="gineungsaWD2015_04">웹디자인기능사 2015년 4회</option>
                            <option value="gineungsaWD2015_05">웹디자인기능사 2015년 5회</option>
                            <option value="gineungsaWD2016_01">웹디자인기능사 2016년 1회</option>
                            <option value="gineungsaWD2016_04">웹디자인기능사 2016년 4회</option>
                        </select>
                    </div>
                    <button class="choice_button">시작하기</button>
                </div>
            </div>
        </div>
    </main>
    <!-- //main -->
</body>
<html>
  • 기존HTML 코드에서 모달창 추가하는 태그를 작성했습니다.

JS코드입니다.

const cbt = document.querySelectorAll(".cbt");
const cbtQuiz = document.querySelector(".cbt__quiz");
const cbtOmr = document.querySelector(".cbt__omr");
const cbtSubmit = document.querySelector(".cbt__submit");
const cbtRest = document.querySelector(".cbt_rest");
const cbtLength = document.querySelector(".cbt_length");
const button = document.querySelector(".choice_button");
const cbtStart = document.querySelector(".cbt__start");

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

//모달창 닫기
button.addEventListener("click",()=>{
    cbtStart.style.display="none"
});

//데이터 가져오기
const dataQuestion = () => {
    fetch("json/gineungsajcWD2023_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_answers];  //오답 불러오기
            formattedQuestion.answer = Math.round(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;
            // console.log(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"> <img src="img/${question.question_img}.jpg" alt=""></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>
            <div class="cbt">
                <div class="cbt__question"><span>${question.number}</span>. ${question.question}</div>
                <div class="cbt__question__img"> <img src="img/${question.question_img}.jpg" alt=""></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>
        `)

        // if(question.question_img){

        // } else {
        //     question.question_img.displa`y ="none";
        // }
    });

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

    const cbtQuestionDesc = document.querySelectorAll(".cbt__question__desc");


    cbtQuestionDesc.forEach(el=>{
        if(el.innerHTML == "undefined"){
            el.style.display="none";
        }
    })

}

//정답 확인
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 = (el) => {
    const answer = el.value;
    const answerNum = answer.split("_");

    const select = document.querySelectorAll(".cbt__omr .omr");
    const label = select[answerNum[0]].querySelectorAll("input");
    label[answerNum[1]-1].checked = true;

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

}

// 보기 체크2
const answerSelect = (el) => {
    const answer = el.value;
    const answerNum = answer.split("_");

    const select = document.querySelectorAll(".cbt__quiz .cbt");
    const label = select[answerNum[0]].querySelectorAll("input");
    label[answerNum[1]-1].checked = true;

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

cbtSubmit.addEventListener("click", answerQuiz);
dataQuestion();

요점정리

서언자는 다음과 같습니다.

  • cbt: CBT 시스템의 문제와 보기를 감싸는 요소들을 나타내는 클래스입니다.
  • cbtQuiz: CBT 시스템의 문제들을 감싸는 요소를 나타내는 클래스입니다.
  • cbtOmr: CBT 시스템의 사용자의 답안들을 감싸는 요소를 나타내는 클래스입니다.
  • cbtSubmit: CBT 시스템의 답안 제출 버튼을 나타내는 클래스입니다.
  • cbtRest: CBT 시스템의 남은 문제 수를 출력하는 요소를 나타내는 클래스입니다.
  • cbtLength: CBT 시스템의 전체 문제 수를 출력하는 요소를 나타내는 클래스입니다.
  • button: 모달창에서 확인 버튼을 나타내는 클래스입니다.
  • cbtStart: CBT 시스템의 시작 모달창을 나타내는 클래스입니다.

먼저, 모달창의 확인 버튼을 누르면 모달창이 닫히는 이벤트를 추가하고, dataQuestion() 함수를 호출합니다. dataQuestion() 함수는 JSON 파일에서 CBT 시스템의 문제를 가져와서 questionAll 배열에 저장합니다.

newQuestion() 함수는 questionAll 배열의 각 문제들을 HTML 태그로 변환하여 화면에 출력합니다. 사용자가 답안을 선택하면, answerSelect2() 함수가 호출되어 사용자의 답안을 처리합니다.

이 코드에서는 Promise와 Fetch API를 사용하여 비동기적으로 데이터를 가져오고 처리합니다. 또한, JavaScript의 배열 메소드(map, forEach, splice)와 조건문(if)을 사용하여 데이터를 가공합니다.

 

전이랑 다르게 모달창을 만들었습니다. 확인하기를 클릭했을떄 모달창을 닫는 프로그램을 닫는 프로그램을 작성했습니다.

먼저 cbt_start 와 choice_button 을 선언자로 가져왔습니다. 가져와서 addEventListener 클릭 이벤트를 주고 그안에 cbt_start 의 스타일을 display = block 을 줬습니다.

 

answerSelect2의 함수는 클릭하면 omr도 체크 되게 해준 코드입니다.

answerSelect2 함수는 객관식 문제에서 보기를 선택할 때 실행됩니다. 함수는 el 매개변수로 클릭된 보기의 요소를 받습니다.
클릭된 보기의 값을 answer 변수에 저장합니다.
answerNum 변수에는 answer 값에서 언더스코어(_)로 구분된 두 개의 인덱스가 저장됩니다. 예를 들어 answer 값이 "1_3"이면 answerNum 변수에는 [1, 3]이 저장됩니다.
select 변수에는 문제의 보기들을 나타내는 DOM 요소가 저장됩니다.
label 변수에는 answerNum에 저장된 인덱스를 이용하여 선택한 보기의 input 요소가 저장됩니다.
해당 input 요소를 체크합니다.
answerInput 변수에는 현재 선택된 모든 보기의 input 요소가 저장됩니다.
cbtRest 요소에는 아직 선택되지 않은 보기의 수를 표시합니다.

answerSelect 함수는 answerSelect2 함수와 거의 유사합니다. 다만, 문제가 보기로 나열되지 않고 각각의 문제와 연결된 보기들이 있는 경우 사용됩니다. 함수 내에서 select 변수에는 각 문제에 대한 cbt 클래스가 지정된 DOM 요소가 저장됩니다. 나머지 코드는 answerSelect2 함수와 동일합니다.

 

완성된 코드입니다.

https://github.com/ture403/web2023/blob/main/javascript/quiz/quizEffect07-1.html

 

GitHub - ture403/web2023: 수업시간예제입니다.

수업시간예제입니다. Contribute to ture403/web2023 development by creating an account on GitHub.

github.com

 

완성된 화면 입니다.

https://ture403.github.io/web2023/javascript/quiz/quizEffect07-1.html

 

퀴즈 이펙트07

수험자 : 홍길동 전체 문제수 : 60문항 남은 문제수 : 59문항

ture403.github.io

전에있던 퀴즈 이펙트 07번입니다.

https://ture403.tistory.com/62