[HTML, CSS, JS] 모달 작성

Yungsang Hwang·2022년 4월 22일
0

모달 작성

작성 규칙

  1. 작성일자와 프로젝트, 작성 상태 기술
    ✅ 작성한 날짜를 기록함으로써 리마인드
    ✅ 작성한 프로젝트를 기록함으로써 프로젝트를 찾아볼 수 있음
    ✅ 작성 상태를 기술함으로써 수정된 이력 버전과 완성여부를 확인
  2. 기능이름
    ✅ 레퍼런스 링크
  3. 기능 설명
    ✅ 사용된 기술에 대해 스스로 설명
  4. 기술 상세 기록
    ✅ 프로젝트에 사용한 코드를 한 줄씩 리뷰하는 것으로 코드 전체 이해 증명
    ✅ 코드를 설계한 내용과 왜 이렇게 작성했는지

작성계기


  • 팀 소개를 위한 게임 테마 웹을 만드는 도중, 원페이지로 팀을 소개하기 위해 개별 클라이언트 페이지를 만들지 않고 팀 멤버 각각을 소개하기 위해서 자연스럽게 모달로 실행하기로 합의됨

작성기능


  1. 모달 HTML

    • HTML 내부에 display 값이 none 으로 설정된 모달 컨테이너를 넣어두어야 함

    • 모달 내부에 들어갈 구역을 나눈 태그를 넣어두어야 함

  2. 모달 CSS

    • 모달 기본적으로 보이지 않게 초기 설정해 두어야함
    • 모달은 특정 버튼을 클릭해서 여는 순서로 설계되어 있기 때문
    display: none;
    • 모달 위치를 항상 같은 위치에서 보기 위한 고정이 필요
    position: absolute; 
    • 모달은 화면에서 가장 최상단의 레이어에 위치해 있어야함
    z-index: 50;
    • 모달 열기 버튼을 눌렀을 때, 모달이 보여야 함
    .modal.show { display: block; }
    • 모달 내부 설정
      - 백그라운드 이미지 넣기

      background-image: url("...")
      • 보더라인에 이미지 삽입

        border-style: solid;
        border-width: 40px 40px 40px 40px;
        -moz-border-image: url("../../static/image/source/border_image.png") 42 44 44 44 repeat repeat;
        -webkit-border-image: url("../../static/image/source/border_image.png") 42 44 44 44 repeat repeat;
        -o-border-image: url("../../static/image/source/border_image.png") 42 44 44 44 repeat repeat;
        border-image: url("../../static/image/source/border_image.png") 42 44 44 44 repeat repeat;
        
      • 모달을 페이지 가운데 올 수 있도록 비치

        position: absolute;
        width: 600px;
        height: 800px;
        padding: 40px;
        border-radius: 10px;
        box-shadow: 0 2px 3px 0 rgba(34, 36, 38, 0.15);
        transform: translateX(-50%) translateY(-50%);
      • 모달 크기 조정

        .modal-header {
             display: flex;
             width: 100%;
             height: 40%;
             justify-content: space-between;
         }
      • 모달 내부 닫기 버튼 CSS

        .x-button {
             font-size: 60px;
             margin-left: 60px;
             cursor: pointer;
         }
                    
  3. 모달 JS

    • 하나의 모달에 동적으로 붙일 데이터 선언
    const team_summary = [
    {
        'name': '황영상',
        'nick_name': '공상쟁이',
        'mbti': 'INFP',
        'strong_point': '의견을 경청하고 조율하기 위해 노력함',
        'teaming_method': '프로젝트를 완수하는 것을 최우선 목표로 삼음',
        'tmi_to_line': '나는 하루종일도 공상할 수 있다고!',
        'img_url': '../../static/image/team_mate/ys.jpeg'
    }, 
    ...
    ]
    • 모달 로직을 수행하기 위한 odbject 선언
    • 모달 html 태그 부분을 가져옴(모달자체, 모달 내부 바디, 닫기 버튼, 모달 열기 속성)
    const body = document.querySelector('body');
    const modal = document.querySelector('.modal');
    const x_button = document.querySelector('.x-button');
    let btns = document.querySelectorAll(".open-modal");
    • 모달에 들어갈 데이터를 변경하기 위한 각각의 javascript object 값을 각 변수에 저장
    • 데이터 부분과 일치하게 변수 초기화
    const status_name = document.getElementById('status-name');
    const status_nickname = document.getElementById('status-nickname');
    const status_mbti = document.getElementById('status-mbti');
    const strong_point = document.getElementById('strong-point')
    const teaming_method = document.getElementById('teaming-method');
    const tmi_to_line = document.getElementById('tmi-to-line');
    const status_img_url = document.getElementById('status-image');
    • 모달을 제어하기 위한 전체 버튼의 eventListener 적용
    • 모달 여는 속성 값인 btns를 받아 호출받으면, 리스트 형식의 클래스값을 받아오는데, 위 배열을 기준으로 리스트 순서값을 인자 column 넘버로 받아 함수를 실행함
    • 4가지의 리스트값인 0,1,2,3의 값을 하나씩 받아오고, 0의 순서에 대응하는 값이 col.
    • 해당 값을 순서로 받아 open_modal(순서) 함수를 실행
    [].forEach.call(btns, function (col) {
        col.addEventListener('click', (e) => {
            open_modal(e)
        })
    });
    • 모달을 여는 함수 기능
    • 발생한 이벤트를 변수 클래스로 저장
    • innerHTML을 활용하여 각 자바스크립트 오브젝트를 제어 및 데이터 값 변경
    function open_modal(e) {
    // 이벤트를 가져와 num 변수에 저장해 어떤 버튼이 눌러졌는지 확인한다.
    let num = e.target.classList[1]
    // 각 javascript object를 제어해서 값을 변경한다.
    status_name.innerHTML = team_summary[num].name
    status_nickname.innerHTML = team_summary[num].nick_name
    status_mbti.innerHTML = team_summary[num].mbti
    strong_point.innerHTML = team_summary[num].strong_point
    teaming_method.innerHTML = team_summary[num].teaming_method
    tmi_to_line.innerHTML = team_summary[num].tmi_to_line
    status_img_url.src = team_summary[num].img_url
    // 모달을 화면을 보여준다.
    
    modal.style.top = ((window.innerHeight - modal.scrollHeight) / 2 + window.scrollY) + "px"
    modal.style.left = ((window.innerWidth - modal.scrollWidth) / 2 + window.scrollX) + "px"
    modal.classList.toggle('show');
    if (modal.classList.contains('show')) {
        body.style.overflow = 'hidden';
    	}
    }
    • 모달 켜기.끄기
    modal.classList.toggle('show');
    • 모달이 켜지면 스크롤바 숨기기
    body.style.overflow = 'hidden';
    • 모달이 꺼지면 스크롤바 생기기
    body.style.overflow = 'auto'; 
    // 모달이 켜졌을 때/꺼졌을 때
    modal.addEventListener('click', (event) => {
      if (event.target === modal) {
          // 모달이 켜졌다면 끄고, 꺼졌다면 키기
          modal.classList.toggle('show');
          // 모달이 꺼진다면 스크롤바 다시 생기기
          if (!modal.classList.contains('show')) {
              		body.style.overflow = 'auto';
          		}
    		}
    	});
    • ESC 키를 눌렀을 때 모달 끄기, 스크롤바 생기기
    // ESC 키를 눌렀을 때
    document.addEventListener('keyup', function (e) {
      if ((e.key === "Escape") && (modal.classList.contains('show'))) {
          modal.classList.toggle('show')
          body.style.overflow = 'auto';
      }
    })
    • 모달 폼의 X 버튼을 클릭했을 때, 모달 끄기, 스크롤바 생기기
    // 모달 폼의 X 버튼을 클릭했을 때
    x_button.addEventListener('click', function () {
        modal.classList.toggle('show');
        if (!modal.classList.contains('show')) {
            body.style.overflow = 'auto';
        }
    })

