实现Node.js与EJS动态搜索:无刷新实时结果更新教程

实现Node.js与EJS动态搜索:无刷新实时结果更新教程

本教程将指导您如何在node.js和ejs应用中实现无刷新动态搜索功能。通过利用javascript的dom事件监听和fetch api进行异步请求,我们将优化后端控制器以返回json数据,并在前端实时更新搜索结果,彻底解决传统表单提交导致的页面重载问题,显著提升用户体验。

在现代Web应用中,用户期望在输入搜索关键词或调整筛选条件时,能够即时看到结果更新,而无需点击提交按钮或等待页面刷新。这正是动态搜索的核心价值。本教程将针对一个Node.js Express应用结合EJS模板引擎的场景,详细阐述如何构建一个高效、响应式的动态搜索系统。

1. 问题分析与现有代码审视

在提供的代码中,存在两个主要问题,导致动态搜索功能未能按预期工作:

oninput=”this.form.submit()” 导致的页面重载: 在user.ejs的搜索输入框上,oninput=”this.form.submit()” 属性使得每一次输入都会触发表单提交,导致整个页面刷新。这不仅阻止了JavaScript中fetch请求对#search-results div的局部更新,也可能与req.flash机制产生冲突,因为req.flash通常用于一次性消息,且在重定向后才会清除。后端响应类型不匹配: 前端JavaScript的updateSearchResults函数使用fetch请求,并期望从/search路径获取JSON格式的搜索结果 (.then(response => response.json()))。然而,后端user.controllers.js中的getHome函数(通常处理/或/search路由)最终是调用res.render(‘../Views/user.ejs’, …)来渲染EJS模板,而不是返回JSON数据。req.flash(‘search_results’, search_results) 也是为EJS渲染准备的,不适合直接作为AJAX响应。

为了解决这些问题,我们需要对前端和后端进行相应的改造。

2. 后端控制器优化:提供JSON API

为了支持前端的异步请求,我们需要一个能够返回JSON格式搜索结果的后端API端点。我们可以修改现有的getHome函数,使其能够根据请求类型(是普通页面加载还是AJAX请求)返回不同的响应,或者创建一个新的专用API函数。考虑到清晰性和职责分离,我们推荐创建一个新的API函数来处理AJAX请求。

user.controllers.js 改造:

我们将保留getHome用于初始页面加载和渲染EJS,并创建一个getSearchResultsAPI函数来专门处理前端的fetch请求。

const { pool } = require('../config/database.config');const MiniSearch = require('minisearch');const userModels = require('../models/user.models'); // 假设 formatDate 在这里// 辅助函数:执行搜索和过滤逻辑const performSearchAndFilter = async (searchedValue, deptFilter, yearFilter, fromDate, toDate) => {  const all_results = [];  const client = await pool.connect();  try {    const query = `select * from "users"`;    const result = await client.query(query);    result.rows.forEach((row) => {      all_results.push(row);    });  } catch (err) {    console.error("Database query error:", err);    // 可以在这里抛出错误或返回空数组    return [];   } finally {    client.release(); // 确保释放客户端  }  const minisearch = new MiniSearch({    fields: ['id', 'name', 'description', 'dept', 'year', 'fromDate', 'toDate'],    storeFields: ['id', 'name', 'description', 'dept', 'year', 'fromDate', 'toDate'],  });  minisearch.addAll(all_results);  const filterCriteria = (result, filters) => {    return Object.entries(filters).every(([key, value]) => {      if (!value) {        return true;      }      if (key === 'fromDate') {        const formattedDate = userModels.formatDate(result.fromDate);        return value = formattedDate;      }      return result[key] !== undefined && result[key] === value;    });  };  const filters = {    dept: deptFilter,    year: yearFilter,    fromDate: fromDate,    toDate: toDate,  };  let results = [];  if (searchedValue) {    results = minisearch.search(searchedValue, {      prefix: true,      fuzzy: 0.4,      filter: (result) => filterCriteria(result, filters),    });  } else {    results = all_results.filter((result) => filterCriteria(result, filters));  }  return results.map((result) => ({    id: result.id,    name: result.name,    description: result.description,  }));};// 首页渲染函数 (保持不变,用于首次加载页面)const getHome = async (req, res) => {  const searchedValue = req.query.searchedValue || '';  const deptFilter = req.query.deptFilter || '';  const yearFilter = req.query.yearFilter || '';  const fromDate = req.query.fromDate || '';  const toDate = req.query.toDate || '';  const search_results = await performSearchAndFilter(searchedValue, deptFilter, yearFilter, fromDate, toDate);  // 渲染EJS模板,包含初始或刷新后的搜索结果  res.render('../Views/user.ejs', {    search_results: search_results,    // 传递当前筛选值,以便前端可以回显    currentFilters: { searchedValue, deptFilter, yearFilter, fromDate, toDate }  });};// 新增的API函数,用于处理AJAX请求,返回JSON数据const getSearchResultsAPI = async (req, res) => {  try {    const searchedValue = req.query.searchedValue || '';    const deptFilter = req.query.deptFilter || '';    const yearFilter = req.query.yearFilter || '';    const fromDate = req.query.fromDate || '';    const toDate = req.query.toDate || '';    const search_results = await performSearchAndFilter(searchedValue, deptFilter, yearFilter, fromDate, toDate);    // 直接返回JSON数据    res.json({ search_results: search_results });  } catch (error) {    console.error("API search error:", error);    res.status(500).json({ error: "Internal Server Error" });  }};module.exports = {  getHome,  getSearchResultsAPI, // 导出新的API函数};

