본문 바로가기
JAVASCRIPT

객관식 여러문제 확인하기 유형 : 슬라이드 유형 퀴즈 이펙트

by dongjin6539 2023. 3. 27.
728x90
반응형

객관식 여러문제 확인하기 유형 : 슬라이드 유형 퀴즈 이펙트

수업 시간에 배운 내용을 복습하면서 해보겠습니다.

코드 보기(HTML, JAVASCRIPT / CSS1 / CSS2) / 완성화면

 

 

 

코드 블럭

<!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>퀴즈 이펙트06</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>
            <li><a href="quizWebd.html">W</a></li>
        </ul>
    </header>
    <!-- //header -->

    <main id="main">
        <div class="quiz__wrap">
            <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="quiz__total"></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 class="quiz__info"><span>??</span>점</div>
            <div class="quiz__check">정답 : <span>?</span> 개</div>
        </div>        
    </main>     
    <!-- //main -->

    <footer id="footer">
        <a href="mailto:dongjin6539@naver.com">dongjin6539@naver.com</a>
    </footer>
    <!-- //footer -->
</body>
</html>

 

코드 블럭 구성

  • 코드 블럭 구성은 저번에 작성했던 객관식 확인하기 유형 퀴즈 이펙트에 있는 구성과 똑같아서 그대로 가져옵니다.(객관식 확인하기 유형 퀴즈 이펙트 : https://dongjin6539.tistory.com/43)
  • 추가로 현재 문제 번호와 총 문제 의 갯수를 알려주기 위해 class "dog__wrap" 안에 class "quiz__total"을 입력해줍니다.
  • 사용자가 문제를 다 풀면 점수와 정답 갯수를 가져오기 위해 class "quiz__info", class "quiz__check"를 입력해줍니다.
  • 강아지 이미지는 인터넷을 통해 얻어왔습니다. (참조: https://wsss.tistory.com/913)
  • 위 페이지에 들어가서 코드펜을 통해 html 구조를 가져와서 main 구역에 입력해줍니다.(css 구조는 css 파일에 입력했습니다.)
  • CSS는 각 태그에 알맞는 속성을 입력하고 속성 값을 입력해줍니다.

 

CSS1

/* header */
#header {
    position: fixed;
    left: 0;
    top: 0;
    background-color: #000;
    color: #fff;
    padding: 10px;
    width: 100%;
    z-index: 1000;
    display: flex;
    justify-content: space-between;
}
#header::before {
    content: '';
    border: 4px ridge #cacaca;
    position: absolute;
    left: 5px;
    top: 5px;
    width: calc(100% - 10px);
    height: calc(100% - 10px);
}
#header h1 {
    font-size: 28px;
    padding: 5px 5px 5px 10px;
    font-family: 'DungGeunMo';
    z-index: 10;
    position: relative;
}
#header h1 a {
    color: #fff;
}
#header h1 em {
    font-size: 0.5em;
}
#header ul {
    padding: 5px;
}
#header li {
    display: inline;
    z-index: 10;
    position: relative;
}
#header li a {
    color: #fff;
    font-family: 'DungGeunMo';
    border: 1px dashed #fff;
    display: inline-block;
    padding: 5px;
}
#header li.active a,
#header li a:hover {
    background-color: #fff;
    color: #000;
}

/* footer */
#footer {
    position: fixed;
    left: 0;
    bottom: 0;
    width: 100%;
    background-color: #000;
    text-align: center;
}
#footer a {
    color: #fff;
    padding: 20px;
    display: block;
    font-family: 'DungGeunMo';
    z-index: 10;
    position: relative;
}
#footer::before {
    content: '';
    border: 4px ridge #cacaca;
    position: absolute;
    left: 5px;
    top: 5px;
    width: calc(100% - 10px);
    height: calc(100% - 10px);
}
#main {
    padding: 100px 0;
}

