기초부터 시작하는 코딩/Javascript

Javascript를 이용한 사이트 만들기! - 퀴즈 효과 사이트 만들기06

kebab00 2023. 3. 27. 22:54

728x90

- 오늘은 퀴즈효과 6번째 입니다.

- 문제를 풀 때 마다 정답과 해설이 나오며 다음문제로 넘어가기를 누르면 다음문제가 나오는 형식입니다.

- 전체코드를 먼저 보고 스크립트 부분을 보겠습니다.

<!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>퀴즈 이펙트05</title>
    <link rel="stylesheet" href="css/reset.css">
    <link rel="stylesheet" href="css/quiz.css">
 <!-- //외부링크 -->
</head>
<body>
    <header id="header">
        <h1><a href="../javascript14.html"> Quiz</a> <em>주관식 확인하기(여러문제) 유형</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 class="active"><a href="quizEffect06.html">6</a></li>
        </ul>
    </header>
    <!-- //header -->
    <main id="main">
        <div class="quiz__wrap">
            <div class="quiz">
                <div class="qiuz__header">
                    <h2 class="quiz__title"></h2>
                </div>
                <div class="qiuz__main">
                    <div class="qiuz__question">
                    </div>
                    <div class="qiuz__view">
                        <div class="quiz__cnt"></div>
                        <div class="quiz__result"></div>
                        <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">
                    </div>
                    <div class="quiz__answer">
                        <button class="next">다음문제</button>
                    </div>
                    <div class="quiz__desc"></div>
                </div>       
            </div> 
            <!-- // 1번 문제 -->
        </div>
    </main>
    <!-- //main -->
    <footer id="footer">
        <a href="mailto:im.kebab00@gmail.com">im.kebab00@gmail.com</a>
    </footer>
    <!-- //footer -->
    <script>
        // 문제 정보

        const quizInfo = [
            {
                infoType : "정보처리 기능사" ,
                infoTime : "2011년 4회" ,
                infoNumber : "20110401",
                infoQuestion : "일반적으로 명령어의 패치 사이클 중에는 현재 수행하고 있는 명령어의 위치를 가리키고, 실행 사이클 중에는 바로 다음에 실행할 명령어의 위치를 가리키는 Register는?",
                infoChoice : ["누산기(accumulator)", "프로그램 카운터(program counter)", "명령어 레지스터(instruction register)", "범용 레지스터(general purpose register)"],
                infoAnswer: "프로그램 카운터(program counter)",
                infoDesc : "누산기(accumulator) : 연산결과 일시 저장<br>프로그램 카운터(program counter) : 다음 수행 명령번지 기억<br>명령어 레지스터(instruction register) : 현재수행중인 명령어를 일시저장"
            },{
                infoType : "정보처리 기능사" ,
                infoTime : "2011년 4회"  ,
                infoNumber : "20110402",
                infoQuestion : "스택 연산에서 데이터를 삽입하거나 삭제하는 동작을 나타내는 것은?",
                infoChoice : ["ADD, SUB", "LOAD, STORE", "PUSH, POP", "MOV, MUL"],
                infoAnswer: "PUSH, POP",
                infoDesc : "PUSH : 데이터 삽입<br>POP : 데이터 꺼내기(삭제)",
            },{
                infoType : "정보처리 기능사" ,
                infoTime : "2011년 4회" ,
                infoNumber : "20110403",
                infoQuestion : "다음 중 제어장치에서 명령어의 실행 사이클에 해당하지 않는 것은?",
                infoChoice : ["인출 주기(fetch cycle)", "직접 주기(direct cycle)", "간접 주기(indirect cycle)", "실행 주기(execute cycle)"],
                infoAnswer: "직접 주기(direct cycle)",
                infoDesc : "제어장치의 명령어 실행 사이클은 기계주기(Machine Cycle)라 하며, 다음과 같다..<br> ① 인출사이클(Fetch Cycle) - 중앙처리장치가 기억장치에서 다음에 실행할 명령을 가져오는 주기)<br>② 명령사이클(Instruction Cycle) - 기억장치의 번지를 확인하여 명령을 읽어 낼 때까지의 단계<br>③ 실행사이클(Execution Cycle) - 명령에 따라 필요한 신호를 만들어 결과를 얻을 때까지의단계",
            },{
                infoType : "정보처리 기능사" ,
                infoTime : "2011년 4회"  ,
                infoNumber : "20110404",
                infoQuestion : "전가산기(Full Adder)는 어떤 회로로 구성되는가?",
                infoChoice : ["반가산기 1개와 OR 게이트로 구성된다.", "반가산기 1개와 AND 게이트로 구성된다.", "반가산기 2개와 OR 게이트로 구성된다.", "반가산기 2개와 AND 게이트로 구성된다."],
                infoAnswer: "반가산기 2개와 OR 게이트로 구성된다.",
                infoDesc : "전가산기 구성 : 반가산기 2개와 OR 게이트로 구성된다.",
            },{
                infoType : "정보처리 기능사" ,
                infoTime : "2011년 4회"  ,
                infoNumber : "20110405",
                infoQuestion : "CISC(Complex Instruction Set Computer)의 특징으로 틀린 것은?",
                infoChoice : ["많은 수의 명령어", "다양한 주소지정 방식", "가변 길이 명령어 형식", "단일 사이클의 명령어 실행"],
                infoAnswer: "단일 사이클의 명령어 실행",
                infoDesc : "단일 사이클의 명령어 실행은 RISC방식입니다. <br>CISC 방식은 다양한 길이의 가변 사이클을 지원 합니다.",
            },{
                infoType : "정보처리 기능사" ,
                infoTime : "2011년 4회"  ,
                infoNumber : "20110406",
                infoQuestion : "EBCDIC 코드는 몇개의 Zone bit를 갖는가?",
                infoChoice : ["1",  "2", "3", "4"],
                infoAnswer: "4",
                infoDesc : "총 코드비트<br>BCD 코드(6)<br>ASCII 코드(7)<br>EBCDIC 코드(8)<br>모든 코드의 디지트 비트는 4임<br>따라서 각 코드의 존비트는 코드총비트 - 4 입니다. 즉 8-4 = 4 입니다<br>",
            },{
                infoType : "수고하셨습니다" ,
                infoTime : " " ,
                infoNumber : "20110407",
                infoQuestion : " ",
                infoChoice : [" ",  " ", " ", " "],
                infoAnswer: " ",
                infoDesc : " ",
            }
        ]
        // 선택자 
        const quizWrap = document.querySelector(".quiz__wrap");
        const quizTitle = quizWrap.querySelector(".quiz__title");
        const quizChoice = quizWrap.querySelector(".quiz__choice");
        const quizQuestion = quizWrap.querySelector(".qiuz__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 quizLength = quizWrap.querySelector(".quiz__length");
        const quizCnt = quizWrap.querySelector(".quiz__cnt");
        const quizResult = quizWrap.querySelector(".quiz__result");
        let quizCount = 0;
        let quizScore = 0;

        // 문제 출력 
        updateQuiz = (index) => {
            let typeTag = `
                <span>${quizInfo[index].infoType}</span>
                <em>${quizInfo[index].infoTime}</em>
            `;
            const 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 cntTag = `
            남은 문제는 ${quizInfo.length-quizCount-1}개 입니다
            `
            let ResultTag = `
            <span>점수는 ${Math.ceil((quizScore / (quizInfo.length-1)) * 100)}점 입니다.<br>
            맞은 문제는 ${quizScore}개고 <br> 틀린문제는 ${quizInfo.length-quizScore-1} 개 입니다.</span>
            
            `
            quizResult.innerHTML = ResultTag;
            quizCnt.innerHTML = cntTag;
            quizTitle.innerHTML = typeTag;
            quizQuestion.innerHTML = questionTag;
            quizChoice.innerHTML = choiceTag;
            quizDesc.innerHTML = descTag;
            // 보기 선택자
            const quizChoiceSpan = quizWrap.querySelectorAll(".quiz__choice span");
            const quizInput = 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)");
                // quizInput[i].disabled = "true" 
            }
            if(quizCount+1==quizInfo.length){
                dogWrap.style.display = "none";
                quizResult.style.display = "block";
                quizCnt.style.display = "none";
                quizChoice.style.display = "none";
                quizQuestion.style.display = "none";
            }
        };
        // 다음숨기기, 해설 숨기기
        quizAnswer.style.display = "none";
        quizDesc.style.display = "none";
        quizResult.style.display = "none";
        //
        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";
        }
        updateQuiz(quizCount);

        // 정답확인 
        quizNext.addEventListener("click", () => {
            dogWrap.classList.remove("like" ,"dislike")
            quizAnswer.style.display = "none";
            quizDesc.style.display = "none";
            quizCount++;
            updateQuiz(quizCount);
        })
    </script>
