본문 바로가기

DOM

DOM 기초(3) - DOMContentLoaded, Form 태그 이벤트, Element 생성

728x90
반응형

1) Event

    1-1) DOMContentLoaded
    1-2) Form 태그 이벤트 예제

    1-3) Element 생성

    1-4) 정리

2) 코드 실습 - Lotto 번호 뽑기(중복값, 정렬 이슈 해결)

 

 

 

 

1) Event

 

+) <script> 태그의 위치에 따른 출력값 변화( 코드 실행 예측을 통한 자바스크립트 동작 순서 파악 목적 )

<!-- HTML -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="./public/js/index.js" type="text/javascript"></script>
  </head>
  <body>
    <div class="box"></div>
  </body>
</html>

 

// JS
const box = document.querySelector(".box");
console.log(box); // null

/*

null 값이 출력된 이유
>> 바로 위의 html 문서 상 head 태그 내부에 있는 script 태그를 먼저 읽고, 자바스크립트 코드를 실행시키기 때문에 밑에 body 태그 내부의 div 태그가 읽히지 못해 해당 코드를 실행하면 결과값이 'null'이 반환됨!!
>> 즉, HTML 문서의 load가 완료되지 않은 상태에서 "querySelector"를 사용했기 때문에 "null"이 반환됨
>> "querySelector"를 실행하면 결과값으로 element를 가져오거나 null을 반환함!!

*/

// 만일 바로 위의 자바스크립트 코드는 고정한 채 html 문서 상 script 태그의 위치를 기존의 head 태그 내부에서 body 태그 내부 맨 아래에 위치시키면 위의 코드는 해당 element를 정상적으로 출력함!!

 

 

1-1) DOMContentLoaded

// DOMContentLoaded

// 브라우저가 load가 완료되면 DOMContentLoaded 이벤트를 한 번만 발동시킴

function init() {
  console.log("hello world!");

  const box = document.querySelector(".box");
  console.log(box);
}

document.addEventListener("DOMContentLoaded", init);

 

 

 

1-2) Form 태그 이벤트 예제

 

<!-- HTML -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      #error {
        color: red;
        font-size: 20px;
        display: none;
      }
    </style>
  </head>
  <body>
    <div class="box"></div>

    <form method="get" id="loginForm" action="./">
      <!-- form 태그의 action 속성은 페이지를 해당 값(주소)으로 이동시켜줌(여기서는 "./"을 통해 현재 페이지로 이동하도록 설정함) -->
      <input type="text" id="uid" name="userid" value="" />
      <!-- query string 형태(객체는 아니지만 객체 같은 형태 >> 여기서 key: name 속성의 값 / value: value 속성의 값)로 데이터를 전달함 -->
      <input type="password" id="upw" name="userpw" value="" />
      <button type="submit">로그인</button>
    </form>
    <div id="error">아이디나 패스워드가 비어있음!</div>
    <ul id="userList"></ul>

    <h3>비밀번호</h3>
    <input type="password" id="pw" />
    <h3>비밀번호 확인</h3>
    <input type="password" id="repw" />
    <div id="pwError"></div>

    <script src="./public/js/index.js" type="text/javascript"></script>
  </body>
</html>

 

// JS version1
function submitHandler() {
  alert("!!!");
}

function init() {
  const form = document.querySelector("#loginForm");
  // console.log(form); // 코드 동작 확인용
  form.addEventListener("submit", submitHandler);
  // "submit" 이벤트는 form element에서만 이벤트 등록 가능.
}
document.addEventListener("DOMContentLoaded", init); // DOMContentLoaded: 호출시간이 언제이다를 명확히 정하는 것
// document.addEventListener("click", init); // 이벤트는 기본적으로 html 문서의 load가 완료된 이후 동작 가능함!!
// 이벤트의 경우, 최종적으로 html 파일이 로드가 완료된 이후에 수행될 수 있으므로 위의 querySelector 구문이 정상적으로 동작함!!

