Lumpy Space Princess - Adventure Time

JAVASCRIPT

Quiz Effect 06 - 객관식(슬라이드) 확인하기(여러 문제) 유형에 대해 알아보자

jongyung 2023. 3. 27. 19:00

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

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

링크: 퀴즈 이펙트 06 (daanbi1345.github.io)

객관식 문제 유형인데, 한 문제를 풀면 다음 문제로 넘어가는 슬라이드 형식으로 만들어 보았습니다.

 

body의 main부분과 script를 나눠서 보여드리겠습니다.

body의 main

<main id="main">
    <div class="quiz__wrap">
        <div class="quiz">
            <div class="quiz__header">
                <h2 class="quiz__title"></h2>                    
            </div>
            <div class="quiz__main">
                <h4>몇 문제 째인지</h4>
                <div class="quiz__question"></div>
                <div class="quiz__view">
                    <div class="dog__wrap">
                    <div class="true">정답입니다!</div>
                    <div class="false">오답입니다!</div>
                        <div class="card-container">
                            <div class="dog">
                                <div class="head">
                                    <div class="ears"></div>
                                    <div class="face"></div>
                                    <div class="eyes">
                                        <div class="teardrop"></div>
                                    </div>
                                    <div class="nose"></div>
                                    <div class="mouth">
                                        <div class="tongue"></div>
                                    </div>
                                    <div class="chin"></div>
                                </div>
                                <div class="body">
                                    <div class="tail"></div>
                                    <div class="legs"></div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="quiz__choice">
                    <!-- <label for="choice1">
                        <input type="radio" id="choice1" name="choice" value="1">
                        <span></span>
                    </label>
                    <label for="choice2">
                        <input type="radio" id="choice2" name="choice" value="2">
                        <span></span>
                    </label>
                    <label for="choice3">
                        <input type="radio" id="choice3" name="choice" value="3">
                        <span></span>
                    </label>
                    <label for="choice4">
                        <input type="radio" id="choice4" name="choice" value="4">
                        <span></span>
                    </label> -->
                </div>
                <div class="quiz__answer">                      
                    <button class="next">다음 문제</button>                        
                </div>
                <div class="quiz__desc"></div>
            </div>                
        </div>           
    </div>        
</main>

구조는 지난 번과 같습니다.

quiz choice 부분을 script 에서 choiceTag 를 만들어서 ``안에 입력했습니다.

보다 간결한 방식이므로 이 방법을 적용했습니다.

script

