티스토리 뷰

SMALL

안녕하세요. 오늘 포스팅은 전에 올렸던 커스텀 컨펌 창의 디벨롭 버전입니다.

https://cocoseom.tistory.com/4

 

[js/javaScript] 컨펌 창 구현하기: custom confirm

javascript의 confirm() 함수는 강력합니다. 하지만 솔직히 말해서 시각적으로 눈에 잘 안들어오고, 사이트와 어울리지 않는 경우가 많습니다. 때문에 커스텀 해달라는 요청이 발생하기도 합니다. 오

cocoseom.tistory.com

해당 버전에는 한 가지 문제가 있었습니다.

바로 alert창이나 confirm 창이 떴을 때 사용자는 Enter키를 사용하기도 한다는 점이였는데요, 이 부분을 제어하지 않는다면, 아무 키보드나 눌렀을 때 안내창이 계속해서 증식하는 모습을 볼 수 있었습니다.

enter키를 누른만큼 click이벤트 발생. 확인, 취소 버튼 이벤트 반응 없고 배경을 눌러야만 종료되는 현상.

1. 왜 이런 일이 생겼을까?

이벤트 버블링(bubbling)과 관련 있습니다.

이벤트 버블링이란, 자식 요소에서 발생한 이벤트가 부모 요소로 전파되는 동작을 말합니다.

한 element에 이벤트가 발생하면, 이 요소에 할당된 핸들러가 동작하고, 이어서 부모 요소의 핸들러가 동작합니다. 가장 최상단의 조상 요소를 만날 때까지 이 과정이 반복되면서 요소 각각에 할당된 핸들러가 실행됩니다.

  1. 사용자가 confrim 창을 띄운 상태에서 키보드를 누르면 keydown이벤트가 발생
  2. 이때 keydown 이벤트는 문서의 최상위 요소인 <html>에서부터 시작해서 하위 요소로 전파되고 다시 click 이벤트를 실행되어서 confrim 창이 중복되어 화면에 나타남.

그렇다면 왜 confirm창에서 클릭이 안먹었을까요? z-index가 동일하기 때문인데, 해결하기 위해서는 핸들링을 통해 진행 하거나, 한번에 confirm창이 여러개 뜨지 않도록 코드를 수정해야 합니다.

 

2. keydown 제어

 

See the Pen confirm custom_2 by My study Storage (@Eunbi13) on CodePen.

keydown을 제어하기 위해 다음의 코드를 추가했습니다. 

// element 제거,  keydown event 제거
var removeAlert = function() {
    document.body.removeChild(confirmBox);
    document.body.removeChild(overlay);
    window.removeEventListener('keydown', handleKeyDown);
};

// keydown event 생성
var handleKeyDown = function(event) {
    event.preventDefault();
};

// 키를 눌렀을 때 기본 동작을 막습니다.
window.addEventListener('keydown', handleKeyDown);

더 이상 수정하는 일이 없으면 좋겠네요. 전체 코드를 마지막으로 글을 마치겠습니다.

3. js코드 전체 보기

function customConfirm(message, callback) {
        // 대화 상자를 HTML 요소로 생성(동적)
        var confirmBox = document.createElement('div');
        confirmBox.setAttribute('class', 'confirm-box');
        confirmBox.innerHTML = '<p>' + message + '</p><button class="confirm">확인</button><button class="cancel">취소</button>';

        // HTML 요소를 body 요소의 하위 요소로 추가합니다.
        document.body.appendChild(confirmBox);

        // 모달창의 위치 중앙 설정
        confirmBox.style.position = 'fixed';
        confirmBox.style.top = '50%';
        confirmBox.style.left = '50%';
        confirmBox.style.transform = 'translate(-50%, -50%)';
        confirmBox.style.zIndex = 999;

        // 배경을 회색으로 덮어서 모달 창을 띄웠을 때 다른 요소들을 클릭할 수 없도록 제어
        var overlay = document.createElement('div');
        overlay.setAttribute('class', 'overlay');
        document.body.appendChild(overlay);
        overlay.style.position = 'fixed';
        overlay.style.top = '0';
        overlay.style.left = '0';
        overlay.style.width = '100%';
        overlay.style.height = '100%';
        overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
        overlay.style.zIndex = '998';

		var removeAlert = function() {
			document.body.removeChild(confirmBox);
			document.body.removeChild(overlay);
			window.removeEventListener('keydown', handleKeyDown);
		 };
		
		 var handleKeyDown = function(event) {
			event.preventDefault();
		 };

        return new Promise(function(resolve) {
            // 확인 버튼을 클릭했을 때 이벤트
            var confirmButton = document.querySelector('.confirm');
            confirmButton.addEventListener('click', function() {
                // 확인 버튼을 눌렀을 때 resolve 메서드를 호출
                removeAlert();
                setTimeout(function() {
                    resolve(true);
                }, 100);
            });

            // 취소 버튼을 클릭했을 때 이벤트
            var cancelButton = document.querySelector('.cancel');
            cancelButton.addEventListener('click', function() {
                // 취소 버튼을 눌렀을 때 resolve 메서드를 호출
                removeAlert();
                setTimeout(function() {
                    resolve(false);
                }, 100);
            });

            // 회색 영역 클릭할 때 창을 닫도록 이벤트 추가
            overlay.addEventListener('click', function() {
                removeAlert();
            });
			
			// 키를 눌렀을 때 기본 동작을 막습니다.
			 window.addEventListener('keydown', handleKeyDown);
		 });
      
    }

    // 함수 호출
    function Click_bth(message){
        customConfirm(message)
            .then(function(result){
                if (result) {
                    //"확인 클릭";
                } else {
                    //"취소 클릭";
                }
        });
    }
SMALL