no image
[Javascript] 댓글 구현하기 (Create, Read)
작업 내용 댓글을 구현하기 위한 CRUD 중 C와 R을 구현해 보자! 다음은 구현해야 할 내용이다. 댓글 입력 (Create) 댓글 입력 폼에 내용을 입력한 뒤 submit을 누르면 리스트에 추가된다. 입력 폼이 비어있는 상태에서 submit을 누르면 경고 팝업을 띄운다. 댓글을 성공적으로 처리하면 입력 폼을 reset 한다. 입력한 값을 아이디, 댓글 내용, 날짜 형태로 된 객체로 만들어 리스트에 넣는다. 댓글 출력 (Read) 댓글 내용은 아이디, 댓글 내용, 날짜로 표현한다. 댓글 리스트는 최신순으로 나타낸다. 댓글 총 개수를 표시한다. 구현 HTML / CSS 일단 HTML과 CSS는 미리 구현되어 있는 걸 사용한다. Javascript로 Create와 Read를 구현하는데 집중하자! # prac..
2022.11.16
no image
[Javascript] 로또 번호 생성기 - 버블 정렬 [+전체 코드 수정]
이번에는 이전에 만들었던 로또 번호 생성기에 정렬 기능을 추가하고 전체적으로 코드 수정을 해보았다. 추가로 로또의 보너스 번호도 추가해 보았다. 먼저 정렬 알고리즘에 대해서 알아보고 가자! 정렬 알고리즘 정렬(Sorting)이란 데이터를 특정한 기준에 따라서 순서대로 나열하는 것을 말한다. 사람들은 정렬을 쉽게 할 수 있지만 컴퓨터에게는 쉬운 일은 아니다. 컴퓨터는 사람과 다르게 규칙성을 알지 못하기 때문에 우리가 코드를 잘 짜줘야 한다. 정렬 알고리즘의 종류는 다양하다. 버블 정렬, 선택 정렬, 삽입 정렬, 퀵 정렬, 계수 정렬 등이 있다. 이 중에서 나는 오늘 배운 버블 정렬을 사용해 보았다. 찾아보니 버블 정렬이 가장 효율이 좋지 않은 정렬이라고 한다. 하지만 초보자가 이해하기 쉽고 코드가 짧은 장..
2022.11.15
no image
[Javascript] Script 태그 위치
HTML 문서에서 Javascript 파일을 연결하기 위해서 script 태그를 사용한다. HTML 문서에서 어느 곳에든 사용해도 되는데 위치에 따라 동작의 차이가 발생할 수 있다. 이는 브라우저의 동작 방식과 연관이 있다. 브라우저의 동작 방식 1 const box = document.querySelector(".box"); console.log(box); 브라우저는 다음과 같이 HTML 파일을 읽게 된다. 브라우저는 HTML 파일을 위에서부터 아래로 읽게 되는데 코드를 읽는 도중 script 태그를 만나게 되면 Javascript 파일을 불러와 읽고 실행한 다음에 다시 나머지 HTML 파일을 읽게 된다. Javascript를 읽을 당시에는 class 명이 box인 요소를 찾을 수 없기 때문에 null..
2022.11.11
no image
[Javascript] Event Object
이벤트 객체 (Event Object) 이벤트 등록을 할 때 addEventListener에 두 가지 인자를 전달해 주었다. 첫 번째 인자는 이벤트 이름이고 두 번째 인자는 이벤트 핸들러라고 부르는 사용자가 만든 함수이다. 이 함수에 이벤트가 발생하면 동작될 코드들을 넣어 놓게 된다. 이벤트가 발생되어서 이벤트 핸들러 함수가 호출이 되면 이벤트 객체를 전달받게 되는데 이벤트 핸들러 함수에 매개변수를 선언해주면 이벤트 객체를 사용할 수 있다. 클릭 const btn = document.querySelector(".btn"); btn.onclick = function (e) { console.log(e); }; 위 방법으로 이벤트를 등록해 이벤트 객체를 사용할 수 있지만 나는 아래 방법으로 이벤트를 등록해서..
2022.11.10
no image
[Javascript] Event (Add, Remove)
Event란? Event 인터페이스는 DOM 내에 위치한 이벤트를 나타냅니다. 이벤트는 마우스를 클릭하거나 키보드를 누르는 등 사용자의 액션에 의해 발생할 수도 있고, 비동기적 작업의 진행을 나타내기 위해서 API가 생성할 수도 있습니다. Event - MDN 이벤트(Event)란 우리가 브라우저에서 버튼을 클릭하거나 로그인을 하기 위해 ID/PW를 입력하는 행동이라고 볼 수 있다. 버튼을 클릭하면 브라우저에 click 이벤트가 발생하고 ID/PW를 입력할 때 브라우저에서 keyup이나 keydown등의 이벤트가 발생한다. Event Handling 브라우저에서 발생되는 이벤트를 처리하는 걸 이벤트 핸들링이라고 부른다. 이벤트 핸들링을 하는 방법이 3가지 정도 있는데 한 번 알아보자! 1. Element..
2022.11.09
no image
[Javascript] 로또 번호 생성기
구현해야 할 것 1. 로또 번호가 출력되는 페이지를 만든다. 2. 누르면 이벤트가 발생하는 번호 생성 버튼을 만든다. 1. 번호 생성 버튼을 눌리면 로또 번호가 보이게 한다. 2. 랜덤한 숫자를 뽑는다. 3. 각 요소에 뽑은 숫자를 넣어준다. 구현 아래와 같이 HTML 파일을 작성해 주자! 로또 번호 생성기 1 2 3 4 5 6 번호 생성 번호 생성 버튼을 누르면 li 태그의 요소가 보이도록 Javascript 파일을 작성해보자! const lottoElement = document.querySelector("#lotto"); const btnElement = document.querySelector("#btn"); function buttonEvent() { lottoElement.className =..
2022.11.08
no image
[Javascript] DOM(Document Object Model)
Window 객체란? DOM을 설명하기 전에 먼저 Window라는 객체를 알아야 한다. Window 객체란 브라우저의 창을 대변한다고 볼 수 있다. Javascript에서 최상단에 존재하는 객체로 이 객체 안에는 수많은 프로퍼티들이 존재하는데 Javascript의 거의 모든 내장 객체, 내장 함수들이 Window 객체에 속해 있다. Window 객체는 Javascript 코드의 어느 곳에서나 항상 접근 할 수 있는 객체로 전역 객체(Global Object)라고도 부른다. 사실 우리가 자주 사용하는 console.log도 window.console.log로 사용해야 하지만 무엇을 사용하든 결국 윈도우 객체 내부의 것이기 때문에 window.을 생략해도 된다. 그리고 우리가 선언한 변수나 함수도 windo..
2022.11.08
no image
[Javascript] 객체
객체란? 관련된 데이터와 함수(일반적으로 여러 데이터와 함수로 이루어지는데, 객체 안에 있을 때는 보통 프로퍼티와 메소드라고 부릅니다)의 집합 - JavaScript 객체 기본 [MDN] 객체는 실제로 존재하는 사물로 이해할 수 있다. 예를 들어 강아지를 객체라고 한다면 프로퍼티는 이름, 나이, 무게 등이 들어 갈 수 있다. 메소드는 앉아, 일어서, 기다려 등 강아지에게 시킬 수 있는 행동들이 들어갈 수 있다. const siba = { name: "siba", age: 1, weight: "5kg", alive: true, sitDown: function () {}, standUp: function () {}, wait: function () {}, }; 위와 같이 객체 안에는 어떤 데이터 타입이든 들..
2022.11.04

작업 내용

댓글을 구현하기 위한 CRUD 중 C와 R을 구현해 보자!

다음은 구현해야 할 내용이다.

댓글 입력 (Create)

  1. 댓글 입력 폼에 내용을 입력한 뒤 submit을 누르면 리스트에 추가된다.
  2. 입력 폼이 비어있는 상태에서 submit을 누르면 경고 팝업을 띄운다.
  3. 댓글을 성공적으로 처리하면 입력 폼을 reset 한다.
  4. 입력한 값을 아이디, 댓글 내용, 날짜 형태로 된 객체로 만들어 리스트에 넣는다.

댓글 출력 (Read)

  1. 댓글 내용은 아이디, 댓글 내용, 날짜로 표현한다.
  2. 댓글 리스트는 최신순으로 나타낸다.
  3. 댓글 총 개수를 표시한다.

구현

HTML / CSS

일단 HTML과 CSS는 미리 구현되어 있는 걸 사용한다. Javascript로 Create와 Read를 구현하는데 집중하자!

#  practice.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>
    <link rel="stylesheet" href="public/css/practice.css" />
  </head>
  <body>
    <div>
      <ul class="comment">
        <li class="comment-form">
          <form id="commentFrm">
            <h4>댓글쓰기 <span></span></h4>
            <span class="ps_box">
              <input type="text" placeholder="댓글 내용을 입력해주세요." class="int" name="content" />
            </span>
            <input type="submit" class="btn" value="등록" />
          </form>
        </li>
        <li id="comment-list"></li>
      </ul>
    </div>
    <script src="public/js/practice.js"></script>
  </body>
</html>

# practice.css

* {
  margin: 0;
  padding: 0;
}
ul,
li {
  list-style: none;
}

.comment {
  display: flex;
  flex-direction: column;
  flex-wrap: nowrap;
  padding: 30px;
  width: 600px;
  margin: 0 auto;
}

.comment > li {
  margin-top: 20px;
}
.comment > li:nth-child(1) {
  margin: 0px;
}

.comment-row {
  display: flex;
  justify-content: space-between;
  flex-direction: row;
}

.comment-row {
  margin-top: 20px;
  width: 100%;
}

.comment-row > li:nth-child(2) {
  flex-shrink: 0;
  flex-grow: 1;
  padding-left: 25px;
  z-index: 1;
  width: 100%;
}

.comment-row > li:nth-child(2) {
  width: 85px;
}

.comment-form > form {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: space-between;
}

.comment-form > form > h4 {
  width: 100%;
  margin: 14px 0 14px 0;
}

.comment-content {
  cursor: pointer;
  word-break: break-all;
  padding-right: 25px;
}

.ps_box {
  display: block;
  position: relative;
  width: 80%;
  height: 51px;
  border: solid 1px #dadada;
  padding: 10px 14px 10px 14px;
  background: #fff;
  box-sizing: border-box;
}

.ps_box > input {
  outline: none;
}

.int {
  display: block;
  position: relative;
  width: 100%;
  height: 29px;
  padding-right: 25px;
  line-height: 29px;
  border: none;
  background: #fff;
  font-size: 15px;
  box-sizing: border-box;
  z-index: 10;
}

.btn {
  width: 18%;
  padding: 18px 0 16px;
  text-align: center;
  box-sizing: border-box;
  text-decoration: none;
  border: none;
  background: #333;
  color: #fff;
  font-size: 14px;
}

.comment-delete-btn {
  display: inline-block;
  margin-left: 7px;
  cursor: pointer;
}

.comment-update-input {
  border: none;
  border-bottom: 1px solid #333;
  font-size: 16px;
  color: #666;
  outline: none;
}

댓글쓰기 초기 화면

Javascript

1. 댓글 입력폼에 내용을 입력한 뒤 submit을 누르면 리스트에 추가 (Create)

댓글 입력폼에 내용을 입력한 뒤 submit을 누르면 리스트에 추가가 되도록 해보자!

먼저 사용할 요소를 선택해 준다. submit 이벤트는 form 요소에서만 사용이 가능하기 때문에 submit 이벤트 사용을 위해 form 요소를 선택해주자!

const commentBtn = document.querySelector("#commentFrm");
const list = [];

console.log(commentBtn);

사용할 요소 선택 후 확인

Event.preventDefault()

이벤트 흐름의 어떤 단계에서도 preventDefault()를 호출하면 기본 동작을 취소할 수 있다.
예를 들면 a 태그나 submit 태그는 누르면 href를 통해 이동하거나 창이 새로고침 되어 실행이 된다.
preventDefault()를 이용해 이 동작을 막아줄 수 있다.

submit 버튼을 누르면 input을 통해 입력받은 값들을 key=value 형태로 서버에 전송해 준다고 한다. 전송되고 나면 창이 새로고침 되는데 이렇게 되면 input에 입력한 값을 javascript에서 사용하기 어렵다. preventDefault()를 이용해서 submit 버튼의 기본 동작을 막아주자! 그리고 이벤트도 등록해 주자!

추가로 버튼을 클릭해 input에 값을 입력하면 어디에 값이 들어가는지도 찾아보자! 

const commentBtn = document.querySelector("#commentFrm");
const list = [];

function commentBtnHandler(e) {
  e.preventDefault();
  console.dir(e.target);
  console.log(e.target[0]);
  console.log(e.target[0].value);
  console.log(e.target.elements[0]);
  console.log(e.target.elements[0].value);
  console.log(e.target.elements.content);
  console.log(e.target.elements.content.value);
  console.log(e.target.content);
  console.log(e.target.content.value);
}

commentBtn.addEventListener("submit", commentBtnHandler);

input에 값을 넣고 submit을 누르면 이벤트 객체가 생성되는데 이 객체안에객체 안에 무수한 객체들이 존재한다. 내가 원하는 input에 입력한 값을 찾아보았는데 위와 같은 객체 안에 value값으로 값이 존재하고 있었다. 이 부분은 조금 더 공부를 해봐야 알 것 같다. 나는 일단 마지막에 적은 e.target.content의 value를 사용할 예정이다.

 

input에 입력한 Hello값을 찾아본 결과

input에 입력한 값을 찾았으니 이걸 list에 push를 사용해 담아주기만 하면 이 작업은 끝난다.

const commentBtn = document.querySelector("#commentFrm");
const list = [];

function commentBtnHandler(e) {
  e.preventDefault();
  list.push(e.target.content.value);
  return console.log(list);
}

commentBtn.addEventListener("submit", commentBtnHandler);

list에 input에 입력한 값 담기

2. 입력폼이 비어있는 상태에서 submit을 누르면 경고 팝업 띄우기 (Create)

이건 간단하다 조건문을 사용해 빈 문자열이면 alert를 이용해 경고창을 띄우면 된다.

const commentBtn = document.querySelector("#commentFrm");
const list = [];

function commentBtnHandler(e) {
  e.preventDefault();
  const input = e.target.content;
  if (input.value === "") {
    alert("내용을 넣고 등록 버튼을 눌러주세요.");
    return;
  }
  list.push(input.value);
}

commentBtn.addEventListener("submit", commentBtnHandler);

비어있는 상태에서 등록을 하려고 하면 경고 팝업띄우기

 

3. 댓글을 성공적으로 처리하면 입력폼을 reset (Create)

이 작업은 form 요소의 reset 메서드를 사용한다. 

 

HTMLFormElement.reset()
이 메서드는 요소의 값을 기본값으로 초기화하는 메서드이다.
const commentBtn = document.querySelector("#commentFrm");
const list = [];

function commentBtnHandler(e) {
  e.preventDefault();
  const input = e.target.content;
  if (input.value === "") {
    alert("내용을 넣고 등록 버튼을 눌러주세요.");
    return;
  }
  list.push(input.value);
  e.target.reset();
}

commentBtn.addEventListener("submit", commentBtnHandler);

내용을 넣고 등록을 하면 입력 내용이 초기화되는 모습

4. 입력한 값을 아이디, 댓글 내용, 날짜 형태로 된 객체로 만들어 리스트에 넣기 (Create)

사용자의 아이디, 댓글 내용, 날짜는 달라야 하지만 여기서는 댓글 내용 부분만 다르게 할 예정이다.

먼저 아이디, 댓글 내용, 날짜를 담는 객체를 찍어낼 생성자 함수를 선언하자!

function Comment(content) {
  this.userid = "cloudcoke";
  this.content = content;
  this.date = "2022-11-15";
}

생성자 함수 테스트 결과

submit을 누르면 작성한 댓글이 들어간 객체를 생성해 리스트에 넣는 작업을 해보자!

const commentBtn = document.querySelector("#commentFrm");
const list = [];

function Comment(content) {
  this.userid = "cloudcoke";
  this.content = content;
  this.date = "2022-11-15";
}

function commentBtnHandler(e) {
  e.preventDefault();
  const input = e.target.content;
  if (input.value === "") {
    alert("내용을 넣고 등록 버튼을 눌러주세요.");
    return;
  }
  const commentObj = new Comment(input.value);
  list.push(commentObj);
  e.target.reset();
}

commentBtn.addEventListener("submit", commentBtnHandler);

작성한 댓글이 들어간 객체를 가진 리스트

Create은 끝났다. 이제 Read 부분을 작업해 보자!

1. 댓글 내용은 아이디, 댓글 내용, 날짜로 표현 (Read)

댓글을 쓰게되면 입력란 아래에 쓴 댓글이 보이도록 해야 한다. 출력돼야 하는 부분은 아이디, 댓글 내용, 날짜이다.

먼저 요소를 추가할 부분인 comment-list를 선택한다.

const commentList = document.querySelector("#comment-list");

console.log(commentList);

comment-list를 선택한 결과

이제 요소를 생성해야 한다.

요소 생성은 createElement 메서드를 이용해 요소를 생성할 수 있다.

요소를 생성할 때 다음과 같이 하지 않도록 주의해야 한다.

요소를 생성한 값은 리턴값이 참조 형태이기 때문에 하나를 수정해도 다른 것들에게 영향을 미치기 때문이다.

const li1 = document.createElement("li"); // 참조형태이기 때문에
const li2 = li1;
const li3 = li1;

// 위 코드는 다음과 같음
const obj1 = {};
const obj2 = obj1;
const obj3 = obj1;

요소를 다음과 같이 생성하자!

ul 태그 생성하고 li 태그를 3개를 생성해 주면 된다. 

function createRow() {
  const ul = document.createElement("ul");
  const li1 = document.createElement("li");
  const li2 = document.createElement("li");
  const li3 = document.createElement("li");

  console.log(ul);
  console.log(li1);
  console.log(li2);
  console.log(li3);
}

생성된 요소 확인

요소를 생성하면 생성만 되고 아무 변화가 없다. 원하는 형태로 만들려면 ul안에 li를 넣어 줘야 한다.

function createRow() {
  const ul = document.createElement("ul");
  const li1 = document.createElement("li");
  const li2 = document.createElement("li");
  const li3 = document.createElement("li");

  ul.append(li1);
  ul.append(li2);
  ul.append(li3);

  return ul;
}

원하는 형태로 만들어진 모습

CSS를 적용하려면 생성한 요소에 class 속성을 부여해야 한다. setAttribute 메서드를 사용하면 원하는 속성을 부여할 수 있다.

function createRow() {
  const ul = document.createElement("ul");
  const li1 = document.createElement("li");
  const li2 = document.createElement("li");
  const li3 = document.createElement("li");

  ul.append(li1);
  ul.append(li2);
  ul.append(li3);

  ul.setAttribute("class", "comment-row");
  li1.setAttribute("class", "comment-id");
  li2.setAttribute("class", "comment-content");
  li3.setAttribute("class", "comment-date");

  return ul;
}

class 속성이 부여된 모습

아이디, 댓글 내용, 날짜를 화면에 출력하려면 innerHTML을 이용해 요소에 내용을 추가해 주어야 한다.

매개변수 3개를 받아서 각각 li1과 li2와 li3에 내용을 추가해 주자!

function createRow(userid, content, date) {
  const ul = document.createElement("ul");
  const li1 = document.createElement("li");
  const li2 = document.createElement("li");
  const li3 = document.createElement("li");

  ul.append(li1);
  ul.append(li2);
  ul.append(li3);

  ul.setAttribute("class", "comment-row");
  li1.setAttribute("class", "comment-id");
  li2.setAttribute("class", "comment-content");
  li3.setAttribute("class", "comment-date");

  li1.innerHTML = userid;
  li2.innerHTML = content;
  li3.innerHTML = date;

  return ul;
}

createRow 테스트 결과

이제 생성한 요소를 comment-list 부분에 추가하자!

function drawing() {
  for (let i = 0; i < list.length; i++) {
    const row = createRow(list[i].userid, list[i].content, list[i].date);
    commentList.append(row);
  }
}

 

drawing 함수 테스트 결과

ul을 만들기 위해 createRow 함수를 호출 했고 li 안에 콘텐츠를 채우기 위해 list에 있는 객체를 이용했다. 그다음 그 값을 commentList에 추가해 comment-list에 요소가 추가될 수 있도록 하였다.

여기서 문제가 발생한다. drawing 함수를 호출할 때마다 값이 누적되어서 출력이 된다.

drawing 함수를 호출할 때 commentList를 빈 값으로 만들어 주면 문제가 해결된다.

function drawing() {
  commentList.innerHTML = "";
  for (let i = 0; i < list.length; i++) {
    const row = createRow(list[i].userid, list[i].content, list[i].date);
    commentList.append(row);
  }
}

drawing 함수 문제 해결

이 함수를 commentBtnHandler에 넣어주면 댓글을 쓸때마다 아래에 입력한 내용이 출력되게 된다.

function commentBtnHandler(e) {
  e.preventDefault();
  const input = e.target.content;
  if (input.value === "") {
    alert("내용을 넣고 등록 버튼을 눌러주세요.");
    return;
  }
  const commentObj = new Comment(input.value);
  list.push(commentObj);

  drawing();
  e.target.reset();
}

commentBtnHandler에 drawing 함수를 넣은 결과

2. 댓글 리스트는 최신순으로 나타낸다. (Read)

댓글을 등록하면 마지막에 작성한 댓글이 맨 아래에 나오게 된다. 맨 마지막에 작성한 댓글이 맨 위로 올라오게 하려면 어떻게 해야 할까?

다음과 같이 drawing 함수를 수정해 주면 된다.

function drawing() {
  commentList.innerHTML = "";
  for (let i = list.length - 1; i >= 0; i--) {
    const row = createRow(list[i].userid, list[i].content, list[i].date);
    commentList.append(row);
  }
}

for문을 역순으로 돌린 결과

for문을 역순으로 돌리면 해결이 된다.

3. 댓글 총개수를 표현다. (Read)

댓글의 총 개수를 표현하는 방법은 쉽다. 다음과 같이 코드를 작성해 주면 된다.

const total = document.querySelector("h4 > span");

function totalRecord() {
  total.innerHTML = `(${list.length})`;
}

totalRecord 함수 테스트

이 함수도  commentBtnHandler에 넣어주면 댓글을 작성할 때마다 갱신이 된다.

function totalRecord() {
  total.innerHTML = `(${list.length})`;
}

function commentBtnHandler(e) {
  e.preventDefault();
  const input = e.target.content;
  if (input.value === "") {
    alert("내용을 넣고 등록 버튼을 눌러주세요.");
    return;
  }
  const commentObj = new Comment(input.value);
  list.push(commentObj);
  totalRecord();

  drawing();
  e.target.reset();
}

commentBtnHandler에 totalRecord 함수를 넣은 결과

그리고 처음부터 댓글 개수를 보고 싶다면 전역에 호출을 해주면 된다.

아래는 최종 완성 코드이다.

const commentBtn = document.querySelector("#commentFrm");
const commentList = document.querySelector("#comment-list");
const total = document.querySelector("h4 > span");
const list = [];

function Comment(content) {
  this.userid = "cloudcoke";
  this.content = content;
  this.date = "2022-11-15";
}

function createRow(userid, content, date) {
  const ul = document.createElement("ul");
  const li1 = document.createElement("li");
  const li2 = document.createElement("li");
  const li3 = document.createElement("li");

  ul.append(li1);
  ul.append(li2);
  ul.append(li3);

  ul.setAttribute("class", "comment-row");
  li1.setAttribute("class", "comment-id");
  li2.setAttribute("class", "comment-content");
  li3.setAttribute("class", "comment-date");

  li1.innerHTML = userid;
  li2.innerHTML = content;
  li3.innerHTML = date;

  return ul;
}

function drawing() {
  commentList.innerHTML = "";
  for (let i = list.length - 1; i >= 0; i--) {
    const row = createRow(list[i].userid, list[i].content, list[i].date);
    commentList.append(row);
  }
}

function totalRecord() {
  total.innerHTML = `(${list.length})`;
}

function commentBtnHandler(e) {
  e.preventDefault();
  const input = e.target.content;
  if (input.value === "") {
    alert("내용을 넣고 등록 버튼을 눌러주세요.");
    return;
  }
  const commentObj = new Comment(input.value);
  list.push(commentObj);
  totalRecord();

  drawing();
  e.target.reset();
}

totalRecord();
commentBtn.addEventListener("submit", commentBtnHandler);

최종 완성

참고

https://developer.mozilla.org/ko/docs/Web/API/Event/preventDefault

https://programming119.tistory.com/100

https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/reset

이번에는 이전에 만들었던 로또 번호 생성기에 정렬 기능을 추가하고 전체적으로 코드 수정을 해보았다.

추가로 로또의 보너스 번호도 추가해 보았다.

먼저 정렬 알고리즘에 대해서 알아보고 가자!

정렬 알고리즘


정렬(Sorting)이란 데이터를 특정한 기준에 따라서 순서대로 나열하는 것을 말한다. 사람들은 정렬을 쉽게 할 수 있지만 컴퓨터에게는 쉬운 일은 아니다. 컴퓨터는 사람과 다르게 규칙성을 알지 못하기 때문에 우리가 코드를 잘 짜줘야 한다.

정렬 알고리즘의 종류는 다양하다. 버블 정렬, 선택 정렬, 삽입 정렬, 퀵 정렬, 계수 정렬 등이 있다. 이 중에서 나는 오늘 배운 버블 정렬을 사용해 보았다. 찾아보니 버블 정렬이 가장 효율이 좋지 않은 정렬이라고 한다. 하지만 초보자가 이해하기 쉽고 코드가 짧은 장점이 있다고 한다. 

버블 정렬


버블 정렬은 옆에 있는 값과 비교해서 더 큰 값을 뒤로 보내는 정렬이다. (오름차순 기준)

다음 그림과 같이 바로 옆에 있는 값을 비교해 앞쪽 값이 더 크다면 뒤로 보내면서 정렬을 한다.

버블 정렬 예시
 

버블 정렬 코드는 다음과 같다.

function bubleSort(arr) {
  let temp;
  for (let i = 0; i < arr.length - 1; i++) {
    for (let j = 0; j < arr.length - 1 - i; j++) {
      if (arr[j] > arr[j + 1]) {
        temp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = temp;
      }
    }
  }
  return arr;
}

1. 값을 임시로 담을 temp 변수를 선언한다.

2. 배열의 길이의 - 1 만큼 반복한다. (2개씩 비교하기 때문에)

3. 배열의 길이의 -1 - i 만큼 반복한다. (마지막 값은 정렬이 끝났기 때문에 1번씩 반복 횟수가 줄어듬)

4. 만약 왼쪽 배열의 값이 오른쪽 배열의 값보다 크다면

5. 왼쪽 배열의 값을 temp 변수에 넣는다.

6. 오른쪽 배열의 값을 왼쪽 배열에 넣는다.

7. temp 변수의 값을 오른쪽 배열의 값에 넣는다. (5 ~ 7 : swap)

 

버블 정렬을 이용해 수정한 코드를 보자!

로또 번호 생성기 코드


# practice.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>
    <link rel="stylesheet" href="public/css/practice.css" />
  </head>
  <body>
    <div id="wrap">
      <h2>로또 번호 생성기</h2>
      <div id="container">
        <div id="lotto_contain">
          <ul id="lotto_box">
            <li>1</li>
            <li>2</li>
            <li>3</li>
            <li>4</li>
            <li>5</li>
            <li>6</li>
          </ul>
          <div id="result">당첨번호</div>
        </div>
        <div id="plus">+</div>
        <div id="bonus_contain">
          <div id="bonus_box">
            <div id="bonus_num">6</div>
          </div>
          <div id="bonus_text">보너스</div>
        </div>
      </div>
      <button id="create_num">번호 생성</button>
    </div>
    <script src="public/js/practice.js"></script>
  </body>
</html>

# practice.css

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

ul,
li {
  list-style: none;
}

#wrap {
  width: 100%;
  text-align: center;
}