</body>
</html>

- 크게 달라진 부분은 많이 없습니다. 정답확인 부분이 다음문제 버튼으로 바뀐 정도가 있겠네요

- 큰 틀은 main안에 넣어두고 그외 안에 들어가는 부분은 innnerHTML를 통해서 넣어주었습니다.

- 스크립트를 보시죠

<script>
    // 선택자 
    const quizWrap = document.querySelector(".quiz__wrap");
    const quizTitle = quizWrap.querySelector(".quiz__title");
    const quizChoice = quizWrap.querySelector(".quiz__choice");
    const quizQuestion = quizWrap.querySelector(".qiuz__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 quizLength = quizWrap.querySelector(".quiz__length");
    const quizCnt = quizWrap.querySelector(".quiz__cnt");
    const quizResult = quizWrap.querySelector(".quiz__result");
    let quizCount = 0;
    let quizScore = 0;

    // 문제 출력 
    updateQuiz = (index) => {
        let typeTag = `
            <span>${quizInfo[index].infoType}</span>
            <em>${quizInfo[index].infoTime}</em>
        `;
        const 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 cntTag = `
        남은 문제는 ${quizInfo.length-quizCount-1}개 입니다
        `
        let ResultTag = `
        <span>점수는 ${Math.ceil((quizScore / (quizInfo.length-1)) * 100)}점 입니다.<br>
        맞은 문제는 ${quizScore}개고 <br> 틀린문제는 ${quizInfo.length-quizScore-1} 개 입니다.</span>

        `
        quizResult.innerHTML = ResultTag;
        quizCnt.innerHTML = cntTag;
        quizTitle.innerHTML = typeTag;
        quizQuestion.innerHTML = questionTag;
        quizChoice.innerHTML = choiceTag;
        quizDesc.innerHTML = descTag;
        // 보기 선택자
        const quizChoiceSpan = quizWrap.querySelectorAll(".quiz__choice span");
        const quizInput = 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)");
            // quizInput[i].disabled = "true" 
        }
        if(quizCount+1==quizInfo.length){
            dogWrap.style.display = "none";
            quizResult.style.display = "block";
            quizCnt.style.display = "none";
            quizChoice.style.display = "none";
            quizQuestion.style.display = "none";
        }
    };
    // 다음숨기기, 해설 숨기기
    quizAnswer.style.display = "none";
    quizDesc.style.display = "none";
    quizResult.style.display = "none";
    //
    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";
    }
    updateQuiz(quizCount);
    // 정답확인 
    quizNext.addEventListener("click", () => {
        dogWrap.classList.remove("like" ,"dislike")
        quizAnswer.style.display = "none";
        quizDesc.style.display = "none";
        quizCount++;
        updateQuiz(quizCount);
    })
