自定义导航网站(二)

上篇文章讲到了如何实现动态添加网页,这个功能和书签的功能差不多。

自定义导航网站可以比书签更好用一点,比如有一些经常访问的网站,前缀是一致的,URL 只是尾部略有不同。

举个不是很恰当的例子:使用百度搜索 test,点击搜索按钮后,打开的链接格式类似这样:https://www.baidu.com/s?wd=test。无论搜什么,前缀都是 https://www.baidu.com/s?wd=,只有尾部的搜索内容略有不同。

我们可以把这个前缀保存起来,在后面加个输入框,以后只用输入尾部的内容,点击回车就能打开前缀+输入内容拼接而成的链接了。

效果图

这个功能在实际工作中比较实用,比如切换不同的项目,或访问内网资源。考虑到项目的安全策略,所以本文以百度为例。

代码实现

本文基于上一篇文章的代码修改。

提取 pages.js

首先为了适当解耦,我们把脚本部分提到一个单独的 js 文件中,命名为 pages.js:

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

仿写 custom_links.js

照猫画虎,再写一个 custom_links.js 文件,实现添加前缀,删除前缀,展示前缀,清空/导入/导出前缀:

js 复制代码
// Load custom links when the page loads
loadCustomLinks();
// Function to load custom links from localStorage
function loadCustomLinks() {
    const customSavedCustomPages = JSON.parse(localStorage.getItem('savedCustomPages'));
    const customLinksList = document.getElementById('customLinksList');

    if (customSavedCustomPages && customSavedCustomPages.length > 0) {
        customLinksList.innerHTML = '';
        customSavedCustomPages.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();
                openCustomPage(page.link);
            };

            const input = document.createElement('input');
            input.onkeydown = function (event) {
                if (event.key === 'Enter') {
                    openCustomPage(page.link + input.value);
                }
            }

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

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

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

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

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

    const customPageName = document.getElementById('customPageName').value;
    const pagePrefix = document.getElementById('pagePrefix').value;

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

        loadCustomLinks();

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

// Function to clear all pages
function clearAllCustomPages() {
    localStorage.removeItem('savedCustomPages');
    loadCustomLinks();
}

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

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

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

引用两个 js 文件

然后在 html 文件中(不妨将其命名为 navigation.html),再抄一份表单,引用前两个 js 文件:

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>

    <hr>

    <h2>Add Custom Link</h2>
    <form id="addCustomPageForm">
        <label for="customPageName">Page Name:</label>
        <input type="text" id="customPageName" name="customPageName" required>
        <label for="pagePrefix">Page Prefix:</label>
        <input type="text" id="pagePrefix" name="pagePrefix" required>
        <button type="submit">Submit</button>
    </form>
    <h2>Saved Pages</h2>
    <ul id="customLinksList">
        <!-- Links will be dynamically added here -->
    </ul>
    <button onclick="clearAllCustomPages()">Clear All</button>
    <button onclick="exportAllCustomPages()">Export All</button><br><br>
    <input type="file" id="customFileInput">
    <button onclick="importCustomPagesFile()">Import</button>

    <script src="pages.js"></script>
    <script src="custom_links.js"></script>


</body>

这样就实现了自定义链接前缀的功能。

完整代码已上传至 github:github.com/KevinWangJc...

相关推荐
利刃大大31 分钟前
【在线五子棋对战】五、前端扫盲:html && css && javascript && ajax && jquery && websocket
前端·javascript·html
安心不心安1 小时前
React状态管理——zustand
javascript·react.js·ecmascript
啪叽1 小时前
探索鲜为人知的浏览器API:document.currentScript的实用案例
前端·javascript·dom
我是谁谁1 小时前
Vue3 组合式 API 核心宏详解:defineProps、defineEmits、defineExpose
javascript·vue.js
然我2 小时前
从 Callback 地狱到 Promise:手撕 JavaScript 异步编程核心
前端·javascript·html
sg_knight2 小时前
Rollup vs Webpack 深度对比:前端构建工具终极指南
前端·javascript·webpack·node.js·vue·rollup·vite
穗余2 小时前
WEB3全栈开发——面试专业技能点P3JavaScript / TypeScript
前端·javascript·typescript
前端南玖3 小时前
Vue3响应式核心:ref vs reactive深度对比
前端·javascript·vue.js
微笑边缘的金元宝3 小时前
svg实现3环进度图,可动态调节进度数值,(vue)
前端·javascript·vue.js·svg