#wrap > h2 {
  font-size: 45px;
}

#container {
  display: flex;
  justify-content: center;
  margin-top: 30px;
}

#container > #lotto_contain > #lotto_box {
  width: 800px;
  height: 150px;
  display: flex;
  justify-content: space-evenly;
  align-items: center;
  border-radius: 25px;
  background-color: #fafafa;
}

#container > #lotto_contain > #lotto_box > li {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100px;
  height: 100px;
  font-size: 40px;
  border-radius: 50px;
  color: white;
  font-weight: bold;
}

#container > #lotto_contain > #result {
  margin-top: 15px;
  font-size: 16px;
  font-weight: bold;
  color: #555;
}

#container > #plus {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100px;
  height: 150px;
  font-size: 60px;
  font-weight: bold;
  color: #999;
}

#container > #bonus_contain > #bonus_box {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 150px;
  height: 150px;
  border-radius: 25px;
  background-color: #fafafa;
}

#container > #bonus_contain > #bonus_box > #bonus_num {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100px;
  height: 100px;
  font-size: 40px;
  border-radius: 50px;
  color: white;
  font-weight: bold;
}

#container > #bonus_contain > #bonus_text {
  margin-top: 15px;
  font-size: 16px;
  font-weight: bold;
  color: #555;
}

