惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

罗磊的独立博客
大猫的无限游戏
大猫的无限游戏
WordPress大学
WordPress大学
酷 壳 – CoolShell
酷 壳 – CoolShell
T
Tailwind CSS Blog
Engineering at Meta
Engineering at Meta
MongoDB | Blog
MongoDB | Blog
爱范儿
爱范儿
小众软件
小众软件
MyScale Blog
MyScale Blog
美团技术团队
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
S
SegmentFault 最新的问题
G
Google Developers Blog
Stack Overflow Blog
Stack Overflow Blog
V
V2EX
量子位
云风的 BLOG
云风的 BLOG
A
About on SuperTechFans
阮一峰的网络日志
阮一峰的网络日志
Last Week in AI
Last Week in AI
Martin Fowler
Martin Fowler
C
Check Point Blog
月光博客
月光博客

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Building a Search Bar for Your Firefox New Tab Extension
Weather Cloc · 2026-05-04 · via DEV Community

Weather Clock Dash

Building a Search Bar for Your Firefox New Tab Extension

A search bar is the highest-ROI feature for any new tab extension. Users type queries dozens of times per day — if your extension can save them from navigating to google.com first, that's real value.

Here's how I built the search bar in the Weather & Clock Dashboard extension.

The Basic HTML

<form id="search-form" class="search-form" role="search">
  <div class="search-wrapper">
    <svg class="search-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor">
      <circle cx="11" cy="11" r="8"/>
      <path d="m21 21-4.35-4.35"/>
    </svg>
    <input 
      type="search" 
      id="search-input"
      class="search-input"
      placeholder="Search the web..."
      autocomplete="off"
      spellcheck="false"
      autofocus
    />
  </div>
</form>

Enter fullscreen mode Exit fullscreen mode

Handling Form Submission

The key: submit to the right search engine based on user preference.

const searchEngines = {
  google: 'https://www.google.com/search?q=',
  bing: 'https://www.bing.com/search?q=',
  duckduckgo: 'https://duckduckgo.com/?q=',
  brave: 'https://search.brave.com/search?q=',
  ecosia: 'https://www.ecosia.org/search?q=',
};

document.getElementById('search-form').addEventListener('submit', async (e) => {
  e.preventDefault();

  const query = document.getElementById('search-input').value.trim();
  if (!query) return;

  // Check if it's a URL
  if (isUrl(query)) {
    const url = query.startsWith('http') ? query : `https://${query}`;
    window.location.href = url;
    return;
  }

  // Get preferred search engine
  const { searchEngine = 'google' } = await browser.storage.local.get('searchEngine');
  const baseUrl = searchEngines[searchEngine] || searchEngines.google;

  window.location.href = baseUrl + encodeURIComponent(query);
});

function isUrl(text) {
  // Basic URL detection: has a dot and no spaces, or starts with http
  return /^https?:\/\//.test(text) || 
         (/\.[a-z]{2,}/.test(text) && !text.includes(' '));
}

Enter fullscreen mode Exit fullscreen mode

Keyboard Shortcut to Focus Search

document.addEventListener('keydown', (e) => {
  // Focus search on '/' key (like many web apps)
  if (e.key === '/' && document.activeElement !== document.getElementById('search-input')) {
    e.preventDefault();
    document.getElementById('search-input').focus();
    document.getElementById('search-input').select();
  }

  // Clear on Escape
  if (e.key === 'Escape') {
    const input = document.getElementById('search-input');
    if (document.activeElement === input) {
      input.blur();
      input.value = '';
    }
  }
});

Enter fullscreen mode Exit fullscreen mode

Search Suggestions with OpenSearch

For basic suggestions (Google supports this via their suggest API):

async function getSuggestions(query) {
  if (query.length < 2) return [];

  try {
    // Google suggest API
    const url = `https://suggestqueries.google.com/complete/search?client=firefox&q=${encodeURIComponent(query)}`;
    const response = await fetch(url);
    const data = await response.json();
    return data[1] || []; // [query, [suggestions]]
  } catch {
    return [];
  }
}

let suggestTimeout;
document.getElementById('search-input').addEventListener('input', (e) => {
  clearTimeout(suggestTimeout);
  const query = e.target.value.trim();

  if (!query) {
    hideSuggestions();
    return;
  }

  suggestTimeout = setTimeout(async () => {
    const suggestions = await getSuggestions(query);
    showSuggestions(suggestions, query);
  }, 200);
});

function showSuggestions(suggestions, query) {
  const list = document.getElementById('suggestions-list');
  if (!suggestions.length) { hideSuggestions(); return; }

  list.innerHTML = suggestions.slice(0, 6).map(s => `
    <li class="suggestion" data-query="${escapeHtml(s)}">
      <svg class="search-icon-sm">...</svg>
      ${highlightMatch(s, query)}
    </li>
  `).join('');

  list.classList.remove('hidden');
}

function hideSuggestions() {
  document.getElementById('suggestions-list').classList.add('hidden');
}

Enter fullscreen mode Exit fullscreen mode

Search Engine Switcher

Allow users to pick their search engine:

const engineOptions = [
  { id: 'google', name: 'Google', favicon: 'https://google.com/favicon.ico' },
  { id: 'duckduckgo', name: 'DuckDuckGo', favicon: 'https://duckduckgo.com/favicon.ico' },
  { id: 'bing', name: 'Bing', favicon: 'https://bing.com/favicon.ico' },
  { id: 'brave', name: 'Brave', favicon: 'https://brave.com/favicon.ico' },
];

async function buildEngineSelector() {
  const { searchEngine = 'google' } = await browser.storage.local.get('searchEngine');

  const selector = document.getElementById('engine-selector');
  selector.innerHTML = engineOptions.map(e => `
    <button class="engine-btn ${e.id === searchEngine ? 'active' : ''}" data-engine="${e.id}">
      <img src="${e.favicon}" alt="${e.name}" width="16" height="16">
    </button>
  `).join('');

  selector.addEventListener('click', async (event) => {
    const btn = event.target.closest('.engine-btn');
    if (!btn) return;

    const newEngine = btn.dataset.engine;
    await browser.storage.local.set({ searchEngine: newEngine });

    // Update active state
    selector.querySelectorAll('.engine-btn').forEach(b => b.classList.remove('active'));
    btn.classList.add('active');

    // Focus search input
    document.getElementById('search-input').focus();
  });
}

Enter fullscreen mode Exit fullscreen mode

CSS

.search-form {
  width: 100%;
  max-width: 600px;
  margin: 0 auto;
}

.search-wrapper {
  position: relative;
  display: flex;
  align-items: center;
}

.search-input {
  width: 100%;
  padding: 12px 16px 12px 44px;
  font-size: 16px;
  border: none;
  border-radius: 24px;
  background: rgba(255, 255, 255, 0.15);
  color: white;
  backdrop-filter: blur(10px);
  transition: background 0.2s, box-shadow 0.2s;
}

.search-input:focus {
  outline: none;
  background: rgba(255, 255, 255, 0.25);
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.3);
}

.search-input::placeholder {
  color: rgba(255, 255, 255, 0.5);
}

.search-icon {
  position: absolute;
  left: 14px;
  width: 18px;
  height: 18px;
  color: rgba(255, 255, 255, 0.6);
  pointer-events: none;
}

/* Remove browser's default search cancel button */
.search-input::-webkit-search-cancel-button {
  display: none;
}

Enter fullscreen mode Exit fullscreen mode


The search bar in Weather & Clock Dashboard supports Google, Bing, DuckDuckGo, and Brave Search with a quick toggle.

firefox #javascript #ux #browserextension #webdev