<script>
//문제 정보
    const quizInfo = [
        {
            infoType : "정보처리 기능사",
            infoTime : "2011년 5회",
            infoNumber : "20110501",
            infoQuestion : "입출력 조작의 시간과 중앙처리장치의 처리시간과의 불균형을 보완하는 것은?",
            infoChoice : ["채널장치", "제어장치", "터미널장치", "콘솔장치"],
            infoAnswer : "채널장치",
            infoDesc : "채중입 : 채널은 중앙처리장치와 입출력장치 사이에 존재하며 서로의 속도 차이를 보완 합니다."
        }, {
            infoType : "정보처리 기능사",
            infoTime : "2011년 5회",
            infoNumber : "20110502",
            infoQuestion : " 명령어 형식(instruction format)에서 첫 번째 바이트에 기억되는 것은?",
            infoChoice : ["operand", "length", "question mark", "opcode"],              
            infoAnswer : "opcode",
            infoDesc : "명령어의 구성명령어 코드부 + 주소부 (한글),<br> OP-code + Operand (영문)"
        },{
            infoType : "정보처리 기능사",
            infoTime : "2011년 5회",
            infoNumber : "20110503",
            infoQuestion : "반가산기(Half-Adder)의 논리회로도에서 자리올림이 발생하는 회로는?",
            infoChoice : ["OR", "NOT", "ExclusiveOR", "AND"],
            infoAnswer : "AND",
            infoDesc : "반가산기 구성: S = A XOR B, C = A AND B, <br>S : SUM(합), C : Carry(자리올림)"
        }, {
            infoType : "정보처리 기능사",
            infoTime : "2011년 5회",
            infoNumber : "20110504",
            infoQuestion : "기억 장치 고유의 번지로서 0, 1, 2, 3, ...과 같이 16진수로 약속하여 순서대로 정해 놓은 번지는?",
            infoChoice : ["절대번지", "상대번지", "필수번지", "선택번지"],
            infoAnswer : "절대번지",
            infoDesc : "절대번지는 처음부터 순서대로 0, 1, 2, 3 과같이 주소를 부여 하는 방식입니다."
        }, {
            infoType : "정보처리 기능사",
            infoTime : "2011년 5회",
            infoNumber : "20110505",
            infoQuestion : "2진수의 1011의 1의 보수는?",
            infoChoice : ["0100", "1000", "0010", "1010"],
            infoAnswer : "0100",
            infoDesc : "1의 보수는 1은 0으로, 0은 1로 바꾸어 주면 답이 됩니다."
        }, {
            infoType : "정보처리 기능사",
            infoTime : "2011년 5회",
            infoNumber : "20110506",
            infoQuestion : "다음 진리표에 해당하는 GATE는 어느 것인가? <br><img style = 'width:340px; padding-left:110px' src='../quiz/quiz.image/2011.10.09.6번 문제.png'>",
            infoChoice : ["<img src='../quiz/quiz.image/2011.10.09.6번 객관식 1.png'>", "<img src='../quiz/quiz.image/2011.10.09.6번 객관식 2.png'>", "<img src='../quiz/quiz.image/2011.10.09.6번 객관식 3.png'>", "<img src='../quiz/quiz.image/2011.10.09.6번 객관식 4.png'>]"],
            infoAnswer : "2",
            infoDesc : "같으면 0다르면 1인 XOR 게이트 입니다."
        }
    ];

    //선택자
    const quizWrap = document.querySelector(".quiz__wrap");
    const quizTitle = quizWrap.querySelector(".quiz__title");
    const quizChoice = quizWrap.querySelector(".quiz__choice");
    const quizQuestion = quizWrap.querySelector(".quiz__question");
    const dogWrap = quizWrap.querySelector(".dog__wrap");
    const quizAnswer = quizWrap.querySelector(".quiz__answer")
    const quizNext = quizWrap.querySelector(".quiz__answer .next");//정답 버튼
    const quizDesc = quizWrap.querySelector(".quiz__desc");
    const quizMainH4 = quizWrap.querySelector(".quiz__main h4");

    let quizCount = 0;
    let quizScore = 0;

    //문제 출력
    const updateQuiz = (index) => {
        let typeTag = `
            <span>${quizInfo[index].infoType}</span>
            <em>${quizInfo[index].infoTime}</em>
        `;
        let questionTag = `
            <em>${index + 1}</em>.
            <span>${quizInfo[index].infoQuestion}</span>
        `;

        let choiceTag = `
            <label for="choice1">
                    <input type="radio" id="choice1" name="choice" value="1">
                    <span>${quizInfo[index].infoChoice[0]}</span>
            </label>
            <label for="choice2">
                <input type="radio" id="choice2" name="choice" value="2">
                <span>${quizInfo[index].infoChoice[1]}</span>
            </label>
            <label for="choice3">
                <input type="radio" id="choice3" name="choice" value="3">
                <span>${quizInfo[index].infoChoice[2]}</span>
            </label>
            <label for="choice4">
                <input type="radio" id="choice4" name="choice" value="4">
                <span>${quizInfo[index].infoChoice[3]}</span>
            </label>
        `;

        let descTag =`
            정답은 ${quizInfo[index].infoAnswer}입니다.<br>
            ${quizInfo[index].infoDesc}
        `;

        let h4Tag = `
            ${quizInfo.length - index} 문제 남았습니다.
        `;

        quizTitle.innerHTML = typeTag;
        quizChoice.innerHTML = choiceTag;
        quizQuestion.innerHTML = questionTag;
        quizDesc.innerHTML = descTag;
        quizMainH4.innerHTML = h4Tag;


        //보기 선택자
        const quizChoiceSpan = quizWrap.querySelectorAll(".quiz__choice span");
        const quizChoiceInput = quizWrap.querySelectorAll(".quiz__choice input");

        // quizChoiceSpan.forEach((span, num) => {
        //     span.setAttribute("onclick", "choiceSelected(this)");
        // });

        for(let i =0; i<quizChoiceSpan.length; i++){
            quizChoiceSpan[i].setAttribute("onclick", "choiceSelected(this)");
            quizChoiceInput[i].disabled = "true";
        };

        //다음 버튼, 해설 숨기기
        quizAnswer.style.display = "none";
        quizDesc.style.display = "none";

    };
    updateQuiz(quizCount); 

    //객관식 선택
    function choiceSelected(answer){
        let userAnswer = answer.textContent; //사용자 정답
        let currentAnswer = quizInfo[quizCount].infoAnswer; //문제 정답

        if(userAnswer == currentAnswer){
            console.log("정답입니다.");
            dogWrap.classList.add("like");
            quizScore++;
        } else {
            console.log("오답입니다.");
            dogWrap.classList.add("dislike");
        }

        //다음 버튼, 해설 나타나기
        quizAnswer.style.display = "block";
        quizDesc.style.display = "block";

        if(quizInfo.length - quizCount == 1){
            alert(quizScore + "개 맞았고, " + (quizInfo.length - quizScore) +"개 틀렸습니다. " + "점수는 " + Math.ceil((quizScore/quizInfo.length)*100) + "점입니다.");
        }

    };

    //정답 확인
    quizNext.addEventListener("click", () => {
        quizCount++;
        updateQuiz(quizCount); 

        dogWrap.classList.remove("like", "dislike");
    });

