JAVASCRIPT

퀴즈 사이트 만들기 ( 6 )!!

김도현2 2023. 3. 27. 19:54
반응형

퀴즈 사이트 만들기 ( 6 )

객관식 문제들을 많이 풀 수 있도록 슬라이드 형식으로 만들어 보겠습니다.

 

 

 

 

 

 

[ 한번 풀어보개~ ]

 

 

 

https://ehcjswo.github.io/web2023/javascript/quiz/quizEffect06.html

링크 들어가시는게 이펙트가 안깨지고 좋습니다!

 

 

 

VSCode

<body>
    <header id="header">
        <h1><a href="../javascript14.html">Quiz <em>객관식 확인하기 (슬라이드) 유형</em></a></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 class="active"><a href="quizEffect06.html">6</a></li>
        </ul>
    </header>
    <!--//header-->
    
    <main id="main">
        <div class="quiz__wrap">
            <div class="quiz__info2">점수!</div>
            <div class="quiz__info3">수고했개!</div>
            <div class="quiz">
                <div class="quiz__header">
                    <h2 class="quiz__title"></h2>
                </div>
                <div class="quiz__main">
                    <div class="quiz__question">
                    </div>
                    <div class="quiz__view"> 
                        <div class="dog__wrap">
                            <div class="all"></div>
                            <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>
    <!--//main-->
    
    <footer id="footer">
        <a href="mailto:ehcjswo1@gmail.com">ehcjswo1@gmail.com</a>
    </footer>
    <!--//footer-->

 

html 부분 입니다.

페이지는 header main 요소로 구성됩니다. header 요소에는 페이지 제목과 각 퀴즈에 대한 링크가 포함된 메뉴가 있습니다.

main 요소에는 quiz__wrap 클래스가 있는 div 요소가 있으며, 여러 퀴즈의 정보가 이 div 요소 안에 동적으로 로딩됩니다.

 

.quiz__choice 클래스는 문제가 많기때문에 문제 출력을 스크립트로 하려고 주석처리를 했습니다.

주석 처리된 코드를 제거하면, 퀴즈 질문과 선택지가 나타납니다.

 

문제가 많기 때문에 문제 정보들을 배열안에 넣어 사용할 겁니다.

quiz 클래스를 html에서 사용하기엔 수많은 코드를 반복해야 하기 때문에 스크립트에 넣어 반복문을 이용하여 코드를 줄여줄 수 있습니다.

 

 

 

 

 

script

