自定义导航网站

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

效果图

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

代码实现

UI

HTML 固定格式

html 复制代码
<!DOCTYPE html>

语言设置为英语

html 复制代码
<html lang="en">

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

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

body 中设置 title

html 复制代码
<body>
    <h1>Web Navigation</h1>

</body>

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

html 复制代码
    <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

html 复制代码
    <h2>Saved Pages</h2>
    <ul id="linksList">
        <!-- Links will be dynamically added here -->
    </ul>

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

html 复制代码
    <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 字符串,例如:

json 复制代码
[
    {
        "name": "百度",
        "link": "https://www.baidu.com/"
    }
]

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

html 复制代码
    <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 方法如下:

html 复制代码
        // 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:

html 复制代码
        // 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 功能:

html 复制代码
        // Function to clear all pages
        function clearAll() {
            localStorage.removeItem('savedPages');
            loadLinks();
        }

添加 exportAll 功能:

html 复制代码
        // 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 功能:

html 复制代码
        // 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.');
            }
        }

完整代码

html 复制代码
<!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>

使用

为了避免部署

替换启动页

替换启动页可以在 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。这样两个设置就统一起来了。

相关推荐
小白64027 小时前
前端梳理体系从常问问题去完善-基础篇(html,css,js,ts)
前端·css·html
蓝胖子的多啦A梦9 小时前
【前端】VUE+Element UI项目 页面自适应横屏、竖屏、大屏、PDA及手机等适配方案
前端·javascript·elementui·html·前端页面适配
面向星辰14 小时前
html各种常用标签
前端·javascript·html
李昊哲小课1 天前
HTML 完整教程与实践
前端·html
小*-^-*九2 天前
php 使用html 生成pdf word wkhtmltopdf 系列2
pdf·html·php
hashiqimiya2 天前
html实现右上角有个图标,鼠标移动到该位置出现手型,点击会弹出登录窗口。
前端·html
BillKu2 天前
Vue3 中使用 DOMPurify 对渲染动态 HTML 进行安全净化处理
前端·安全·html
BUG创建者3 天前
html获取16个随机颜色并不重复
css·html·css3
DevilSeagull3 天前
JavaScript WebAPI 指南
java·开发语言·javascript·html·ecmascript·html5
面向星辰3 天前
html中css的四种定位方式
前端·css·html