예시코드

  1. HTML
<div class="modal">
    <div class="modal-container">
        <div class="modal-header">
            <!--  status 창 이미지 태그  -->
            <img src="" class="team-image" id="status-image">
            <div class="name-strong-point">
                <!--  id 값을 지정해서 text값 변경 기능 추가 가능 >> 코드를 일일이 치기 보다는 만들어 놓은 부분 재사용 가능  -->
                <p class="name" id="status-name"></p>
                <!--  닉네임 아이디 지정  -->
                <p class="nickname" id="status-nickname"></p>
            </div>
            <div class="x-button">x</div>
        </div>
        <div class="modal-body">
            <div class="modal-body-row1">
                <!--  level은 0으로 지정 우리는 다 초보니까 ^^-->
                <div class="blue-status-category">
                    lv : <span class="status-content">0</span>
                </div>
                <!--  mbti id 지정-->
                <div class="blue-status-category" id="status-mbti">
                    mbti : <span class="status-content"></span>
                </div>
            </div>
            <div class="modal-body-row2">
                <div class="blue-status-category">
                    <!--  장점 아이디 생성  -->
                    장점 : <span class="status-content" id="strong-point"></span>
                </div>
                <div class="blue-status-category">
                    <!--  협업 스타일 아이디 생성  -->
                    협업 스타일 : <span class="status-content" id="teaming-method"></span>
                </div>
            </div>
            <div class="modal-body-row2">
                <!--  tmi를 옮긴 대사 아이디 생성  -->
                <div class="status-content" id="tmi-to-line">
                </div>
            </div>
        </div>
    </div>