#create_num {
  margin-top: 20px;
  width: 150px;
  height: 50px;
}

.range_1 {
  background-color: rgb(251, 196, 0);
}

.range_2 {
  background-color: rgb(105, 200, 242);
}

.range_3 {
  background-color: rgb(255, 114, 114);
}

.range_4 {
  background-color: rgb(170, 170, 170);
}

.range_5 {
  background-color: rgb(176, 216, 64);
}

# practice.js

const lottoList = document.querySelectorAll("#lotto_contain > #lotto_box > li");
const bonusElement = document.querySelector("#bonus_contain > #bonus_box > #bonus_num");
const numCreateBtn = document.querySelector("#create_num");

function numCreate() {
  const numList = [];
  const lottoNum = [];
  const result = {};
  let ranNumIndex = 0;
  for (let i = 0; i < 45; i++) {
    numList.push(i + 1);
  }

  for (let i = 0; i < 7; i++) {
    ranNumIndex = Math.floor(Math.random() * numList.length);
    lottoNum.push(numList[ranNumIndex]);
    numList.splice(ranNumIndex, 1);
  }

  const bonusNum = lottoNum[lottoNum.length - 1];
  lottoNum.splice(lottoNum.length - 1, 1);
  result["lottoNum"] = lottoNum;
  result["bonusNum"] = bonusNum;

  return result;
}

