✏️기록하는 즐거움
article thumbnail

상단에 사용된 이미지 출처: [notion] https://www.pinterest.co.kr/pin/593701163386718854/

 

🔍 설문조사 폼 HTML구조 파악하기

<form id="mainForm">
    <fieldset>
        <legend>JS 스터디 모집 설문조사</legend>
        <!-- progress : 작업의 완료 정도를 나타내는 요소 -->
        <progress max="100" value="0" class="bar-progress"></progress>
        <p class="msg-notice">설문지를 작성하세요!</p>

        <label for="q1">질문 1. 무엇을 가장 배우고 싶으세요?</label>
        <input type="text" id="q1" required />
        <label for="q2">질문 2. 시간은 언제가 적당한가요?</label>
        <input type="text" id="q2" required />
        <label for="q3">질문 3. 몇 시간정도 예상하세요?</label>
        <input type="text" id="q3" required />
        <label for="q4">질문 4. 난이도를 조정한다면?</label>
        <input type="text" id="q4" required />
        <label for="q5">질문 5. 끝으로 하고싶은 말이 있나요?</label>
        <input type="text" id="q5" required />

        <button type="submit">제출</button>
        <button type="reset">초기화</button>
    </fieldset>
</form>

설문지 폼은 사용자에게 입력을 받고, 입력 받은 내용을 서버로 제출하는 목적을 가지고 있기 때문에 <form> 요소로 구성되어 있다.

<input> 요소로 사용자에게 질문에 대한 응답을 입력받고 있고, <label> 요소를 사용하여 입력창과 연결시켜 스크린 리더와 터치스크린 사용자에게 좋은 UX를 줄 수 있다.

 

<fieldset> 요소는 form과 같은 양식 요소를 그룹화 할 수 있다. <legend> 요소로 그룹의 설명을 제공할 수 있어서 해당 콘텐츠가 어떤 목적 혹은 주제를 가지고 있는지 나타낼 수 있다.

 

<progress> 요소는 설문조사의 진행도를 나타내기 위해 사용하였다. progress는 진전, 진행, 진보의 의미를 갖고 있어서 어느 작업의 완료 정도를 나타낸다.

속성에 사용된 max는 작업량에 대해 나타내는데, 값을 따로 지정하지 않는다면 기본값은 1이다. 반드시 0보다 큰 유효한 숫자를 가져야 한다.

value는 작업을 완료한 양을 나타낸다. max를 지정한 경우 0이상 max이하, 그렇지 않으면 0이상 1이하이다.

value 값을 지정하지 않으면 현재 작업의 종료시점을 예측할 수 없음을 나타낸다.

<meter> 요소와는 다르게 최솟값은 항상 0이며, min 속성을 지정할 수 없다.

 

💄progress bar 스타일링하기

사용된 CSS 코드

#mainForm progress {
  width: 100%;
  height: 14px;
  appearance: none;
}

#mainForm progress::-webkit-progress-bar {
  /* progressbar의 배경이 되는 요소 */
  border-radius: 20px;
  background-color: #ebebeb;
}

#mainForm progress::-webkit-progress-value {
  /* progress의 진행 bar가 되는 요소 */
  border-radius: 20px;
  background-color: #ff7f50;
}

::webkit-progress-bar

progressbar의 배경이 되는 요소에 대해 스타일링 한다.

 

::webkit-progress-value

progressbar의 진행 바에 해당하는 요소에 대해 스타일링 한다.

 

progress에 apperance: none을 해주어야 지정된 스타일을 적용할 수 있다.

 

apperance

대부분의 HTML 요소는 CSS로 제어할 수 있지만 <progress> 요소나 <select>, <input> 등 기본 모양을 가지고 있는 위젯들은 사용자의 운영 체제의 기본 UI 컨트롤을 사용하여 사용자 에이전트(UA, User Agent)에 의해 스타일이 지정되어 있다.

 

따라서 이러한 스타일을 제어하기위해서는 apperance 속성을 사용하여야 한다. 참고: CSS Basic User Interface Module Level 4

 

apperance 속성을 사용하면 브라우저와 장치에서 일관된 스타일을 제공할 수 있다.

대부분의 최신 브라우저에서는 지원하고 있지만 Can I use에서 볼 수 있듯이 모든 브라우저에서는 지원하고 있지 않기 때문에 현재 프로젝트에 적합한지 테스트 후 사용하는 것이 좋다.

만약 지원하지 않는 브라우저라면 벤더 프리픽스(Vender Prefix)를 사용하여 이전 버전의 웹 브라우저에서 해당 CSS를 사용할 수 있다.

브라우저 IE Chrome FireFox Safari Opera
벤더 프리픽스 -ms- -webkit- -moz- -webkit- -o-
예시 -ms-apperance -webkit-apperance -moz-apperance -webkit-apperance -o-apperance

 

🛠️ 진행도 기능 구현하기

