]> git.ardis.lu - minimal-routing/commitdiff
Initial commit
authorArdis Lu <ardisglu@gmail.com>
Sun, 30 Jan 2022 06:36:29 +0000 (22:36 -0800)
committerArdis Lu <ardisglu@gmail.com>
Sun, 30 Jan 2022 06:36:29 +0000 (22:36 -0800)
README.md [new file with mode: 0644]
errors/404.html [new file with mode: 0644]
index.html [new file with mode: 0644]

diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..cf6a9a4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,15 @@
+# minimal-routing
+
+Demo website for the minimum JavaScript required to implement client-side routing.
+
+## Web server configuration
+
+This demo website assumes that the web server has been configured to serve `index.html` on all routes **EXCEPT** for the special path: `/errors/404.html`. For `/errors/404.html`, the web server must respond with a `404 Not Found` error and the file `/errors/404.html`.
+
+Note: this file path is intentionally nested in a folder because some static site hosts (like [Cloudflare Pages](https://developers.cloudflare.com/pages/platform/serving-pages)) use a top-level `404.html` page as a flag to override the desired behavior of serving `index.html` on all routes.
+
+The desired behavior can be replicated locally using the npm package [local-web-server](https://www.npmjs.com/package/local-web-server):
+
+```
+ws --spa index.html
+```
diff --git a/errors/404.html b/errors/404.html
new file mode 100644 (file)
index 0000000..54dc4ca
--- /dev/null
@@ -0,0 +1,17 @@
+<!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">
+  <link rel="icon" href="data:,">
+  <title>404 Not Found</title>
+</head>
+
+<body>
+  <h1>404 Not Found</h1>
+  <p>The requested page does not exist.</p>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/index.html b/index.html
new file mode 100644 (file)
index 0000000..46dcce4
--- /dev/null
@@ -0,0 +1,88 @@
+<!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">
+  <link rel="icon" href="data:,">
+  <title>minimal-routing</title>
+</head>
+
+<body>
+  <header>
+    <button data-router data-route="home">Go to the home page</button>
+    <button data-router data-route="page_1">Go to page 1</button>
+    <button data-router data-route="page_2">Go to page 2</button>
+    <button data-router data-route="page_3">Go to page 3</button>
+  </header>
+
+  <main>
+    <article data-page data-route="home">
+      Hello from the home page!
+    </article>
+    <article data-page data-route="page_1">
+      Hello from page 1!
+    </article>
+    <article data-page data-route="page_2">
+      Hello from page 2!
+    </article>
+    <article data-page data-route="page_3">
+      Hello from page 3!
+    </article>
+  </main>
+
+  <script>
+    const routers = document.querySelectorAll('[data-router]');
+    const main = document.querySelector('main');
+    const pages = Array.from(document.querySelectorAll('[data-page]')); // Must convert to array to preserve elements
+
+    // Display the appropriate page based on the URL path
+    function route(path) {
+      if (path === '') {
+        path = 'home';
+      }
+
+      let match = false;
+      for (const page of pages) {
+        if (page.dataset.route === path) {
+          main.appendChild(page);
+          match = true;
+        }
+        else {
+          page.remove();
+        }
+      }
+      if (!match) {
+        // MUST redirect to a page where the web server serves an actual 404 error (i.e. not just 
+        // index.html again) otherwise this triggers an infinite loop
+        window.location.href = `${window.location.protocol}//${window.location.host}/errors/404.html`;
+      }
+    }
+
+    // Update the browser history, then route
+    function changePage(path) {
+      // If the new page is equal to the current page, don't do anything
+      if (window.location.pathname.substring(1) === path) {
+        return;
+      }
+
+      const base = new URL(`${window.location.protocol}//${window.location.host}`);
+      const url = new URL(path, base);
+      window.history.pushState({}, '', url);
+
+      route(path);
+    }
+
+    // Set event handlers
+    for (const router of routers) {
+      router.addEventListener('click', e => changePage(e.target.dataset.route));
+    }
+    window.onpopstate = _ => route(window.location.pathname.substring(1));
+
+    // Route to the current URL (to support direct linking aka deep links)
+    route(window.location.pathname.substring(1));
+  </script>
+</body>
+
+</html>
\ No newline at end of file
This page took 0.041132 seconds and 4 git commands to generate.