※ 여기서 "addEventListener"의 경우, 첫 번째 인자값에 정의된 event를 등록한 뒤 해당 event가 발생하면 두 번째 인자값에 정의된 callback 함수를 호출하여 실행시켜주는 역할을 함!!

 

 

// JS version2
/*
    아이디랑 패스워드가 비어있지 않을 때
    해당 정보를 가져와서
    list를 만듦
        1. 어디에 element를 추가할 것인지
        2. 어떤 element를 추가할 것인지
        3. 추가했던 element의 내용을 어떻게 만들지
        4. 만들었던 element를 어떻게 추가할 것인지
*/
function submitHandler(e) {
  // 여기서 submitHandler 함수는 이벤트 객체(e)를 받는다!!
  e.preventDefault(); // preventDefault(): 기본적으로 해당 element가 가지고 있는 기능(ex. a 태그의 링크 이동 기능)을 막는 메서드

  const uid = document.querySelector("#uid");
  const upw = document.querySelector("#upw");
  const error = document.querySelector("#error");
  console.log(uid);
  console.log("아이디는 : ", uid.value !== "");
  console.log("패스워드는 : ", upw.value !== "");

  if (uid.value !== "" && upw.value !== "") {
    // e.target.submit();
    const userList = document.querySelector("#userList");
    const li = document.createElement("li");
    li.innerHTML = uid.value + " " + upw.value;
    userList.append(li);
    // console.log(uid.value, upw.value); // 코드 내용 확인용

    uid.value = "";
    upw.value = "";
    error.style = "display: none";
  } else {
    alert("아이디나 패스워드를 입력해주세요!");
    error.style = "display: block;";
  }
  uid.focus(); // 아이디와 비밀번호 입력 후 혹은 아이디와 비밀번호를 입력하지 않은 경우, 자동적으로 "uid" id를 가진 input 태그에 커서가 가도록 하기 위한 용도!!
}

function init() {
  const form = document.querySelector("#loginForm");
  // console.log(form); // 코드 동작 확인용
  form.addEventListener("submit", submitHandler);
  // "submit" 이벤트는 form element에서만 이벤트 등록 가능.
}
document.addEventListener("DOMContentLoaded", init); // DOMContentLoaded: 웹 페이지 load가 완료되면 addEventListener에 지정된 콜백함수를 호출하여 실행하는 역할(호출시간이 언제이다를 명확히 정하는 역할)
// document.addEventListener("click", init);
// 이벤트의 경우, 최종적으로 html 파일이 로드가 완료된 이후에 수행할 수 있으므로 위의 querySelector 구문이 정상적으로 동작함!!

/*
submit 이벤트가 없을 경우
1. 브라우저에서 "submit" 버튼을 누르면
2. 내용(queryString: 객체처럼 'key:value'의 형태를 가지지만 객체는 아니다!!)을 만들어서 Action 값에 있는 URL로 이동함
*/


/*
submit 이벤트가 있을 경우
1. 브라우저에서 "submit" 버튼을 누르면
2. "submit" 이벤트를 발동시킴. 이후 submitHandler 함수가 호출되고 호출이 끝나면.
3. 내용(queryString: 객체처럼 'key:value'의 형태를 가지지만 객체는 아니다!!)을 만들어서 Action 값에 있는 URL로 이동함

Action 값에 있는 URL 이동을 막고 싶다면
submitHandler 함수 안에서 이벤트 객체를 받아서
"e.preventDefault()"

e.preventDefault()

>> 내용(queryString: 객체처럼 'key:value'의 형태를 가지지만 객체는 아니다!!)을 만들어서 Action 값에 있는 URL로 이동함
>> 위의 내용이 작동 안 됨!


이후 내가 원하는 시점에서 submit을 날리고 싶다면

e.target.submit()
*/


/*
1. 만약에 input이 비어 있다면. submit 안 날리고.
2. 비어 있다면 input 색깔을 바꾸기!
3. input이 채워져 있다면 submit 날리기!

1) 만약에 input이 비어있다면 submit 안 날리고
inptu 내용이 비어있다라는 것을 어떻게 알 수 있을까?

id="uid"인 input 박스를 가져오기
value 값도 가져오기
*/