</script>

문제 정보 quizInfo 는 배열 안에 넣은 객체로 만들었습니다. 

그 중에서도 infoChoice 즉, 객관식 선택지는 배열로 만들었습니다.

 

이전에 썼던 방식과 같이 선택자를 만들어서 반복적인 입력을 피했습니다.

이번에는 선택자로 00Tag 라는 변수를 만들어서 ``안에 내용을 입력하는 방식을 썼습니다.

이렇게 하면, 반복적인 입력을 하지 않고, ${}안에 입력하고 싶은 부분을 불러오면 됩니다.

 

quizCount 와 quizScore 라는 변수를 만들어서 문제의 갯수와 점수를 구할 때 썼습니다.

문제 출력은 익명 함수를 써서 입력했습니다.

index 라는 자릿수를 매개 변수로 써서 문제 정보가 담겨있는 quizInfo의 배열을 불러왔습니다.

문제에 필요한 정보들을 쭉 입력한 후에, 

선택한 객관식 답이 맞으면 정답입니다 화면이 나오고, 틀리면 오답입니다 화면이 나오도록 설정했습니다.

 

.setAttribute()는 선택한 요소(element)의 속성(attribute) 값을 정합니다.
지정된 요소의 속성 값을 설정합니다. 속성이 이미 존재하면 값이 업데이트됩니다. 

그렇지 않으면 지정된 이름과 값으로 새 속성이 추가됩니다.

 

disabled 실제 DOM 개체의 속성을 사용하여 입력을 활성화하거나 비활성화합니다.
<input> 태그의 disabled 속성은 해당 <input> 요소가 비활성화됨을 명시합니다.
disabled 속성이 명시된 <input> 요소는 사용할 수 없으며, 사용자가 클릭할 수도 없습니다.
또한, 폼 데이터가 제출될 때도 disabled 속성이 명시된 <input> 요소의 데이터는 제출되지 않습니다.

 

따라서 이 속성을 사용하면 특정 조건이 충족될 때까지 사용자가 입력 필드를 클릭할 수 없도록 설정하고, 특정 조건이 충족되면 자바스크립트 등으로 disabled 속성값을 삭제하여 사용자가 입력 필드를 다시 사용할 수 있도록 조절할 수 있습니다. 
disabled 속성은 불리언(boolean) 속성입니다.
불리언 속성은 해당 속성을 명시하지 않으면 속성값이 자동으로 false 값을 가지게 되며, 명시하면 자동으로 true 값을 가지게 됩니다.