自定义导航网站
打算做一个自己的导航网站。主要功能是支持输入网页名和 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
的一些重要特性和使用方法:
简单的键值对存储:
localStorage
使用键值对存储数据。你可以通过指定一个键来存储和检索数据。持久性:与会话存储(session storage)不同,
localStorage
中存储的数据在浏览器关闭后仍然保持不变。这意味着它提供了持久性存储,数据会一直存在,直到被显式删除或清除。同源策略:
localStorage
遵循同源策略,它只允许页面访问与自身相同域名、相同端口和相同协议的数据。 4. 同源策略(Same-Origin Policy)是浏览器安全策略的一部分,它是一种安全机制,用于防止不同源的网页间的恶意行为。同源策略规定了浏览器只允许加载同一源(指协议、域名和端口号都相同)的资源,并限制了不同源页面之间的交互。具体来说,同源策略包括以下几个方面:
- 限制跨域资源访问:网页只能向同一域名下的资源发送请求,不能直接访问其他域名下的资源。这意味着如果一个网页想要加载来自不同域名的资源(例如图片、脚本、样式表或数据接口),则需要目标资源所在域名同意跨域访问。
- 限制跨域脚本访问:浏览器不允许脚本从一个域名加载并执行另一个域名的脚本。这防止了恶意网站利用跨域脚本执行跨站点请求伪造(CSRF)等攻击。
- 限制跨域 Cookie 访问:Cookie 也受到同源策略的限制。浏览器只会发送同一域名下的 Cookie,不会发送给其他域名的请求,从而保护用户的隐私信息。
同源策略的目的是保护用户的数据安全和隐私,防止恶意网站窃取用户的敏感信息或进行其他恶意行为。虽然同源策略限制了跨域资源的访问,但同时也提供了一些安全性和隐私保护的好处。然而,对于需要进行跨域资源共享的情况,例如一些 API 请求或者第三方资源加载,可以通过 CORS(跨域资源共享)等机制来进行合理的跨域访问控制。
数据类型:
localStorage
只能存储字符串类型的数据。如果需要存储其他类型的数据(如对象或数组),你需要先将它们转换为字符串形式,然后在存储和检索时再进行相应的转换。容量限制:
localStorage
存储的数据量通常限制在几 MB,具体取决于不同浏览器的实现和设置。不会被发送到服务器:
localStorage
中的数据仅存储在用户的本地浏览器中,并不会自动发送到服务器。这使得它适合存储用户偏好设置、会话状态、缓存数据等不需要与服务器交互的信息。简单易用:
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。这样两个设置就统一起来了。
继续阅读:
转载自:https://juejin.cn/post/7350571075548676146