preventDefault(): 기본적으로 해당 element가 가지고 있는 기능(ex. a 태그의 링크 이동 기능)을 막는 메서드

 

 

 

1-3) Element 생성

/*
js로 element 만들기

uid : web7722
upw : 1234

아이디: web7722 패스워드 : 1234
>> 로그인 버튼을 누르면 위와 같이 아이디와 패스워드 폼의 입력값이 계속 화면 영역에 쌓이도록 할 계획

* element 만들기
document.createElement(element 이름);
document.createElement('li'); // li element가 만들어짐!


* element 넣기
<ul id="userList"></ul>


// const userList = document.querySelector("#userList");
// console.dir(userList);
// append(); >> 자식 element를 추가하겠다는 의미!!

const userList = document.querySelector("#userList");
const liElement = document.createElement("li");
liElement.innerHTML = "hello world!";
userList.append(liElement);
*/

 

// JS(비밀번호/비밀번호 확인 form 비교 구문)
const pw = document.querySelector("#pw"); // 객체
const repw = document.querySelector("#repw");
const error = document.querySelector("#pwError");

function changeHandler(e) {
  if (pw.value === "") {
    error.style = "color: red;";
    error.innerHTML = "비밀번호를 먼저 입력해주시기 바랍니다!";
    pw.focus();
    e.target.value = "";
    return;
  }

  if (e.target.value === pw.value) {
    error.style = "color: green;";
    error.innerHTML = "패스워드가 일치합니다!";
  } else {
    error.style = "color: red;";
    error.innerHTML = "패스워드가 일치하지 않습니다!";
  }
}

/*
	keyup : 키보드를 누르고 땠을 때 실행됨
	keydown : 키보드를 누르고 있을 때마다 실행됨
	keypress : 키보드를 누르고 있을 때 실행됨(한글 인식 안 됨)
*/

repw.addEventListener("keyup", changeHandler);

 

 

 

1-4) 정리

/*
1) Event

1-1) Element 생성

function submitHandler() {
alert("!!!");
}

function init() {
const form = document.querySelector("#loginForm");
// console.log(form); // 코드 동작 확인용
form.addEventListener("submit", submitHandler);
}
document.addEventListener("DOMContentLoaded", init); // 여기서 "DOMContentLoaded" 이벤트 등록이 완료됨


1-2) Event

click
keyup
DOMContentLoaded

<초점>
document // window(전역객체) 객체에 포함되어 있음

window

this // this를 써서 window를 가리킬 수 있음!!


1-3) DOM

DataType : Object - 참조 데이터

Method

- document.getElementById()
- document.getElementsClassName()
- document.getElementsTagName()

querySelector 방식이 좋아서 주로 이 방식을 사용함!!

- document.querySelector()
- document.querySelectorAll()

* HTML에서 직접 값을 주는 방식 >> onClick="alert('hello!')"
* Element.onClick = function(){}
  - 1개의 element에 2개의 이벤트를 등록할 수 없음!
  - 삭제하기 편하다!
* Element.addEventListener(이벤트, 콜백함수)
  - 복수의 이벤트를 넣을 수 있음!
  - 삭제하기 불편하다!
  - 코드 형태가 너무 어렵다!(콜백함수의 개념을 사전에 알고 있어야 코드를 이해하고 사용할 수 있음)


Element.addEventListener(이벤트, callback)
// 위의 코드는 Element에 이벤트를 등록하겠다는 의미이며, 위의 이벤트가 발생할 시 callback 함수를 실행하라는 코드임!!

function listener(event, callback) {
// 자바스크립트가 "click"이라는 event가 올 때까지 멈췄다가 "click" event가 발생할 시 아래 코드를 실행시켜줌!!
if (event === ""click) {
callback()
}
}

listener('click', function(){})


Event Object...

function handler(e) {
console.log(e)
// 이벤트 객체에서 가장 자주 쓰이는 속성 3가지
e.target
e.type
e.preventDefault()
}
div.addEventListener('click', handler)


// 지금까지 배운 이벤트들(이외 이벤트는 따로 필요 시 추가로 알아볼 계획!!)

- submit
- click
- mouseover
- mouseout
- keyup
- keydown
- keypress

Property


+) CRUD
Create
Read
Update
Delete

- ToDoList
- Comment
- Board

>> CRUD 안에 ToDoList, Comment, Board가 포함됨!!
*/

 

 

 

 

 