/* quiz__wrap */
.quiz__wrap {
    display: flex;
    justify-content: center;
    flex-wrap: wrap;
    align-items: flex-start;
}
.quiz__wrap .quiz {
    width: 500px;
    background-color: #fff;
    border: 8px ridge #000;
    margin: 10px;
}
.quiz__title {
    background-color: #000;
    border: 3px ridge #cacaca;
    border-bottom-width: 6px;
    padding: 5px;
    font-family: 'DungGeunMo';
    font-size: 16px;
    color: #fff;
    text-align: center;
}
.quiz__question {
    padding: 20px;
    font-size: 24px;
    font-family: 'PyeongChang';
    font-weight: 300;
    line-height: 1.4;
    border-bottom: 6px ridge #cacaca;
}
.quiz__question em {
    color: #cacaca;
}
.quiz__total {
    text-align: center;
    font-family: 'PyeongChang';
    font-size: 20px;
}
.quiz__answer {
    font-family: 'PyeongChang';
    padding: 20px;
    text-align: center;
    font-size: 24px;
}
.quiz__answer .confirm {
    color: #fff;
    background-color: #000;
    border: 6px ridge #000;
    width: 100%;
    font-family: 'PyeongChang';
    padding: 10px 20px;
    font-size: 22px;
    cursor: pointer;
    transition: all 0.3s;
    font-weight: bold;
}
.quiz__answer .confirm:hover {
    color: #000;
    background-color: #cacaca;
}
.quiz__answer .next {
    color: #000;
    background-color: #cacaca;
    border: 6px ridge #000;
    width: 100%;
    font-family: 'PyeongChang';
    padding: 10px 20px;
    font-size: 22px;
    cursor: pointer;
    transition: all 0.3s;
    font-weight: bold;
}
.quiz__answer .next:hover {
    color: #fff;
    background-color: #9b9b9b;
}
.quiz__answer .result {
    background-color: #cacaca;
    border: 6px ridge #000;
    width: 100%;
    font-family: 'PyeongChang';
    padding: 10px 20px;
    font-size: 22px;
}
.quiz__answer .input {
    background-color: #fff;
    border: 6px groove #000;
    width: 100%;
    font-family: 'PyeongChang';
    padding: 10px 20px;
    font-size: 22px;
    margin-bottom: 10px;
}
.quiz__view {
    border-bottom: 6px ridge #cacaca;
    overflow: hidden;
}
.quiz__desc {
    border-top: 6px ridge #000;
    padding: 20px;
    font-family: 'PyeongChang';
    background-color: #cacaca;
}
.quiz__desc::before {
    content: '✍️ Tip ';
    color: #ff3c3c;
    font-weight: bold;
}
.quiz__choice {
    padding: 20px;
    border-bottom: 6px ridge #000;
    font-family: 'PyeongChang';
}
.quiz__choice label {
    display: flex;
}
.quiz__choice label input {
    position: absolute;
    clip: rect(0 0 0 0);
    width: 1px;
    height: 1px;
    margin: -1px;
    overflow: hidden;
}
.quiz__choice label span {
    font-size: 20px;
    line-height: 1.4;
    padding: 6px;
    display: flex;
    cursor: pointer;
    margin: 2px 0;
}
.quiz__choice label span::before {
    content: '';
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: #fff;
    box-shadow: inset 0 0 0 4px #000;
    margin-right: 15px;
    transition: all 0.2s;
    flex-shrink: 0;
}
.quiz__choice label input:checked + span {
    background-color: #cacaca;
    border-radius: 10px;
}
.quiz__choice label input:checked + span::before {
    box-shadow: inset 0 0 0 5px #cacaca;
    border: 4px solid #000;
}
.quiz__check {
    position: fixed;
    right: 10px;
    bottom: 60px;
    width: 150px;
    height: 150px;
    line-height: 150px;
    border-radius: 50%;
    z-index: 1000;
    text-align: center;
    background: #cacaca;
    color: #000;
    font-family: 'PyeongChang';
    cursor: pointer;
    border: 5px solid #000;
    font-size: 24px;
    padding-bottom: 100px;
}
.quiz__info {
    position: fixed;
    right: 20px;
    bottom: 220px;
    background-color: #cacaca;
    border: 5px solid #000;    
    text-align: center;
    width: 130px;
    height: 50px;
    line-height: 50px;
    border-radius: 10px;
    font-family: 'PyeongChang';
    color: #000;
    padding-bottom: 50px;
}
.quiz__info::after {
    content: '';
    position: absolute;
    left: 50%;
    margin-left: -10px;
    bottom: -10px;
    border-top: 10px solid #000;
    border-left: 10px solid transparent;
    border-right: 10px solid transparent;
}

 