function rangeNum(num) {
  if (num >= 1 && num <= 10) {
    return "range_1";
  }
  if (num >= 11 && num <= 20) {
    return "range_2";
  }
  if (num >= 21 && num <= 30) {
    return "range_3";
  }
  if (num >= 31 && num <= 40) {
    return "range_4";
  }
  if (num >= 41 && num <= 45) {
    return "range_5";
  }
}

function sorting(arr) {
  let temp;
  for (let i = 0; i < arr.length - 1; i++) {
    for (let j = 0; j < arr.length - 1 - i; j++) {
      if (arr[j] > arr[j + 1]) {
        temp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = temp;
      }
    }
  }
  return arr;
}

function resultLottoNum() {
  const resultNum = numCreate();
  const sortingNum = sorting(resultNum.lottoNum);
  for (i = 0; i < lottoList.length; i++) {
    lottoList[i].innerHTML = sortingNum[i];
    lottoList[i].className = rangeNum(sortingNum[i]);
  }
  bonusElement.innerHTML = resultNum.bonusNum;
  bonusElement.className = rangeNum(resultNum.bonusNum);

  return resultNum.lottoNum;
}

resultLottoNum();
numCreateBtn.addEventListener("click", resultLottoNum);

로또 번호 생성기 동작 화면

이전과 달라진 점은 이전에는 번호를 뽑을 때 매번 1부터 45까지에서 랜덤 한 번호를 뽑았다면 이번에는 1부터 45까지 있는 배열을 생성해서 번호를 뽑을 때마다 배열에서 요소를 하나씩 제거하면서 중복을 처리했다는 점이다.

 

 

 

HTML 문서에서 Javascript 파일을 연결하기 위해서 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/test.js"></script>
  </head>
  <body>
    <div class="box" style="background: orange">1</div>
  </body>
</html>
const box = document.querySelector(".box");
console.log(box);

브라우저는 다음과 같이 HTML 파일을 읽게 된다.

브라우저가 HTML 파일을 읽는 방법

브라우저는 HTML 파일을 위에서부터 아래로 읽게 되는데 코드를 읽는 도중 script 태그를 만나게 되면 Javascript 파일을 불러와 읽고 실행한 다음에 다시 나머지 HTML 파일을 읽게 된다. Javascript를 읽을 당시에는 class 명이 box인 요소를 찾을 수 없기 때문에 null 값이 콘솔에 출력되게 된다.

script 태그를 상단에 작성한 결과

그래서 script 태그는 body 태그의 맨 아래쪽에 적어주는 것이 좋다.

<!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>
  </head>
  <body>
    <div class="box" style="background: orange">1</div>
    <script src="public/js/test.js"></script>
  </body>
</html>
const box = document.querySelector(".box");
console.log(box);

script 태그를 하단에 작성한 결과

그렇다면 HTML 파일을 다 읽고 Javascript 파일을 실행할 수 없을까?

DomContentLoaded


Javasript에는 DOMContentLoaded라는 이벤트가 존재한다. 이 이벤트는 HTML 파일을 브라우저가 다 읽었을 때 실행이 되게 된다. 위에 작성한 코드를 DOMContentLoaded 이벤트를 활용해 바꿔보자!

<!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/test.js"></script>
  </head>
  <body>
    <div class="box" style="background: orange">1</div>
  </body>
</html>
function init() {
  const box = document.querySelector(".box");
  console.log(box);
}

document.addEventListener("DOMContentLoaded", init);

DOMContentLoaded 이벤트 사용

DOMContentLoaded 이벤트를 사용할 때 주의해야 할 점이 있다. DOMContentLoaded 이벤트는 맨 마지막에 발생한다는 점이다.

<!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/test.js"></script>
  </head>
  <body>
    <div class="box" style="background: orange">1</div>
    <script>
      console.log("왜 이게 먼저 실행돼?");
    </script>
  </body>
</html>
function init() {
  const box = document.querySelector(".box");
  console.log(box);
}

document.addEventListener("DOMContentLoaded", init);

하단에 있는 스크립트 코드가 먼저 실행되는 모습

DOMContentLoaded 이벤트는 모든 script가 실행이 된 후에 마지막에 발생되기 때문에 하단에 작성한 script 코드가 먼저 출력이 된 것이다. DOMContentLoaded 이벤트는 맨 마지막에 발생된다는 점을 꼭 기억하자!

script 코드는 어디에 적는게 가장 좋을까?


script 코드를 HTML 문서 상단에 작성했을 경우 script가 적은 경우에는 문제가 되지 않지만 script가 많은 경우 script가 끝날 때까지 html 로드가 끝나지 않게 된다. 이렇게 되면 아마 사용자는 화를 내면서 떠날 것이다.

script 코드를 HTML 문서 하단에 작성했을 경우에는 어떨까?

script 코드를 HTML 문서 하단에 작성하게 되면 HTML 문서가 모두 로드가 된 이후에 script 코드가 실행이 된다. 그러면 사용자는 script 코드의 실행 여부에 상관 없이 일단 사이트가 보이게 되니 script 코드가 실행되지 않았다는 사실을 알지 못할 것이다. 결론을 말하면 script 코드는 HTML 문서 하단에 작성하자! 만약 상단에 작성하였다면 DOMContentLoaded 이벤트라도 걸어주자!!!

[Javascript] Event Object

CloudCoke
|2022. 11. 10. 17:26

이벤트 객체 (Event Object)


이벤트 등록을 할 때 addEventListener에 두 가지 인자를 전달해 주었다. 첫 번째 인자는 이벤트 이름이고 두 번째 인자는 이벤트 핸들러라고 부르는 사용자가 만든 함수이다. 이 함수에 이벤트가 발생하면 동작될 코드들을 넣어 놓게 된다. 이벤트가 발생되어서 이벤트 핸들러 함수가 호출이 되면 이벤트 객체를 전달받게 되는데 이벤트 핸들러 함수에 매개변수를 선언해주면 이벤트 객체를 사용할 수 있다.

<!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>
  </head>
  <body>
    <button class="btn">클릭</button>
    <script src="public/js/test.js"></script>
  </body>
</html>
const btn = document.querySelector(".btn");

btn.onclick = function (e) {
  console.log(e);
};

이벤트 객체

위 방법으로 이벤트를 등록해 이벤트 객체를 사용할 수 있지만 나는 아래 방법으로 이벤트를 등록해서 사용할 예정이다.

const btn2 = document.querySelector(".btn");

const btnHandler = function (e) {
  console.log(e);
};

btn2.addEventListener("click", btnHandler);

이벤트 객체

이벤트 객체 사용


이벤트 객체에는 여러 속성이 존재하지만 우선은 자주 사용하는 2가지 속성만 알아보자!

target 속성

target 속성은 이벤트를 발동시킨 요소를 말한다. 

const btn = document.querySelector(".btn");

const btnHandler = function (e) {
  console.log(e.target);
};

btn.addEventListener("click", btnHandler);

클릭 버튼을 눌렀을 때

target 속성을 이용해 Style도 변경이 가능하다. 이벤트가 발생했을 때 지정한 Style이 적용이 된다.

const btn = document.querySelector(".btn");

const btnHandler = function (e) {
  e.target.style = "background: green;";
};

btn.addEventListener("click", btnHandler);

클릭 버튼을 눌렀을 때

type 속성

type 속성은 어떤 이벤트가 발생했는지 확인할 때 유용하다. 

const btn = document.querySelector(".btn");

const btnHandler = function (e) {
  console.dir(e.type);
};

btn.addEventListener("click", btnHandler);
btn.addEventListener("mouseover", btnHandler);

이벤트 발생시 이벤트명이 출력되는 모습

type 속성은 등록된 이벤트가 실행됬을 때 어떤 이벤트 명으로 실행이 되었는지 알 수 있다.

활용하기


<!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>
  </head>
  <body>
    <button class="btn">클릭</button>
    <div class="display" style="background: red"></div>
    <script src="public/js/test.js"></script>
  </body>
</html>
const btn = document.querySelector(".btn");
const display = document.querySelector(".display");

const btnHandler = function (e) {
  if (e.type === "click") display.innerHTML = "마우스를 클릭했다!";
  if (e.type === "mouseover") display.innerHTML = "마우스를 올렸다!";
};

btn.addEventListener("click", btnHandler);
btn.addEventListener("mouseover", btnHandler);

이벤트 활용하기

이벤트가 발생했을 때 이벤트 객체의 type 속성을 이용해 마우스 클릭과 마우스오버를 구분해 display에 innerHTML로 텍스트를 추가해 주었다.

<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>
    .a {
      background: red;
    }
    .b {
      background: orange;
    }
  </style>
</head>
<body>
  <button class="btn">버튼1</button>
  <button class="btn2">버튼2</button>
  <div id="display"></div>
  <script src="public/js/test.js"></script>
</body>
const btn = document.querySelector(".btn");
const btn2 = document.querySelector(".btn2");
const display = document.querySelector("#display");

function handler(e) {
  if (e.type === "click") {
    display.innerHTML = `${e.target.textContent} 클릭`;
    display.className = "a";
  } else if (e.type === "mouseover") {
    display.innerHTML = `${e.target.textContent}에 마우스를 올렸다!`;
    display.className = "b";
  }
}

function handler2(e) {
  display.innerHTML = "";
  display.className = "";
}

btn.addEventListener("click", handler);
btn.addEventListener("mouseover", handler);
btn.addEventListener("mouseout", handler2);
btn2.addEventListener("click", handler);
btn2.addEventListener("mouseover", handler);
btn2.addEventListener("mouseout", handler2);

이벤트가 발생했을 때 이벤트 객체의 type 속성을 이용해 마우스 클릭과 마우스오버를 구분해 display에 innerHTML로 텍스트를 추가해 주고 클래스명도 변경해주었다.

Event란?


Event 인터페이스는 DOM 내에 위치한 이벤트를 나타냅니다.
이벤트는 마우스를 클릭하거나 키보드를 누르는 등 사용자의 액션에 의해 발생할 수도 있고, 비동기적 작업의 진행을 나타내기 위해서 API가 생성할 수도 있습니다. Event - MDN

이벤트(Event)란 우리가 브라우저에서 버튼을 클릭하거나 로그인을 하기 위해 ID/PW를 입력하는 행동이라고 볼 수 있다.

버튼을 클릭하면 브라우저에 click 이벤트가 발생하고 ID/PW를 입력할 때 브라우저에서 keyup이나 keydown등의 이벤트가 발생한다. 

Event Handling


브라우저에서 발생되는 이벤트를 처리하는 걸 이벤트 핸들링이라고 부른다. 이벤트 핸들링을 하는 방법이 3가지 정도 있는데 한 번 알아보자!

