바닐라 JavaScript를 사용한 가상 URL 탐색

저는 GitHub 프로젝트에서 관리자가 실제로 다른 HTML 파일을 생성하지 않고 페이지의 콘텐츠를 동적으로 변경하고 "네이티브"페이지라는 느낌을 주기를 원하는 문제를 발견했습니다.

그는 또한 자신의 웹 사이트에서 바닐라 JS 및 HTML 파일만 사용하기를 원했기 때문에 Angular, Svelte 또는 기타 프레임워크를 그에게 소개하는 것은 선택 사항이 아니었습니다.

다행스럽게도 이런 종류의 문제를 정확하게 해결할 수 있는 HTML5에 도입된 흥미로운 API를 발견했습니다.

단계별로 살펴보겠습니다.

설정



우리의 예에서는 일종의 라우팅을 사용하는 느낌을 줄 수 있는 매우 간단한 페이지를 만들 것입니다.

먼저 간단한 HTML 페이지를 만듭니다.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Fake URL navigation</title>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
  </head>
  <body>
    <h1>Fake routing</h1>
    <button id="details">Show details</button>
  </body>
</html>


지금은 많이 렌더링되지 않습니다.

URL 변경



이제 사용자가 버튼을 클릭할 때마다 URL을 변경하려고 합니다.

이를 위해 pushState 함수를 사용하여 프로그래밍 방식으로 기록에 새 항목을 추가할 수 있으며, 그러면 URL도 수정됩니다.

사용법은 매우 간단하며 세 가지 매개변수로 구성됩니다.
  • URL을 따라 푸시되는 상태로 가벼운 JS 개체 또는 일종의 구조화된 데이터일 수 있습니다
  • .
  • 빈 문자열로 바꿀 수 있는 이전 버전과의 호환성으로 인해 필수인 미사용 매개변수
  • 푸시하려는 실제 URL

  • 이를 변경하기 위해 작은 스크립트를 추가해 보겠습니다.

    <script>
      document.getElementById("details").onclick = (_event) => history.pushState({}, "", "/details");
    </script>
    


    그리고 그것을 테스트:



    아주 멋진! 이제 콘텐츠에 집중할 수 있습니다.

    페이지 업데이트



    콘텐츠를 업데이트하려면 일종의 템플릿이 필요하고 현재 HTML 본문을 템플릿으로 대체해야 합니다.

    기본 API를 사용하면 매우 간단합니다.

    <script>
    + const template = "<h1>Details</h1> <p>Some highly useful information</p>";
    
      document.getElementById("details").onclick = (_event) => {
        history.pushState({}, "", "/details");
    +   document.body.innerHTML = template;
      };
    </script>
    


    이제 가상 페이지가 생겼습니다!



    뒤로 버튼 수정



    현재 솔루션에서는 세부 정보 페이지가 표시되면 전체 페이지를 다시 로드하지 않고 인덱스를 다시 표시할 수 없습니다.

    이 문제를 해결하기 위해 body 요소의 내용을 제거하기 전에 스택에 푸시하여 로컬 히스토리를 생성하는 것으로 시작할 수 있습니다.

    <script>
      const template = "<h1>Details</h1> <p>Some highly useful information</p>";
    
    + const bodyHistory = [];
    
      document.getElementById("details").onclick = (_event) => {
        history.pushState({}, "", "/details");
    
    +   bodyHistory.push(document.body.innerHTML);
        document.body.innerHTML = template;
      };
    </script>
    


    그러면 pushState : popstate 의 거울과 같은 이벤트를 사용할 수 있습니다.

    이를 들으면 뒤로 버튼을 누를 때마다 알 수 있으며 페이지의 이전 콘텐츠를 복원할 수 있습니다.

    <script>
      const template = "<h1>Details</h1> <p>Some highly useful information</p>";
    
      const bodyHistory = [];
    
      document.getElementById("details").onclick = (_event) => {
        history.pushState({}, "", "/details");
    
        bodyHistory.push(document.body.innerHTML);
        document.body.innerHTML = template;
      };
    
    + onpopstate = (_event) => {
    +   const previousContent = bodyHistory.pop();
    +
    +   if (previousContent) {
    +     document.body.innerHTML = previousContent;
    +   }
    + };
    </script>
    


    이 시점에서 기록을 되돌리면 작동하지만 전체 페이지를 복원했지만 이벤트 리스너가 다시 설정되지 않았기 때문에 다시 탐색할 수 없습니다.

    이를 수정하는 빠른 방법은 이 로직을 처음 추가할 때와 동일한 방식으로 onpopstate 이벤트 핸들러 내부에 다시 ​​등록하는 것입니다.

    이 논리를 전용 함수로 추출하고 호출해 보겠습니다.

    <script>
      const template = "<h1>Details</h1> <p>Some highly useful information</p>";
    
      const bodyHistory = [];
    
    + registerHandler();
    
    + function registerHandler() {
    +   document.getElementById("details").onclick = (_event) => {
    +     history.pushState({}, "", "/details");
    +
    +     bodyHistory.push(document.body.innerHTML);
    +     document.body.innerHTML = template;
    +   };
    + };
    
      onpopstate = (_event) => {
        const previousContent = bodyHistory.pop();
    
        if (previousContent) {
          document.body.innerHTML = previousContent;
    +     registerHandler();
        }
      };
    </script>
    


    그리고 끝났습니다!




    이 짧은 기사에서 우리는 pushState/popState 커플을 활용하여 사용자에게 다른 페이지를 탐색하는 느낌을 주는 방법을 살펴보았습니다.

    이것은 일종의 가상 내비게이션에 대한 간단한 소개이지만 제 경우에는 이 유지 관리자의 프로젝트에 성공적으로 구현하는 데 도움이 되었습니다.

    그러나 사용자가 실제 경로와 일치하지 않는 URL에 있을 때 페이지를 다시 로드하면 HTTP 404 오류가 발생합니다.

    한 가지 해결책은 ...#details 대신 .../details와 같은 해시로 가상 페이지에 대한 탐색을 대체하는 것입니다. 이렇게 하면 브라우저는 다시 로드할 때 여전히 루트 페이지를 렌더링하고 대신 세부 정보 페이지를 렌더링하기 위해 해시 기호 뒤에 있는 모든 항목을 가로챌 수 있습니다.

    거기서 뭔가 유용한 것을 배웠기를 바랍니다!


    데모에 사용된 전체 소스:

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8" />
        <title>Fake URL navigation</title>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
      </head>
      <body>
        <h1>Fake routing</h1>
        <button id="details">Show details</button>
      </body>
    
      <script>
        const template = "<h1>Details</h1> <p>Some highly useful information</p>";
    
        const bodyHistory = [];
    
        registerHandler();
    
        function registerHandler() {
          document.getElementById("details").onclick = (_event) => {
            history.pushState({}, "", "/details");
    
            bodyHistory.push(document.body.innerHTML);
            document.body.innerHTML = template;
          };
        }
    
        onpopstate = (_event) => {
          const previousContent = bodyHistory.pop();
    
          if (previousContent) {
            document.body.innerHTML = previousContent;
            registerHandler();
          }
        };
      </script>
    </html>
    

    좋은 웹페이지 즐겨찾기