路由配置 (app.js 或 routes.js):

确保您的Express应用中,/ 路由映射到getHome,而/search(或/api/search)路由映射到getSearchResultsAPI。

// 示例 Express 路由配置const express = require('express');const router = express.Router();const userController = require('./controllers/user.controllers');router.get('/', userController.getHome); // 初始页面加载router.get('/search', userController.getSearchResultsAPI); // AJAX请求module.exports = router;

3. 前端EJS与JavaScript改造:实现实时更新

前端的主要任务是移除导致页面重载的表单提交,并确保JavaScript能够监听所有相关输入的变化,然后通过fetch API向新的后端API端点发送请求,并将返回的JSON数据动态渲染到页面上。

user.ejs 改造:

移除 oninput=”this.form.submit()”: 这是最关键的一步,它将阻止页面的自动刷新。移除表单 action=”/search” method=”GET”: 因为我们将通过JavaScript进行异步请求,不再需要传统的表单提交。为所有输入字段设置初始值: 如果您希望在页面刷新后保留用户的筛选条件,可以在EJS中设置输入字段的value属性。确保 search-results div 存在: 这是JavaScript将更新的区域。

                User        

SEARCH HERE

<input type="search" name="searchedValue" id="searchedValue" value="">
Department <option value="CSE" >CSE <option value="EEE" >EEE <option value="ME" >ME Year <option value="first" >First Year <option value="second" >Second Year <option value="third" >Third Year <option value="fourth" >Fourth Year
<input type="date" id="fromDate" name="fromDate" value=""> <input type="date" id="toDate" name="toDate" value="">
0) { %> { %>

No results found

const searchedValueInput = document.querySelector('#searchedValue'); const deptFilterInput = document.querySelector('#deptFilter'); const yearFilterInput = document.querySelector('#yearFilter'); const fromDateInput = document.querySelector('#fromDate'); const toDateInput = document.querySelector('#toDate'); const searchResultsContainer = document.querySelector('#search-results'); let searchTimeout; // 用于 debouncing function updateSearchResults() { clearTimeout(searchTimeout); // 清除之前的计时器 searchTimeout = setTimeout(() => { // 设置新的计时器 const searchedValue = searchedValueInput.value; const deptFilter = deptFilterInput.value; const yearFilter = yearFilterInput.value; const fromDate = fromDateInput.value; const toDate = toDateInput.value; // 构建查询字符串 const queryParams = new URLSearchParams({ searchedValue: searchedValue, deptFilter: deptFilter, yearFilter: yearFilter, fromDate: fromDate, toDate: toDate }).toString(); // 发送异步请求到新的API端点 fetch(`/search?${queryParams}`) // 注意这里指向 /search 路由 .then(response => { if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); }) .then(data => { searchResultsContainer.innerHTML = ''; // 清空现有结果 if (data.search_results && data.search_results.length > 0) { data.search_results.forEach(result => { const resultElement = document.createElement('div'); resultElement.innerHTML = `

${result.id}

${result.name}

${result.description}

`; searchResultsContainer.appendChild(resultElement); }); } else { searchResultsContainer.innerHTML = '

No results found

'; } }) .catch(error => { console.error('Error fetching search results:', error); searchResultsContainer.innerHTML = `

Error loading results: ${error.message}

`; }); }, 300); // 300毫秒的 debouncing 延迟 } // 监听所有相关输入字段的变化 searchedValueInput.addEventListener('input', updateSearchResults); deptFilterInput.addEventListener('change', updateSearchResults); yearFilterInput.addEventListener('change', updateSearchResults); fromDateInput.addEventListener('change', updateSearchResults); toDateInput.addEventListener('change', updateSearchResults); // 页面加载时执行一次搜索,以防页面刷新后需要重新加载数据 // updateSearchResults(); // 首次加载已由EJS渲染,不需要在此处再次调用

4. 关键改进点与注意事项

Debouncing (去抖动):在searchedValueInput的input事件监听中,每次按键都会触发updateSearchResults。频繁的AJAX请求会增加服务器负担。通过引入searchTimeout和setTimeout/clearTimeout,我们实现了去抖动。这意味着用户停止输入300毫秒后,才会发送实际的搜索请求。这显著提升了用户体验和系统性能。AJAX请求的URL构造:使用URLSearchParams来构建查询字符串是更健壮和可读的方式,它能自动处理URL编码错误处理:在fetch请求中增加了.then(response => { if (!response.ok) … }) 和 .catch(error => …),用于处理网络错误或服务器返回的非2xx状态码。初始页面加载:getHome函数现在负责在首次访问或页面刷新时渲染EJS,并带上初始的搜索结果。这意味着用户在刷新页面后,不会看到空白的搜索结果区域,而是会显示当前的搜索/筛选条件下的结果。前端JavaScript的updateSearchResults函数在页面加载时不再需要显式调用,因为EJS已经完成了首次渲染。req.flash 的移除:由于AJAX请求直接返回JSON,req.flash机制不再适用于实时更新。它主要用于在Express会话中存储一次性消息,并在重定向后传递给下一个请求。

通过上述改造,您的Node.js和EJS应用将拥有一个功能完善、响应迅速的动态搜索功能,大大提升用户体验。

以上就是实现Node.js与EJS动态搜索:无刷新实时结果更新教程的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1532779.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
FullCalendar周视图的正确配置与初始化:解决空白页面问题
上一篇 2025年12月21日 00:01:30
根据单选按钮的选择禁用文本输入框
下一篇 2025年12月21日 00:01:43

相关推荐

  • Java方法重载和重写核心概念

    方法重载发生在同一类中,方法名相同但参数列表不同,与返回类型无关;方法重写发生在子类继承父类时,方法名、参数列表和返回类型必须相同,访问权限不能更严格,且不能重写private、static或final方法。重载实现编译时多态,重写实现运行时多态。 方法重载和重写是Java中实现多态的两种重要机制,…

    2026年9月8日
    000
  • VSCode编码格式怎么设置_VSCode更改文件编码与解决乱码问题教程

    解决VSCode乱码需统一编码为UTF-8,优先通过状态栏编码选项重新打开或保存文件,设置全局默认编码为utf8并开启autoGuessEncoding,针对项目在.vscode/settings.json中配置特定编码,排查系统区域、终端编码及程序内编码处理,确保各环节编码一致。 VSCode中调…

    2026年9月8日
    000
  • 教资认定申请官网入口_2025年教师资格证认定网报

    教资认定申请官网入口是https://www.jszg.edu.cn,申请人需登录中国教师资格网,点击“网上办事”进入教师资格认定网上办理通道,完成注册并按提示填报信息、选择认定机构,结合体检与材料提交要求推进认定流程。 教资认定申请官网入口在哪里?这是不少网友都关注的,接下来由PHP小编为大家带来…

    2026年9月8日
    000
  • 解决SQL中父子表事务内自增主键关联插入的策略

    本教程详细阐述了在SQL数据库中,如何在同一事务内安全地处理父表自增主键的生成与子表关联插入的问题。核心在于利用数据库提供的特定函数,在父表插入后、事务提交前即时获取生成的自增主键,并将其用于子表的插入操作,从而确保数据的一致性和完整性。文章将提供MySQL、SQL Server和PostgreSQ…

    2026年9月8日
    000
  • VSCode怎么样运行_VSCode执行不同语言代码的基本方法教程

    VSCode运行不同语言代码的核心在于其强大的扩展生态系统、灵活的内置终端以及对调试器的良好集成。简单来说,你需要为特定语言安装对应的扩展,然后通过扩展提供的运行/调试按钮,或者直接在集成终端中调用语言解释器或编译器来执行代码。 VSCode作为我个人最常用的代码编辑器,它能运行各种语言代码,这背后…

    2026年9月8日
    000
  • 使用TextMate打开PHP后缀文件的操作方法_Mac下高效编辑PHP后缀文件的技巧

    首先将TextMate设为.php文件默认打开程序,右键选择“打开方式”并勾选“始终以此方式打开”;接着通过终端输入mate文件名或路径可快速编辑;确保语法模式为PHP并利用代码片段如foreach+Tab提升效率;最后自定义快捷键、启用行号与高亮当前行,并使用项目抽屉管理多文件,从而全面提升开发效…

    2026年9月8日
    200
  • PHP实现视频分类管理_PHP实现视频分类管理

    首先设计数据库表结构,包括分类表和视频表,实现多级分类与视频关联;接着使用PHP进行后端开发,完成分类增删改查、视频上传及分类筛选功能;通过递归函数构建分类树结构;前端展示侧边栏分类导航,支持点击加载对应视频列表;最后强调安全措施如文件类型校验、SQL预处理、权限控制及缓存优化,确保系统稳定可扩展。…

    2026年9月8日
    100
  • VSCode怎么设置断点调试_VSCode调试功能配置教程

    答案:在VSCode中进行断点调试需安装对应语言的调试扩展,配置launch.json文件指定调试参数,通过点击行号旁空白区域设置断点,最后启动调试会话。常见陷阱包括扩展未安装、launch.json中type类型错误、program路径不正确、cwd工作目录设置不当及环境变量缺失。熟练使用调试面板…

    2026年9月8日
    100
  • iPhone Air 天猫双11首发,Apple Store官方旗舰店率先开启预约

    10月13日晚,天猫apple store 官方旗舰店率先上线iphone air 国行版预约通道。消费者打开淘宝,搜索“iphone air”即可提前预约,10月17日上午9点正式开启预售。作为苹果在电商平台的全球最大官方旗舰店,天猫apple store 官方旗舰店不仅货源充足,还提供丰富权益,…

    2026年9月7日
    000
  • JavaBean规范是什么 如何实现getter和setter方法

    JavaBean是Java中用于封装数据的可重用组件规范,需满足公共类、无参构造、属性私有、提供getter/setter、建议实现Serializable等条件。 JavaBean 是一种 Java 语言编写的可重用组件规范,常用于封装数据。它不是 Java 的关键字或内置类,而是一套约定俗成的编…

    2026年9月7日
    200
  • 如何为iPhone11ProMax下载固件?安全来源操作指南

    通过苹果开发者中心、可信第三方网站或爱思助手可安全下载iPhone 11 Pro Max固件。首先推荐访问苹果开发者官网登录账户,进入开发者中心下载iOS恢复镜像,确保文件原始完整;其次可选用ipsw.me等信誉良好的聚合站,搜索设备型号选择已签名版本,并校验SHA1或MD5码确认文件无篡改;最后可…

    2026年9月7日
    100
  • VSCode怎么运行代码网页_VSCode搭建本地服务器运行网页教程

    VSCode本身并不能直接“运行”一个代码网页,因为它本质上是一个文本编辑器。我们通常说的“运行”网页,指的是在浏览器中预览和交互。为了实现这一点,尤其是当网页包含JavaScript动态内容、需要加载外部资源或模拟后端请求时,我们需要一个本地服务器环境。最直接且常用的方法是利用VSCode的扩展,…

    2026年9月7日
    000
  • Java方法重载时如何选择最合适的重载版本

    Java调用重载方法时按参数类型、数量和顺序在编译期选择最匹配版本,优先级为:精确匹配 > 自动类型提升 > 装箱 > 可变参数,多参数需整体判断具体性,避免歧义。 Java在调用重载方法时,会根据传入的参数类型、数量和顺序来选择最合适的方法版本。这个过程发生在编译期,称为静态分派…

    2026年9月7日
    500
  • 夸克AI怎么进行市场分析_夸克AI市场数据挖掘与分析教程

    夸克AI怎么进行市场分析_夸克AI市场数据挖掘与分析教程夸克AI怎么进行市场分析_夸克AI市场数据挖掘与分析教程夸克AI怎么进行市场分析_夸克AI市场数据挖掘与分析教程夸克AI怎么进行市场分析_夸克AI市场数据挖掘与分析教程

    夸克AI凭借深度搜索、AI写作、AI总结和AI相机功能,实现市场数据的高效挖掘与分析:首先通过深度搜索整合权威宏观数据并支持多轮追问,继而利用AI写作自动生成结构化分析报告初稿,再通过AI总结快速提炼PDF等长篇文档的核心信息,最后借助AI相机识别纸质图表并转化为可编辑数据,全流程提升市场研究效率。…

    2026年9月7日 用户投稿
    000
  • PHP函数文档怎么写_PHP函数文档编写规范与工具

    答案:编写PHP函数文档应遵循PHPDoc规范,使用@param、@return等标签描述参数、返回值及异常,配合PHPDocumentor等工具生成可视化文档,提升代码可读性与维护效率。 编写清晰、规范的PHP函数文档不仅能提升代码可读性,还能方便团队协作和后期维护。良好的文档让其他开发者(包括未…

    2026年9月7日
    100
  • 小红书原创视频怎么发布?小红书原创视频怎么发布作品

    在短视频风靡的当下,小红书作为融合时尚、生活方式与娱乐内容的热门社交平台,吸引了无数创作者加入。越来越多的人选择在这里分享日常点滴,而原创视频也逐渐成为涨粉的关键方式。那么,小红书原创视频怎么发布呢?别担心,接下来就为你一步步解析发布流程。 一、前期准备 1. 注册并登录账号 首先你需要拥有一个小红…

    2026年9月7日
    000
  • 如何理解Linux的inode和文件权限?

    inode是Linux中存储文件元信息的数据结构,包含文件大小、权限、所有者及数据块指针等,不包含文件名;每个文件有唯一inode号,支持硬链接。文件权限由rwx组成,分属所有者、组和其他用户,通过chmod修改,目录的x权限决定能否进入,w权限控制增删改操作,两者协同实现文件系统的安全与组织管理。…

    2026年9月7日
    200
  • 英特尔Arc A580对决AMD Radeon RX 7600:千元级显卡的1080P游戏性能,谁是预算有限玩家的福音?

    Intel Arc A580凭借更低售价和接近甚至反超RX 7600的游戏表现,成为1080P高性价比首选;其支持AV1编码、XeSS与光追,性能释放依赖Resizable BAR开启,且在视频创作中优势显著,搭配550W以上电源可兼顾功耗与散热,适合预算有限且追求多功能的玩家。 面对1080P分辨…

    2026年9月7日
    100
  • 腾讯优图实验室开源 Youtu-Embedding

    腾讯优图实验室近日宣布开源 youtu-embedding,这是一款专为企业级场景设计的通用文本表示模型,具备强大的多任务处理能力,可广泛应用于文本检索、意图识别、语义相似度计算、文本分类与聚类等六大核心自然语言处理任务。该模型在信息检索(ir)、语义匹配(sts)、重排序、聚类分析及文本分类等多个…

    2026年9月7日
    000
  • 优化 Google Cloud Pub/Sub 拉取消息的延迟

    本文旨在帮助开发者优化在使用 Java 客户端从 Google Cloud Pub/Sub 拉取消息时的延迟问题。通过分析同步拉取模式的局限性,并介绍异步流式拉取方案,帮助读者理解如何通过增加并发拉取请求或采用异步模式来显著降低消息处理延迟,提升应用性能。 在使用 Google Cloud Pub/…

    2026年9月7日
    100

发表回复

登录后才能评论
关注微信