DOM 요소 탐색

const surveyForm = document.querySelector("#mainForm");
const progressBar = surveyForm.querySelector(".bar-progress");
const inp = surveyForm.querySelectorAll("input");
const message = surveyForm.querySelector(".msg-notice");

먼저, JavaScript로 제어할 DOM 요소들을 querySelector를 사용하여 탐색한다.

현재 제어할 요소들이 모두 form 안에 들어있기 때문에 form 요소를 탐색하고, 그 외의 요소들을 form에서부터 탐색하면 탐색 비용을 줄일 수 있다.

 

이벤트 추가하기 - 사용자 입력

// 폼에서 입력 이벤트가 발생할 때 두 번째 인자의 콜백 함수를 호출한다.
surveyForm.addEventListener("input", () => {
  // 값이 입력된 응답 개수를 저장할 변수
  let count = 0;

  // input 요소들을 순회하면서 input에 입력된 값이 있으면 count를 증가시킨다.
  inp.forEach((el) => el.value.length > 0 && count++);

  // progress의 값을 입력된 응답 개수만큼 %로 변환하여 변경한다.
  // ex) 5개의 질문 중 1개가 응답되었다면 20(%)
  progressBar.value = 100 * (count / inp.length);

  // 진행도에 따라서 메시지를 변경한다.
  if (progressBar.value === 100) {
    message.textContent = "모든 질문에 응답하셨습니다. 제출해주세요.";
  } else {
    message.textContent = "설문지를 작성하세요!";
  }
});

질문에 응답을 입력할 때마다 진행도를 표시해야 하므로 form 요소에 input으로 이벤트 타입을 지정한다.

progress의 value 속성으로 진행도가 나타나기 때문에 조건식을 통해 상황에 따른 메시지를 보여준다.

form에 추가된 이벤트가 input 요소에 입력에도 적용되는 이유는, input 요소에 입력이 될 때 이벤트 버블링으로 인해 상위 요소인 form에 추가된 이벤트가 발생하기 때문이다.

 

이벤트 추가하기 - 버튼 클릭

surveyForm.addEventListener("click", (e) => {
  // form에서 클릭한 요소의 타입이 reset일 때 동작
  if (e.target.type === "reset") {
    progressBar.value = 0; // 진행도 값을 0으로 설정
  }

  // form에서 클릭한 요소의 타입이 submit이고, 모든 질문에 응답했을 때 동작
  if (e.target.type === "submit" && progressBar.value === 100) {
    alert("설문에 참여해주셔서 감사합니다🎉");
  }
});

위의 사용자 입력 이벤트와 같이 이벤트 버블링을 이용하여 form 안에 있는 버튼을 클릭했을 때 이벤트 리스너의 콜백 함수를 동작시킬 수 있다.

현재 초기화 버튼의 type은 reset, 제출 버튼의 type은 submit이므로 각각 e.target.type을 기준으로 실행시킬 코드를 제어한다.

추가적으로 <button type="reset"></button>의 경우 기본적으로 연결된 양식의 모든 컨트롤을 초기 값으로 재설정하는 동작을 한다.

따라서 초기화 버튼을 클릭했을 때 input 요소를 초기화하는 코드는 생략하여도 된다.

 

✨ 완성된 코드 보기

 

🏃 더 나아가기

기존 코드에는 input 요소에 입력된 값이 있다면(input.value.length > 0) count를 증가해주는 코드를 사용하였다.

이외에도 HTMLObjectElementvalidity 속성을 사용할 수 있다.

validity 속성은 읽기 전용 속성으로 해당하는 요소의 유효성 상태와 ValidityState 를 반환한다.

ValidityState는 유효성 검사에 따라 요소가 가질 수 있는 유효성 상태를 나타낸다.

ValidityState에는 valid라는 속성이 존재하는데 요소가 가진 유효성 검사 제약 조건을 충족하면 true, 그렇지 않으면 false를 반환하는 Boolean 값이다.

 

현재 input 요소는 required 속성이 붙어 있기 때문에 필수 입력 요소이다. 즉 유효성 상태를 체크해볼 수 있는데,

input 요소에 텍스트가 입력된다면 input.validity.validtrue가 되고, 텍스트가 입력되지 않았다면 input.validity.validfalse가 된다.

따라서 기존의 코드를 아래와 같이 변경해볼 수도 있다.

surveyForm.addEventListener("input", () => {
  // ...

  // -- 기존 코드
  // inp.forEach((el) => el.value.length > 0 && count++);
  // -- 변경된 코드
  inp.forEach((el) => el.validity.valid && count++);

  // ...
});

 

이외에도 valid는 CSS 가상(의사) 클래스(pseudo-class)로도 사용된다. 참고

/* input에 입력된 값이 유효한 값이라면 적용 */
#mainForm input:valid {
  background-color: #ebebeb;
}
profile

✏️기록하는 즐거움

@nor_coding

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!