soma0sd

코딩 & 과학 & 교육

오늘의 JS 놀이: 정적 페이지를 위한 퀴즈엔진 제작

반응형

객관식 퀴즈나 간단한 심리테스트 등에 이용할 수 있는 마크업 규칙과 그것을 작동하게 하는 스크립트를 만들어보려고 합니다. 이전 글 오늘의 JS 놀이: 정적 페이지를 위한 퀴즈 엔진을 만들 계획에 이어서, 계획을 실제로 작동하게 하는 스크립트를 제작합니다. 스크립트는 초기화 단계와 작동 단계로 나눌 수 있습니다.

퀴즈 초기화

document.addEventListener('DOMContentLoaded', (event) => {
  // 컨텐츠 로드 후에 할 일
});

웹 페이지가 모든 요소를 표시할 준비가 끝나면 퀴즈를 초기화하도록 합니다. 모종의 이유로 마크업을 모두 표시하기 전에 작동해버리면 오류로 인해 멈출 수 있거든요.

document.addEventListener('DOMContentLoaded', (event) => {
  document.querySelectorAll('.quiz-list').forEach((quiz_container) => {
    // 각 변수의 초기값을 컨테이너의 속성에 추가한다
    quiz_container.setAttribute('quiz-num', 1);
    quiz_container.setAttribute('quiz-tot', 1);
    quiz_container.setAttribute('point-tot', 0);
    quiz_container.setAttribute('point-get', 0);

    // 컨테이너 안에 있는 퀴즈 페이지 갯수 세기
    let quiz_count = 1;
    quiz_container.querySelectorAll('quiz').forEach((quiz_page) => {
      // 첫 페이지가 아니면 숨김
      if (quiz_count > 1) quiz_page.style.display = 'none';
      // 페이지마다 번호 속성 주기
      quiz_page.setAttribute('page', quiz_count);
      quiz_count++;
    });
    quiz_container.setAttribute('quiz-tot', quiz_count - 1);


    // 총 점수 세기
    let point_count = 0;
    quiz_container.querySelectorAll('answer').forEach((answer_item) => {
      if (answer_item.hasAttribute('point')) {
        point_count += Number(answer_item.getAttribute('point'));
      }
    });
    quiz_container.setAttribute('point-tot', point_count);

    // 총점 태그에 값 넣기
    quiz_container.querySelectorAll('total').forEach((tot_str) => {
      tot_str.innerHTML = point_count;
    });

    // 답안에 온클릭 이벤트 넣기
    quiz_container.querySelectorAll('answer').forEach((answer_item) => {
      answer_item.onclick = (event) => {
        let quiz_page = event.target.closest('quiz');
        let quiz_container = quiz_page.parentNode;
        let next = Number(quiz_page.getAttribute('page')) + 1;
        let get_point = Number(quiz_container.getAttribute('point-get'));

        // 클릭한 답안에 포인트가 있는 경우 점수에 합산
        if (event.target.hasAttribute('point')) {
          get_point += Number(event.target.getAttribute('point'));
          quiz_container.setAttribute('point-get', get_point);
        }

        // 포인트 표시 태그 업데이트
        quiz_container.querySelectorAll('point').forEach((get_str) => {
          get_str.innerHTML = get_point;
        });

        // 다음 페이지로 이동
        quiz_page.style.display = 'none';
        quiz_container.querySelector(`quiz[page="${next}"]`).style.display = 'block';
        quiz_container.setAttribute('quiz-num', next);
      }
    });

    // 리셋 태그에 온클릭 이벤트 넣기
    quiz_container.querySelectorAll('reset').forEach((reset_btn) => {
      reset_btn.onclick = (event) => {
        let quiz_page = event.target.closest('quiz');
        let quiz_container = quiz_page.parentNode;

        // 퀴즈 컨테이너의 변수 초기화
        quiz_container.setAttribute('quiz-num', 1);
        quiz_container.setAttribute('point-get', 0);

        // 첫 페이지로 넘어가기
        quiz_page.style.display = 'none';
        quiz_container.querySelector(`quiz[page="1"]`).style.display = 'block';
      }
    });
  });
});

마크업 예시

<div class="quiz-list">
  <quiz>
    <ask>첫 번째 문제입니다</ask>
    <answer point="10">정답</answer>
    <answer>오답</answer>
  </quiz>
  <quiz>
    <ask>두 번째 문제입니다</ask>
    <answer>오답</answer>
    <answer point="20">정답</answer>
    <answer>오답</answer>
  </quiz>
  <quiz>
    <ask>세 번째 문제입니다</ask>
    <answer>오답</answer>
    <answer>오답</answer>
    <answer point="30">정답</answer>
  </quiz>
  <quiz>
    획득한 점수는
    <point></point> / <total></total>
    입니다.
    <reset>다시하기</reset>
  </quiz>
</div>

스타일시트 예시

대충 알아볼 수 있을 정도로만 했습니다. 추가로 여러 스타일 속성을 써서 보기 좋게 만들면 됩니다.

.quiz-list {
  width: 100%;
  height: 120px;
  background: #EEEEEE;
  border-radius: 2px;
}
quiz {
  display: block;
  padding: 0.5rem;
  width: 100%;
  height: 100%;
  text-align: center;
}
quiz p {
  display: block;
  margin: 0.5rem 0;
}
answer,
reset {
  display: inline-block;
  padding: 0.2rem 0.3rem;
  border-radius: 2px;
  border: orange 1px solid;
  cursor: pointer;
}

결과

첫 번째 문제입니다

정답 오답

두 번째 문제입니다

오답 정답 오답

세 번째 문제입니다

오답 오답 정답

획득한 점수는 / 입니다.

다시하기
반응형
태그:

댓글

End of content

No more pages to load