likes
comments
collection
share

自定义导航网站

作者站长头像
站长
· 阅读数 4

打算做一个自己的导航网站。主要功能是支持输入网页名和 URL,并将其保存到一个列表中。点击列表中的项,可以跳转到对应的 URL。

效果图

自定义导航网站

另外,还实现了删除网页、清空所有网页、导出所有网页,导入网页等功能。

代码实现

UI

HTML 固定格式

<!DOCTYPE html>

语言设置为英语

<html lang="en">

设置UTF-8编码,设置宽度占满屏幕,设置缩放比例 100%,设置 Title

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Web Navigation</title>
</head>

body 中设置 title

<body>
    <h1>Web Navigation</h1>

</body>

body 中设置 form 表格,包含两个输入框,page name 和 page link,一个提交按钮

    <form id="addPageForm">
        <label for="pageName">Page Name:</label>
        <input type="text" id="pageName" name="pageName" required>
        <label for="pageLink">Page Link:</label>
        <input type="url" id="pageLink" name="pageLink" required>
        <button type="submit">Submit</button>
    </form>

添加一个列表展示已保存的 links

    <h2>Saved Pages</h2>
    <ul id="linksList">
        <!-- Links will be dynamically added here -->
    </ul>

添加一个 clearAll,exportAll,import 按钮。用于清空所有数据,导出所有数据,导入数据:

    <button onclick="clearAll()">Clear All</button>
    <button onclick="exportAll()">Export All</button>
    <input type="file" id="fileInput">
    <button onclick="importFile()">Import</button>

数据

将网页数据保存到哪里比较合适呢?可以使用 localStorage 持久化存储到本地。

localStorage 是一个 Web API,它提供了在浏览器中存储数据的简单持久化解决方案。它允许开发者将数据存储在浏览器中,以便在同一域名下的页面之间共享和访问这些数据。下面是关于 localStorage 的一些重要特性和使用方法:

  1. 简单的键值对存储localStorage 使用键值对存储数据。你可以通过指定一个键来存储和检索数据。

  2. 持久性:与会话存储(session storage)不同,localStorage 中存储的数据在浏览器关闭后仍然保持不变。这意味着它提供了持久性存储,数据会一直存在,直到被显式删除或清除。

  3. 同源策略localStorage 遵循同源策略,它只允许页面访问与自身相同域名、相同端口和相同协议的数据。 4. 同源策略(Same-Origin Policy)是浏览器安全策略的一部分,它是一种安全机制,用于防止不同源的网页间的恶意行为。同源策略规定了浏览器只允许加载同一源(指协议、域名和端口号都相同)的资源,并限制了不同源页面之间的交互。

    具体来说,同源策略包括以下几个方面:

    1. 限制跨域资源访问:网页只能向同一域名下的资源发送请求,不能直接访问其他域名下的资源。这意味着如果一个网页想要加载来自不同域名的资源(例如图片、脚本、样式表或数据接口),则需要目标资源所在域名同意跨域访问。
    2. 限制跨域脚本访问:浏览器不允许脚本从一个域名加载并执行另一个域名的脚本。这防止了恶意网站利用跨域脚本执行跨站点请求伪造(CSRF)等攻击。
    3. 限制跨域 Cookie 访问:Cookie 也受到同源策略的限制。浏览器只会发送同一域名下的 Cookie,不会发送给其他域名的请求,从而保护用户的隐私信息。

    同源策略的目的是保护用户的数据安全和隐私,防止恶意网站窃取用户的敏感信息或进行其他恶意行为。虽然同源策略限制了跨域资源的访问,但同时也提供了一些安全性和隐私保护的好处。然而,对于需要进行跨域资源共享的情况,例如一些 API 请求或者第三方资源加载,可以通过 CORS(跨域资源共享)等机制来进行合理的跨域访问控制。

  4. 数据类型localStorage 只能存储字符串类型的数据。如果需要存储其他类型的数据(如对象或数组),你需要先将它们转换为字符串形式,然后在存储和检索时再进行相应的转换。

  5. 容量限制localStorage 存储的数据量通常限制在几 MB,具体取决于不同浏览器的实现和设置。

  6. 不会被发送到服务器localStorage 中的数据仅存储在用户的本地浏览器中,并不会自动发送到服务器。这使得它适合存储用户偏好设置、会话状态、缓存数据等不需要与服务器交互的信息。

  7. 简单易用localStorage 提供了简单的 API,包括 setItem()getItem()removeItem() 等方法,用于存储、检索和删除数据。

总的来说,localStorage 是一个方便且易于使用的浏览器存储解决方案,适用于许多前端应用场景,如本地缓存、持久性数据存储等。但需要注意,由于其容量限制和同源策略,不适合存储大量敏感信息或需要与服务器同步的数据。

