본문 바로가기
Language/JavaScript

[TIL] JavaScript - innerHTML된 파일에서 함수 구현하기...?

by pin9___9 2022. 11. 21.
728x90

이 타이핑 효과를 구현하기 위해서 많은 시행착오가 있었습니다... 혼자 고민 끝에 결론이 도출되지 않아 매니저님과 튜터님들께 자문을 구했습니다...😥 

그냥 HTML에서 JS로 타이핑 효과를 구현하는 것은 성공했었습니다.

하지만 문제는 이번 프로젝트를 SPA방식으로 구현하여야 했고, 그 과정에서 main page를 innerHTML로 뿌려주면서 발생하게 되었습니다.

 

문제점

 

<!-- index.html -->

<div id="main-page"></div>

<script src="static/js/pages/home.js"></script>
<script src="static/js/main.js" type="module"></script>

<!-- main.html -->

<div class="wrap">
  <div class="top_text">
    <h1 class="first_text"></h1>
  </div>
  <div class="flicker">
    <span class="light">Hype</span>
  </div>
  <div class="slide">
    <span class="draft">Express</span>
  </div>
</div>

 

index.html에서 모든 페이지들을 받아옵니다. 밑에 router.js에서 innerHTML을 이용하여 <div id="main-page">에 main.html을 뿌려줍니다.

 

//router.js

export const route = (event) => {
  event.preventDefault();
  window.location.hash = event.target.hash;
};

const routes = {
  "/": "/templates/pages/main.html"
};

export const handleLocation = async () => {
  let path = window.location.hash.replace("#", "");

  if (path.length === 0) {
    path = "/";
  }
  const route = routes[path] || routes[404];
  const html = await fetch(route).then((data) => data.text());

  document.getElementById("main-page").innerHTML = html;
};

 

//home.js

const text = document.querySelector('.first_text');
const txt = `여러분들의 공간에 
Hype함을 배달합니다.`;
const typing = function(_, counter = 0) {
    // 출력할 내용
  setInterval(() => {
    // 글자가 모두 출력되면 setInterval을 멈출 것
    if (txt.length === counter) {
      return 
    };
    // 문자열 하나하나 h2의 텍스트 컨텐츠로 추가한다 
    text.innerHTML += txt[counter] === "\n"?`<br>`:txt[counter];
    
    // 카운터 증산
    counter++;
  }, 60); 
};

 

이렇게 했을 경우 DOM이 구축되기 전에 home.js가 실행되기 때문에 그 안에 있는 요소를 가져오는 것이 불가능하여 text는 계속해서 null값을 가져오게 됩니다.

 

해결방법

1. 중첩 함수

 

//home.js

const TypeText = () => {


const text = document.querySelector('.first_text');
const txt = `여러분들의 공간에 
Hype함을 배달합니다.`;
return (_, counter = 0) => {
    // 출력할 내용
  setInterval(() => {
    // 글자가 모두 출력되면 setInterval을 멈출 것
    if (txt.length === counter) {
      return 
    };
    // 문자열 하나하나 h2의 텍스트 컨텐츠로 추가한다 
    text.innerHTML += txt[counter] === "\n"?`<br>`:txt[counter];
    
    // 카운터 증산
    counter++;
  }, 60); 
};
};

 

DOM이 그려지고 그 안에 있는 요소를 가져오는 것을 보장하기 위해 함수로 만들어 줍니다.

하지만 이 함수는 중첩함수가 되어 함수를 두 번 실행시켜 줘야 합니다...

 

//router.js

export const route = (event) => {
  event.preventDefault();
  window.location.hash = event.target.hash;
};

const routes = {
  "/": "/templates/pages/main.html"
};

export const handleLocation = async () => {
  let path = window.location.hash.replace("#", "");

  if (path.length === 0) {
    path = "/";
  }
  const route = routes[path] || routes[404];
  const html = await fetch(route).then((data) => data.text());

  document.getElementById("main-page").innerHTML = html;
  if(path === "/") TypeText()();
};

 

2. 중첩 함수 해결

 

//home.js

const TypeText = () => {

const text = document.querySelector('.first_text');
const txt = `여러분들의 공간에 
Hype함을 배달합니다.`;

	let counter = 0
    // 출력할 내용
  setInterval(() => {
    // 글자가 모두 출력되면 setInterval을 멈출 것
    if (txt.length === counter) {
      return 
    };
    // 문자열 하나하나 h2의 텍스트 컨텐츠로 추가한다 
    text.innerHTML += txt[counter] === "\n"?`<br>`:txt[counter];
    
    // 카운터 증산
    counter++;
  }, 60); 
};
};

 

중첩 함수를 해소하기 위해서 함수 안에 코드를 살펴보았습니다. 구현하려는 함수는 페이지 렌더링 됐을 때, 딱 한 번만 실행해 줄 것이기 때문에, event 인자를 받을 필요가 없어 굳이 함수를 적지 않고 변수만 정하여 함수를 실행하면 되기 때문에 내부에 있는 함수는 지워줍니다. 이렇게 함수를 만들 시 함수를 한 번만 실행하면 됩니다!!

 

//router.js

export const handleLocation = async () => {
  let path = window.location.hash.replace("#", "");

  if (path.length === 0) {
    path = "/";
  }
  const route = routes[path] || routes[404];
  const html = await fetch(route).then((data) => data.text());

  document.getElementById("main-page").innerHTML = html;
  if(path === "/") TypeText();
};

 

마치며...

 

 

728x90

댓글