
이 타이핑 효과를 구현하기 위해서 많은 시행착오가 있었습니다... 혼자 고민 끝에 결론이 도출되지 않아 매니저님과 튜터님들께 자문을 구했습니다...😥
그냥 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();
};
마치며...

'Language > JavaScript' 카테고리의 다른 글
[TIL] JavaScript - 로딩되는 시점 이벤트 제어 (DOMContentLoaded, load, beforeunload/unload) (0) | 2022.11.25 |
---|---|
[TIL] JavaScript - debugger (0) | 2022.11.23 |
[TIL] JavaScript - push( ), pop( ), unshift( ), shift( ) (0) | 2022.11.15 |
[TIL] JavaScript - 정렬 함수, sort( ) !!! (0) | 2022.11.13 |
[TIL] JavaScript - map( ), filter( ), reduce( ) (0) | 2022.11.11 |
댓글