1. Element에 직접 넣기

이 방법은 html에 요소에 style 속성을 넣는 것 처럼 이벤트도 넣을 수 있다.

이 방법을 사용하는 대부분의 이벤트이름에는 앞 글자에 on이라는 게 붙어 있다.

on[Event 이름] = "동작"
  <body>
    <button onclick="alert('hello world!')">버튼</button>
  </body>

위와 같이 코드를 작성하고 실행해 보자!

버튼이 클릭되면 hello world!라는 텍스트를 출력하는 경고창이 뜨게 된다.

Element에 직접 넣어서 이벤트 처리

이 방법은 잘 사용하지 않는다. 간단한 코드일 경우에는 괜찮지만 코드가 복잡해지면 HTML 파일의 가독성이 떨어지게 된다. 

2. DOM 속성으로 넣기

이 방법은 Element에 직접 넣어서 HTML 파일의 가독성을 해치는 단점을 해결해 준다. 이 방법은 HTML 파일과 자바스크립트 파일로 나눠서 작성해야 한다. 

<body>
    <button class="btn">버튼</button>
    <script src="public/js/practice.js"></script>
  </body>
const btn = document.querySelector(".btn");

btn.onclick = function () { // 이벤트 핸들러(Event Handler)
  console.log("hello world!");
};

1. querySelector로 .btn이라는 클래스 이름을 선택하면 만족하는 첫 번째 요소(Element) 객체를 반환해 준다. (참조 형태)

2. 요소 객체를 btn이라는 변수에 대입해준 다음 onclick이라는 속성에 함수를 넣어준다. 구체적으로 어떤 동작을 할 지 명시해주는 함수 부분을 이벤트 핸들러라고 부른다.

DOM 속성을 이용해 이벤트 처리

정말 querySelector로 존재하는 선택자를 선택했을 때 반환되는 Element가 참조형태일까?
const btn = document.querySelector(".btn");

btn.onclick = function () {
  console.log("hello world!");
};

console.log(btn.onclick);

const btn2 = document.querySelector(".btn");

btn2.onclick = function () {
  console.log("update onclick");
};

console.log(btn.onclick);
console.log(btn.onclick === btn2.onclick); // true

 

이 코드를 실행해 보면 btn2의 onclick 속성을 수정해 주었는데 btn의 onclick 속성까지 변경이 되었다.

따라서 querySelector는 Element를 참조형으로 반환한다고 볼 수 있다.

위 코드를 실행한 결과 querySelector는 Element를 참조 형태로 반환해 준다.

DOM 속성으로 이벤트 핸들링을 하는 방법에도 치명적인 단점이 있다.

하나의 이벤트에 여러 이벤트 핸들러를 등록할 수 없다는 점이다.

const btn = document.querySelector(".btn");

btn.onclick = function () {
  console.log("hello world!");
};

console.log(btn.onclick);

btn.onclick = function () {
  console.log("hello world!2");
};

console.log(btn.onclick);

두번째 이벤트를 등록하게되면 값이 덮어 씌여진다.

이 방법에는 기존의 값을 유지할 수 없다는 단점이 있다. 

3. addEventListener 

이 방법은 DOM 속성으로 이벤트 핸들링을 할 때 여러 핸들러를 등록하지 못하는 단점을 해결해 준다.

문법은 다음과 같다.

Element.addEventLitener("event_name", callback_function_value, [options]);

addEventListener를 이용해 이벤트를 등록하는 방법은 함수를 직접 넣어주는 방법과 함수를 선언하고 넣어주는 방법이 있다. 하나씩 살펴보자!

<body>
    <button class="btn">버튼</button>
    <script src="public/js/practice.js"></script>
  </body>

방법 1 - 익명함수를 이용해 함수를 직접 넣어주기

const btn = document.querySelector(".btn");

console.log("익명함수를 이용해 함수를 직접 넣어주기")
btn.addEventListener("click", function () {
  console.log("hello world!");
});

btn.addEventListener("click", function () {
  console.log("hello world2!");
});

익명함수를 이용해 함수를 직접 넣어주기

방법 2 - 함수를 선언하고 넣어주기 [권장]

const btn = document.querySelector(".btn");

console.log("함수를 선언하고 넣어주기");
const btnHandler = function () {
  console.log("hello world!");
};

const btnHandler2 = function () {
  console.log("hello world2!");
};

btn.addEventListener("click", btnHandler);
btn.addEventListener("click", btnHandler2);

함수를 선언하고 넣어주기

둘 다 같은 결과가 나오지만 익명함수를 이용해 함수를 직접 넣어주는 방식으로 이벤트를 등록하게 되면 이벤트를 삭제할 때 문제가 발생한다. (이벤트를 등록할 때는 함수 표현식이나 함수 선언식으로 등록을 하자!)

Event Remove


addEventListener를 이용해 등록한 이벤트를 제거하는 방법은 다음과 같다. 삭제하고 싶은 이벤트가 있다면 등록했을 때와 동일하게 인자를 removeEventListener에 넣어주면 된다.

Element.removeEventListener("event_name", callback_function_value);

먼저 [방법 1 - 익명함수를 이용해 함수를 직접 넣어주기]에서 등록한 이벤트를 제거해 보자.

const btn = document.querySelector(".btn");

console.log("익명함수를 이용해 함수를 직접 넣은 이벤트 제거");
btn.addEventListener("click", function () {
  console.log("hello world!");
});

btn.addEventListener("click", function () {
  console.log("hello world2!");
});

btn.removeEventListener("click", function () {
  console.log("hello world!");
});

익명함수를 이용해 함수를 직접 넣은 이벤트 제거

hello world를 출력하는 이벤트가 지워지지 않았다. 이유는 간단하다.

두 함수는 서로 다르다

두 함수는 같지 않기 때문이다. 그래서 removeListener가 삭제하려고 해도 다른 함수이기 때문에 정상적으로 삭제가 되지 않는 것이다. 그래서 [방법 2 - 함수를 선언하고 넣어주기]로 이벤트를 등록하는걸 권장한다.

그러면 [방법 2 - 함수를 선언하고 넣어주기]로 등록한 이벤트를 제거해 보자!

const btn = document.querySelector(".btn");

console.log("함수를 선언하고 넣어준 이벤트 제거");
const btnHandler = function () {
  console.log("hello world!");
};

const btnHandler2 = function () {
  console.log("hello world2!");
};

btn.addEventListener("click", btnHandler);
btn.addEventListener("click", btnHandler2);

btn.removeEventListener("click", btnHandler);

함수를 선언하고 넣어준 이벤트 제거

이번에는 정상적으로 hello world를 출력하는 이벤트가 제거가 되었다. 

⚠️Warning⚠️

addEvnetListener를 등록할 때 두 번째 파라미터로 callback 함수를 받게 된다.
주의해야 할 점은 callback 함수를 호출하는게 아니라 함수 값 자체를 넣어 주어야 한다는 점이다.

다음과 같은 코드를 실행해 보자!

const btn = document.querySelector(".btn");

const event1 = function () {
  console.log("hello world!");
};
const event2 = function () {
  console.log("hello world2!");
};

btn.addEventListener("click", event1);
btn.addEventListener("click", event2());

addEventListener 인자에 callback 함수를 호출한 결과

페이지가 로드되면 event2함수가 동작하고 버튼을 클릭하면 이벤트가 even1만 등록이 되어있다. 인자에 함수를 호출하는 코드를 넣었기 때문에 제대로 이벤트가 등록되지 않았기 때문이다. 이 부분을 잘 기억해 두자!

마무리


오늘은 이벤트를 등록하고 제거하는 방법을 알아보았다. 이벤트를 등록할 때 콜백 함수 값을 넣어야 하는데 콜백 함수를 호출하는 경우가 없도록 주의하자!

 

'Javascript' 카테고리의 다른 글

[Javascript] Script 태그 위치  (0) 2022.11.11
[Javascript] Event Object  (0) 2022.11.10
[Javascript] 로또 번호 생성기  (0) 2022.11.08
[Javascript] DOM(Document Object Model)  (0) 2022.11.08
[Javascript] 객체  (0) 2022.11.04

구현해야 할 것


<HTML>

1. 로또 번호가 출력되는 페이지를 만든다.

2. 누르면 이벤트가 발생하는 번호 생성 버튼을 만든다.

<Javascript>

1. 번호 생성 버튼을 눌리면 로또 번호가 보이게 한다.

2. 랜덤한 숫자를 뽑는다.

3. 각 요소에 뽑은 숫자를 넣어준다.

구현


아래와 같이 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.5);
      }
      #lotto.none {
        display: none;
      }
    </style>
  </head>
  <body>
    <h1>로또 번호 생성기</h1>
    <ul id="lotto" class="none">
      <li>1</li>
      <li>2</li>
      <li>3</li>
      <li>4</li>
      <li>5</li>
      <li>6</li>
    </ul>
    <button id="btn">번호 생성</button>
    <script src="public/js/practice.js"></script>
  </body>
</html>

로또 번호 생성기 메인 페이지

번호 생성 버튼을 누르면 li 태그의 요소가 보이도록 Javascript 파일을 작성해보자!

const lottoElement = document.querySelector("#lotto");
const btnElement = document.querySelector("#btn");

function buttonEvent() {
  lottoElement.className = "";
}

btnElement.addEventListener("click", buttonEvent);

1. querySelector를 이용해 id가 lotto인 요소를 lottoElement 변수에 담는다.

2. querySelector를 이용해 id가 btn인 요소를 btnElement 변수에 담는다.

3. 이벤트 핸들러에 전달할 buttonEvent 함수를 작성한다. 이 함수는 호출이 되면 lottoElement의 className을 빈 문자열로 수정해 준다.

4. btnElement에 click 이벤트를 등록해 준다. 만약 사용자가 번호 생성 버튼을 누르게 되면 buttonEvent 함수를 호출해 lottoElement의 className을 빈 문자열로 바꾸어 주게 되고 그러면 lottoElement는 class 속서에 none 값이 사라져 화면에 보이게 될 것이다.

번호 생성 버튼을 누르기 전
번호 생성 버튼을 누른 후

이번에는 랜덤한 수를 생성하는 함수를 만들어 보자!

자바스크립트 내장 객체에 있는 Math 함수를 이용해서 랜덤 한 수를 생성해 보자!

 

Math.random()

