(29)-23.11.02[JS]

DANA·2023년 11월 2일

KDT-구디아카데미

목록 보기
29/56

step7

index.html

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>실습 - 뉴스API</title>
  <script src="./index.js" defer></script>
</head>
<body>
  <div id="root"></div>
</body>
</html>

index.js

const ajax = new XMLHttpRequest();
const TITLE_URL = "https://api.hnpwa.com/v0/news/1.json"; // 목록 - hashchange - 관찰
const container = document.getElementById("root");
const content = document.createElement("div");
// #38074701.json -> substring(1) -> 38074701
// 상세페이지를 구분할 수 있어야 한다 - 'show'라고 이름 붙이자
// 목록페이지로 가는 경우 - 구분할 수 있는 뭔가가 필요하다 - 이름 'page'
// 페이지 번호가 계속 바뀐다-> 상태가 바뀐다(쿠키나 세션 - 리덕스)
const CONTENT_URL = "https://api.hnpwa.com/v0/item/@id.json";

// 페이징처리를 위해 필요한 상태값을 관리하는 객체(VO 느낌)
const store = {
  currentPage: 1,
  totalRecord: 47,
};

getData = (url) => {
  ajax.open("GET", url, false);
  ajax.send();
  return JSON.parse(ajax.response);
};

// 글 내용을 보려면 글 목록 화면에서 제목을 클릭했을 때 (a태그가 발동되었을 때)
/* 나는 상세화면으로 간다(라우팅 -> 라우팅 구현을 라우터 함수로 정의했음 
- 라우터 함수에는 조건이 3가지고, */
getNewsData = () => {
  const newsData = getData(TITLE_URL);
  const newsList = [];
  newsList.push("<ul>");
  for (let i = (store.currentPage -1)*10; i < store.currentPage*10; i++) {
    // a(anchor)태그 - 상세보기(글 내용보기로 간다) - #/show/
    // -> localhost:1234/#/show/2       xxx.substring(7)-Number('2') Integer.parseInt()
    
    newsList.push(`
      <li>
        <a href = "#/show/${newsData[i].id}">
          ${newsData[i].title}(🖱️${newsData[i].comments_count})
        </a>
      </li>
    `);
  }
  newsList.push("</ul>");

  newsList.push( `
    <div>
      <a href = "#/page/${store.currentPage > 1 ? store.currentPage-1 : 1
      }">이전페이지</a>
      <a href = "#/page/${store.currentPage < 3 ? store.currentPage+1 : 3
      }">다음페이지</a>
    </div>
  `);
  container.innerHTML = newsList.join("");
}; 

newsDetail = () => {
    /*
    지금까지는 http://localhost:1234/#38088856이었으니까
    -> routerPath.substring(1)을 통해서 가져올 수 있었는데
  newsDetail은 id를 추출하는게 show,page단어가 추가되어 있어서 1을 7로 바꾸어주자
  다음 페이지 2번째 페이지를 보고 내용을 보면 잘 나온다
  다시 목록으로 돌아가면 목록이 잘 나오는 걸 볼 수 있다
  하지만 목록 보기로 왔을 때 정확히 몇번째 페이지인지 애매하다
  왜냐면 바로 #으로 나왔기 때문에 그렇다
  #으로 나오면 라우터 쪽에선 어떻게 동작할까? 라우터에 아무것도 없는 상태로 나올 것이다
  그리고 getNewsData()로 바로 호출한다. 그럼 getNewsData함수로 가면 현재의 페이지
  상태로 보이긴 할것이다
  왜냐면 currentPage를 바꾸고 난 다음에 목록으로 돌아갔다가 다시 뒤로 돌아오니까
  하지만 url기준으로는 몇 페이지인지 알수 없는 상황이니까
  일관된 동작을 위해서 목록 화면에서 넘어갈때 즉 바로 # 으로 가지 않고 
  현재 currentPage를 알고 있으니까 이러한 용도로 우리가 store라고 하는 상태를 
  #으로 가지않고 여기다가 page를 넘겨주도록 합시다 - 85라인에 대한 설명입니다.참고하세요!!!
  */
  const id = location.hash.substring(7);
  const ncontent = getData(CONTENT_URL.replace("@id", id));
  container.innerHTML = `
  <h1>${ncontent.title}</h1>
  <div><a href="#/page/${store.currentPage}">목록으로</a></div>
`;
}; 