CSS2

@import url('https://webfontworld.github.io/DungGeunMo/DungGeunMo.css');
@import url('https://webfontworld.github.io/PyeongChang/PyeongChang.css');

* {
    margin: 0;
    padding: 0;
}
*, *::before, *::after {
    box-sizing: border-box;
}
a {
    text-decoration: none;
    color: #222;
}
h1, h2, h3, h4, h5, h6 {
    font-weight: normal;
}
li, ul, ol {
    list-style: none;
}
img {
    vertical-align: top;
    width: 100%;
}
em {
    font-style: normal;
}
body {
    background:
        radial-gradient(#cacaca 3px, transparent 4px),
        radial-gradient(#cacaca 3px, transparent 4px),
        linear-gradient(#fff 4px, transparent 0),
        linear-gradient(45deg, transparent 74px, transparent 75px, #cacaca55 75px, #cacaca55 76px, transparent 77px, transparent 109px),
        linear-gradient(-45deg, transparent 75px, transparent 76px, #cacaca55 76px, #cacaca55 77px, transparent 78px, transparent 109px),
        #fff;
    background-size: 109px 109px, 109px 109px, 100% 6px, 109px 109px, 109px 109px;
    background-position: 54px 55px, 0px 0px, 0px 0px, 0px 0px, 0px 0px;
}

 

JAVASCRIPT

<script>
    // 문제 정보
    const quizInfo = [
        {
            infoType: "정보처리 기능사",
            infoTime: "2010년 2회",
            infoNumber: "20100201",
            infoQuestion: "입출력 채널의 기능으로 적합하지 않은 것은?",
            infoChoice: ["입출력 명령을 해독한다.", "각 입출력 장치의 명령 실행을 지시한다.", "지시된 명령의 실행 상황을 제어한다.", "많은 입출력 장치를 한 번에 종속적으로 동작시킨다."],
            infoAnswer: "많은 입출력 장치를 한 번에 종속적으로 동작시킨다.",
            infoDesc: "채널은 한번에 1개의 입출력장치를 동작 시킵니다. 많은 입출력 장치를 동시에 제어 하기 위해서는 여러 채널을사용해야 합니다."
        },{
            infoType: "정보처리 기능사",
            infoTime: "2010년 2회",
            infoNumber: "20100202",
            infoQuestion: " 연속되는 2개의 숫자를 표현한 코드에서 한 개의 비트를변경하면 새로운 코드가 되기 때문에 아날로그-디지털 변환, 데이터 전송 등에 주로 사용되는 코드는?",
            infoChoice: ["DEBDIC Code", "Hamming Code", "ASCII Code", "Gray Code"],
            infoAnswer: "Gray Code",
            infoDesc: "Gray Code 는 입출력변환할 때, 아날로그와 디지털 변환할때 사용하는 코드입니다."
        },{
            infoType: "정보처리 기능사",
            infoTime: "2010년 2회",
            infoNumber: "20100203",
            infoQuestion: " 1비트(bit)를 기억할 수 있는 능력을 가진 기억의 최소 단위로 클록이 있는 순서회로에 기억된 기억 소자는?",
            infoChoice: ["플립플롭(Flip-Flop)", "전가산기(Full Adder)", "반가산기(Half Adder)", "부호기(Encoder)"],
            infoAnswer: "플립플롭(Flip-Flop)",
            infoDesc: "전가산기, 반가산기, 부호기, 멀티플렉서 등은 조합논리회로입니다."
        },{
            infoType: "정보처리 기능사",
            infoTime: "2010년 2회",
            infoNumber: "20100204",
            infoQuestion: "다음 그림과 같은 논리회로는?<br><img style='width:300px'src='../assets/img/20100204.jpg'>",
            infoChoice: ["Inhibit", "OR", "AND", "Flip-Flop"],
            infoAnswer: "Inhibit",
            infoDesc: "금지 회로(Inhibit Circuit)로써 AND게이트의 여러 입력 중 한 입력을 NOT회로를 이용하여금지 입력으로 사용하는 회로입니다. 금지 입력값이 1인경우 AND게이트의 출력이 1이 될수 없는회로입니다."
        },{
            infoType: "정보처리 기능사",
            infoTime: "2010년 2회",
            infoNumber: "20100205",
            infoQuestion: "연산의 중심이 되는 레지스터(Register)는?",
            infoChoice: ["General Register", "Address Register", "Accumulator", "Filp-Flop"],
            infoAnswer: "Accumulator",
            infoDesc: "Accumulator는 누산기(연산결과 일시(임시)저장)입니다."
        },{
            infoType: "정보처리 기능사",
            infoTime: "2010년 2회",
            infoNumber: "20100206",
            infoQuestion: "순차적인 주소지정 등에 유리하며, 주소지정에 2개의 레지스터가 사용되는 방식은?",
            infoChoice: ["직접 Addressing", "간접 Addressing", "상대 Addressing", "색인 Addressing"],
            infoAnswer: "색인 Addressing",
            infoDesc: "순차적인 주소지정 등에 유리하며, 주소지정에 2개의 레지스터가 사용되는 방식은 색인 Addressing입니다."
        }
    ];

    // 선택자
    const quizWrap = document.querySelector(".quiz__wrap");
    const quizTitle = quizWrap.querySelector(".quiz__title");
    const quizQuestion = quizWrap.querySelector(".quiz__question");
    const quizTotal = quizWrap.querySelector(".quiz__total");
    const quizChoice = quizWrap.querySelector(".quiz__choice");
    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 quizInfoSpan = quizWrap.querySelector(".quiz__info span"); 
    const quizCheck = quizWrap.querySelector(".quiz__check span"); 

    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 totalTag = `
            현재 문제 ${index+1} / 총 문제 ${quizInfo.length}
        `;

        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 = `
            <em>정답은 '${quizInfo[index].infoAnswer}'입니다.</em><br>${quizInfo[index].infoDesc}
        `;

        quizTitle.innerHTML = typeTag;
        quizChoice.innerHTML = choiceTag;
        quizQuestion.innerHTML = questionTag;
        quizTotal.innerHTML = totalTag;
        quizDesc.innerHTML = descTag;

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

        // quizChoiceSpan.forEach((span, num) => {
        //     span.setAttribute("onclick", "choiceSelected(this)");		// setAttribute : 속성 부여하는 메서드
        // });
        for(let i=0; i<quizChoiceSpan.length; i++){
            quizChoiceSpan[i].setAttribute("onclick", "choiceSelected(this)");
        };

        // 다음 버튼, 해설 숨기기
        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){
            quizScore++;
            console.log("정답입니다.");
            dogWrap.classList.add("like");
        } else {
            console.log("오답입니다.");
            dogWrap.classList.add("dislike");
        };

        quizAnswer.style.display = "block";
        quizDesc.style.display = "block";
    };

    // 정답 확인
    quizNext.addEventListener("click", () => {
        quizCount++;
        dogWrap.classList.remove("like", "dislike");
        if(quizCount == quizInfo.length){
            quizInfoSpan.innerHTML = Math.ceil((quizScore / quizInfo.length) * 100);   
            quizCheck.innerHTML = quizScore;      
        }    
        updateQuiz(quizCount);
    });
</script>

 

자바스크립트 구성

  • 문제의 정보는 변수 quizInfo를 입력하고 배열 안에 객체가 있는 형태로 데이터를 저장해줍니다.
  • 문제 유형은 "infoType", 문제 회차는 "infoTime",  문제 번호는 "infoNumber", 문제는 "infoQuestion", 문제 보기는 "infoChoice"로 입력하고 객체로 보기 번호를 입력해주고, 정답은 "infoAnswer", 문제 해설은 "infoDesc"로 입력해서 문제의 정보를 입력해줍니다.
  • 각 텍스트가 들어가야 할 부분에 class 명과 태그를 확인하고 선택자를 만들어줍니다.
  • 문제의 카운트 번호 quizCount 와 문제의 정답 quizScore 변수를 0으로 데이터를 저장해줍니다.
  • 문제를 출력하기 위해 함수 변수 updateQuiz를 주고 문제의 카운터 번호를 매개 변수를 가져오기 위해 실행문을 updateQuiz(quixCount);를 입력해주고 화살표 함수로 index로 값을 가져오게 해줍니다.
  • 함수 안에 지역변수로 만들어서 각 텍스트를 입력할 부분에 변수를 입력하고 ``(벡텟)을 사용해서 각각 텍스트를 입력하게 해줍니다. 문제의 정보는 배열 안에 있는 객체의 데이터를 불러오는 형태로 각각의 데이터를 입력해줍니다.
  • 각각의 데이터를 선택자 부분에 입력하기 위해 '선택자.innerHTML = 지역변수;' 를 입력해 데이터가 출력되게 해줍니다.
  • 보기에 선택자를 입력해줍니다. 보기가 다중이 이므로 querySelectorAll을 줍니다. 그리고 함수를 forEach( )를 사용해서 값이 setAttribute("onclick")를 입력해서 클릭했을 때의 속성을 부여하게 해줍니다. this를 사용해서 클릭해서 선택했을 경우의 함수를 입력해줍니다.(forEach( )를 for문으로도 변경해봤습니다.)
  • 사용자가 보기를 선택하기 전에 다음 버튼과 설명을 '선택자.style.display = "none";' 를 입력해서 숨겨줍니다.
  • this를 사용해서 클릭해서 선택했을 경우의 함수를 사용해서 사용자가 선택한 답과 문제의 정답을 가져와서 각각 지역변수로 데이터를 저장해주고 if문을 사용해서 사용자가 선택한 답과 문제의 정답이 맞는지 틀린지 확인해서 각각 강아지 스타일을 추가시켜주고 정답일 때 quizScore++; 연산를 입력해서 점수가 올라가게끔 해줍니다.
  • 그리고 사용자가 보기를 선택했을 때 다음 버튼과 설명을 '선택자.style.display = "block";' 를 입력해서 나타나게 해줍니다.
  • 다음 문제 버튼을 클릭했을 때를 addEventListener("click") 메서드를 사용해주고 버튼을 클릭하면 문제의 카운트 번호 quizCount 데이터를 하나씩 올라가게 quizCount++; 연산자를 입력해줍니다. 그리고 강아지의 스타일이 지워지도록 classList.remove("class명")을 사용해서 지워줍니다.
  • if문을 사용해서 마지막 문제를 풀었을 때 점수와 정답 갯수를 나오게 입력해줍니다.

 

참고

this
Javascript에서의 this는 인스턴스 자신(self)을 가리키는 참조변수이다. this가 객체 자신에 대한 참조 값을 가지고 있다는 뜻이다. 주로 매개변수와 객체 자신이 가지고 있는 멤버변수명이 같을 경우 이를 구분하기 위해서 사용된다.

참고 홈페이지 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/this

 

 

  • 아직 잘 모르는 자바스크립트 속성, 메서드에 대해 알아보겠습니다.
속성, 메서드 설명
setAttribute( ) 선택한 요소(element)의 속성(attribute) 값을 정해주는 메서드입니다.

 

https://dongjin6539.github.io/web2023/javascript/javascript14.html

 

728x90
반응형