2) 코드 실습 - Lotto 번호 뽑기(중복값, 정렬 이슈 해결)

 

/*
	lotto 코드 구현 이슈
	- 코드를 줄이는 refactoring 작업
	- 중복값 제거
	- 정렬
*/

 

 

lotto.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      #lotto > li {
        display: inline-block;
        font-size: 40px;
        width: 80px;
        height: 80px;
        border-radius: 40px;
        text-align: center;
        line-height: 80px;
        margin: 30px;
        box-shadow: 2px 2px 4px 1px rgba(0, 0, 0, 0.3);
      }

      #lotto.none {
        display: none;
      }

      /* 1 ~ 10 */
      #lotto > li.a {
        background: yellow;
      }
      /* 11 ~ 20 */
      #lotto > li.b {
        background: blue;
      }
      /* 21 ~ 30 */
      #lotto > li.c {
        background: red;
      }
      /* 31 ~ 40 */
      #lotto > li.d {
        background: gray;
      }
      /* 41 ~ 45 */
      #lotto > li.e {
        background: green;
      }
    </style>
  </head>
  <body>
    <h1>로또번호 생성기</h1>
    <ul id="lotto" class="none">
      <li class="a"></li>
      <li class="b"></li>
      <li class="c"></li>
      <li class="d"></li>
      <li class="e"></li>
      <li class="e"></li>
    </ul>
    <button id="btn">번호 생성</button>
    <script src="./public/js/lotto.js" type="text/javascript"></script>
  </body>
</html>

 

 

lotto.js

// 쉬운 것부터 하나하나 구현하는 것이 좋음!!
// 1. 번호생성 버튼을 누르면 #lotto element를 보이게 하고 싶음
//    1.1 번호생성 버튼에다가 이벤트(click)를 넣자
//      1.1.1 번호생성 element를 선택해서 가져올 수 있어야 함 = querySelector를 사용할 수 있어야 한다는 의미
//      1.1.2 #lotto 선택해서 가져오기
// 2. 랜덤 숫자를 뽑아서 부여하는 것
//    2.1 랜덤 숫자(1 ~ 45)를 뽑는 것
//    2.2 랜덤 숫자를 부여
//      2.2.1 클릭할 때 랜덤 값 생성해보기
//      2.2.2 랜덤 값 6개 만들기
//      2.2.3 element 선택 >> querySelectorAll() 사용
//      2.2.4 각각 순서에 맞게 내용을 넣어주기

// 문제 확인하기!
// 1. 색깔이 안 바뀜
//    1.1 내가 랜덤으로 뽑은 숫자의 범위가 어디에 해당되는지를 알아야 함
//        (1~10, 11~20, 21~30, 31~40, 41~45)
//        1.1.1 12라는 숫자가 있을 때 1 ~ 10
//              9라는 숫자가 있을 때 1 ~ 10
//    1.2 input 1 ~ 45 >> 1 ~ 10 = a, 11 ~ 20 = b, 21 ~ 30 = c ...
//        className을 활용해서 처리하자
// 2. 로또는 중복값이 없어야 함
// 배열에서 추출 값 제거(splice 활용) >> if 문을 통해 이미 추출한 값이 나오면 다시 랜덤으로 숫자를 돌려 추출하도록 함
// 이 경우 전체 횟수는 기존의 6번만 돌리려던 것에서 다시 숫자를 추출한 만큼 전체 수행 횟수가 늘어남!

// 현재는 코드로서 문제가 있다!!

