dialog 태그 사용하기

By Sera on

2022년 하반기부터 모든 브라우저에서 dialog를 지원하게 되었다. dialog는 js를 통해 조작해야 하며 아래의 인스턴스 메소드를 상속한다.

  • open() : boolean으로 사용 여부를 나타냄
  • returnValue() : 반환값을 설정/반환하는 문자열
  • close() : 선택적 문자열을 인수로 전달하여 returnValue를 업데이트 함
  • show() : 대화 상자 모델을 불필요하게 표시, 대화상자 밖의 내용과 상호작용을 계속 허용함
  • showModal() : 존재할 수 있는 다른 대화 상자 위에 대화 상자를 모달로 표시합니다. 대화 외부의 모든 것은 대화 외부의 상호 작용이 차단되어 비활성화됩니다.

버튼을 누르면 대화창이 나옵니다.

결과 값 :

선호하는 색 조사

안녕? 😋 다음 질문에 답을 해줘.

section {
  max-width: 300px;
  margin: 0 auto;
  border: 1px solid #ccc;
  padding: 1rem;
}

:modal {
  /*:modal은 모달 상태의 대화상자 박스에 대한 선택자이다. 비모달 대화상자는 dialog 태그 선택자로 정의가 가능하다. */
  border: none;
  padding: 0;
}
:modal form {
  /* 모달 안에 있는 서식 컨테이너를 grid로 만들겁니다. 3개의 행을 가지며, 각 행은, header, body, bottom 영역으로 나뉩니다.*/
  display: grid;
  grid-template-rows: auto 1fr auto;
}
:modal form > * {
  /* 서식 컨테이너 바로 아래에 있을 각 row에 padding 여백을 주어서 보기 시원한 디자인을 만듭니다. */
  box-sizing: border-box;
  padding: 0.5em;
}

:modal .header {
  /* header에는 display를 flex로 주고, 안에 있는 모든 내용이 옆으로 달라붙도록 합니다. */
  display: flex;
}
:modal .header > .tit {
  flex: 1; /*class="tit"은 타이틀 텍스트 요소입니다. flex:1;을 주면, 이 요소가 주변 요소가 있는 자리를 침범하지 않고 .header의 대부분을 차지합니다. */
}
:modal .body {
  min-height: 150px; /* 최소 세로 넓이를 150픽셀로 줘서 너무 작은 대화상자가 나오는 것을 방지합니다. */
}
:modal .bottom {
  display: flex;
  justify-content: end; /* bottom 영역 역시 display:flex;를 줘서 가로로 정렬되게 합니다. bottom에는 대화상자 내에 있는 버튼을 넣을겁니다. 오른쪽에 버튼이 나타나길 원하니, justify-content를 end로 줍니다.*/
}

:modal button {
  /* 버튼 스타일을 무력화합니다. */
  appearance: none;
  border: none;
  background-color: transparent;
  font-weight: bold;
}
:modal #negative.default {
  /* 부정적인 답변에 대한 버튼이 .default이면 부정답변 버튼의 텍스트를 빨강색으로 표시 */
  color: red;
}
:modal #postive.default {
  /* 긍정적인 답변에 대한 버튼이 .default면 긍정 답변 버튼의 텍스트를 파란색으로 표시*/
  color: #2264ff;
}
<section>
  <h3>버튼을 누르면 대화창이 나옵니다.</h3>
  <button id="btnOpen" aria-haspopup="dialog">버튼 😋</button>
  <p>결과 값 : <span id="message" aria-live="assertive"></span></p>
</section>

<dialog id="myDialog" aria-labelledby="wyn_title" aria-describedby="wyn_desc">
  <!--대화상자를 만듭니다. 대화상자에 스크린리더가 접근하면, 대화상자 타이틀과 메시지를 안내하도록 aria-labelledby와 describedby를 활용해 줍니다.-->
  <form method="dialog">
    <!--form의 새로운 method, dialog값은 대화상자 내에서 form이 전송되었을 때에 대한 동작을 얘기합니다.-->
    <div class="header">
      <strong id="wyn_title" class="tit">선호하는 색 조사</strong
      ><button id="btnClose" aria-label="close" value="cancel">X</button>
    </div>
    <!--제목 표시줄과 닫기 버튼입니다.-->
    <div class="body">
      <!--대화상자 내부에 표시될 질문 내용입니다.-->
      <p id="wyn_desc">안녕? 😋 다음 질문에 답을 해줘.</p>
      <div>
        <label for="typeName">
          좋아하는 색이 뭐야?
          <input type="text" name="typeName" id="typeName" autofocus />.
        </label>
      </div>
    </div>
    <div class="bottom">
      <button id="negative" name="negative" type="submit" value="negative">
        나중에
      </button>
      <button
        class="default"
        name="postive"
        type="submit"
        id="postive"
        value="postive"
      >
        확인
      </button>
    </div>
  </form>
</dialog>
const dialog = document.querySelector("dialog#myDialog");
const message = document.querySelector("#message");

const [btnOpen, btnClose, typeName] = [
  document.querySelector("#btnOpen"),
  document.querySelector("#btnClose"),
  document.querySelector("#typeName"),
];

// HTMLDialogElement.show()로 열면 비모달 대화상자, HTMLDialogElement.showModal()로 열면 모달 대화상자가 열림. show()함수에 showModal() 메소드가 사용됨
btnOpen.addEventListener("click", () => dialog.showModal());

// X 버튼을 눌러 닫으면
btnClose.addEventListener("click", () => dialog.close());

// 편집창 값 바뀔 때 긍정 버튼의 value 속성을 바꿔서 반환시킨다.
typeName.addEventListener("keydown", (evt) => {
  if (evt.code === "Enter") {
    // evt.code의 반환값이 "Enter" 스트링이면,
    evt.preventDefault(); //preventDefault로 기본 키 동작을 무시
    dialog.querySelector(".default").click(); // Enter가 눌리면 dialog의 기본 응답 동작을 무시하고, default 클래스가 부여된 버튼이 눌리도록함.
  }
});

// dialog가 닫혔을 때에 대한 Event
dialog.addEventListener("close", () => {
  switch (dialog.returnValue) {
    case "cancel":
      return false;
    case "negative":
      message.innerHTML = "왜쥬? 지금 답해요";
      break;
    case "postive":
      dialog.returnValue = typeName.value;
      if (dialog.returnValue) {
        message.innerHTML = `난, ${dialog.returnValue} 좋아효❤`;
      } else {
        message.innerHTML = "비어있습니다. 다시 입력하세요.";
      }
      break;
  }
});
참고자료 MDN web docs, NULI