</div>
  1. CSS
/* 모달 크기, display none 이다가 버튼이 눌려지면 밑에
modal.show가 불리면서 모달창이 켜짐*/
.modal {
    /* 모달 화면 위치 고정 */
    position: absolute;
    top: 0;
    left: 0;
    z-index: 50;
    /* 버튼을 눌러 모달을 실행해야 하므로 display 값을 none */
    display: none;
    background-color: rgba(0, 0, 0, 0.4);
}

/* 버튼을 눌렀을 때, 모달을 켜기 */
.modal.show {
    display: block;
}

/* 모달 내부 설정 */
.modal-container {
    /* 종이 질감을 백그라운드에 넣음*/
    background-image: url("../../static/image/source/old_paper.jpeg");
    background-size: cover;
    color: dimgray;
    /* 보더라인에 액자 이미지를 넣음 (올드게임 스테이터스) */
    border-style: solid;
    border-width: 40px 40px 40px 40px;
    -moz-border-image: url("../../static/image/source/border_image.png") 42 44 44 44 repeat repeat;
    -webkit-border-image: url("../../static/image/source/border_image.png") 42 44 44 44 repeat repeat;
    -o-border-image: url("../../static/image/source/border_image.png") 42 44 44 44 repeat repeat;
    border-image: url("../../static/image/source/border_image.png") 42 44 44 44 repeat repeat;
    /* 모달을 페이지 가운데 올 수 있도록 비치*/
    position: absolute;
    width: 600px;
    height: 800px;
    padding: 40px;
    border-radius: 10px;
    box-shadow: 0 2px 3px 0 rgba(34, 36, 38, 0.15);
    transform: translateX(-50%) translateY(-50%);
}

/* 모달 크기 조정 */
.modal-body {
    width: 100%;
    height: 50%;
}

/* 모달 헤더: 사진, 이름 부문 */
.modal-header {
    display: flex;
    width: 100%;
    height: 40%;
    justify-content: space-between;
}

/* 모달 헤더 : 제목 부분 */
.name-strong-point {
    margin-left: 60px;

}

/* 모달 헤더 : 이름 부분 */
.name {
    color: cornflowerblue;
    margin-top: 30px;
    font-weight: bold;
    font-size: 50px;
}

/* 모달 헤더 : 닉네임 부분 */
.nickname {
    margin: 60px 0 0 5px;
    font-size: 35px;

}

/* 모달 바디 : 스테이터스 부분 */
.blue-status-category {
    margin-top: 30px;
    color: cornflowerblue;
    font-size: 40px;
    width: 100%;
}

/* 모달 바디 : 첫번째 줄 */
.modal-body-row1 {
    width: 100%;
    display: flex;
}

/* 모달 바디 : 두번째 줄 */
.modal-body-row2 {
    width: 100%;
    display: block;
}

/* 모달 바디 : 내용 부분 */
.status-content {
    margin-top: 30px;
    color: dimgray;
    font-size: 40px;
}

/* 모달 X버튼 크기 속성 */
.x-button {
    font-size: 60px;
    margin-left: 60px;
    cursor: pointer;
}
  1. Javascript