localStorage 以键值对的形式存储数据,所以我们可以存储一个 key 为 savedPages 的数据,value 为包含所有网页的 json 字符串,例如:

[
    {
        "name": "百度",
        "link": "https://www.baidu.com/"
    }
]

添加 script 脚本,加载此 json 数据,并展示到列表中。每一项数据带一个 delete 按钮,用于删除:

    <script>
        // Load links when the page loads
        loadLinks();
        // Function to load links from localStorage
        function loadLinks() {
            const savedPages = JSON.parse(localStorage.getItem('savedPages'));
            const linksList = document.getElementById('linksList');

            if (savedPages && savedPages.length > 0) {
                linksList.innerHTML = '';
                savedPages.forEach(page => {
                    const listItem = document.createElement('li');

                    const nameElement = document.createElement('span');
                    nameElement.textContent = page.name;
                    nameElement.style.cursor = 'pointer';
                    nameElement.onclick = function (event) {
                        event.stopPropagation();
                        openPage(page.link);
                    };

                    const deleteButton = document.createElement('button');
                    deleteButton.textContent = 'Delete';
                    deleteButton.onclick = function (event) {
                        event.stopPropagation();
                        deletePage(page);
                    };

                    listItem.appendChild(nameElement);
                    listItem.appendChild(deleteButton);
                    linksList.appendChild(listItem);
                });
            } else {
                linksList.innerHTML = '<li>No saved pages</li>';
            }
        }
    </script>

其中,deletePage 方法和 openPage 方法如下:

        // Function to delete a page
        function deletePage(page) {
            const savedPages = JSON.parse(localStorage.getItem('savedPages'));
            const updatedPages = savedPages.filter(p => p.name !== page.name);
            localStorage.setItem('savedPages', JSON.stringify(updatedPages));
            loadLinks();
        }

        // Function to open a page in a new tab
        function openPage(link) {
            window.open(link, '_blank');
        }

添加 script 脚本,当点击 submit 时,将 pageName 和 pageLink 取出来,存到 localStorage 中,并刷新 links:

        // Function to handle form submission
        document.getElementById('addPageForm').addEventListener('submit', function(event) {
            event.preventDefault();

            const pageName = document.getElementById('pageName').value;
            const pageLink = document.getElementById('pageLink').value;

            if (pageName && pageLink) {
                const savedPages = JSON.parse(localStorage.getItem('savedPages')) || [];
                savedPages.push({ name: pageName, link: pageLink });
                localStorage.setItem('savedPages', JSON.stringify(savedPages));

                loadLinks();

                // Reset form fields
                document.getElementById('pageName').value = '';
                document.getElementById('pageLink').value = '';
            } else {
                console.error('Page name and link are required.');
            }
        });

添加 clearAll 功能:

        // Function to clear all pages
        function clearAll() {
            localStorage.removeItem('savedPages');
            loadLinks();
        }

添加 exportAll 功能:

        // Function to export all pages to a file
        function exportAll() {
            const savedPages = JSON.parse(localStorage.getItem('savedPages'));

            if (savedPages && savedPages.length > 0) {
                const savedPagesJSON = JSON.stringify(savedPages, null, 4);
                const blob = new Blob([savedPagesJSON], { type: 'application/json' });
                const a = document.createElement('a');
                a.href = URL.createObjectURL(blob);
                a.download = 'savedPages.json';
                a.click();
            } else {
                console.error('No saved pages found.');
            }
        }

添加 import 功能:

        // Function to handle file import
        function importFile() {
            const fileInput = document.getElementById('fileInput');
            const file = fileInput.files[0];
            if (file) {
                const reader = new FileReader();
                reader.onload = function (event) {
                    const importedPages = JSON.parse(event.target.result);
                    if (Array.isArray(importedPages)) {
                        const savedPages = JSON.parse(localStorage.getItem('savedPages')) || [];
                        const updatedPages = savedPages.concat(importedPages);
                        localStorage.setItem('savedPages', JSON.stringify(updatedPages));
                        loadLinks();
                        // Clear file input value 
                        fileInput.value = '';
                    } else {
                        console.error('Invalid JSON format.');
                    }
                };
                reader.readAsText(file);
            } else {
                console.error('No file selected.');
            }
        }

完整代码

<!DOCTYPE html>
<html lang="en">

</html>

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Web Navigation</title>
</head>

