You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Express+EJS待办列表删除任务后页面不更新问题求助

待办列表开发问题排查与解决方案

我的需求

  • 使用ExpressJS作为后端开发基础待办列表
  • 通过EJS检测用户点击任务的操作
  • 使用AJAX将待删除任务项提交至后端
  • 后端从列表中删除对应项
  • 后端更新前端页面

遇到的问题

  • 控制台日志显示任务已删除,但页面未更新
  • 后端POST请求中重新渲染index.ejs无效
  • 刷新页面后已删除任务才会消失
  • AJAX中的console.log无输出
  • 后端/delete接口中res.send('hello')无响应

前端EJS代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Proton Planner</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-4bw+/aepP/YC94hEpVNVgiZdgIC5+VKNBQNGCHeKRQN+PtmoHDEXuppvnDJzQIu9" crossorigin="anonymous">
    <link rel="stylesheet" href="./styles/styles.css">
    
</head>
<body>
    
    
    <h1>Proton Planner</h1>
    <% if (locals.tasks) { %>
        <% for (var ind = 0; ind<locals.tasks.length; ind++) { %>
        <!-- Change the id and for for each checkbox-->
        <div class="form-check">
            <div class="checker">
                <input class="form-check-input" type="checkbox" value="" id="flexCheckDefault<%=ind%>">
            </div>
            <label class="form-check-label" for="flexCheckDefault<%=ind%>">
                <%= tasks[ind] %>
            </label>
          </div>
          <% } %>
    <% } %>
    <form action="/submit" method="post">
        <input placeholder="Add a task" type="text" id="task" name="task" required>
        <input type="submit" value="Submit">
    </form>

    <!-- JS scripts -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-HwwvtgBNo3bZJJLYd8oVXjrBZt8cqVSpeBNS5n7C8IVInixGAoxmnlMuBnhbgrkm" crossorigin="anonymous"></script>
    <script src="https://code.jquery.com/jquery-3.7.0.js" integrity="sha256-JlqSTELeR4TLqP0OG9dxM7yDPqX1ox/HfgiSLBj8+kM=" crossorigin="anonymous"></script>
    <script src="./index.js"></script>
    <script>
// AREA OF INTEREST --------------------------------------------------------------
        $('.checker').on('click', function() {
            
            var itemDel = $(this).next().text().trim();
            console.log('Item deleted', itemDel);
    
            $.ajax({
                type: 'POST',
                url: '/delete',
                data: {
                   itemDel,
                },
                success: function(response) {
                    console.log(response);
                    console.log('ajax post success')
                    $.ajax({
                        type: 'GET',
                        url: '/',
                        success: function(response) {
// Handle the successful response here
                            console.log(response);
                            console.log('ajax get success')
                        },
                        error: function(xhr, status, error) {
// Handle errors here
                            console.error(error);
                        }
                
            }) 
                },
                error: function(xhr, status, error) {
// Handle errors here
                    console.error(error);
                }
            })
            

        })
        
    </script>
</body>
</html>

后端ExpressJS代码

// Imports
import express from "express";
import bodyParser from "body-parser";

// Constants
const app = express();
// MAKE NOTE OF THIS IN TRELLO 
app.set('view engine', 'ejs')
const port = 3000;
let taskList = [];

// Getting the absolute file path
import { dirname } from "path";
import { fileURLToPath } from "url";
const __dirname = dirname(fileURLToPath(import.meta.url));

// App.use
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static("public"));

// App.get
app.get("/", (req, res) => { 
    res.render("index.ejs", { tasks: taskList })
  });

// App.post for new task
app.post("/submit", (req, res) => {
    let newTask = req.body["task"]
    taskList.push(newTask)  
    console.log(taskList)
    res.render("index.ejs", { tasks: taskList })
});
// Look here -------------------------------------------------------------------
//App.post to delete a task
app.post("/delete", (req, res) => {
  var remItem = req.body.itemDel;
  console.log(remItem)
  /*
  for (var item = 0; item<taskList.length; item++) {
    if (remItem == taskList[item]) {
      taskList.splice(item, 1);
      taskList.push(remItem);
      console.log(taskList)
    }
  }*/
  var i = 0;
  while (i < taskList.length) {
    if (taskList[i] === remItem) {
      taskList.splice(i, 1);
    } else {
      ++i;
    }}
    console.log(taskList);
  res.send('Hello')
  //res.render("index.ejs", { tasks: taskList });
});

// App.listen
app.listen(port, () => {
    console.log(`Listening on port ${port}`);
  });

解决方案

1. 修复动态元素的点击事件绑定

原代码直接给.checker绑定点击事件,但这些元素是EJS动态渲染生成的,页面初始化时不存在,导致点击事件无法触发。改为事件委托方式,确保动态生成的元素也能响应点击:

$(document).on('click', '.checker', function() {
    var itemDel = $(this).next().text().trim();
    console.log('Item deleted', itemDel);

    // 后续AJAX逻辑保持不变,或按下面步骤修改
})

这一步解决AJAX无输出、后端/delete接口无响应的核心问题,点击事件现在能正确触发,AJAX请求会正常发送。

2. 调整后端响应逻辑,返回JSON格式的任务列表

原后端返回字符串Hello无法满足前端更新页面的需求,改为返回更新后的任务列表JSON数据,让前端可以直接使用:

app.post("/delete", (req, res) => {
  var remItem = req.body.itemDel;
  var i = 0;
  while (i < taskList.length) {
    if (taskList[i] === remItem) {
      taskList.splice(i, 1);
    } else {
      ++i;
    }
  }
  console.log(taskList);
  // 返回更新后的任务列表
  res.json({ tasks: taskList });
});

3. 前端动态渲染任务列表,无需刷新页面

原代码在AJAX成功后重新请求整个页面但未替换内容,改为直接用后端返回的JSON数据重新渲染任务区域,实现无刷新更新:

$(document).on('click', '.checker', function() {
    var itemDel = $(this).next().text().trim();
    console.log('Item deleted', itemDel);

    $.ajax({
        type: 'POST',
        url: '/delete',
        data: {
           itemDel: itemDel // 显式指定键名,避免解析歧义
        },
        success: function(response) {
            console.log('ajax post success', response);
            // 清空现有任务列表
            $('.form-check').remove();
            // 重新渲染新的任务列表
            if (response.tasks.length > 0) {
                response.tasks.forEach((task, ind) => {
                    const taskHtml = `
                    <div class="form-check">
                        <div class="checker">
                            <input class="form-check-input" type="checkbox" value="" id="flexCheckDefault${ind}">
                        </div>
                        <label class="form-check-label" for="flexCheckDefault${ind}">
                            ${task}
                        </label>
                    </div>
                    `;
                    $('h1').after(taskHtml);
                });
            }
        },
        error: function(xhr, status, error) {
            console.error(error);
        }
    })
})

这一步解决页面未更新、刷新后任务才消失的问题,前端直接用最新数据重新渲染任务区域,无需手动刷新。

4. 关于后端渲染无效的说明

原代码中在/delete接口使用res.render无效,是因为AJAX请求不会自动替换当前页面内容,只有浏览器直接发起的导航请求才会加载渲染后的页面。因此,返回JSON数据让前端动态更新DOM是更适合AJAX场景的方案。

内容的提问来源于stack exchange,提问作者Ahmed Kehinde

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 03:10:53