</script>

- 저번처럼 배열안에 객체를 넣어 문제 정보를 저장하였습니다.

- 그건 쉬운니까 생략하겠습니다.

- 문제를 출력하는데 저번과 차이가 있습니다.

- 저번에는 모든 태그를 반복문에 넣고 돌려서 출력하였다면 이번에는 내용부분만 따로 반복문을 사용하여 내용을 바뀌어주었습니다.

- 그리고 보기의 버튼을 눌렀을 때 바로 정답을 확인하고 다음문제를 클릭 했을 때 다음 문제로 넘어가는 것 입니다.

// 사용자가 선택한 정답을 속성 값으로 불러오기 
for(let i=0; i<quizChoiceSpan.length; i++){
        quizChoiceSpan[i].setAttribute("onclick", "choiceSelected(this)");
    }
// 정답확인
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";
}
updateQuiz(quizCount);   
// 다음문제 
quizNext.addEventListener("click", () => {
    dogWrap.classList.remove("like" ,"dislike")
    quizAnswer.style.display = "none";
    quizDesc.style.display = "none";
    quizCount++;
    updateQuiz(quizCount);
})

- setAttribute를 사용해서 사용자가 선택한 보기를 해당문제의 속성 값으로 가져오고 onclick, choiceSelected(this)를 클릭되었을 때 함수 choiceSelected의 매개변수로 넣어주었습니다.

- 그것을 변수 userAnswer 안에 저장을 해주고 currentAnswer안에 정답을 저장해주었습니다.

- 그리고 그 둘을 비교하는 if 문을 써주면 정답확인하는 부분은 끝이납니다.

- 그리고 다음문제를 클릭하였을 때 정답과 해설을 다시 가려주고 몇문제 남았는지 계산할 때 사용할 quizCount를 1씩 더해주면 다음문제까지는 끝이 납니다.

// 남은 문제갯수와 마지막에 점수표시
let cntTag = `
    남은 문제는 ${quizInfo.length-quizCount-1}개 입니다
    `
let ResultTag = `
    <span>점수는 ${Math.ceil((quizScore / (quizInfo.length-1)) * 100)}점 입니다.<br>
    맞은 문제는 ${quizScore}개고 <br> 틀린문제는 ${quizInfo.length-quizScore-1} 개 입니다.</span>
    
    
// 마지막 문제일 때     
if(quizCount==quizInfo.length-1){
    dogWrap.style.display = "none";
    quizResult.style.display = "block";
    quizCnt.style.display = "none";
    quizChoice.style.display = "none";
    quizQuestion.style.display = "none";
}

- 퀴즈를 풀 때 마다 남은 문제가 몇개인지 알려주는 문구를 만들어주었습니다.

- 마지막 문제일 때 다음문제가 없는데 문제를 받아오려고 하는 버그가 있기 때문에 저는 하나의 객체를 추가해서 내용이 없는 빈 껍데기를 하나 만들었습니다. 그렇기 때문에 남은 문제를 알려고 할 때 quizInfo.length-quizCount이 아닌 quizInfo.length-quizCount-1를 사용해서 숫자를 맞춰주었습니다.

- 이후에 들어가는 모든 quizInfo.length에도 1씩 빼주었습니다.

- 그리고 숫자를 올림하여 정수로 바꾸어 주는 Math.ceil를 통해 점수를 계산해 주었습니다 {Math.ceil((quizScore / (quizInfo.length-1)) * 100)}

- 그리고 if문을 통해 quizInfo.length-1 과 quizCount가 같아질 때 필요없는 나머지 창들을 없애고 점수와 맞춘문제의 갯수, 틀린 문제의 갯만 띄워주었습니다.

- 끝!!