// 모달에 동적으로 붙일 데이터 선언
const team_summary = [
    {
        'name': '황영상',
        'nick_name': '공상쟁이',
        'mbti': 'INFP',
        'strong_point': '의견을 경청하고 조율하기 위해 노력함',
        'teaming_method': '프로젝트를 완수하는 것을 최우선 목표로 삼음',
        'tmi_to_line': '나는 하루종일도 공상할 수 있다고!',
        'img_url': '../../static/image/team_mate/ys.jpeg'
    },
    {
        'name': '이민기',
        'nick_name': '세딸아빠',
        'mbti': 'ENFP',
        'strong_point': '사교적인 성격으로 팀과의 친밀감 형성을 잘함',
        'teaming_method': '자신에게 주어진 바에 최선을 다하기, 약속 준수',
        'tmi_to_line': '세이브 로드가 없는 프린세스 메이커중... 눈에서 흐르는 건 땀이겠지?',
        'img_url': '../../static/image/team_mate/mk.jpg'
    },
    {
        'name': '김하진',
        'nick_name': '그로잉 집착남',
        'mbti': 'ENFP',
        'strong_point': '흥미있는 일에는 굉장한 몰입도',
        'teaming_method': '맡은 일은 반드시 마무리하기, 약속 준수',
        'tmi_to_line': '고양이, 물고기, 풀떼기 다음은 뭘 키워볼까?',
        'img_url': '../../static/image/team_mate/hj.jpg'
    },
    {
        'name': '주정한',
        'nick_name': '자유로운 영혼',
        'mbti': 'ENTP',
        'strong_point': '성장 지향적인 성격과 실천력',
        'teaming_method': '다수가 납득할 수 있는 합의점을 찾는 것과 토론하기',
        'tmi_to_line': '엄청난 귀차니즘 때문에 뭘 하려면 마음을 다섯번은 먹어야 해!',
        'img_url': '../../static/image/team_mate/joo.jpeg'
    },
]

//모달로직을 수행하기 위한 object들
const body = document.querySelector('body');
const modal = document.querySelector('.modal');
const x_button = document.querySelector('.x-button');
let btns = document.querySelectorAll(".open-modal");

// 데이터 변경을 위한 각각의 javascript object값을 각 변수에 저장
const status_name = document.getElementById('status-name');
const status_nickname = document.getElementById('status-nickname');
const status_mbti = document.getElementById('status-mbti');
const strong_point = document.getElementById('strong-point')
const teaming_method = document.getElementById('teaming-method');
const tmi_to_line = document.getElementById('tmi-to-line');
const status_img_url = document.getElementById('status-image');
// 모달을 제어하기 위해 전체 버튼들에 대해서 이벤트 리스너를 적용함
[].forEach.call(btns, function (col) {
    col.addEventListener('click', (e) => {
        open_modal(e)
    })
});

// 모달을 오픈하는 메서드
function open_modal(e) {
    // 이벤트를 가져와 num 변수에 저장해 어떤 버튼이 눌러졌는지 확인한다.
    let num = e.target.classList[1]
    // 각 javascript object를 제어해서 값을 변경한다.
    status_name.innerHTML = team_summary[num].name
    status_nickname.innerHTML = team_summary[num].nick_name
    status_mbti.innerHTML = team_summary[num].mbti
    strong_point.innerHTML = team_summary[num].strong_point
    teaming_method.innerHTML = team_summary[num].teaming_method
    tmi_to_line.innerHTML = team_summary[num].tmi_to_line
    status_img_url.src = team_summary[num].img_url
    // 모달을 화면을 보여준다.

    modal.style.top = ((window.innerHeight - modal.scrollHeight) / 2 + window.scrollY) + "px"
    modal.style.left = ((window.innerWidth - modal.scrollWidth) / 2 + window.scrollX) + "px"
    modal.classList.toggle('show');
    if (modal.classList.contains('show')) {
        body.style.overflow = 'hidden';
    }
}

// 모달이 켜졌을 때
modal.addEventListener('click', (event) => {
    if (event.target === modal) {
        modal.classList.toggle('show');
        if (!modal.classList.contains('show')) {
            body.style.overflow = 'auto';
        }
    }
});
// ESC 키를 눌렀을 때
document.addEventListener('keyup', function (e) {
    if ((e.key === "Escape") && (modal.classList.contains('show'))) {
        modal.classList.toggle('show')
        body.style.overflow = 'auto';
    }
})
// 모달 폼의 X 버튼을 클릭했을 때
x_button.addEventListener('click', function () {
    modal.classList.toggle('show');
    if (!modal.classList.contains('show')) {
        body.style.overflow = 'auto';
    }
})

수정

링크

  1. 모달 html 뼈대
  • 모달을 구현하기 위해서는 다음과 같은 뼈대로 구성된다
    • 모달 주위를 어둡게 만들 배경
    • 모달을 담을 그릇 윈도우
    • 실제 모달 내용
profile
하루종일 몽상가

0개의 댓글