이 함수는 0 이상 1 미만의 수를 랜덤하게 반환해 준다.

Math.random() 함수 사용 예시

이 함수에 10을 곱해주면 0 이상 10 미만의 수를 랜덤 하게 반환해 준다.

Math.random() 함수에 10을 곱한 결과

 

Math.floor()

이 함수는 인자 값에 가우스 기호 씌워서 반환을 해준다고 볼 수 있다. 소수점을 버린다고 볼 수도 있다.

Math.floor() 함수 사용 예시

그럼 이번에는 두 함수를 이용해 1부터 45까지 숫자를 랜덤하게 생성하는 함수를 작성해 보자!

function randomNum() {
  const ranNum = Math.floor(Math.random() * 45 + 1);
  return ranNum;
}

 

1. Math.random 함수를 이용해 0부터 1미만의 수를 랜덤 하게 생성한다.

2. 그 값에 45를 곱해 0부터 45미만의 랜덤 한 수를 생성할 수 있도록 한다.

3. 그 값에 1을 더해 1부터 46미만의 수를 생성할 수 있도록 한다.

4. Math.floor 함수를 이용해 소수점을 버려 최종적으로 1부터 45까지의 수를 생성할 수 있도록 한다.

5. 이 값을 ranNum 변수에 담아 return해 준다.

randomNum() 함수 실행 결과

이번에는 생성한 랜덤한 수를 li 태그 안에 넣어보자!

이전에 만들었던 buttonEvent 함수를 다음과 같이 수정해 주자!

const lottoElement = document.querySelector("#lotto");
const btnElement = document.querySelector("#btn");
const liList = document.querySelectorAll("#lotto > li"); // 추가

function buttonEvent() {
  lottoElement.className = "";

// 추가
  for (let i = 0; i < 6; i++) {
    liList[i].innerHTML = randomNum();
  }
}

btnElement.addEventListener("click", buttonEvent);

1. querySelectorAll로 li 요소들을 선택해 유사 배열 형태로 liList 변수에 담는다.

2. for문을 사용해 0번 인덱스 부터 5번 인덱스까지 liList 배열에 randomNum 함수를 호출한 결과를 innerHTML을 이용해 담는다.

번호 생성을 누르면 번호가 바뀌는 모습

일단은 구현하고자 하는 것은 다 구현이 되었다.

구현을 하고 보니 문제가 생겼다.

문제점


1. 진짜 로또 번호같은 색이 없음

2. 랜덤 번호에 중복값이 존재함

해결


이번에는 진짜 로또 번호 처럼 색을 입혀보자!

이전에 작성한 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.5);
      }
      #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>1</li>
      <li>2</li>
      <li>3</li>
      <li>4</li>
      <li>5</li>
      <li>6</li>
    </ul>
    <button id="btn">번호 생성</button>
    <script src="public/js/practice.js"></script>
  </body>
</html>

추가된 부분을 설명하면

1 ~ 10 : a라는 클래스명을 선택해 background를 노란색

11 ~ 20 : b라는 클래스명을 선택해 background를 파란색

21 ~ 30 : c라는 클래스명을 선택해 background를 빨간색

31 ~ 40 : d라는 클래스명을 선택해 background를 회색

41 ~ 45 : e라는 클래스명을 선택해 background를 녹색

으로 꾸며주는 코드이다.

그런데 꾸며야 할 li 태그 안에는 클래스가 존재하지 않는다.

그래서 자바스크립트를 이용해 번호에 맞게 클래스명을 넣어주어야 한다.

우선은 매개변수로 판별할 숫자와 범위를 받아서 그 숫자가 범위 안에 속하는지 판별하는 함수를 작성해 보자!

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

1. num이라는 매개변수와 최소와 최대를 의미하는 min, max 매개변수를 받아 num 값이 min, max 사이 값이라면 true를 반환하고 그렇지 않다면 false를 반환해주는 함수이다.

betweenNum() 함수 실행 결과

우리는 li 태그에 클래스 속성을 적용해 주어야한다. 그러려면 생성된 랜덤 숫자가 어느 범위에 속하는지 판별을 해야 한다. 특정 범위에 속한다면 문자열을 리턴해주어서 그 값을 나중에 활용할 수 있는 함수를 작성해 보자!

function rangeNum(num) {
  if (betweenNum(num, 1, 10)) {
    return "a";
  }
  if (betweenNum(num, 11, 20)) {
    return "b";
  }
  if (betweenNum(num, 21, 30)) {
    return "c";
  }
  if (betweenNum(num, 31, 40)) {
    return "d";
  }
  if (betweenNum(num, 41, 45)) {
    return "e";
  }
}

1. num이라는 매개변수를 받아서 그 값을 판별하기 위해 위에 작성한 betweenNum 함수를 이용하였다. 만약 num 값이 2라면 첫 번째 if문에 걸리게 되고 betweenNum함수를 호출하게 되는데 이때 매개변수로 받았던 2를 첫 번째 인자로 넣어주고 1~10 사이에 있는지 확인하고 싶기 때문에 두 번째 인자에는 1, 마지막 인자에는 10을 넣어주었다. 

2. 만약 결과 값이 참이라면 문자열 a를 return하고 아니라면 다음 if 문으로 넘어가게 된다.

3. 우리는 이 값을 이용해 li 태그에 클래스 속성을 추가할 예정이다.

rangeNum() 함수 실행 결과

이제 li 태그에 클래스 속성을 추가해보자!

const lottoElement = document.querySelector("#lotto");
const btnElement = document.querySelector("#btn");
const liList = document.querySelectorAll("#lotto > li");

function buttonEvent() {
  lottoElement.className = "";

  for (let i = 0; i < 6; i++) {
    const num = randomNum(); // 추가
    liList[i].innerHTML = num; // 수정
    liList[i].className = rangeNum(num); // 추가
  }
}

btnElement.addEventListener("click", buttonEvent);

1. randomNum()에서 생성한 수를 여러번 사용하고 싶기 때문에 변수 num을 선언해 대입해 주었다.

2. liList에 innerHTML 속성에 num값을 대입해주었다.

3. liList에 className 속성에 rangeNum함수에 인자로 num을 넣어서 리턴된 값을 대입해 주었다.

번호 생성을 누르면 li의 class 속성이 변하는 모습

위에 그림에서 보이는 것처럼 로또 번호에 중복 값이 존재 한다. 이 중복 값을 제거해 보자!

나는 처음에 아래 처럼 코드를 작성해 보았다. 아래 코드는 1부터 45의 랜덤 한 수를 중복 없이 6개 생성해 주는 함수이다.

function randomNumCreate() {
  const randomNumArr = [randomNum()];
  for (let i = 0; i < 5; i++) {
    let temp = randomNum();
    for (let j = 0; j < randomNumArr.length; j++) {
      if (randomNumArr[j] === temp) {
        randomNumArr.splice(j, 1);
        i--;
      }
    }
    randomNumArr.push(temp);
  }
  return randomNumArr;
}

1. 배열에 랜덤한 값이 1개 들어있는 randomNumArr이라는 변수에 대입한다.

2. 6개의 로또 번호 중 1개는 이미 만들어져 있기 때문에 for문을 5번만 반복시킨다.

3. temp라는 변수에 랜덤 한 값을 대입한다.

4. randomNumArr의 배열 길이만큼 내부 for문을 돌려서 배열 안에 있는 값이 위에서 생성한 랜덤 값과 같다면 그 값을 지우고 i의 값을 1 줄여주어서 상위에 있는 for문이 한번 더 돌 수 있도록 해준다.

5. 내부 for문이 끝나면 randomNumArr 배열에 위에서 생성한 랜덤값을 넣어준다.

6. 최종적으로는 길이가 6인 randomNumArr 배열을 return 한다.

randomNumCreate() 함수 실행 결과

이 함수를 이용하기 위해 buttonEvent 함수를 수정하자!

const lottoElement = document.querySelector("#lotto");
const btnElement = document.querySelector("#btn");
const liList = document.querySelectorAll("#lotto > li");

function buttonEvent() {
  lottoElement.className = "";
  const ranNum = randomNumCreate(); // 추가

  for (let i = 0; i < 6; i++) {
    liList[i].innerHTML = ranNum[i]; // 수정
    liList[i].className = rangeNum(ranNum[i]); // 수정
  }
}

btnElement.addEventListener("click", buttonEvent);

1. randomNumCreate 함수의 결과 값을 ranNum 변수에 대입한다.

2. ranNum의 i 번째 요소를 liList의 i 번째 요소의 innerHTML 속성에 대입한다.

3. rangeNum함수를 이용해 ranNum의 i 번째 요소가 어느 구간에 속하는지 판별한 뒤 결괏값을 liList의 i 번째 요소의 className 속성에 대입한다.

최종 코드


전체 코드를 보면 다음과 같다.

<!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.5);
      }
      #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>1</li>
      <li>2</li>
      <li>3</li>
      <li>4</li>
      <li>5</li>
      <li>6</li>
    </ul>
    <button id="btn">번호 생성</button>
    <script src="public/js/practice.js"></script>
  </body>
</html>
const lottoElement = document.querySelector("#lotto");
const btnElement = document.querySelector("#btn");
const liList = document.querySelectorAll("#lotto > li");

function randomNum() {
  const ranNum = Math.floor(Math.random() * 45 + 1);
  return ranNum;
}

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

function rangeNum(num) {
  if (betweenNum(num, 1, 10)) {
    return "a";
  }
  if (betweenNum(num, 11, 20)) {
    return "b";
  }
  if (betweenNum(num, 21, 30)) {
    return "c";
  }
  if (betweenNum(num, 31, 40)) {
    return "d";
  }
  if (betweenNum(num, 41, 45)) {
    return "e";
  }
}

function randomNumCreate() {
  const randomNumArr = [randomNum()];
  for (let i = 0; i < 5; i++) {
    let temp = randomNum();
    for (let j = 0; j < randomNumArr.length; j++) {
      if (randomNumArr[j] === temp) {
        randomNumArr.splice(j, 1);
        i--;
      }
    }
    randomNumArr.push(temp);
  }
  return randomNumArr;
}

function buttonEvent() {
  lottoElement.className = "";
  const ranNum = randomNumCreate();

  for (let i = 0; i < 6; i++) {
    liList[i].innerHTML = ranNum[i];
    liList[i].className = rangeNum(ranNum[i]);
  }
}

btnElement.addEventListener("click", buttonEvent);

최종 결과물

마무리


 