const btn = document.querySelector("#btn");
const lottoDisplay = document.querySelector("#lotto");
const lottoBox = document.querySelectorAll("#lotto > li");
let lottoNum = [];
for (let i = 1; i < 46; i++) {
  lottoNum.push(i);
}

function randomLotto() {
  const random = Math.floor(Math.random() * 45) + 1;
  // Math.floor(Math.random() * 45)는 0 ~ 44까지의 숫자를 랜덤하게 반환하는데
  // 우리가 뽑으려는 숫자의 범위는 1 ~ 45이므로 1을 위의 코드에 더해준다!!
  return random;
}

function randomFinal() {
  let temp = [];
  for (let i = 0; i < 6; i++) {
    let randomNum = randomLotto();
    // temp.push(randomLotto());
    if (temp.length !== 0) {
      for (let j = 0; j < temp.length; j++) {
        if (temp[j] === randomNum) {
          temp.splice(j, 1);
          i--;
        }
      }
    }
    temp.push(randomNum);
  }

  // 랜덤 숫자 배열 오름차순 정렬 함수
  function randomNumAsc(arr) {
    let tempAsc;
    for (let i = 0; i < arr.length; i++) {
      for (let j = i + 1; j < arr.length; j++) {
        if (arr[i] - arr[j] > 0) {
          tempAsc = arr[i];
          arr[i] = arr[j];
          arr[j] = tempAsc;
        }
      }
    }
    return arr;
  }
  return randomNumAsc(temp);
}

function between(num, min, max) {
  if (min <= num && num <= max) {
    return true;
  }
  return false;
}

// input 1 ~ 45
// 문제 1 ~ 10 = a, 11 ~ 20 = b, 21 ~ 30 = c ...
// output : a || b || c || d || e
function getClassName(num) {
  if (between(num, 41, 45)) {
    return "e";
  }
  if (between(num, 31, 40)) {
    return "d";
  }
  if (between(num, 21, 30)) {
    return "c";
  }
  if (between(num, 11, 20)) {
    return "b";
  }
  if (between(num, 1, 10)) {
    return "a";
  }
}

function lottoHandler(e) {
  /*
  const num1 = randomLotto();
  const num2 = randomLotto();
  const num3 = randomLotto();
  const num4 = randomLotto();
  const num5 = randomLotto();
  const num6 = randomLotto();

  console.log(num1);
  console.log(num2);
  console.log(num3);
  console.log(num4);
  console.log(num5);
  console.log(num6);

  const num1ClassName = getClassName(num1);
  const num2ClassName = getClassName(num2);
  const num3ClassName = getClassName(num3);
  const num4ClassName = getClassName(num4);
  const num5ClassName = getClassName(num5);
  const num6ClassName = getClassName(num6);

  lottoBox[0].innerHTML = num1;
  lottoBox[0].className = num1ClassName;
  lottoBox[1].innerHTML = num2;
  lottoBox[1].className = num2ClassName;
  lottoBox[2].innerHTML = num3;
  lottoBox[2].className = num3ClassName;
  lottoBox[3].innerHTML = num4;
  lottoBox[3].className = num4ClassName;
  lottoBox[4].innerHTML = num5;
  lottoBox[4].className = num5ClassName;
  lottoBox[5].innerHTML = num6;
  lottoBox[5].className = num6ClassName;
  */

  //   function linearSearch(array, target) {
  //     const length = array.length;
  //     for (let i = 0; i < length; i++) {
  //       if (array[i] === target) {
  //         return i;
  //       }
  //     }
  //     return -1;
  //   }

  /*
  for (let i = 0; i < 6; i++) {
    num[i] = randomLotto();

    lottoBox[i].innerHTML = num[i];
    lottoBox[i].className = getClassName(num[i]);
  }
*/

  let num = randomFinal();
  for (let i = 0; i < 6; i++) {
    lottoBox[i].innerHTML = num[i];
    lottoBox[i].className = getClassName(num[i]);
  }

  lottoDisplay.classList.remove("none");
}
btn.addEventListener("click", lottoHandler);