From 4bceda0084aa6cca075d1491a36cc0b7db31730e Mon Sep 17 00:00:00 2001
From: piyushkumar0707 <121piyush466mits@gmail.com>
Date: Sat, 18 Oct 2025 21:39:50 +0530
Subject: [PATCH] feat: add real-time FAQ search and filter functionality
- Add search bar with icon and clear button above FAQ section
- Implement instant search filtering as user types
- Add search result counter with visual feedback
- Include 'no results' message for better UX
- Add keyboard shortcuts (Escape to clear)
- Implement smooth animations for filtered items
- Add purple accent highlight for matching FAQs
- Support dark mode styling
- Mobile responsive design
- Add data attributes to FAQs for searchability
- Include comprehensive CSS animations and transitions
This significantly improves FAQ discoverability and user experience by allowing users to quickly find relevant information without scrolling through all FAQs.
---
views/css/index.css | 108 ++++++++++++++++++++++++++
views/index.html | 35 ++++++++-
views/scripts/components.js | 149 ++++++++++++++++++++++++++++++++++++
3 files changed, 288 insertions(+), 4 deletions(-)
diff --git a/views/css/index.css b/views/css/index.css
index f2f7233..37f6ff4 100644
--- a/views/css/index.css
+++ b/views/css/index.css
@@ -584,3 +584,111 @@ hr{
cursor: pointer;
border-radius: 99%;
}
+
+/* ========================================
+ FAQ SEARCH FEATURE STYLES
+ ======================================== */
+
+/* Search input focus effect */
+#faq-search-input:focus {
+ box-shadow: 0 0 0 4px rgba(168, 85, 247, 0.1);
+}
+
+.tw-dark #faq-search-input:focus {
+ box-shadow: 0 0 0 4px rgba(168, 85, 247, 0.2);
+}
+
+/* Clear button hover effect */
+#faq-search-clear {
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+#faq-search-clear:hover {
+ transform: scale(1.1);
+}
+
+/* Results counter animation */
+#faq-search-results {
+ animation: slideDown 0.3s ease-out;
+}
+
+@keyframes slideDown {
+ from {
+ opacity: 0;
+ transform: translateY(-10px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+/* FAQ highlight effect when searching */
+.faq-highlight {
+ position: relative;
+}
+
+.faq-highlight::before {
+ content: '';
+ position: absolute;
+ left: -8px;
+ top: 50%;
+ transform: translateY(-50%);
+ width: 4px;
+ height: 60%;
+ background: linear-gradient(180deg, #a855f7, #8b5cf6);
+ border-radius: 2px;
+ animation: pulseGlow 2s ease-in-out infinite;
+}
+
+@keyframes pulseGlow {
+ 0%, 100% {
+ opacity: 0.6;
+ }
+ 50% {
+ opacity: 1;
+ box-shadow: 0 0 8px rgba(168, 85, 247, 0.5);
+ }
+}
+
+/* No results message styling */
+#faq-result-count {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.25rem;
+ padding: 0.5rem 1rem;
+ border-radius: 0.5rem;
+ font-weight: 500;
+ background-color: rgba(168, 85, 247, 0.05);
+}
+
+.tw-dark #faq-result-count {
+ background-color: rgba(168, 85, 247, 0.1);
+}
+
+/* Search input placeholder animation */
+@keyframes placeholderShift {
+ 0%, 100% {
+ opacity: 0.6;
+ }
+ 50% {
+ opacity: 0.8;
+ }
+}
+
+#faq-search-input::placeholder {
+ animation: placeholderShift 3s ease-in-out infinite;
+}
+
+/* Mobile responsiveness for search */
+@media (max-width: 768px) {
+ #faq-search-input {
+ font-size: 14px;
+ padding: 0.75rem 2.5rem;
+ }
+
+ #faq-search-input::placeholder {
+ font-size: 13px;
+ }
+}
diff --git a/views/index.html b/views/index.html
index 043fe0c..67d5cf9 100644
--- a/views/index.html
+++ b/views/index.html
@@ -1831,10 +1831,37 @@
>
Regarding GSSOC FAQ BOT
+
+
+
+
-
-
-
+
@@ -2058,7 +2085,7 @@
-
+
diff --git a/views/scripts/components.js b/views/scripts/components.js
index 343858f..92366b1 100644
--- a/views/scripts/components.js
+++ b/views/scripts/components.js
@@ -259,6 +259,155 @@ fetch('/faqs.json')
});
+// ========================================
+// FAQ SEARCH & FILTER FUNCTIONALITY
+// ========================================
+
+document.addEventListener('DOMContentLoaded', function() {
+ const searchInput = document.getElementById('faq-search-input');
+ const clearButton = document.getElementById('faq-search-clear');
+ const resultsDiv = document.getElementById('faq-search-results');
+ const resultCount = document.getElementById('faq-result-count');
+ const faqContainer = document.getElementById('faq-container');
+
+ if (!searchInput || !faqContainer) return;
+
+ // Get all FAQ items
+ const faqItems = faqContainer.querySelectorAll('[data-faq-item]');
+ const totalFaqs = faqItems.length;
+
+ /**
+ * Filter FAQ items based on search query
+ */
+ function filterFAQs() {
+ const query = searchInput.value.toLowerCase().trim();
+
+ // Show/hide clear button
+ if (query.length > 0) {
+ clearButton.classList.remove('tw-hidden');
+ } else {
+ clearButton.classList.add('tw-hidden');
+ }
+
+ // If query is empty, show all FAQs
+ if (query.length === 0) {
+ faqItems.forEach(item => {
+ item.style.display = '';
+ item.classList.remove('faq-highlight');
+ const hr = item.nextElementSibling;
+ if (hr && hr.tagName === 'HR') {
+ hr.style.display = '';
+ }
+ });
+ resultsDiv.classList.add('tw-hidden');
+ return;
+ }
+
+ let visibleCount = 0;
+
+ // Filter FAQs
+ faqItems.forEach((item, index) => {
+ const question = item.getAttribute('data-faq-question') || '';
+ const answer = item.getAttribute('data-faq-answer') || '';
+ const searchText = (question + ' ' + answer).toLowerCase();
+
+ const matches = searchText.includes(query);
+
+ if (matches) {
+ item.style.display = '';
+ item.classList.add('faq-highlight');
+ visibleCount++;
+
+ // Show separator if not last visible item
+ const hr = item.nextElementSibling;
+ if (hr && hr.tagName === 'HR') {
+ hr.style.display = '';
+ }
+ } else {
+ item.style.display = 'none';
+ item.classList.remove('faq-highlight');
+
+ // Hide separator
+ const hr = item.nextElementSibling;
+ if (hr && hr.tagName === 'HR') {
+ hr.style.display = 'none';
+ }
+ }
+ });
+
+ // Update results count
+ resultsDiv.classList.remove('tw-hidden');
+ if (visibleCount === 0) {
+ resultCount.innerHTML = `
+
+ No FAQs found for "${escapeHtml(query)}". Try different keywords.
+ `;
+ resultCount.className = 'tw-text-orange-600 dark:tw-text-orange-400';
+ } else if (visibleCount === totalFaqs) {
+ resultCount.innerHTML = `
+
+ Showing all ${totalFaqs} FAQs
+ `;
+ resultCount.className = 'tw-text-green-600 dark:tw-text-green-400';
+ } else {
+ resultCount.innerHTML = `
+
+ Found ${visibleCount} of ${totalFaqs} FAQs matching "${escapeHtml(query)}"
+ `;
+ resultCount.className = 'tw-text-purple-600 dark:tw-text-purple-400';
+ }
+ }
+
+ /**
+ * Clear search input and reset filters
+ */
+ function clearSearch() {
+ searchInput.value = '';
+ searchInput.focus();
+ filterFAQs();
+ }
+
+ /**
+ * Escape HTML to prevent XSS
+ */
+ function escapeHtml(text) {
+ const div = document.createElement('div');
+ div.textContent = text;
+ return div.innerHTML;
+ }
+
+ // Event listeners
+ searchInput.addEventListener('input', filterFAQs);
+ searchInput.addEventListener('keyup', function(e) {
+ if (e.key === 'Escape') {
+ clearSearch();
+ }
+ });
+
+ clearButton.addEventListener('click', clearSearch);
+
+ // Add subtle animation to highlighted FAQs
+ const style = document.createElement('style');
+ style.textContent = `
+ .faq-highlight {
+ animation: highlightFade 0.3s ease-in-out;
+ }
+
+ @keyframes highlightFade {
+ 0% {
+ transform: translateX(-5px);
+ opacity: 0.7;
+ }
+ 100% {
+ transform: translateX(0);
+ opacity: 1;
+ }
+ }
+ `;
+ document.head.appendChild(style);
+});
+
+
//Scroll Button Functionality
// Get reference to the button
const scrollBtn = document.getElementById('scrollTopBtn');