router = () => {
  const routerPath = location.hash;

  if (routerPath === "") { // http://localhost:1234/#
    getNewsData(); 
  } 
  
  // 만약 여기를 #/page/ 이런 형태로 들어와 있으면 이게 0보다 크면 page라고 하는
  // 해시변경이라는 걸 확인할 수 있다
  else if(routerPath.indexOf('#/page/') >= 0) {
    /*
    currentPage에 2페이지, 3페이지 이렇게 들어가 있겠지!
    페이지 뒤에 숫자값을 넣으면 되고요
    그런데 우리는 아직 추출하지 않았으니까 상수로 2를 주어서 일단 동작을 확인해 봅시다
    */
    store.currentPage = Number(routerPath.substring(7)); // 전역변수의 값을 변경해놓는다. 다른 함수에서도 그 값을 공유한다
    getNewsData(); // 글 내용 보기
  } 
  // 현재 페이지를 바꿔주고 새로운 페이지 값으로 그리고 getNewsData 게시글 함수를
  // 다시 한 번 호출하면 됨. 게시글 함수는 현재페이지 정보를 가지고
  // 리스트를 뿌리는 코드가 들어가게 됩니다
  // #/show/의 경우 
  else {
    newsDetail(); 
  }
};

// 자바스크립트에서는 함수도 객체다
window.addEventListener("hashchange", router); // 두 번째 파라미터에서 라우팅을 위한 함수
router(); // 전처리과정 

//routerPath 앞단에 page가 들어가 있으면 이건 페이징이다. 글 내용을 보는게 아니고
//그래서 routerPath라고 하는 문자열 안에 이런 문자열이 있는지 찾는 메소드가 있는데
//indexOf가 그것이다. 이메소드는 입력으로 주어진 문자열을 찾아서 있다면 0이상의
//위치값을 리턴하게 되어 있고 없다면 -1을 반환하는 함수이다.
//-1즉 0보다 작은 값이면 입력으로 주어진 문자열이 없다는 뜻이 된다
//-> #/page/ 이런 형태로 들어와 있으면 이게 0보다 크면 page라고 하는
//해시변경이라는 걸 확인할 수 있다.

workout ver.1

workout.html

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Workout - 실습</title>
  <script src="https://kit.fontawesome.com/55780a7e38.js" crossorigin="anonymous"></script>
  <link rel="stylesheet" href="workout.css">
  <script src="workout.js" defer></script>
</head>
<body>
  <!-- 1 전체 테두리 -->
  <section class="list">
    <!-- 2 제목 -->
    <header class="header">Workout</header>
    <!-- 3 ul body -->
    <ul class = "items">
      <!-- 4 추가적으로 쌓임-->
      <li class="item_row">
        <!-- 8 왜 div인가? 글자, 이미지 배치를 위해 -->
        <div class="item">
          <span class="item_name">벤치프레스</span>
          <button class="item_delete">
            <i class="fas fa-trash-alt"></i>
          </button>
        </div>
        <div class="item_divider"></div>
      </li>
      <!-- li class 복사 -->
      <li class="item_row">
        <div class="item">
          <span class="item_name">벤치프레스</span>
          <button class="item_delete">
            <i class="fas fa-trash-alt"></i>
          </button>
        </div>
        <div class="item_divider"></div>
      </li>
    </ul>
    <!-- 5 바닥 쪽 플러스버튼 추가 -->
    <footer class="footer">
      <!-- 6 운동명 입력받는 콤포넌트 -->
      <input type="text" id="footer_input">
      <!-- 7 추가버튼 -->
      <button class="footer_button">
        <i class="fas fa-plus"></i>
      </button>
    </footer>
  </section>
</body>
</html>
<!--  
  javascript: DOM을 조작하기 위해서 JS 활용해 본다. (목표)
  css: 디자인에 관해서 일괄처리 위한 파일작성
-->

workout.js

// 우리가 접근해야 할 DOM요소 선언하기
const items = document.querySelector('.items');
const input = document.querySelector('#footer_input');
const btnPlus = document.querySelector('.footer_button');

// arrow function으로 바꾸는 것 연습해보기
// plusEvent = () => {
function plusEvent() {
  // 사용자가 입력한 텍스트를 받아온다
  // 변수 선언시 : const(상수), let(변수), var(사용금지-호이스팅이슈)
  const text = input.value;
  console.log(text);
  // 비교시 == 두개면 값만 비교하고 === 세 개면 타입도 비교
  if (text === '') {
    input.focus();
    return;
  }
  // 새로운 아이템을 만든다(텍스트 + 삭제버튼)

  // items 컨테이너 안에 새로 만든 아이템을 추가한다

  // 인풋값을 초기화한다
  input.value = '';
  // 커서가 미리 가있으면 마우스 클릭하지 않고도 즉시 입력가능함
  input.focus(); // 커서가 위치하게 함
  // 만일 아무것도 입력하지 않고 버튼을 누르면
  // 포커스를 input text로 옮기고 함수를 탈출함
}