추가적으로 정렬 기능이 필요할 것 같다. 그리고 randomNumCreate 함수도 조금 더 다듬으면 좋을 것 같다.

시간이 되면 수정한 결과물도 올릴 예정이다.

'Javascript' 카테고리의 다른 글

[Javascript] Event Object  (0) 2022.11.10
[Javascript] Event (Add, Remove)  (0) 2022.11.09
[Javascript] DOM(Document Object Model)  (0) 2022.11.08
[Javascript] 객체  (0) 2022.11.04
[Javascript] 피보나치 수열 - 알고리즘  (0) 2022.11.03

Window 객체란?


DOM을 설명하기 전에 먼저 Window라는 객체를 알아야 한다. Window 객체란 브라우저의 창을 대변한다고 볼 수 있다. Javascript에서 최상단에 존재하는 객체로 이 객체 안에는 수많은 프로퍼티들이 존재하는데 Javascript의 거의 모든 내장 객체, 내장 함수들이 Window  객체에 속해 있다. Window 객체는 Javascript 코드의 어느 곳에서나 항상 접근 할 수 있는 객체로 전역 객체(Global Object)라고도 부른다.

window 객체안에 수 많은 객체와 메소드가 존재한다.

사실 우리가 자주 사용하는 console.log도 window.console.log로 사용해야 하지만 무엇을 사용하든 결국 윈도우 객체 내부의 것이기 때문에 window.을 생략해도 된다. 그리고 우리가 선언한 변수나 함수도 window 객체 안에 등록이 된다.

function a() {
  console.log("hello world");
}
var aa = "hello";

사용자가 선언한 변수나 함수도 window 객체 안에 등록이 된 모습

그렇다면 DOM이란 무엇일까?

DOM이란?


문서 객체 모델(The Document Object Model, 이하 DOM) 은 HTML, XML 문서의 프로그래밍 interface 이다. DOM은 문서의 구조화된 표현(structured representation)을 제공하며 프로그래밍 언어가 DOM 구조에 접근할 수 있는 방법을 제공하여 그들이 문서 구조, 스타일, 내용 등을 변경할 수 있게 돕는다. DOM 은 nodes와 objects로 문서를 표현한다. 이들은 웹 페이지를 스크립트 또는 프로그래밍 언어들에서 사용될 수 있게 연결시켜주는 역할을 담당한다. DOM 소개 - MDN

 DOM이란 window객체 안에 존재하는 document라는 객체를 말한다. 이 객체는 HTML 문서 전체를 객체로 표현한 것이다. 

그리고 이 객체안에는 문서의 구조나 스타일, 내용 등을 변경 할 수 있는 메소드들이 존재한다. 메소드들은 브라우저마다 표준으로 제공하는 기능 이외에도 추가적인 기능을 제공하는 경우도 있다.

그렇다면 DOM은 브라우저에서만 사용할 수 있을까?

about:blank 페이지의 요소들
document 객체를 출력해본 결과 about:blank 페이지의 요소들이 출력된다.
document 객체 자체를 출력하고 싶은 경우 dir 메소드를 사용한다.
Node.js에서 document 객체를 출력해본 결과 document가 정의되어 있지 않다고 출력된다.

Document 객체는 Javascript의 요소가 아니라 브라우저에서 제공하는 Window 객체의 한 요소이다. 그래서 Node.js에서는 출력이 되지 않는다.

😊Tip
log 메소드는 파라미터로 전달받은 값을 위주로 출력하는 반면, dir 메소드는 객체의 속성을 좀 더 자세하게 출력해준다.
log 메소드는 여러 값을 쉼표로 구분해서 전달하면 전달받은 모든 값을 출력해준다.
dir 메소드는 여러 값을 전달하더라도 첫 번째 값만 출력해준다.
값에 좀 더 중점을 둔 log 메소드는 대상을 HTML 형태로 출력해주고, 객체의 속성에 좀 더 중점을 둔 dir 메소드는 대상을 객체 형태로 출력해준다.

그렇다면 이번에는 DOM을 조작해 보자!

DOM 조작


일단 아래와 같이 HTML 파일을 작성을 한 후 파일을 열어보자!

  <body>
    <h1 id="title"></h1>
    <span>span2</span>
    <h2 id="DOM-title">
      DOM
      <span class="sp">span1</span>
      <span class="sp">span1</span>
      <span class="sp">span1</span>
    </h2>
    <script src="public/js/practice.js"></script>
  </body>

위에 작성한 HTML 코드를 실행한 결과

DOM을 조작하기 위해서는 먼저 조작하고 싶은 HTML 요소부터 선택해야 한다. 선택하는 방법은 다음과 같다.

window.document.getElementById()


이 메소드는 id 속성 이름을 인자로 받아서 해당 id 속성을 가진 요소를 가져오는 메소드이다.

인자값으로는 id 속성 이름을 string으로 전달해 주어야 한다.

Javascript 파일에 다음과 같이 작성해보자!

const domTitle = document.getElementById("DOM-title"); // DOM-title이라는 id를 가진 요소 선택
console.log(domTitle);

DOM-title이라는 id 속성을 가진 HTML의 요소가 반환된 모습

window.document.getElementsByTagName()


이 메소드는 HTML의 태그 이름을 인자로 받아서 해당하는 태그 전체를 선택하는 메소드이다.

인자값으로는 태그 이름을 string으로 전달해 주어야 한다.

이 메드에는 Element에 s가 붙어 있는데 태그 이름으로 요소를 찾는 경우 여러 개의 요소가 선택될 수 있기 때문에 Element에 s가 붙어 있다. 그리고 실행결과 유사배열인 HTMLCollection을 리턴한다는 점도 기억해두자! (배열인데 요소 내용들이 객체)

Javascript 파일에 다음과 같이 작성해보자!

const spanList = document.getElementsByTagName("span"); // span이라는 이름을 가진 태그 전부 선택
console.log(spanList);

HTML에 존재하는 span 태그가 전부 선택되어 HTMLCollection으로 반환된 모습

window.document.getElementsByClassName()


이 메소드는 class 속성 이름을 인자로 받아 해당 class 속성을 가진 요소 전체를 선택하는 메소드이다.

인자값으로는 class 속성 이름을 string으로 전달해 주어야 한다.

이 메드에도 Element에 s가 붙어 있는데 class 속성으로 요소를 찾는 경우 여러 개의 요소가 선택될 수 있기 때문에 Element에 s가 붙어 있다. 그리고 실행결과 유사배열인 HTMLCollection을 리턴한다는 점도 기억해두자! (배열인데 요소 내용들이 객체)

Javascript 파일에 다음과 같이 작성해보자!

const spList = document.getElementsByClassName("sp"); // sp라는 class명을 가진 요소 전부 선택
console.log(spList);

sp라는 class 속성을 가진 HTML 요소가 전부 선택되어 HTMLCollection으로 반환된 모습

HTMLCollection에 들어가는 요소들의 순서는 HTML 태그에서 봤을 때 깊이와는 상관없이 무조건 위에서부터 차례대로 들어간다!

아래와 같이 HTML 파일을 작성을 한 후 파일을 열어보자!

  <body>
    <h1 id="title"></h1>
    <ul>
      <div>
        <div>
          <div><li class="ls ls1">list1</li></div>
        </div>
      </div>
      <li class="ls ls2">list2</li>
      <li class="ls ls3">list3</li>
      <li class="ls ls4">list4</li>
      <li class="ls ls5">list5</li>
    </ul>
    <script src="public/js/practice.js"></script>
  </body>

위에 작성한 HTML 코드를 실행한 결과

const lsList = document.getElementsByClassName("ls");
console.log(lsList);

 

HTML 태그에서 봤을 때 깊이와는 상관없이 위에서부터 HTMLCollection에 들어간 모습

유사배열(Array-Like Object)이란?
위와 같이 배열과 유사하게 생긴 객체를 말한다.
0부터 시작하는 숫자 형태의 index가 존재하고 length 속성이 존재한다.
하지만 배열의 기본 메소드는 사용할 수 없다. (for ... of 문은 사용 가능)

index 존재/length 속성 존재/배열은 아님

const lsList = document.getElementsByClassName("ls");

for (let element of lsList) {
  console.log(element);
}

HTMLCollection에서 for ... of 문

그런데 만약 다음과 같은 HTML에서 h2 안에 있는 span을 선택하려면 어떻게 해야 할까?

  <body>
    <h1 id="title"></h1>
    <span>span2</span>
    <h2 id="DOM-title">
      DOM
      <span>span1-1</span>
      <span>span1-2</span>
      <span>span1-3</span>
    </h2>
    <script src="public/js/practice.js"></script>
  </body>

위에 작성한 HTML 코드를 실행한 결과

먼저 h2를 선택한 다음 그 값을 이용해 span을 선택하면 된다.

const domTitle = document.getElementById("DOM-title");
const spList = domTitle.getElementsByTagName("span");

for (let i = 0; i < spList.length; i++) {
  console.log(spList[i]);
}

span1-1 ~ span1-3까지 출력된 모습

하지만 이렇게 쓰면 코드가 길어진다. 그렇다면 조금 더 편리한 방법이 없을까?

 

querySelector를 사용하면 된다!

querySelector는 CSS 선택자를 이용해 요소를 선택할 수 있다.

일단 다음과 같이 HTML 파일을 작성해 보자!

<body>
    <h1 id="title"></h1>
    <ul>
      <div>
        <li class="ls ls1">list1</li>
      </div>
      <li class="ls ls2">list2</li>
      <li class="ls ls3">list3</li>
      <li class="ls ls4">list4</li>
      <li class="ls ls5">list5</li>
    </ul>
    <script src="public/js/practice.js"></script>
 </body>

위에 작성한 HTML 코드를 실행한 결과

window.document.querySelector()


이 메소드는 선택하고 싶은 태그나 id, class 속성을 CSS 선택자를 사용하듯이 인자값으로 넣어주면 된다.

만약 ul 태그 안에 div 태그안에 li 태그를 가진 요소를 선택하고 싶으면 다음과 같이 코드를 작성해 주면 된다.

const qs = document.querySelector("ul > div > li");
console.log(qs);

ul 태그 안에 div 태그안에 li 태그를 가진 요소를 선택한 겱과

만약 ul 태그 안에 class가 ls5인 요소를 선택하고 싶으면 다음과 같이 코드를 작성해 주면 된다.

const qs = document.querySelector("ul > .ls5");
console.log(qs);

