Searchable FAQ Component
An advanced searchable FAQ component with real-time filtering, search suggestions, and smart search capabilities
Responsive Design
Yes
Dark Mode Support
No
lines
344
Browser Compatibility
No
Live Preview
Interact with the component without leaving the page.
Searchable FAQ Component
An advanced searchable FAQ component with real-time filtering, search suggestions, smart search capabilities, and comprehensive search analytics.
Features
- Real-time Search: Instant filtering as you type with debounced input
- Smart Search: Fuzzy matching and typo tolerance for better results
- Search Suggestions: Auto-complete with popular search terms
- Advanced Filters: Category, date, and relevance filtering options
- Search History: Recent searches with quick access
- Highlighted Results: Search term highlighting in questions and answers
- Search Analytics: Track popular searches and user behavior
- Keyboard Navigation: Full keyboard support for search and results
- Voice Search: Speech-to-text search capability
- Export Results: Save search results as PDF or text
- Responsive Design: Works perfectly on all device sizes
- Accessibility: Full ARIA support and screen reader compatibility
HTML Structure
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Searchable FAQ Component</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="searchable-faq-container">
<!-- Header -->
<div class="faq-header">
<h1>Searchable FAQ</h1>
<p>Find answers quickly with our intelligent search system</p>
</div>
<!-- Search Section -->
<div class="search-section">
<div class="search-container">
<div class="search-input-wrapper">
<svg class="search-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<circle cx="11" cy="11" r="8"></circle>
<path d="m21 21-4.35-4.35"></path>
</svg>
<input type="text"
class="search-input"
placeholder="Search questions, answers, or topics..."
autocomplete="off"
aria-label="Search FAQ">
<button class="voice-search-btn"
aria-label="Voice search"
title="Voice search">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"></path>
<path d="M19 10v2a7 7 0 0 1-14 0v-2"></path>
<line x1="12" y1="19" x2="12" y2="23"></line>
<line x1="8" y1="23" x2="16" y2="23"></line>
</svg>
</button>
<button class="clear-search-btn"
aria-label="Clear search"
title="Clear search"
style="display: none;">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
</div>
<!-- Search Suggestions -->
<div class="search-suggestions" style="display: none;">
<div class="suggestions-header">
<span>Popular searches</span>
</div>
<div class="suggestions-list"></div>
</div>
<!-- Search History -->
<div class="search-history" style="display: none;">
<div class="history-header">
<span>Recent searches</span>
<button class="clear-history-btn">Clear</button>
</div>
<div class="history-list"></div>
</div>
</div>
<!-- Advanced Filters -->
<div class="filters-container">
<button class="filters-toggle" aria-expanded="false">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<polygon points="22,3 2,3 10,12.46 10,19 14,21 14,12.46"></polygon>
</svg>
<span>Filters</span>
</button>
<div class="filters-panel" style="display: none;">
<div class="filter-group">
<label>Category</label>
<select class="category-filter">
<option value="">All Categories</option>
<option value="general">General</option>
<option value="billing">Billing</option>
<option value="technical">Technical</option>
<option value="support">Support</option>
</select>
</div>
<div class="filter-group">
<label>Sort by</label>
<select class="sort-filter">
<option value="relevance">Relevance</option>
<option value="date">Date</option>
<option value="popularity">Popularity</option>
<option value="alphabetical">Alphabetical</option>
</select>
</div>
<div class="filter-group">
<label>Show only</label>
<div class="checkbox-group">
<label class="checkbox-label">
<input type="checkbox" class="answered-only">
<span>Answered questions</span>
</label>
<label class="checkbox-label">
<input type="checkbox" class="recent-only">
<span>Recent updates</span>
</label>
</div>
</div>
</div>
</div>
</div>
<!-- Search Results Info -->
<div class="search-results-info" style="display: none;">
<div class="results-count"></div>
<div class="search-time"></div>
<button class="export-results-btn">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="7,10 12,15 17,10"></polyline>
<line x1="12" y1="15" x2="12" y2="3"></line>
</svg>
Export Results
</button>
</div>
<!-- FAQ Content -->
<div class="faq-content">
<!-- No Results Message -->
<div class="no-results" style="display: none;">
<div class="no-results-icon">🔍</div>
<h3>No results found</h3>
<p>Try adjusting your search terms or browse all questions below.</p>
<div class="search-suggestions-inline">
<span>Popular searches:</span>
<div class="suggestion-tags"></div>
</div>
</div>
<!-- FAQ Items -->
<div class="faq-items">
<div class="faq-item" data-category="general" data-keywords="account registration signup create profile">
<div class="faq-question">
<h3>How do I create an account?</h3>
<button class="toggle-btn" aria-expanded="false">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<polyline points="6,9 12,15 18,9"></polyline>
</svg>
</button>
</div>
<div class="faq-answer">
<p>Creating an account is simple and takes just a few minutes. Follow these steps:</p>
<ol>
<li>Click the "Sign Up" button in the top right corner</li>
<li>Enter your email address and create a secure password</li>
<li>Verify your email address by clicking the link we send you</li>
<li>Complete your profile with basic information</li>
<li>Start using all our features immediately</li>
</ol>
<div class="answer-meta">
<span class="last-updated">Last updated: January 15, 2024</span>
<div class="answer-actions">
<button class="helpful-btn" data-helpful="true">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"></path>
</svg>
Helpful
</button>
<button class="not-helpful-btn" data-helpful="false">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"></path>
</svg>
Not helpful
</button>
</div>
</div>
</div>
</div>
<div class="faq-item" data-category="billing" data-keywords="payment methods credit card paypal billing">
<div class="faq-question">
<h3>What payment methods do you accept?</h3>
<button class="toggle-btn" aria-expanded="false">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<polyline points="6,9 12,15 18,9"></polyline>
</svg>
</button>
</div>
<div class="faq-answer">
<p>We accept all major payment methods to make your experience convenient:</p>
<div class="payment-methods">
<div class="payment-category">
<h4>Credit & Debit Cards</h4>
<ul>
<li>Visa</li>
<li>MasterCard</li>
<li>American Express</li>
<li>Discover</li>
</ul>
</div>
<div class="payment-category">
<h4>Digital Wallets</h4>
<ul>
<li>PayPal</li>
<li>Apple Pay</li>
<li>Google Pay</li>
<li>Samsung Pay</li>
</ul>
</div>
</div>
<div class="answer-meta">
<span class="last-updated">Last updated: January 10, 2024</span>
<div class="answer-actions">
<button class="helpful-btn" data-helpful="true">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"></path>
</svg>
Helpful
</button>
<button class="not-helpful-btn" data-helpful="false">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"></path>
</svg>
Not helpful
</button>
</div>
</div>
</div>
</div>
<div class="faq-item" data-category="technical" data-keywords="password reset forgot login security">
<div class="faq-question">
<h3>How do I reset my password?</h3>
<button class="toggle-btn" aria-expanded="false">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<polyline points="6,9 12,15 18,9"></polyline>
</svg>
</button>
</div>
<div class="faq-answer">
<p>If you've forgotten your password, you can easily reset it:</p>
<div class="reset-steps">
<div class="step">
<div class="step-number">1</div>
<div class="step-content">
<h4>Go to Login Page</h4>
<p>Click "Forgot Password" on the login screen</p>
</div>
</div>
<div class="step">
<div class="step-number">2</div>
<div class="step-content">
<h4>Enter Email</h4>
<p>Provide the email address associated with your account</p>
</div>
</div>
<div class="step">
<div class="step-number">3</div>
<div class="step-content">
<h4>Check Email</h4>
<p>Look for a password reset link in your inbox</p>
</div>
</div>
<div class="step">
<div class="step-number">4</div>
<div class="step-content">
<h4>Create New Password</h4>
<p>Follow the link and set a new secure password</p>
</div>
</div>
</div>
<div class="answer-meta">
<span class="last-updated">Last updated: January 18, 2024</span>
<div class="answer-actions">
<button class="helpful-btn" data-helpful="true">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"></path>
</svg>
Helpful
</button>
<button class="not-helpful-btn" data-helpful="false">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"></path>
</svg>
Not helpful
</button>
</div>
</div>
</div>
</div>
<div class="faq-item" data-category="support" data-keywords="contact support help customer service">
<div class="faq-question">
<h3>How can I contact customer support?</h3>
<button class="toggle-btn" aria-expanded="false">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<polyline points="6,9 12,15 18,9"></polyline>
</svg>
</button>
</div>
<div class="faq-answer">
<p>We're here to help! Contact our support team through multiple channels:</p>
<div class="contact-options">
<div class="contact-option">
<div class="contact-icon">📧</div>
<div class="contact-details">
<h4>Email Support</h4>
<p>support@example.com</p>
<span>Response within 24 hours</span>
</div>
</div>
<div class="contact-option">
<div class="contact-icon">💬</div>
<div class="contact-details">
<h4>Live Chat</h4>
<p>Available on our website</p>
<span>Mon-Fri 9AM-6PM EST</span>
</div>
</div>
<div class="contact-option">
<div class="contact-icon">📞</div>
<div class="contact-details">
<h4>Phone Support</h4>
<p>1-800-123-4567</p>
<span>Mon-Fri 9AM-6PM EST</span>
</div>
</div>
</div>
<div class="answer-meta">
<span class="last-updated">Last updated: January 12, 2024</span>
<div class="answer-actions">
<button class="helpful-btn" data-helpful="true">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"></path>
</svg>
Helpful
</button>
<button class="not-helpful-btn" data-helpful="false">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"></path>
</svg>
Not helpful
</button>
</div>
</div>
</div>
</div>
<div class="faq-item" data-category="general" data-keywords="shipping delivery international worldwide">
<div class="faq-question">
<h3>Do you ship internationally?</h3>
<button class="toggle-btn" aria-expanded="false">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<polyline points="6,9 12,15 18,9"></polyline>
</svg>
</button>
</div>
<div class="faq-answer">
<p>Yes! We ship to over 50 countries worldwide. International shipping rates and delivery times vary by destination.</p>
<div class="shipping-info">
<div class="shipping-detail">
<strong>Shipping Costs:</strong>
<span>Calculated at checkout based on weight and destination</span>
</div>
<div class="shipping-detail">
<strong>Delivery Time:</strong>
<span>7-21 business days depending on location</span>
</div>
<div class="shipping-detail">
<strong>Tracking:</strong>
<span>Full tracking provided for all international orders</span>
</div>
</div>
<div class="answer-meta">
<span class="last-updated">Last updated: January 8, 2024</span>
<div class="answer-actions">
<button class="helpful-btn" data-helpful="true">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"></path>
</svg>
Helpful
</button>
<button class="not-helpful-btn" data-helpful="false">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"></path>
</svg>
Not helpful
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>CSS Styles
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.searchable-faq-container {
max-width: 900px;
margin: 0 auto;
background: white;
border-radius: 16px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
/* Header Styles */
.faq-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 40px 30px;
text-align: center;
}
.faq-header h1 {
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 8px;
}
.faq-header p {
font-size: 1.1rem;
opacity: 0.9;
}
/* Search Section */
.search-section {
padding: 30px;
background: #f8fafc;
border-bottom: 1px solid #e2e8f0;
}
.search-container {
position: relative;
margin-bottom: 20px;
}
.search-input-wrapper {
position: relative;
display: flex;
align-items: center;
}
.search-input {
width: 100%;
padding: 16px 20px 16px 50px;
border: 2px solid #e2e8f0;
border-radius: 25px;
font-size: 16px;
background: white;
transition: all 0.3s ease;
}
.search-input:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.search-icon {
position: absolute;
left: 18px;
color: #94a3b8;
z-index: 2;
}
.voice-search-btn,
.clear-search-btn {
position: absolute;
right: 50px;
background: none;
border: none;
padding: 8px;
cursor: pointer;
border-radius: 50%;
transition: all 0.3s ease;
color: #64748b;
}
.clear-search-btn {
right: 15px;
}
.voice-search-btn:hover,
.clear-search-btn:hover {
background: #f1f5f9;
color: #667eea;
}
.voice-search-btn.recording {
color: #ef4444;
animation: pulse 1s infinite;
}
/* Search Suggestions */
.search-suggestions,
.search-history {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: white;
border: 1px solid #e2e8f0;
border-radius: 12px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
z-index: 10;
margin-top: 8px;
}
.suggestions-header,
.history-header {
padding: 12px 20px;
border-bottom: 1px solid #f1f5f9;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 14px;
font-weight: 600;
color: #64748b;
}
.clear-history-btn {
background: none;
border: none;
color: #667eea;
cursor: pointer;
font-size: 12px;
padding: 4px 8px;
border-radius: 4px;
transition: background 0.3s ease;
}
.clear-history-btn:hover {
background: #f1f5f9;
}
.suggestions-list,
.history-list {
max-height: 200px;
overflow-y: auto;
}
.suggestion-item,
.history-item {
padding: 12px 20px;
cursor: pointer;
transition: background 0.3s ease;
display: flex;
align-items: center;
gap: 12px;
}
.suggestion-item:hover,
.history-item:hover {
background: #f8fafc;
}
.suggestion-icon,
.history-icon {
color: #94a3b8;
flex-shrink: 0;
}
/* Filters */
.filters-container {
position: relative;
}
.filters-toggle {
background: white;
border: 2px solid #e2e8f0;
padding: 12px 16px;
border-radius: 12px;
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
font-weight: 500;
color: #64748b;
transition: all 0.3s ease;
}
.filters-toggle:hover {
border-color: #cbd5e1;
background: #f8fafc;
}
.filters-toggle.active {
border-color: #667eea;
color: #667eea;
}
.filters-panel {
position: absolute;
top: 100%;
left: 0;
background: white;
border: 1px solid #e2e8f0;
border-radius: 12px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
padding: 20px;
margin-top: 8px;
min-width: 300px;
z-index: 10;
}
.filter-group {
margin-bottom: 16px;
}
.filter-group:last-child {
margin-bottom: 0;
}
.filter-group label {
display: block;
font-size: 14px;
font-weight: 600;
color: #374151;
margin-bottom: 8px;
}
.filter-group select {
width: 100%;
padding: 8px 12px;
border: 1px solid #d1d5db;
border-radius: 6px;
font-size: 14px;
background: white;
}
.checkbox-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.checkbox-label {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
color: #374151;
cursor: pointer;
}
.checkbox-label input[type="checkbox"] {
margin: 0;
}
/* Search Results Info */
.search-results-info {
padding: 20px 30px;
background: #f8fafc;
border-bottom: 1px solid #e2e8f0;
display: flex;
justify-content: space-between;
align-items: center;
}
.results-count {
font-size: 14px;
color: #64748b;
}
.search-time {
font-size: 12px;
color: #94a3b8;
}
.export-results-btn {
background: #667eea;
color: white;
border: none;
padding: 8px 16px;
border-radius: 6px;
font-size: 14px;
cursor: pointer;
display: flex;
align-items: center;
gap: 6px;
transition: background 0.3s ease;
}
.export-results-btn:hover {
background: #5a67d8;
}
/* FAQ Content */
.faq-content {
padding: 30px;
}
/* No Results */
.no-results {
text-align: center;
padding: 60px 20px;
color: #64748b;
}
.no-results-icon {
font-size: 64px;
margin-bottom: 20px;
opacity: 0.5;
}
.no-results h3 {
font-size: 1.5rem;
color: #374151;
margin-bottom: 8px;
}
.no-results p {
margin-bottom: 24px;
}
.search-suggestions-inline {
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
}
.suggestion-tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
justify-content: center;
}
.suggestion-tag {
background: #f1f5f9;
color: #667eea;
padding: 6px 12px;
border-radius: 16px;
font-size: 14px;
cursor: pointer;
transition: all 0.3s ease;
}
.suggestion-tag:hover {
background: #667eea;
color: white;
}
/* FAQ Items */
.faq-items {
display: flex;
flex-direction: column;
gap: 16px;
}
.faq-item {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 12px;
overflow: hidden;
transition: all 0.3s ease;
}
.faq-item:hover {
border-color: #cbd5e1;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
}
.faq-item.highlighted {
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.faq-question {
padding: 20px 24px;
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
transition: background 0.3s ease;
}
.faq-question:hover {
background: #f1f5f9;
}
.faq-question h3 {
font-size: 1.1rem;
font-weight: 600;
color: #1e293b;
margin: 0;
flex: 1;
}
.toggle-btn {
background: #667eea;
border: none;
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
flex-shrink: 0;
}
.toggle-btn:hover {
background: #5a67d8;
transform: scale(1.05);
}
.toggle-btn svg {
color: white;
transition: transform 0.3s ease;
}
.faq-item.active .toggle-btn svg {
transform: rotate(180deg);
}
.faq-answer {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
background: white;
}
.faq-item.active .faq-answer {
max-height: 1000px;
padding: 0 24px 24px;
}
.faq-answer p {
color: #64748b;
line-height: 1.6;
margin-bottom: 16px;
}
.faq-answer ol {
color: #64748b;
line-height: 1.6;
margin: 16px 0;
padding-left: 20px;
}
.faq-answer li {
margin-bottom: 8px;
}
/* Payment Methods */
.payment-methods {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
margin: 16px 0;
}
.payment-category {
background: #f8fafc;
padding: 16px;
border-radius: 8px;
border-left: 4px solid #667eea;
}
.payment-category h4 {
color: #374151;
margin-bottom: 8px;
font-size: 14px;
}
.payment-category ul {
margin: 0;
padding-left: 16px;
}
.payment-category li {
color: #64748b;
font-size: 14px;
margin-bottom: 4px;
}
/* Reset Steps */
.reset-steps {
margin: 20px 0;
}
.step {
display: flex;
align-items: flex-start;
gap: 16px;
margin-bottom: 20px;
padding: 16px;
background: #f8fafc;
border-radius: 8px;
}
.step-number {
background: #667eea;
color: white;
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 14px;
flex-shrink: 0;
}
.step-content h4 {
color: #374151;
margin-bottom: 4px;
font-size: 16px;
}
.step-content p {
color: #64748b;
margin: 0;
font-size: 14px;
}
/* Contact Options */
.contact-options {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 16px;
margin: 20px 0;
}
.contact-option {
display: flex;
align-items: center;
gap: 16px;
padding: 20px;
background: #f8fafc;
border-radius: 12px;
border: 1px solid #e2e8f0;
transition: all 0.3s ease;
}
.contact-option:hover {
border-color: #667eea;
transform: translateY(-2px);
}
.contact-icon {
font-size: 24px;
flex-shrink: 0;
}
.contact-details h4 {
color: #374151;
margin-bottom: 4px;
font-size: 16px;
}
.contact-details p {
color: #667eea;
font-weight: 600;
margin-bottom: 2px;
font-size: 14px;
}
.contact-details span {
color: #64748b;
font-size: 12px;
}
/* Shipping Info */
.shipping-info {
margin: 16px 0;
}
.shipping-detail {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
background: #f8fafc;
border-radius: 6px;
margin-bottom: 8px;
}
.shipping-detail strong {
color: #374151;
font-size: 14px;
}
.shipping-detail span {
color: #64748b;
font-size: 14px;
}
/* Answer Meta */
.answer-meta {
margin-top: 20px;
padding-top: 16px;
border-top: 1px solid #f1f5f9;
display: flex;
justify-content: space-between;
align-items: center;
}
.last-updated {
font-size: 12px;
color: #94a3b8;
}
.answer-actions {
display: flex;
gap: 8px;
}
.helpful-btn,
.not-helpful-btn {
background: none;
border: 1px solid #e2e8f0;
padding: 6px 12px;
border-radius: 6px;
font-size: 12px;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
transition: all 0.3s ease;
color: #64748b;
}
.helpful-btn:hover {
border-color: #10b981;
color: #10b981;
background: #f0fdf4;
}
.not-helpful-btn:hover {
border-color: #ef4444;
color: #ef4444;
background: #fef2f2;
}
.helpful-btn.voted {
border-color: #10b981;
color: #10b981;
background: #f0fdf4;
}
.not-helpful-btn.voted {
border-color: #ef4444;
color: #ef4444;
background: #fef2f2;
}
/* Search Highlighting */
.search-highlight {
background: #fef08a;
padding: 2px 4px;
border-radius: 3px;
font-weight: 600;
}
/* Animations */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
.faq-item {
animation: fadeIn 0.3s ease;
}
/* Responsive Design */
@media (max-width: 768px) {
body {
padding: 10px;
}
.searchable-faq-container {
border-radius: 12px;
}
.faq-header {
padding: 30px 20px;
}
.faq-header h1 {
font-size: 2rem;
}
.search-section {
padding: 20px;
}
.faq-content {
padding: 20px;
}
.faq-question {
padding: 16px 20px;
}
.faq-item.active .faq-answer {
padding: 0 20px 20px;
}
.filters-panel {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 90%;
max-width: 400px;
}
.payment-methods,
.contact-options {
grid-template-columns: 1fr;
}
.search-results-info {
flex-direction: column;
gap: 12px;
align-items: flex-start;
}
}
@media (max-width: 480px) {
.faq-header h1 {
font-size: 1.75rem;
}
.search-input {
padding: 14px 16px 14px 45px;
font-size: 16px;
}
.voice-search-btn {
right: 45px;
}
.faq-question h3 {
font-size: 1rem;
}
.step {
flex-direction: column;
text-align: center;
}
}JavaScript Functionality
class SearchableFAQ {
constructor() {
this.searchInput = document.querySelector('.search-input');
this.faqItems = document.querySelectorAll('.faq-item');
this.searchSuggestions = document.querySelector('.search-suggestions');
this.searchHistory = document.querySelector('.search-history');
this.filtersToggle = document.querySelector('.filters-toggle');
this.filtersPanel = document.querySelector('.filters-panel');
this.noResults = document.querySelector('.no-results');
this.searchResultsInfo = document.querySelector('.search-results-info');
this.voiceSearchBtn = document.querySelector('.voice-search-btn');
this.clearSearchBtn = document.querySelector('.clear-search-btn');
this.searchTimeout = null;
this.searchHistory = this.loadSearchHistory();
this.popularSearches = ['account', 'payment', 'password', 'shipping', 'support'];
this.isVoiceSearchSupported = 'webkitSpeechRecognition' in window || 'SpeechRecognition' in window;
this.init();
}
init() {
this.bindEvents();
this.setupVoiceSearch();
this.setupKeyboardNavigation();
this.populateSuggestions();
}
bindEvents() {
// Search input events
this.searchInput.addEventListener('input', (e) => {
this.handleSearchInput(e.target.value);
});
this.searchInput.addEventListener('focus', () => {
this.showSearchSuggestions();
});
this.searchInput.addEventListener('blur', (e) => {
// Delay hiding to allow clicking on suggestions
setTimeout(() => {
if (!e.relatedTarget || !e.relatedTarget.closest('.search-suggestions, .search-history')) {
this.hideSearchSuggestions();
}
}, 150);
});
// Clear search button
this.clearSearchBtn.addEventListener('click', () => {
this.clearSearch();
});
// FAQ item toggles
document.addEventListener('click', (e) => {
const faqQuestion = e.target.closest('.faq-question');
if (faqQuestion) {
this.toggleFAQItem(faqQuestion.parentElement);
}
const helpfulBtn = e.target.closest('.helpful-btn, .not-helpful-btn');
if (helpfulBtn) {
this.handleFeedback(helpfulBtn);
}
});
// Filters toggle
this.filtersToggle.addEventListener('click', () => {
this.toggleFilters();
});
// Filter changes
document.addEventListener('change', (e) => {
if (e.target.matches('.category-filter, .sort-filter, .answered-only, .recent-only')) {
this.applyFilters();
}
});
// Export results
document.querySelector('.export-results-btn')?.addEventListener('click', () => {
this.exportResults();
});
// Click outside to close panels
document.addEventListener('click', (e) => {
if (!e.target.closest('.search-container')) {
this.hideSearchSuggestions();
}
if (!e.target.closest('.filters-container')) {
this.hideFilters();
}
});
}
setupVoiceSearch() {
if (!this.isVoiceSearchSupported) {
this.voiceSearchBtn.style.display = 'none';
return;
}
this.voiceSearchBtn.addEventListener('click', () => {
this.startVoiceSearch();
});
}
setupKeyboardNavigation() {
document.addEventListener('keydown', (e) => {
if (e.target === this.searchInput) {
this.handleSearchKeyboard(e);
} else if (e.target.closest('.faq-question')) {
this.handleFAQKeyboard(e);
}
});
}
handleSearchInput(value) {
clearTimeout(this.searchTimeout);
// Show/hide clear button
this.clearSearchBtn.style.display = value ? 'block' : 'none';
// Debounce search
this.searchTimeout = setTimeout(() => {
this.performSearch(value);
}, 300);
// Update suggestions
if (value.length > 0) {
this.updateSearchSuggestions(value);
} else {
this.showSearchSuggestions();
}
}
performSearch(query) {
const startTime = performance.now();
if (!query.trim()) {
this.showAllItems();
this.hideSearchResultsInfo();
return;
}
// Add to search history
this.addToSearchHistory(query);
// Perform fuzzy search
const results = this.fuzzySearch(query);
// Display results
this.displaySearchResults(results, query);
// Show search info
const searchTime = performance.now() - startTime;
this.showSearchResultsInfo(results.length, searchTime, query);
// Track search analytics
this.trackSearch(query, results.length);
}
fuzzySearch(query) {
const searchTerms = query.toLowerCase().split(' ').filter(term => term.length > 0);
const results = [];
this.faqItems.forEach(item => {
const question = item.querySelector('h3').textContent.toLowerCase();
const answer = item.querySelector('.faq-answer').textContent.toLowerCase();
const keywords = item.dataset.keywords?.toLowerCase() || '';
const category = item.dataset.category?.toLowerCase() || '';
const searchText = `${question} ${answer} ${keywords} ${category}`;
let score = 0;
let matchedTerms = 0;
searchTerms.forEach(term => {
// Exact match bonus
if (searchText.includes(term)) {
score += 10;
matchedTerms++;
}
// Fuzzy match
const fuzzyScore = this.calculateFuzzyScore(term, searchText);
if (fuzzyScore > 0.6) {
score += fuzzyScore * 5;
matchedTerms++;
}
// Question title bonus
if (question.includes(term)) {
score += 5;
}
// Keywords bonus
if (keywords.includes(term)) {
score += 3;
}
});
// Require at least one term match
if (matchedTerms > 0) {
results.push({ item, score, matchedTerms });
}
});
// Sort by score and matched terms
return results.sort((a, b) => {
if (a.matchedTerms !== b.matchedTerms) {
return b.matchedTerms - a.matchedTerms;
}
return b.score - a.score;
});
}
calculateFuzzyScore(term, text) {
const words = text.split(' ');
let bestScore = 0;
words.forEach(word => {
const score = this.levenshteinSimilarity(term, word);
if (score > bestScore) {
bestScore = score;
}
});
return bestScore;
}
levenshteinSimilarity(a, b) {
const matrix = [];
for (let i = 0; i <= b.length; i++) {
matrix[i] = [i];
}
for (let j = 0; j <= a.length; j++) {
matrix[0][j] = j;
}
for (let i = 1; i <= b.length; i++) {
for (let j = 1; j <= a.length; j++) {
if (b.charAt(i - 1) === a.charAt(j - 1)) {
matrix[i][j] = matrix[i - 1][j - 1];
} else {
matrix[i][j] = Math.min(
matrix[i - 1][j - 1] + 1,
matrix[i][j - 1] + 1,
matrix[i - 1][j] + 1
);
}
}
}
const maxLength = Math.max(a.length, b.length);
return (maxLength - matrix[b.length][a.length]) / maxLength;
}
displaySearchResults(results, query) {
// Hide all items first
this.faqItems.forEach(item => {
item.style.display = 'none';
item.classList.remove('highlighted');
});
if (results.length === 0) {
this.showNoResults(query);
return;
}
this.hideNoResults();
// Show and highlight matching items
results.forEach(({ item }) => {
item.style.display = 'block';
item.classList.add('highlighted');
this.highlightSearchTerms(item, query);
});
// Apply current filters
this.applyFilters();
}
highlightSearchTerms(item, query) {
const searchTerms = query.toLowerCase().split(' ').filter(term => term.length > 1);
const question = item.querySelector('h3');
const answer = item.querySelector('.faq-answer');
// Highlight in question
this.highlightInElement(question, searchTerms);
// Highlight in answer (only in text nodes)
const textNodes = this.getTextNodes(answer);
textNodes.forEach(node => {
this.highlightInTextNode(node, searchTerms);
});
}
highlightInElement(element, terms) {
let html = element.innerHTML;
terms.forEach(term => {
const regex = new RegExp(`(${this.escapeRegex(term)})`, 'gi');
html = html.replace(regex, '<span class="search-highlight">$1</span>');
});
element.innerHTML = html;
}
highlightInTextNode(node, terms) {
let text = node.textContent;
let hasHighlight = false;
terms.forEach(term => {
const regex = new RegExp(`(${this.escapeRegex(term)})`, 'gi');
if (regex.test(text)) {
text = text.replace(regex, '<span class="search-highlight">$1</span>');
hasHighlight = true;
}
});
if (hasHighlight) {
const wrapper = document.createElement('span');
wrapper.innerHTML = text;
node.parentNode.replaceChild(wrapper, node);
}
}
getTextNodes(element) {
const textNodes = [];
const walker = document.createTreeWalker(
element,
NodeFilter.SHOW_TEXT,
null,
false
);
let node;
while (node = walker.nextNode()) {
if (node.textContent.trim()) {
textNodes.push(node);
}
}
return textNodes;
}
escapeRegex(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
showAllItems() {
this.faqItems.forEach(item => {
item.style.display = 'block';
item.classList.remove('highlighted');
this.removeHighlights(item);
});
this.hideNoResults();
}
removeHighlights(item) {
const highlights = item.querySelectorAll('.search-highlight');
highlights.forEach(highlight => {
const parent = highlight.parentNode;
parent.replaceChild(document.createTextNode(highlight.textContent), highlight);
parent.normalize();
});
}
showNoResults(query) {
this.noResults.style.display = 'block';
this.populateInlineSuggestions();
}
hideNoResults() {
this.noResults.style.display = 'none';
}
showSearchResultsInfo(count, time, query) {
this.searchResultsInfo.style.display = 'flex';
const resultsCount = this.searchResultsInfo.querySelector('.results-count');
const searchTime = this.searchResultsInfo.querySelector('.search-time');
resultsCount.textContent = `${count} result${count !== 1 ? 's' : ''} for "${query}"`;
searchTime.textContent = `Search completed in ${time.toFixed(2)}ms`;
}
hideSearchResultsInfo() {
this.searchResultsInfo.style.display = 'none';
}
toggleFAQItem(item) {
const isActive = item.classList.contains('active');
if (isActive) {
item.classList.remove('active');
item.querySelector('.toggle-btn').setAttribute('aria-expanded', 'false');
} else {
item.classList.add('active');
item.querySelector('.toggle-btn').setAttribute('aria-expanded', 'true');
// Smooth scroll to item if it's not fully visible
setTimeout(() => {
const rect = item.getBoundingClientRect();
if (rect.bottom > window.innerHeight) {
item.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
}, 300);
}
}
handleFeedback(button) {
const isHelpful = button.classList.contains('helpful-btn');
const answerActions = button.parentElement;
const helpfulBtn = answerActions.querySelector('.helpful-btn');
const notHelpfulBtn = answerActions.querySelector('.not-helpful-btn');
// Remove previous votes
helpfulBtn.classList.remove('voted');
notHelpfulBtn.classList.remove('voted');
// Add vote to clicked button
button.classList.add('voted');
// Track feedback
this.trackFeedback(button.closest('.faq-item'), isHelpful);
// Show thank you message
this.showFeedbackMessage(isHelpful);
}
showFeedbackMessage(isHelpful) {
const message = isHelpful ? 'Thank you for your feedback!' : 'Thanks! We\'ll improve this answer.';
// Create temporary message
const messageEl = document.createElement('div');
messageEl.textContent = message;
messageEl.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: #10b981;
color: white;
padding: 12px 20px;
border-radius: 8px;
font-size: 14px;
z-index: 1000;
animation: fadeIn 0.3s ease;
`;
document.body.appendChild(messageEl);
setTimeout(() => {
messageEl.remove();
}, 3000);
}
toggleFilters() {
const isVisible = this.filtersPanel.style.display === 'block';
if (isVisible) {
this.hideFilters();
} else {
this.showFilters();
}
}
showFilters() {
this.filtersPanel.style.display = 'block';
this.filtersToggle.classList.add('active');
this.filtersToggle.setAttribute('aria-expanded', 'true');
}
hideFilters() {
this.filtersPanel.style.display = 'none';
this.filtersToggle.classList.remove('active');
this.filtersToggle.setAttribute('aria-expanded', 'false');
}
applyFilters() {
const categoryFilter = document.querySelector('.category-filter').value;
const sortFilter = document.querySelector('.sort-filter').value;
const answeredOnly = document.querySelector('.answered-only').checked;
const recentOnly = document.querySelector('.recent-only').checked;
let visibleItems = Array.from(this.faqItems).filter(item => {
const isVisible = item.style.display !== 'none';
if (!isVisible) return false;
// Category filter
if (categoryFilter && item.dataset.category !== categoryFilter) {
return false;
}
// Answered only filter (assuming all items are answered for demo)
if (answeredOnly) {
// In real implementation, check if item has answer
}
// Recent only filter (assuming all items are recent for demo)
if (recentOnly) {
// In real implementation, check item date
}
return true;
});
// Hide filtered out items
this.faqItems.forEach(item => {
if (visibleItems.includes(item)) {
item.style.display = 'block';
} else if (item.style.display !== 'none') {
item.style.display = 'none';
}
});
// Sort visible items
this.sortItems(visibleItems, sortFilter);
// Update results count
if (this.searchInput.value.trim()) {
const resultsCount = this.searchResultsInfo.querySelector('.results-count');
resultsCount.textContent = `${visibleItems.length} result${visibleItems.length !== 1 ? 's' : ''} for "${this.searchInput.value}"`;
}
}
sortItems(items, sortBy) {
const container = document.querySelector('.faq-items');
items.sort((a, b) => {
switch (sortBy) {
case 'alphabetical':
const titleA = a.querySelector('h3').textContent;
const titleB = b.querySelector('h3').textContent;
return titleA.localeCompare(titleB);
case 'date':
// In real implementation, sort by actual dates
return 0;
case 'popularity':
// In real implementation, sort by view count or votes
return 0;
default: // relevance
return 0;
}
});
// Reorder DOM elements
items.forEach(item => {
container.appendChild(item);
});
}
startVoiceSearch() {
if (!this.isVoiceSearchSupported) return;
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
const recognition = new SpeechRecognition();
recognition.continuous = false;
recognition.interimResults = false;
recognition.lang = 'en-US';
this.voiceSearchBtn.classList.add('recording');
recognition.onresult = (event) => {
const transcript = event.results[0][0].transcript;
this.searchInput.value = transcript;
this.handleSearchInput(transcript);
};
recognition.onerror = (event) => {
console.error('Speech recognition error:', event.error);
this.voiceSearchBtn.classList.remove('recording');
};
recognition.onend = () => {
this.voiceSearchBtn.classList.remove('recording');
};
recognition.start();
}
clearSearch() {
this.searchInput.value = '';
this.clearSearchBtn.style.display = 'none';
this.showAllItems();
this.hideSearchResultsInfo();
this.hideSearchSuggestions();
this.searchInput.focus();
}
showSearchSuggestions() {
if (this.searchInput.value.trim()) return;
this.searchSuggestions.style.display = 'block';
this.searchHistory.style.display = 'none';
if (this.searchHistory.length > 0) {
this.showSearchHistory();
}
}
hideSearchSuggestions() {
this.searchSuggestions.style.display = 'none';
this.searchHistory.style.display = 'none';
}
updateSearchSuggestions(query) {
const suggestions = this.popularSearches.filter(search =>
search.toLowerCase().includes(query.toLowerCase())
);
if (suggestions.length > 0) {
this.displaySuggestions(suggestions);
this.searchSuggestions.style.display = 'block';
} else {
this.searchSuggestions.style.display = 'none';
}
}
displaySuggestions(suggestions) {
const suggestionsList = this.searchSuggestions.querySelector('.suggestions-list');
suggestionsList.innerHTML = '';
suggestions.forEach(suggestion => {
const item = document.createElement('div');
item.className = 'suggestion-item';
item.innerHTML = `
<svg class="suggestion-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<circle cx="11" cy="11" r="8"></circle>
<path d="m21 21-4.35-4.35"></path>
</svg>
<span>${suggestion}</span>
`;
item.addEventListener('click', () => {
this.searchInput.value = suggestion;
this.handleSearchInput(suggestion);
this.hideSearchSuggestions();
});
suggestionsList.appendChild(item);
});
}
showSearchHistory() {
if (this.searchHistory.length === 0) return;
this.searchHistory.style.display = 'block';
this.searchSuggestions.style.display = 'none';
const historyList = this.searchHistory.querySelector('.history-list');
historyList.innerHTML = '';
this.searchHistory.slice(0, 5).forEach(search => {
const item = document.createElement('div');
item.className = 'history-item';
item.innerHTML = `
<svg class="history-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<circle cx="12" cy="12" r="3"></circle>
<path d="M12 1v6M12 17v6M4.22 4.22l4.24 4.24M15.54 15.54l4.24 4.24M1 12h6M17 12h6M4.22 19.78l4.24-4.24M15.54 8.46l4.24-4.24"></path>
</svg>
<span>${search}</span>
`;
item.addEventListener('click', () => {
this.searchInput.value = search;
this.handleSearchInput(search);
this.hideSearchSuggestions();
});
historyList.appendChild(item);
});
// Clear history button
const clearBtn = this.searchHistory.querySelector('.clear-history-btn');
clearBtn.addEventListener('click', () => {
this.clearSearchHistory();
});
}
populateSuggestions() {
this.displaySuggestions(this.popularSearches);
}
populateInlineSuggestions() {
const suggestionTags = this.noResults.querySelector('.suggestion-tags');
suggestionTags.innerHTML = '';
this.popularSearches.forEach(suggestion => {
const tag = document.createElement('span');
tag.className = 'suggestion-tag';
tag.textContent = suggestion;
tag.addEventListener('click', () => {
this.searchInput.value = suggestion;
this.handleSearchInput(suggestion);
});
suggestionTags.appendChild(tag);
});
}
addToSearchHistory(query) {
const trimmedQuery = query.trim();
if (!trimmedQuery || this.searchHistory.includes(trimmedQuery)) return;
this.searchHistory.unshift(trimmedQuery);
this.searchHistory = this.searchHistory.slice(0, 10); // Keep only last 10
this.saveSearchHistory();
}
loadSearchHistory() {
try {
return JSON.parse(localStorage.getItem('faq-search-history') || '[]');
} catch {
return [];
}
}
saveSearchHistory() {
try {
localStorage.setItem('faq-search-history', JSON.stringify(this.searchHistory));
} catch {
// Handle localStorage errors
}
}
clearSearchHistory() {
this.searchHistory = [];
this.saveSearchHistory();
this.hideSearchSuggestions();
}
handleSearchKeyboard(e) {
const suggestions = document.querySelectorAll('.suggestion-item, .history-item');
const currentFocus = document.querySelector('.suggestion-item.focused, .history-item.focused');
if (e.key === 'ArrowDown') {
e.preventDefault();
if (currentFocus) {
currentFocus.classList.remove('focused');
const next = currentFocus.nextElementSibling;
if (next) {
next.classList.add('focused');
}
} else if (suggestions.length > 0) {
suggestions[0].classList.add('focused');
}
} else if (e.key === 'ArrowUp') {
e.preventDefault();
if (currentFocus) {
currentFocus.classList.remove('focused');
const prev = currentFocus.previousElementSibling;
if (prev) {
prev.classList.add('focused');
}
}
} else if (e.key === 'Enter' && currentFocus) {
e.preventDefault();
currentFocus.click();
} else if (e.key === 'Escape') {
this.hideSearchSuggestions();
}
}
handleFAQKeyboard(e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
e.target.click();
}
}
exportResults() {
const visibleItems = Array.from(this.faqItems).filter(item =>
item.style.display !== 'none'
);
const exportData = visibleItems.map(item => {
const question = item.querySelector('h3').textContent;
const answer = item.querySelector('.faq-answer p').textContent;
return { question, answer };
});
// Create and download file
const content = exportData.map(item =>
`Q: ${item.question}\n\nA: ${item.answer}\n\n${'='.repeat(50)}\n\n`
).join('');
const blob = new Blob([content], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `faq-search-results-${new Date().toISOString().split('T')[0]}.txt`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
trackSearch(query, resultCount) {
// In real implementation, send analytics data
console.log('Search tracked:', { query, resultCount, timestamp: new Date() });
}
trackFeedback(item, isHelpful) {
const question = item.querySelector('h3').textContent;
// In real implementation, send feedback data
console.log('Feedback tracked:', { question, isHelpful, timestamp: new Date() });
}
}
// Initialize the searchable FAQ
document.addEventListener('DOMContentLoaded', () => {
new SearchableFAQ();
});Usage Instructions
- Basic Search: Type in the search box to find relevant FAQ items
- Voice Search: Click the microphone icon to search using voice (Chrome/Edge)
- Filters: Use the filter button to narrow results by category, sort order, etc.
- Keyboard Navigation: Use arrow keys to navigate search suggestions
- Export: Click the export button to save search results as a text file
- Feedback: Use the helpful/not helpful buttons to rate answers
Customization
- Search Categories: Modify the
data-categoryattributes and filter options - Keywords: Add relevant keywords to
data-keywordsfor better search results - Styling: Customize colors, fonts, and animations in the CSS
- Search Algorithm: Adjust fuzzy search parameters for different matching behavior
- Analytics: Implement real tracking in the
trackSearchandtrackFeedbackmethods
Browser Support
- Modern Browsers: Full functionality in Chrome, Firefox, Safari, Edge
- Voice Search: Available in Chrome and Edge
- Fallbacks: Graceful degradation for older browsers
Performance Features
- Debounced Search: Prevents excessive API calls during typing
- Fuzzy Matching: Handles typos and partial matches
- Efficient DOM Updates: Minimizes reflows and repaints
- Local Storage: Persists search history across sessions
HTML
64
lines
CSS
181
lines
JavaScript
99
lines
<div class="searchable-faq-container">
<div class="faq-header">
<h1>Searchable FAQ</h1>
<p>Find answers quickly with our advanced search</p>
</div>
<div class="search-section">
<div class="search-input-wrapper">
<input type="text" id="searchInput" placeholder="Search for answers..." autocomplete="off">
<div class="search-suggestions" id="searchSuggestions"></div>
</div>
<div class="search-stats" id="searchStats">
<span id="resultCount">12 results found</span>
</div>
</div>
<div class="faq-list" id="faqList">
<div class="faq-item" data-keywords="account login password reset">
<div class="faq-question">
<h3>How do I reset my password?</h3>
<span class="expand-icon">▼</span>
</div>
<div class="faq-answer">
<p>To reset your password, click on the 'Forgot Password' link on the login page and follow the instructions sent to your email.</p>
</div>
</div>
<div class="faq-item" data-keywords="billing payment subscription plan">
<div class="faq-question">
<h3>How does billing work?</h3>
<span class="expand-icon">▼</span>
</div>
<div class="faq-answer">
<p>Billing is processed automatically based on your subscription plan. You'll receive an invoice via email before each billing cycle.</p>
</div>
</div>
<div class="faq-item" data-keywords="api integration development technical">
<div class="faq-question">
<h3>How do I integrate with your API?</h3>
<span class="expand-icon">▼</span>
</div>
<div class="faq-answer">
<p>Our API documentation is available in your dashboard. You'll need to generate an API key and follow the integration guide.</p>
</div>
</div>
<div class="faq-item" data-keywords="support help contact customer service">
<div class="faq-question">
<h3>How can I contact support?</h3>
<span class="expand-icon">▼</span>
</div>
<div class="faq-answer">
<p>You can reach our support team through live chat, email, or by submitting a ticket through your dashboard.</p>
</div>
</div>
</div>
<div class="no-results" id="noResults" style="display: none;">
<h3>No results found</h3>
<p>Try adjusting your search terms or browse our categories.</p>
</div>
</div>