btnPlus.addEventListener('click', () => {
  // insert here
  plusEvent();
});

workout ver.2

workout.html

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Workout - 실습</title>
  <script src="https://kit.fontawesome.com/55780a7e38.js" crossorigin="anonymous"></script>
  <link rel="stylesheet" href="workout.css">
  <script src="workout.js" defer></script>
</head>
<body>
  <!-- 1 전체 테두리 -->
  <section class="list">
    <!-- 2 제목 -->
    <header class="header">Workout</header>
    <!-- 3 ul body -->
    <ul class = "items">
      <!-- 4 추가적으로 쌓임-->
    </ul>
    <!-- 5 바닥 쪽 플러스버튼 추가 -->
    <footer class="footer">
      <!-- 6 운동명 입력받는 콤포넌트 -->
      <input type="text" id="footer_input">
      <!-- 7 추가버튼 -->
      <button class="footer_button">
        <i class="fas fa-plus"></i>
      </button>
    </footer>
  </section>
</body>
</html>
<!--  
  javascript: DOM을 조작하기 위해서 JS 활용해 본다. (목표)
  css: 디자인에 관해서 일괄처리 위한 파일작성
-->

workout.js

// 우리가 접근해야 할 DOM요소 선언하기
const items = document.querySelector('.items');
const input = document.querySelector('#footer_input');
const btnPlus = document.querySelector('.footer_button');

// arrow function으로 바꾸는 것 연습해보기
// plusEvent = () => {
function plusEvent() {
  // 사용자가 입력한 텍스트를 받아온다
  // 변수 선언시 : const(상수), let(변수), var(사용금지-호이스팅이슈)
  const text = input.value;
  console.log(text);
  // 비교시 == 두개면 값만 비교하고 === 세 개면 타입도 비교
  if (text === '') {
    input.focus();
    return;
  }
  // 새로운 아이템을 만든다(텍스트 + 삭제버튼)
  const item = createItem(text);
  // items 컨테이너 안에 새로 만든 아이템을 추가한다
  items.appendChild(item);
  // 새로 추가된 아이템으로 스크롤링을 함
  item.scrollIntoView({ block: 'center' });
  // 인풋값을 초기화한다
  input.value = '';
  // 커서가 미리 가있으면 마우스 클릭하지 않고도 즉시 입력가능함
  input.focus(); // 커서가 위치하게 함
  // 만일 아무것도 입력하지 않고 버튼을 누르면
  // 포커스를 input text로 옮기고 함수를 탈출함
}

function createItem(text) {
  // DOM에서 제공하는 함수를 이용해서 태그를 만들 땐 li 태그를 안붙임
  const itemRow = document.createElement('li');
  // 태그에 속성을 추가할 땐 setAttribute 함수를 사용할 것
  itemRow.setAttribute('class', 'item_row');
  const item = document.createElement('div');
  item.setAttribute('class', 'item');
  const name = document.createElement('span');
  name.setAttribute('class', 'item_name');
  name.innerText = text;
  const deleteBtn = document.createElement('button');
  deleteBtn.setAttribute('class', 'item_delete');
  deleteBtn.innerHTML = '<i class="fas fa-trash-alt"></i>';
  // createitem이 호출되었을 때 화면이 먼저 완성(렌더링==DOM TREE)되고
  // 되고 난 다음에야 버튼이 눌려질테니까 파라미터의 itemRow는 create 함수에서
  // 생성된 전부를 이미 쥐고 있다
  // html, js, css는 로컬pc에 다운로드 된 게 실행되는 것이다
  deleteBtn.addEventListener('click', () => {
    items.removeChild(itemRow);
  });
  const itemDivider = document.createElement('div');
  itemDivider.setAttribute('class', 'item_divider');
  item.appendChild(name);
  item.appendChild(deleteBtn);
  itemRow.appendChild(item);
  itemRow.appendChild(itemDivider);
  return itemRow;
}

btnPlus.addEventListener('click', () => {
  // insert here
  plusEvent();
});

// 엔터 쳤을 때도 동일(하나 - 함수로 묶어야 함)하게 처리하기
input.addEventListener('keypress', (e) => {
  if (e.key === 'Enter') {
    plusEvent();
  }
});

0개의 댓글