<body>
    <h1>Web Navigation</h1>
    <form id="addPageForm">
        <label for="pageName">Page Name:</label>
        <input type="text" id="pageName" name="pageName" required>
        <label for="pageLink">Page Link:</label>
        <input type="url" id="pageLink" name="pageLink" required>
        <button type="submit">Submit</button>
    </form>
    <h2>Saved Pages</h2>
    <ul id="linksList">
        <!-- Links will be dynamically added here -->
    </ul>
    <button onclick="clearAll()">Clear All</button>
    <button onclick="exportAll()">Export All</button><br><br>
    <input type="file" id="fileInput">
    <button onclick="importFile()">Import</button>

    <script>
        // Load links when the page loads
        loadLinks();
        // Function to load links from localStorage
        function loadLinks() {
            const savedPages = JSON.parse(localStorage.getItem('savedPages'));
            const linksList = document.getElementById('linksList');

            if (savedPages && savedPages.length > 0) {
                linksList.innerHTML = '';
                savedPages.forEach(page => {
                    const listItem = document.createElement('li');

                    const nameElement = document.createElement('span');
                    nameElement.textContent = page.name;
                    nameElement.style.cursor = 'pointer';
                    nameElement.onclick = function (event) {
                        event.stopPropagation();
                        openPage(page.link);
                    };

                    const deleteButton = document.createElement('button');
                    deleteButton.textContent = 'Delete';
                    deleteButton.onclick = function (event) {
                        event.stopPropagation();
                        deletePage(page);
                    };

                    listItem.appendChild(nameElement);
                    listItem.appendChild(deleteButton);
                    linksList.appendChild(listItem);
                });
            } else {
                linksList.innerHTML = '<li>No saved pages</li>';
            }
        }

        // Function to delete a page
        function deletePage(page) {
            const savedPages = JSON.parse(localStorage.getItem('savedPages'));
            const updatedPages = savedPages.filter(p => p.name !== page.name);
            localStorage.setItem('savedPages', JSON.stringify(updatedPages));
            loadLinks();
        }

        // Function to open a page in a new tab
        function openPage(link) {
            window.open(link, '_blank');
        }

        // Function to handle form submission
        document.getElementById('addPageForm').addEventListener('submit', function (event) {
            event.preventDefault();

            const pageName = document.getElementById('pageName').value;
            const pageLink = document.getElementById('pageLink').value;

            if (pageName && pageLink) {
                const savedPages = JSON.parse(localStorage.getItem('savedPages')) || [];
                savedPages.push({ name: pageName, link: pageLink });
                localStorage.setItem('savedPages', JSON.stringify(savedPages));

                loadLinks();

                // Reset form fields
                document.getElementById('pageName').value = '';
                document.getElementById('pageLink').value = '';
            } else {
                console.error('Page name and link are required.');
            }
        });

        // Function to clear all pages
        function clearAll() {
            localStorage.removeItem('savedPages');
            loadLinks();
        }

        // Function to export all pages to a file
        function exportAll() {
            const savedPages = JSON.parse(localStorage.getItem('savedPages'));

            if (savedPages && savedPages.length > 0) {
                const savedPagesJSON = JSON.stringify(savedPages, null, 4);
                const blob = new Blob([savedPagesJSON], { type: 'application/json' });
                const a = document.createElement('a');
                a.href = URL.createObjectURL(blob);
                a.download = 'savedPages.json';
                a.click();
            } else {
                console.error('No saved pages found.');
            }
        }

        // Function to handle file import
        function importFile() {
            const fileInput = document.getElementById('fileInput');
            const file = fileInput.files[0];
            if (file) {
                const reader = new FileReader();
                reader.onload = function (event) {
                    const importedPages = JSON.parse(event.target.result);
                    if (Array.isArray(importedPages)) {
                        const savedPages = JSON.parse(localStorage.getItem('savedPages')) || [];
                        const updatedPages = savedPages.concat(importedPages);
                        localStorage.setItem('savedPages', JSON.stringify(updatedPages));
                        loadLinks();
                        // Clear file input value
                        fileInput.value = '';
                    } else {
                        console.error('Invalid JSON format.');
                    }
                };
                reader.readAsText(file);
            } else {
                console.error('No file selected.');
            }
        }
    </script>
</body>

已上传至 github:github.com/KevinWangJc…

使用

为了避免部署,可以采用在浏览器中设置打开新标签页时,加载本地文件的方式。

替换启动页

替换启动页可以在 Chrome 的 Settings 中,On startup -> Open a specific page or set of pages 选项中,设置本地文件路径。

自定义导航网站

替换 New Tab 页

在 Settings 里没有找到替换 New Tab 页的方法,解决方式是装一个 Chrome 插件:Custom New Tab URL,在其设置中,设置打开 new tab 页时,加载本地 html 文件:

自定义导航网站

自定义导航网站

装了这个插件后,在替换启动页时,可以设置为 Open the New Tab page。这样两个设置就统一起来了。

自定义导航网站

继续阅读: