🔍HTML多引擎搜索工具
2025-3-13
| 2025-6-8
字数 2346阅读时长≈ 6 分钟
🤖
AI总结 这篇文章介绍了SimpleSearch,一个简洁的纯HTML代码的多搜索引擎导航工具,集成了必应的每日壁纸。文章提供了应用的结构和主要代码,包括index.html和相关的资源文件。用户可以通过在app.js中添加新的搜索引擎和书签来自定义工具。具体代码示例也被展示。
English Version
This article introduces SimpleSearch, A simple multi-search engine navigation tool written in pure HTML, which integrates Bing's daily wallpaper. The article provides the structure of the application and the main code, including index.html and related resource files. Users can customize the tool by adding new search engines and bookmarks in app.js. Specific code examples are also demonstrated.

SimpleSearch - 多引擎搜索工具

一个简洁的多搜索引擎导航工具,搭配必应每日壁纸

在线预览

应用截图

notion image

版本历史

你可以通过 app.js 文件下的 APP_VERSION 查看。
  • v1.0.0: 首次发布,包含截图所示的基本功能
  • v1.1.0: 增加了对更多搜索引擎的支持
  • v2.0.0: 将书签和搜索引擎更改为动态生成

Github仓库

SimpleSearch
es-vUpdated May 12, 2025

项目结构

plain

ndex.html # 主要页面 Dockerfile # Dockerfile nginx.conf # Nginx Conf worker.js # 支持cloudflare workers部署 assets/ # 静态文件 img/ # icon文件 web.svg ... js/ app.js # 主要应用逻辑
Plain text
💡
提示:应用已更新,下述代码已废弃

主要代码

(1)SimpleSearch/index.html

plain

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Search Navigation</title> <!-- Flowbite and Tailwind CSS --> <link href="<https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.2.0/flowbite.min.css>" rel="stylesheet" /> <script src="<https://cdn.tailwindcss.com>"></script> <script> tailwind.config = { theme: { extend: { colors: { primary: { "50": "#eff6ff", "100": "#dbeafe", "200": "#bfdbfe", "300": "#93c5fd", "400": "#60a5fa", "500": "#3b82f6", "600": "#2563eb", "700": "#1d4ed8", "800": "#1e40af", "900": "#1e3a8a", "950": "#172554" } } }, fontFamily: { 'body': [ 'Inter', 'ui-sans-serif', 'system-ui', 'sans-serif' ], 'sans': [ 'Inter', 'ui-sans-serif', 'system-ui', 'sans-serif' ] } } } </script> <style> .search-gradient { background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #ec4899 100%); -webkit-background-clip: text; background-clip: text; color: transparent; } .card-hover { transition: transform 0.3s ease, box-shadow 0.3s ease; } .card-hover:hover { transform: translateY(-5px); box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); } .engine-logo { width: 24px; height: 24px; margin-right: 8px; } </style> </head> <body class="bg-gray-50 min-h-screen font-body"> <div class="container mx-auto px-4 py-8"> <!-- Header and Navigation Bar --> <header class="mb-8"> <div class="flex flex-col md:flex-row justify-between items-center"> <h1 class="text-2xl font-bold mb-4 md:mb-0 text-gray-800"> <span class="search-gradient">Simple</span>Search </h1> <nav class="w-full md:w-auto"> <div class="flex flex-col md:flex-row md:space-x-4"> <button id="homeBtn" class="px-4 py-2 text-gray-700 rounded-lg hover:bg-gray-100 mb-2 md:mb-0">Home</button> </div> </nav> </div> </header> <!-- Main Search Form --> <div class="bg-white rounded-lg shadow-lg p-6 mb-8 transform transition-all"> <form id="searchForm" class="space-y-6"> <div class="relative"> <div class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none"> <svg class="w-5 h-5 text-primary-600 search-gradient" aria-hidden="true" xmlns="<http://www.w3.org/2000/svg>" fill="none" viewBox="0 0 20 20"> <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z" /> </svg> </div> <input type="search" id="searchInput" class="block w-full p-4 ps-10 text-lg rounded-lg border border-gray-300 focus:ring-primary-500 focus:border-primary-500" placeholder="Search anything..." required> <button type="submit" class="absolute end-2.5 bottom-2.5 bg-primary-600 hover:bg-primary-700 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-md px-5 py-2 text-white">Search</button> </div> <!-- Search Engine Selector --> <div class="flex flex-wrap gap-3 justify-center" id="engineSelector"> <button type="button" class="engine-button px-4 py-2 flex items-center justify-center rounded-full bg-white border border-gray-300 shadow-sm hover:shadow-md transition-shadow" data-engine="google"> <img src="./assets/img/google.ico" alt="Google" class="engine-logo"> <span>Google</span> </button> <button type="button" class="engine-button px-4 py-2 flex items-center justify-center rounded-full bg-white border border-gray-300 shadow-sm hover:shadow-md transition-shadow" data-engine="bing"> <img src="./assets/img/bing.ico" alt="Bing" class="engine-logo"> <span>Bing</span> </button> <button type="button" class="engine-button px-4 py-2 flex items-center justify-center rounded-full bg-white border border-gray-300 shadow-sm hover:shadow-md transition-shadow" data-engine="baidu"> <img src="./assets/img/baidu.ico" alt="Baidu" class="engine-logo"> <span>Baidu</span> </button> <button type="button" class="engine-button px-4 py-2 flex items-center justify-center rounded-full bg-white border border-gray-300 shadow-sm hover:shadow-md transition-shadow" data-engine="duckduckgo"> <img src="./assets/img/duckduckgo.ico" alt="DuckDuckGo" class="engine-logo"> <span>DuckDuckGo</span> </button> <button type="button" class="engine-button px-4 py-2 flex items-center justify-center rounded-full bg-white border border-gray-300 shadow-sm hover:shadow-md transition-shadow" data-engine="searxng"> <img src="./assets/img/searxng.png" alt="SearXNG" class="engine-logo"> <span>SearXNG</span> </button> </div> </form> </div> <!-- Quick Bookmarks Section --> <div class="mb-8"> <h2 class="text-xl font-semibold mb-4 text-gray-700">Quick Bookmarks</h2> <div class="mb-4 bookmark-category"> <div class="flex items-center cursor-pointer mb-2 category-header" data-category="tech"> <svg xmlns="<http://www.w3.org/2000/svg>" class="h-5 w-5 mr-2 category-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z" /> </svg> <h3 class="font-medium text-gray-800">Technical Website</h3> </div> <div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-3 category-content" id="techBookmarks"> <div class="bg-white rounded-lg shadow-sm p-3 card-hover mini-bookmark"> <a href="<https://github.com>" target="_blank" class="flex flex-col items-center justify-center"> <img src="<https://github.com/favicon.ico>" alt="GitHub" class="w-8 h-8 mb-2"> <span class="text-xs font-medium text-gray-800">GitHub</span> </a> </div> <div class="bg-white rounded-lg shadow-sm p-3 card-hover mini-bookmark"> <a href="<https://linux.do>" target="_blank" class="flex flex-col items-center justify-center"> <img src="<https://linux.do/uploads/default/optimized/3X/9/d/9dd49731091ce8656e94433a26a3ef36062b3994_2_32x32.png>" alt="LinuxDO" class="w-8 h-8 mb-2"> <span class="text-xs font-medium text-gray-800">LinuxDO</span> </a> </div> <div class="bg-white rounded-lg shadow-sm p-3 card-hover mini-bookmark"> <a href="<https://www.52pojie.cn>" target="_blank" class="flex flex-col items-center justify-center"> <img src="<https://www.52pojie.cn/favicon.ico>" alt="52破解" class="w-8 h-8 mb-2"> <span class="text-xs font-medium text-gray-800">52Pojie</span> </a> </div> <div class="bg-white rounded-lg shadow-sm p-3 card-hover mini-bookmark"> <a href="<https://www.zhihu.com>" target="_blank" class="flex flex-col items-center justify-center"> <img src="<https://www.zhihu.com/favicon.ico>" alt="知乎" class="w-8 h-8 mb-2"> <span class="text-xs font-medium text-gray-800">Zhihu</span> </a> </div> <div class="bg-white rounded-lg shadow-sm p-3 card-hover mini-bookmark"> <a href="<https://v2ex.com>" target="_blank" class="flex flex-col items-center justify-center"> <img src="<https://v2ex.com/favicon.ico>" alt="V2EX" class="w-8 h-8 mb-2"> <span class="text-xs font-medium text-gray-800">V2EX</span> </a> </div> </div> </div> <div class="mb-4 bookmark-category"> <div class="flex items-center cursor-pointer mb-2 category-header" data-category="tools"> <svg xmlns="<http://www.w3.org/2000/svg>" class="h-5 w-5 mr-2 category-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /> </svg> <h3 class="font-medium text-gray-800">Common Tools</h3> </div> <div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-3 category-content" id="toolsBookmarks"> </div> </div> <div class="mb-4 bookmark-category"> <div class="flex items-center cursor-pointer mb-2 category-header" data-category="social"> <svg xmlns="<http://www.w3.org/2000/svg>" class="h-5 w-5 mr-2 category-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8h2a2 2 0 012 2v6a2 2 0 01-2 2h-2v4l-4-4H9a1.994 1.994 0 01-1.414-.586m0 0L11 14h4a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2v4l.586-.586z" /> </svg> <h3 class="font-medium text-gray-800">Social Media</h3> </div> <div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-3 category-content" id="socialBookmarks"> </div> </div> </div> </div> <!-- Flowbite JS --> <script src="<https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.2.0/flowbite.min.js>"></script> <!-- Application JS --> <script src="./assets/js/app.js"></script> </body> </html>
Plain text

(2)SimpleSearch/assets/js/app.js

plain

// DOM Elements const searchForm = document.getElementById('searchForm'); const searchInput = document.getElementById('searchInput'); const engineSelector = document.getElementById('engineSelector'); const engineButtons = document.querySelectorAll('.engine-button'); const homeBtn = document.getElementById('homeBtn'); const body = document.body; // App State let currentEngine = localStorage.getItem('defaultEngine') || 'google'; // Search engine configurations const searchEngines = { google: { name: 'Google', url: '<https://www.google.com/search>', paramName: 'q', logo: '<https://www.google.com/favicon.ico>' }, bing: { name: 'Bing', url: '<https://www.bing.com/search>', paramName: 'q', logo: '<https://www.bing.com/favicon.ico>' }, baidu: { name: 'Baidu', url: '<https://www.baidu.com/s>', paramName: 'wd', logo: '<https://www.baidu.com/favicon.ico>' }, duckduckgo: { name: 'DuckDuckGo', url: '<https://duckduckgo.com/>', paramName: 'q', logo: '<https://duckduckgo.com/favicon.ico>' }, searxng: { name: 'SearXNG', url: '<https://search.vsar.site/search>', paramName: 'q', logo: '<https://search.vsar.site/favicon.ico>' } }; // Initialize app function init() { loadDefaultEngine(); highlightSelectedEngine(); setDailyBingBackground(); setupTransparentUI(); setupBookmarkCategories(); } // Load default engine from local storage function loadDefaultEngine() { const defaultEngine = localStorage.getItem('defaultEngine') || 'google'; currentEngine = defaultEngine; } // Set daily Bing background function setDailyBingBackground() { // Fetch Bing daily image const bingImageUrl = `https://www.bing.com/HPImageArchive.aspx?format=js&idx=1&n=1`; // Use a proxy to avoid CORS issues fetch(`https://api.allorigins.win/get?url=${encodeURIComponent(bingImageUrl)}`) .then(response => response.json()) .then(data => { const bingData = JSON.parse(data.contents); if (bingData && bingData.images && bingData.images.length > 0) { const imageData = bingData.images[0]; const imageUrl = `https://www.bing.com${imageData.url}`; // Set as background document.body.style.backgroundImage = `url('${imageUrl}')`; document.body.style.backgroundSize = 'cover'; document.body.style.backgroundPosition = 'center'; document.body.style.backgroundRepeat = 'no-repeat'; document.body.style.backgroundAttachment = 'fixed'; } else { // Fallback to a gradient if Bing image can't be fetched document.body.style.background = 'linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d)'; } }) .catch(error => { console.error('Error fetching Bing background:', error); // Fallback background document.body.style.background = 'linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d)'; }); } // Make UI elements transparent function setupTransparentUI() { // Add glass morphism effect to main containers const mainContainers = document.querySelectorAll('.bg-white'); mainContainers.forEach(container => { container.classList.remove('bg-white'); container.classList.add('glass-morphism'); }); // Add glass morphism to bookmark cards const bookmarkCards = document.querySelectorAll('.card-hover'); bookmarkCards.forEach(card => { card.classList.add('glass-bookmark'); }); // Style the search form for transparency const searchContainer = document.querySelector('#searchForm').parentElement; searchContainer.classList.add('search-container-glass'); // Style search input for transparency const searchInputElem = document.querySelector('#searchInput'); searchInputElem.classList.add('glass-input'); // Apply glass effect to engine buttons const engineButtonElements = document.querySelectorAll('.engine-button'); engineButtonElements.forEach(btn => { btn.classList.add('glass-button'); }); // Add CSS rules for glass morphism const style = document.createElement('style'); style.textContent = ` .glass-morphism { background: rgba(255, 255, 255, 0.2) !important; backdrop-filter: blur(8px) !important; -webkit-backdrop-filter: blur(8px) !important; border: 1px solid rgba(255, 255, 255, 0.3) !important; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1) !important; } .glass-bookmark { background: rgba(255, 255, 255, 0.25) !important; backdrop-filter: blur(10px) !important; -webkit-backdrop-filter: blur(10px) !important; border: 1px solid rgba(255, 255, 255, 0.3) !important; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1) !important; transition: all 0.3s ease !important; } .glass-bookmark:hover { background: rgba(255, 255, 255, 0.35) !important; transform: translateY(-5px) !important; box-shadow: 0 15px 35px rgba(0, 0, 0, 0.2) !important; } .search-container-glass { background: rgba(255, 255, 255, 0.15) !important; backdrop-filter: blur(12px) !important; -webkit-backdrop-filter: blur(12px) !important; border-radius: 16px !important; border: 1px solid rgba(255, 255, 255, 0.2) !important; box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1) !important; padding: 24px !important; } .glass-input { background: rgba(255, 255, 255, 0.2) !important; border: 1px solid rgba(255, 255, 255, 0.3) !important; color: rgba(0, 0, 0, 0.8) !important; backdrop-filter: blur(4px) !important; -webkit-backdrop-filter: blur(4px) !important; } .glass-input::placeholder { color: rgba(0, 0, 0, 0.5) !important; } .glass-button { background: rgba(255, 255, 255, 0.25) !important; backdrop-filter: blur(4px) !important; -webkit-backdrop-filter: blur(4px) !important; border: 1px solid rgba(255, 255, 255, 0.3) !important; transition: all 0.3s ease !important; } .glass-button:hover { background: rgba(255, 255, 255, 0.4) !important; transform: translateY(-2px) !important; box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1) !important; } .container { max-width: 900px !important; margin: 0 auto !important; } body { font-family: 'Inter', sans-serif !important; color: white !important; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2) !important; height: 100vh !important; overflow-x: hidden !important; } h1, h2, h3, h4, h5, h6 { color: white !important; text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3) !important; } a { color: rgba(255, 255, 255, 0.8) !important; transition: color 0.3s ease !important; } a:hover { color: white !important; text-shadow: 0 0 8px rgba(255, 255, 255, 0.5) !important; } .text-gray-600, .text-gray-700, .text-gray-800 { color: rgba(255, 255, 255, 0.9) !important; } .mini-bookmark { background: rgba(255, 255, 255, 0.2) !important; backdrop-filter: blur(10px) !important; -webkit-backdrop-filter: blur(10px) !important; border: 1px solid rgba(255, 255, 255, 0.3) !important; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1) !important; transition: all 0.3s ease !important; } .mini-bookmark:hover { background: rgba(255, 255, 255, 0.3) !important; transform: translateY(-3px) !important; box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15) !important; } .category-header { padding: 8px 12px; border-radius: 8px; background: rgba(255, 255, 255, 0.15) !important; backdrop-filter: blur(8px) !important; -webkit-backdrop-filter: blur(8px) !important; transition: all 0.3s ease !important; } .category-header:hover { background: rgba(255, 255, 255, 0.25) !important; } .category-content { transition: all 0.3s ease; } `; document.head.appendChild(style); } // Highlight the currently selected search engine function highlightSelectedEngine() { engineButtons.forEach(button => { const engine = button.getAttribute('data-engine'); if (engine === currentEngine) { button.classList.add('ring-2', 'ring-white', 'bg-white/30'); } else { button.classList.remove('ring-2', 'ring-white', 'bg-white/30'); } }); } // Bookmark Category Collapse/Expand Function function setupBookmarkCategories() { const categoryHeaders = document.querySelectorAll('.category-header'); categoryHeaders.forEach(header => { header.addEventListener('click', function() { const category = this.getAttribute('data-category'); const content = this.nextElementSibling; const icon = this.querySelector('.category-icon'); if (content.style.display === 'none') { content.style.display = 'grid'; icon.style.transform = 'rotate(0deg)'; localStorage.setItem(`category_${category}`, 'open'); } else { content.style.display = 'none'; icon.style.transform = 'rotate(-90deg)'; localStorage.setItem(`category_${category}`, 'closed'); } }); // Initial state: expanded based on local storage or default settings. const category = header.getAttribute('data-category'); const savedState = localStorage.getItem(`category_${category}`); const content = header.nextElementSibling; const icon = header.querySelector('.category-icon'); if (savedState === 'closed') { content.style.display = 'none'; icon.style.transform = 'rotate(-90deg)'; } else { content.style.display = 'grid'; icon.style.transform = 'rotate(0deg)'; } icon.style.transition = 'transform 0.3s ease'; }); } // Handle search form submission searchForm.addEventListener('submit', function(e) { e.preventDefault(); const query = searchInput.value.trim(); if (!query) return; const engine = searchEngines[currentEngine]; const url = new URL(engine.url); url.searchParams.append(engine.paramName, query); window.open(url.toString(), '_blank'); }); // Handle search engine selection engineSelector.addEventListener('click', function(e) { const button = e.target.closest('.engine-button'); if (button) { currentEngine = button.getAttribute('data-engine'); localStorage.setItem('defaultEngine', currentEngine); highlightSelectedEngine(); } }); // Home button functionality if (homeBtn) { homeBtn.addEventListener('click', function() { window.location.reload(); }); } // Add keyboard shortcuts for enhanced UX document.addEventListener('keydown', function(e) { // Alt+S to focus search input if (e.altKey && e.key === 's') { e.preventDefault(); searchInput.focus(); } }); // Prevent form resubmission on page refresh if (window.history.replaceState) { window.history.replaceState(null, null, window.location.href); } // Initialize the application when DOM is fully loaded document.addEventListener('DOMContentLoaded', function() { init(); });
Plain text

自定义设置

(1)添加新的搜索引擎

app.js中的searchEngines对象添加新的搜索引擎配置:

javascript

const searchEngines = { // ...现有引擎 newengine: { name: '引擎名称', url: '搜索URL', paramName: '查询参数名', logo: '图标URL' } };
JavaScript
然后在index.html中的引擎选择器区域添加对应按钮。

(2)添加新的书签

在相应的书签分类下添加新的书签项:

html

<div class="bg-white rounded-lg shadow-sm p-3 card-hover mini-bookmark"> <a href="<https://example.com>" target="_blank" class="flex flex-col items-center justify-center"> <img src="<https://example.com/favicon.ico>" alt="网站名称" class="w-8 h-8 mb-2"> <span class="text-xs font-medium text-gray-800">网站名称</span> </a> </div>
HTML
  • 工具
  • 网络
  • Deno.com 服务DIfy介绍与部署
    Loading...