ul 태그 안에 class가 ls5인 요소를 선택한 결과

그렇다면 ls라는 class 이름을 가진 요소들을 선택하려면 어떻게 해야할까?

window.document.querySelectorAll()


이 메소드는 선택하고 싶은 태그나 id, class 속성을 CSS 선택자를 사용하듯이 인자값으로 넣어주면 된다.

이 메소드에는 All이 붙어 있는데 여러 개의 요소를 선택하고 싶을때 사용하는 메소드라 그렇다. 실행결과는 유사배열인 NodeList를 리턴한다! (배열인데 요소 내용들이 객체)

ls라는 class 이름을 가진 요소들을 선택하려면 다음과 같이 코드를 작성해 주면 된다.

const lsList = document.querySelectorAll(".ls");
console.log(lsList);

ls라는 class 이름을 가진 요소들을 선택한 결과

만약 ls라는 class 이름을 가진 요소들의 텍스트를 수정하고 싶다면 어떻게 해야될까?

element.innerHTML

이 속성은 요소 안에 있는 HTML을 문자열로 리턴해 준다.

innerHTML에 값을 대입하면 값이 덮어씌워진다.

일단 ls라는 class 이름을 가진 요소들이 어떤 값을 가지고 있는지 확인해 보자!

const lsList = document.querySelectorAll(".ls");

for (let i = 0; i < lsList.length; i++) {
  console.log(lsList[i].innerHTML);
}

ls라는 class 이름을 가진 요소들을 innerHTML을 이용해 확인한 결과

이 요소들을 1-1 ~ 1-5로 한 번 바꿔 보자!

const lsList = document.querySelectorAll(".ls");

for (let i = 0; i < lsList.length; i++) {
  lsList[i].innerHTML = `list1-${i + 1}`;
  console.log(lsList[i].innerHTML);
}

ls라는 class 이름을 가진 요소들을 innerHTML을 이용해 수정해본 결과
ls라는 class 이름을 가진 요소들을 innerHTML을 이용해 수정해본 결과

잘 변경된걸 확인 할 수 있다.

마무리


우리가 만든 함수나 변수도 window 객체안에 등록이 되는걸 오늘 처음 알았다.

브라우저를 잘 다루려면 DOM에 대해 조금 더 자세히 알아봐야 겠다.

참고

 

 

 

 

 

 

'Javascript' 카테고리의 다른 글

[Javascript] Event (Add, Remove)  (0) 2022.11.09
[Javascript] 로또 번호 생성기  (0) 2022.11.08
[Javascript] 객체  (0) 2022.11.04
[Javascript] 피보나치 수열 - 알고리즘  (0) 2022.11.03
[Javascript] 형 변환  (0) 2022.11.03

[Javascript] 객체

CloudCoke
|2022. 11. 4. 17:41

객체란?


관련된 데이터와 함수(일반적으로 여러 데이터와 함수로 이루어지는데, 객체 안에 있을 때는 보통 프로퍼티와 메소드라고 부릅니다)의 집합 - JavaScript 객체 기본 [MDN]

객체는 실제로 존재하는 사물로 이해할 수 있다. 

예를 들어 강아지를 객체라고 한다면

프로퍼티는 이름, 나이, 무게 등이 들어 갈 수 있다.

메소드는 앉아, 일어서, 기다려 등 강아지에게 시킬 수 있는 행동들이 들어갈 수 있다.

const siba = {
  name: "siba",
  age: 1,
  weight: "5kg",
  alive: true,

  sitDown: function () {},
  standUp: function () {},
  wait: function () {},
};

위와 같이 객체 안에는 어떤 데이터 타입이든 들어갈 수 있다. 

그렇다면 속성과 메소드는 정확히 무엇일까?

속성(Property)


Object = { Property_Name : Property_Value }

속성 이름(Property Name)과 속성 값(Property Value) 형식으로 구성되는 데이터이다.

속성 이름을 작성할 때 나름의 규칙이 있다.

1. 첫 번째 글자는 반드시 문자, 밑줄(_), 달러 기호($) 중 하나로 시작해야 한다.
2. 띄어쓰기는 지양한다.
3. 하이픈(-)도 지양한다. 

위 사항을 어기고 싶으면 속성 이름을 따옴표로 감싸 주면 된다.

const user = {
	name: "cloudcoke",
    bornYear: 1997,
    "content type": "text/javascript",
    other: {
    	hello: "world!",
    }
}

 

객체의 속성 값을 사용하고 싶다면 두 가지 방법이 있다.

점 표기법

Object.Property_Name
console.log(user.name)

console.log(user.bornYear)

console.log(user.other.hello)

점 표기법으로 속성 값 출력

점 표기법으로는 속성 이름 작성 규칙을 어긴 속성은 사용하지 못한다.

console.log(user.content type)

console.log(user."content type")

속성 이름 작성 규칙을 어긴 속성을 점 표기법으로 출력해 보려고 했을때

그렇다면 이런 속성들은 어떻게 사용할 수 있을까?

대괄호 표기법

Object['Property_Name']
console.log(user['name'])

console.log(user['bornYear'])

console.log(user['content type'])

console.log(user['other']['hello'])

대괄호 표기법으로 속성 값 출력

대괄호 표기법을 사용하면 속성 이름 작성 규칙을 어긴 속성들도 사용할 수 있다.

[번외] 존재하지 않는 속성에 접근

만약 존재하지 않는 속성에 접근하면 어떻게 될까?

console.log(user.hi)

console.log(user['hi'])

존재하지 않는 속성에 접근한 결과

위와 같이 undefined가 출력되는 걸 볼 수 있다.

메소드(Method)


객체 안에 들어가 있는 함수를 말한다.

객체 안에는 어떠한 값이라도 들어갈 수 있는데 함수도 들어갈 수 있다.

예를 들어 우리가 자주 사용하는 console.log도 메소드이다.

console.log("Hello")

console이라는 객체 안에 log라는 함수에 인자로 "Hello"를 전달해 주면 콘솔에 "Hello"라는 문자열을 출력해 주는 것이다. (실제 동작은 다를 수 있음)

그렇다면 실제로 메소드를 한 번 만들어 보자!

const greeting = {
  methodName: "greeting",
  working: true,
  sayHello: function (name) {
    console.log(`Hello ${name}`);
  },
  sayBye: function (name) {
    console.log(`Bye ${name}`);
  },
};

인사를 하는 메소드를 작성해 보았다.

한번 메소드를 사용해 보자!

greeting.sayHello("cloudcoke")

greeting.sayBye("cloudcoke")

점 표기법으로 메소드 실행

greeting["sayHello"]("cloudcoke")

greeting["sayBye"]("cloudcoke")

대괄호 표기법으로 메소드 실행

두 방법 모두 잘 작동한다.

객체 생성


객체를 생성하는 3가지 방법을 알아보자!

1. 객체 생성자 함수 사용하기

첫 번째 방법은 new 키워드를 사용해 객체를 생성하는 방법이다.

const user = new Object();
console.log(user)

객체 생성자 함수를 사용해 빈 객체 생성하기

2. 객체 리터럴 방식 사용하기

두 번째 방법은 { }를 사용해 객체를 생성하는 방법이다.

const user = {}
console.log(user)

객체 리터럴 방식을 사용해 빈 객체 생성하기

이 방식이 제일 간편하고 안전하기 때문에 많이 사용한다고 한다!

3. 생성자 함수 사용하기

객체를 찍어내는 함수다.

사용자가 함수 안에 속성을 미리 정의해 놓고 그 함수를 new 키워드를 사용해 객체로 생성한다.

function User() {}

const user = new User()
console.log(User)

생성자 함수를 사용해 빈 객체 생성하기

객체 속성 다루기


this

객체 내부에는 this라는 특수한 키워드가 존재한다.

this 키워드는 지금 동작하고 있는 코드를 가진 객체를 가리킨다.

const person = {
  firstName: "cloud",
  lastName: "coke",
  greeting: function () {
    console.log(`Hi I'm ${this.firstName}${this.lastName}`);
  },
};
person.greeting()

person 객체의 greeting 메소드를 실행한 결과

위 코드에서 this는 person을 가리키고 있는 것이다.

그리고 this 키워드는 객체가 각각 다른 이름으로 인스턴스화 되어도 언제나 정확한 값을 사용하게 해준다고 한다.

const person1 = {
  name: "cloud",
  greeting: function () {
    console.log(`Hi I'm ${this.name}`);
  },
};

const person2 = {
  name: "coke",
  greeting: function () {
    console.log(`Hi I'm ${this.name}`);
  },
};
person1.greeting()

person2.greeting()

인스턴스화 된 객체의 greeting 메소드를 실행한 결과

두 객체의 메소드는 동일하지만 this가 가리키는 객체는 다른걸 알 수 있다. person1의 greeting 메소드는 person1의 name 속성 값인 cloud를 출력하고 person2의 greeting 메소드는 person2의 name 속성 값인 coke를 출력하고 있다.

객체 속성 추가

이번에는 객체에 속성을 추가해 보자!

일단 빈 객체를 생성하겠다.

const foo = {}

빈 객체 생성

빈 객체에 name 속성과 bornYear 속성을 추가하고 값 넣어보자!

foo.name = "cloudcoke"

foo['bornYear'] = 1997

foo 객체에 name 속성과 bornYear 속성을 추가한 결과

잘 들어간 걸  확인할 수 있다.

그럼 이번에는 속성 값을 수정해보자!

객체 속성 수정

foo 객체의 name 속성 값을 "구름맛콜라"로 수정해주고 bornYear 속성을 2022로 수정해주겠다.

foo.name = "구름맛콜라"

foo['bornYear'] = 2022

foo 객체의 name 속성 값과 bornYear 속성 값을 수정한 결과

객체 속성 존재 여부 확인

in 연산자를 이용해 객체 속성이 존재하는지 확인해보자!

"name" in foo

"bornYear" in foo

"age" in foo

in 키워드를 이용해 객체안에 속성이 존재하는지 확인한 결과

객체 속성 제거

이번에는 delete 키워드를 이용하여 객체의 속성 제거해서 빈 객체를 만들어 보자!

delete foo.name

delete foo['bornYear']

delete 키워드를 이용해 객체 속성을 제거한 결과

깨끗하게 지워졌다.

마무리


객체를 배우기 전까지는 자바스크립트는 객체로 이루어져 있다는 말이 와닿지 않았는데 이제 왜 그런지 알 것 같다.

참조