<script>
        //문제 정보
        const quizInfo = [
            {
                infoType: "정보처리 기능사",
                infoTime: "2010년 5회",
                infoNumber : "20100501",
                infoQuestion: "다음이 설명하고 있는 데이터 입출력 방식은?<br>- 데이터의 입출력 전송이 CPU를 통하지 않고, 입출력 장치와 기억 장치 간에 직접 데이터를 주고받는다.<br>- CPU와 주변 장치간의 속도차를 줄일 수 있다." ,
                infoChoice: ["DCA","DMA","Multiplexer","Channel"],
                infoAnswer: "DMA",
                infoDesc: "DMA(Direct Memory Access) : CPU 관여 없이 주기억장치와 보조기억장치(주변장치) 사이에 데이터를 전송하는 방식"
            },{
                infoType: "정보처리 기능사",
                infoTime: "2010년 5회",
                infoNumber : "20100502",
                infoQuestion: "컴퓨터 시스템의 중앙처리장치를 구성하는 하나의 회로로써 산술 및 논리 연산을 수행하는 장치는?",
                infoChoice: ["Arithmetic Logic Uni","Memory Unit","I/O Unit","Associative Memory Unit",],
                infoAnswer: "Arithmetic Logic Uni",
                infoDesc: "ALU(Arithmetic Logic Unit) : 산술 논리 연산 장치<br>Memory Unit : 기억 장치<br>I/O Unit : 입출력 장치<br>Associative Memory Unit : 연관메모리(주소가 아닌 내용에 <br>의한 참조를 할수 있으며 캐시 메모리로 주로 사용됨)"
            },{
                infoType: "정보처리 기능사",
                infoTime: "2010년 5회",
                infoNumber : "20100503",
                infoQuestion: "제어논리장치(CLU)와 산술논리연산 장치(ALU)의 실행순서를 제어하기 위해 사용되는 레지스터는?",
                infoChoice: ["누산기(accumulator)","프로그램 상태 워드(program Status World)","명령 레지스터(instruction register)","플래그 레지스터(flag register)"],
                infoAnswer: "플래그 레지스터(flag register)",
                infoDesc: "플래그 레지스터는 플래그 비트에 0 또는 1을 기록하여 각종 상태가 ON, OFF 인지 구별할수 있게 해줍니다."
            },{
                infoType: "정보처리 기능사",
                infoTime: "2010년 5회",
                infoNumber : "20100504",
                infoQuestion: "번지(address)로 지정된 저장위치(storage location)의 내용이 실제 번지가 되는 주소지정번지는?",
                infoChoice: ["간접지정방식","완전지정방식","절대지정방식","상대지정방식"],
                infoAnswer: "간접지정방식",
                infoDesc: "번지의 번지가 실제 주소가 된다고 하니 2번 참조되는 것을 <br>의미 합니다. 즉시주소 지정방식 - 메모리 참조횟수 0<br>직접주소 지정방식 - 메모리 참조횟수 1<br>간접주소 지정방식 - 메모리 참조횟수 2<br>그러므로 간접주소 지정방식."
            },{
                infoType: "정보처리 기능사",
                infoTime: "2010년 5회",
                infoNumber : "20100505",
                infoQuestion: "JK플립플롭(flip flop)에서 보수가 출력되기 위한 J, K의 입력상태는??",
                infoChoice: ["J=1, K=0","J=0, K=1","J=1, K=1","J=0, K=0"],
                infoAnswer: "J=1, K=1",
                infoDesc: "J K<br>0 0 - 기존값유지<br>0 1 - 0<br>1 0 - 1<br>1 1 - 반전(=보수)"
            },{
                infoType: "정보처리 기능사",
                infoTime: "2010년 5회",
                infoNumber : "20100506",
                infoQuestion: "2진수 (10001010)를 2의 보수로 옳게 표현한 것은?",
                infoChoice: ["01110101","01110110","10001011","10000110"],
                infoAnswer: "01110110",
                infoDesc: "10001010 을 1의 보수로 바꿉니다 : 0은 1로, 1은 0으로 변경하면됩니다. 01110101 로 바꿔 1보수에 +1을 해주면 2의 보수가 됩니다. 01110110 이 답입니다. 간단하게 바꾸는 방법은 문제의 2진수에서 오른쪽에서 부터 처음 1이 나올때까지는 그대로 적고 나머지는 반대로 바꾸면 됩니다."
            },{
                infoType: "정보처리 기능사",
                infoTime: "2010년 5회",
                infoNumber : "20100506",
                infoQuestion: "수고하셨습니다!",
                infoChoice: [" ", " ", " ", " "],
                infoAnswer: "",
                infoDesc: "10001010 을 1의 보수로 바꿉니다 : 0은 1로, 1은 0으로 변경하면됩니다. 01110101 로 바꿔 1보수에 +1을 해주면 2의 보수가 됩니다. 01110110 이 답입니다. 간단하게 바꾸는 방법은 문제의 2진수에서 오른쪽에서 부터 처음 1이 나올때까지는 그대로 적고 나머지는 반대로 바꾸면 됩니다."
            }
        ];

        //선택자
        const quizWrap = document.querySelector(".quiz__wrap");         
        const quizWrapall = document.querySelector(".dog__wrap .all");  //남은 문제수
        const quizTitle = quizWrap.querySelector(".quiz__title");       //문제출처
        const quizChoice = quizWrap.querySelector(".quiz__choice");     //문제정보
        const quizQuestion = quizWrap.querySelector(".quiz__question"); //문제 번호와 제목
        const quizDesc = quizWrap.querySelector(".quiz__desc");         //문제 설명
        const dogWrap = quizWrap.querySelector(".dog__wrap");           //강아지
        const quizAnswer = quizWrap.querySelector(".quiz__answer");     //정답 버튼 (css박스)
        const quizNext = quizWrap.querySelector(".quiz__answer .next"); //정답 버튼
        const quizInfo2 = quizWrap.querySelector(".quiz__info2");       //점수말풍선
        const quizInfo3 = quizWrap.querySelector(".quiz__info3");       //말풍선
        
        

        let quizCount = 0;
        let quizScore = 0;


        
        //문제 출력
        const updateQuiz = (index) => {
            let allTag = `
                <div>남은문제 : ${5+(-quizCount)}</div>
            `;
            let typeTag = `
                <span>${quizInfo[index].infoType}</span>
                <em>2010년 ${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 checkTag = `
                점수!<br>6문제중 <br>${quizScore}문제를 맞췄개.<br>${Math.ceil((quizScore / (quizInfo.length-1)) *100)} 점!!
            `;  
              
            quizTitle.innerHTML = typeTag;
            quizQuestion.innerHTML = questionTag;
            quizChoice.innerHTML = choiceTag;
            quizDesc.innerHTML = descTag;
            quizWrapall.innerHTML = allTag;
            quizInfo2.innerHTML = checkTag;

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

            //forEach문으로
            quizChoiceSpan.forEach((span) => {
               span.setAttribute("onclick", "choiceSelected(this)");
             });
            //for문으로
            //for(let i=0; i<quizChoiceSpan.length; i++){
            //    quizChoiceSpan[i].setAttribute("onclick", "choiceSelected(this)");
            //};
            

            //정답 버튼,해설 숨기기
            quizAnswer.style.display = "none";
            quizDesc.style.display = "none";
            quizInfo2.style.display = "none";
            quizInfo3.style.display = "none";
            
        };
        
        updateQuiz(quizCount);
        

        //객관식 선택
        function choiceSelected(answer){
            let userAnswer = answer.textContent;                //사용자 정답
            let currentAnswer = quizInfo[quizCount].infoAnswer; //문제 정답
            
            if(userAnswer == currentAnswer){
                dogWrap.classList.remove("dislike");
                dogWrap.classList.add("like");
                quizScore++
                
            } else {
                dogWrap.classList.remove("like");
                dogWrap.classList.add("dislike");
            }
            //정답 버튼,해설 나타나기
            quizAnswer.style.display = "block";
            quizDesc.style.display = "block";

        };

        
        //정답 확인
        quizNext.addEventListener("click", () => {
            
            ++quizCount;
            updateQuiz(quizCount);
            dogWrap.classList.remove("like","dislike");
            if(quizCount == 6){
                quizInfo2.style.display = "block";
                quizInfo3.style.display = "block";
                quizChoice.style.display = "none";
                quizQuestion.style.display = "none";
                dogWrap.classList.add("like");
                quizWrapall.style.display = "none";
                quizTitle.style.display = "none";
            }
            
        });

자바스크립트쪽 코드가 꽤 깁니다.

 

let quizCount = 0; 

let quizScore = 0; 문제 수와, 점수를 저장할 변수입니다.

const quizInfo 이 변수는 정보를 저장한 객체 입니다.

 

 

 

//선택자
const quizWrap = document.querySelector(".quiz__wrap");         
const quizWrapall = document.querySelector(".dog__wrap .all");  //남은 문제수
const quizTitle = quizWrap.querySelector(".quiz__title");       //문제출처
const quizChoice = quizWrap.querySelector(".quiz__choice");     //문제정보
const quizQuestion = quizWrap.querySelector(".quiz__question"); //문제 번호와 제목
const quizDesc = quizWrap.querySelector(".quiz__desc");         //문제 설명
const dogWrap = quizWrap.querySelector(".dog__wrap");           //강아지
const quizAnswer = quizWrap.querySelector(".quiz__answer");     //정답 버튼 (css박스)
const quizNext = quizWrap.querySelector(".quiz__answer .next"); //정답 버튼
const quizInfo2 = quizWrap.querySelector(".quiz__info2");       //점수말풍선
const quizInfo3 = quizWrap.querySelector(".quiz__info3");       //말풍선

선택자는 문제와 제목, 정답, 해설같은 정보들이 들어갈 자리를 const변수에 저장해주는 것 입니다.

이 변수를 사용해, innerHTML, textContent 메서드를 이용하여 정보를 자리에 불러 올 수있습니다.

 

 

 

 

//문제 출력
const updateQuiz = (index) => {
    let allTag = `
        <div>남은문제 : ${5+(-quizCount)}</div>
    `;
    let typeTag = `
        <span>${quizInfo[index].infoType}</span>
        <em>2010년 ${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 checkTag = `
        점수!<br>6문제중 <br>${quizScore}문제를 맞췄개.<br>${Math.ceil((quizScore / (quizInfo.length-1)) *100)} 점!!
    `;  

    quizTitle.innerHTML = typeTag;
    quizQuestion.innerHTML = questionTag;
    quizChoice.innerHTML = choiceTag;
    quizDesc.innerHTML = descTag;
    quizWrapall.innerHTML = allTag;
    quizInfo2.innerHTML = checkTag;

updateQuiz 함수는 매개변수로 index 값을 받습니다.

이 index 값은 quizInfo 배열 내에서 현재 출력할 퀴즈의 인덱스 번호를 의미합니다.

 

함수 내부에는 여러 개의 HTML 태그들이 포함되어 있습니다.

allTag 변수는 현재까지 남은 문제 수를 표시하고,

typeTag 변수는 퀴즈의 유형과 출제 시기를 나타냅니다.

questionTag 변수는 퀴즈의 질문을 나타냅니다. 

choiceTag 변수는 퀴즈의 보기를 나타냅니다.

descTag 변수는 퀴즈의 정답과 설명을 나타냅니다.

checkTag 변수는 사용자가 퀴즈를 푼 후 정답 개수와 점수를 나타냅니다.

 

이 값을 사용자에게 보여주기 위해 HTML의 innerHTML 속성을 이용하여 해당 UI에 변수들의 값을 적용합니다.

 

 

 

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

    //forEach문으로
    quizChoiceSpan.forEach((span) => {
        span.setAttribute("onclick", "choiceSelected(this)");
    });
    //for문으로
    // for(let i=0; i<quizChoiceSpan.length; i++){
    //     quizChoiceSpan[i].setAttribute("onclick", "choiceSelected(this)");
    // };

    //정답 버튼,해설 숨기기
    quizAnswer.style.display = "none";
    quizDesc.style.display = "none";
    quizInfo2.style.display = "none";
    quizInfo3.style.display = "none";
};

updateQuiz(quizCount);


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

    if(userAnswer == currentAnswer){
        dogWrap.classList.remove("dislike");
        dogWrap.classList.add("like");
        quizScore++

    } else {
        dogWrap.classList.remove("like");
        dogWrap.classList.add("dislike");
    }
    //정답 버튼,해설 나타나기
    quizAnswer.style.display = "block";
    quizDesc.style.display = "block";
};

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

    ++quizCount;
    updateQuiz(quizCount);
    dogWrap.classList.remove("like","dislike");
    if(quizCount == 6){
        quizInfo2.style.display = "block";
        quizInfo3.style.display = "block";
        quizChoice.style.display = "none";
        quizQuestion.style.display = "none";
        dogWrap.classList.add("like");
        quizWrapall.style.display = "none";
        quizTitle.style.display = "none";
    }
});

