Valid State Detection

목적

--valid, --invalid 상태 클래스를 추가/제거해서 아래 이미지 처럼 경고 메세지가 뜨게 한다.

nomal 상태
valid 상태
invalid 상태

설계

<div class="emailContainer__block">
    <input type="email" id="userEmail__main" />
    <label for="userEmail__main">이메일 주소</label>
    <span role="alert" aria-live="off">정확한 이메일 주소를 입력하세요.</span>
</div>

변수 선언

const emailContainerNode = document.querySelectorAll('.emailContainer__block')
const emailInputNode = document.querySelectorAll('.emailContainer__block input')
const INVALID_CLASS = 'emailContainer__block--invalid'
const VALID_CLASS = 'emailContainer__block--valid'

const emailInputNodeArr = Array.from(emailInputNode)

사용된 기능

방법

  • <input>'input'이벤트 바인딩을 한다.

  • matches() 메서드를 사용해서 ':invalid' 선택자가 있는지 확인한다.

    • boolean 값으로 반환하기 때문에 조건 문의 조건으로 사용한다.

  • 위의 메서드를 조건으로 해서 .emailContainer__block

    .emailContainer__block--vaild, .emailContainer__block--invalid 상태 클래스를 추가/제거하도록 한다.

    • parentNode를 사용해서 바로 위의 요소를 변수에 담는다.

emailInputNodeArr.forEach(function (item, index) {
  item.addEventListener('input', (e) => {
    const isInvalid = e.target.matches(':invalid')
    const targetParentNode = e.target.parentNode

    if (isInvalid) {
      // 유효하지 않은 상태
      targetParentNode.classList.remove(VALID_CLASS)
      targetParentNode.classList.add(INVALID_CLASS)
    } else {
      // 유효한 상태
      targetParentNode.classList.remove(INVALID_CLASS)
      targetParentNode.classList.add(VALID_CLASS)
    }
  })
})

문제 (1)

<input>에 텍스트가 아닌 공백만 있을 때, label이 원래 자리로 내려오지 않는 이슈

input에 공백이 있다.
포커스를 잃은 상태에서 label이 내려오지 않는다.
emailInputNodeArr.forEach(function (item, index) {
  item.addEventListener('input', (e) => {
    const targetParentNode = e.target.parentNode

    // 사용자가 입력한 정보의 글자 개수가 빈 공백을 지우고 나서 0일 때 valid 상태가 아님을 인지
    if (e.target.value.trim().length === 0) {
      targetParentNode.classList.remove(VALID_CLASS)
    }
  })

  // input이 포커스 상태가 아닐 때, 사용자가 입력한 정보의 글자 개수가 
  // 빈 공백을 지우고 나서 0일 때  valid 상태가 아님을 인지 
  item.addEventListener('blur', (e) => {
    if (e.target.value.trim().length === 0) {
      e.target.parentNode.classList.remove(VALID_CLASS)
    }
  })
})

문제 (2)

입력을 위해 <input>을 클릭 했을 때 label이 선택되었다. 그래서 'focus' 이벤트가 발생했을 때 select() 메서드에 setTimeout()을 사용해서 시간을 추가한다.

입력을 위해 <input>을 클릭 했을 때 label이 선택된 모습
// label의 text가 선택되는 것을 방지 하기 위해 포커스 이벤트에 시간 추가
  item.addEventListener('focus', (e) => {
    if (e.target.value.trim().length === 0) {
      window.setTimeout(() => e.target.select())
    }
  })

결론

const emailContainerNode = document.querySelectorAll('.emailContainer__block')
const emailInputNode = document.querySelectorAll('.emailContainer__block input')
const INVALID_CLASS = 'emailContainer__block--invalid'
const VALID_CLASS = 'emailContainer__block--valid'

const emailInputNodeArr = Array.from(emailInputNode)

emailInputNodeArr.forEach(function (item, index) {
  item.addEventListener('input', (e) => {
    const isInvalid = e.target.matches(':invalid')
    const targetParentNode = e.target.parentNode

    if (isInvalid) {
      // 유효하지 않은 상태
      targetParentNode.classList.remove(VALID_CLASS)
      targetParentNode.classList.add(INVALID_CLASS)
    } else {
      // 유효한 상태
      targetParentNode.classList.remove(INVALID_CLASS)
      targetParentNode.classList.add(VALID_CLASS)
    }

    // 사용자가 입력한 정보의 글자 개수가 빈 공백을 지우고 나서 0일 때 valid 상태가 아님을 인지
    if (e.target.value.trim().length === 0) {
      targetParentNode.classList.remove(VALID_CLASS)
    }
  })

  // input이 포커스 상태가 아닐 때, 사용자가 입력한 정보의 글자 개수가 빈 공백을 지우고 나서 0일 때  valid 상태가 아님을 인지
  item.addEventListener('blur', (e) => {
    if (e.target.value.trim().length === 0) {
      e.target.parentNode.classList.remove(VALID_CLASS)
    }
  })

  // label의 text가 선택되는 것을 방지 하기 위해 포커스 이벤트에 시간 추가
  item.addEventListener('focus', (e) => {
    if (e.target.value.trim().length === 0) {
      window.setTimeout(() => e.target.select())
    }
  })
})

Last updated

Was this helpful?