처음에는 quizCount를 0으로 초기화하고, quizInfo 배열에서 첫 번째 질문을 가져와서 화면에 보여줍니다.

 

그리고 updateQuiz 함수는 현재 quizCount에 해당하는 질문 정보를 가져와서, HTML 요소를 업데이트하여 새로운 질문을 화면에 보여주는 역할을 합니다.

 

각 질문에는 4개의 객관식 답안이 있고, quizChoiceSpanquizChoiceInput은 해당 답안의 span과 input 요소를 선택합니다. 그리고 각 span 요소에 대해 forEach 루프를 사용하여 onclick 이벤트를 추가합니다. 이벤트는 choiceSelected 함수를 호출하며, 선택된 답안의 span 요소를 매개 변수로 전달합니다.

 

choiceSelected 함수는 사용자가 선택한 답안과 문제의 정답을 비교합니다. 만약 둘이 같다면, 개 이미지에 "like" 클래스를 추가하고, 퀴즈 점수를 증가시킵니다. 그렇지 않으면 "dislike" 클래스를 추가합니다.

 

마지막으로, "다음" 버튼에 대한 클릭 이벤트가 처리되며, quizCount를 증가시켜 다음 질문으로 이동합니다. updateQuiz 함수가 호출되어 다음 질문을 화면에 업데이트합니다. 마지막 질문에 도달하면, 결과 화면이 표시됩니다!