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

基于Node.js、Express、Handlebars实现无刷新点赞/收藏功能咨询

Hey there! Let's tackle this no-refresh favorite/like feature you're building—super common for social sites, and totally doable with your Node.js/Express/Handlebars stack. Here's a step-by-step breakdown tailored to your setup:

1. Adjust Frontend Form & Add AJAX Logic

First, we'll ditch the full-page form submit and replace it with JavaScript that sends an async request. We'll keep using Handlebars variables to pass creator data, and add data attributes to make targeting easier.

Updated Handlebars Template

{{!-- Add a meta tag for CSRF protection (critical for security) --}}
<meta name="csrf-token" content="{{csrfToken}}">

{{!-- For each creator in your list --}}
<div class="creator-card">
  <h3>{{this.fname}} {{this.lname}}</h3>
  <button 
    class="favorite-btn" 
    data-creator-id="{{this.id}}"
    data-is-favorited="{{this.isFavorited}}"
  >
    {{#if this.isFavorited}}★ Unfavorite{{else}}☆ Favorite{{/if}}
  </button>
</div>

Frontend JavaScript (No jQuery Needed)

Add this script (either in a separate .js file or inline in your template):

document.addEventListener('DOMContentLoaded', () => {
  const favoriteBtns = document.querySelectorAll('.favorite-btn');

  favoriteBtns.forEach(btn => {
    btn.addEventListener('click', async (e) => {
      e.preventDefault();
      const creatorId = btn.dataset.creatorId;
      const csrfToken = document.querySelector('meta[name="csrf-token"]').content;

      try {
        // Send async POST request to your backend
        const response = await fetch('/favoriteCreator', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'X-CSRF-Token': csrfToken
          },
          body: JSON.stringify({ creatorId })
        });

        const result = await response.json();

        // Update the button UI immediately
        if (result.success) {
          if (result.isFavorited) {
            btn.textContent = '★ Unfavorite';
            btn.classList.add('favorited'); // Add styling for active state
          } else {
            btn.textContent = '☆ Favorite';
            btn.classList.remove('favorited');
          }
        } else {
          alert('Oops, we couldn\'t update your favorite status. Try again later!');
        }
      } catch (err) {
        console.error('Favorite error:', err);
        alert('Something went wrong—please check your connection.');
      }
    });
  });
});
2. Update Backend Express Route

Your existing /favoriteCreator route needs to accept JSON requests, update the user's favorite status, and return a JSON response instead of redirecting.

Backend Code (Express + Mongoose Example)

const express = require('express');
const csrf = require('csurf');
const router = express.Router();
const csrfProtection = csrf({ cookie: true });

// Make sure to parse JSON requests
router.use(express.json());
// Pass CSRF token to Handlebars templates
router.use((req, res, next) => {
  res.locals.csrfToken = req.csrfToken();
  next();
});

// Updated favorite route
router.post('/favoriteCreator', csrfProtection, async (req, res) => {
  try {
    const { creatorId } = req.body;
    const userId = req.session.userId; // Assume you're using express-session for auth

    // Check if user already favorited this creator
    const existingFavorite = await Favorite.findOne({ userId, creatorId });

    if (existingFavorite) {
      // Unfavorite: Remove from database
      await Favorite.deleteOne({ userId, creatorId });
      res.json({ success: true, isFavorited: false });
    } else {
      // Favorite: Add to database
      await Favorite.create({ userId, creatorId });
      res.json({ success: true, isFavorited: true });
    }
  } catch (err) {
    console.error('Favorite route error:', err);
    res.status(500).json({ success: false, message: 'Internal server error' });
  }
});

module.exports = router;
3. Pass Initial Favorite Status to Handlebars

When rendering the creator list, fetch the user's existing favorites and pass that state to the template so buttons load with the correct text:

router.get('/creators', csrfProtection, async (req, res) => {
  const userId = req.session.userId;
  // Fetch all creators
  const creators = await Creator.find({});
  
  // Add isFavorited flag to each creator
  const creatorsWithStatus = await Promise.all(creators.map(async (creator) => {
    const isFavorited = await Favorite.exists({ userId, creatorId: creator._id });
    return {
      ...creator.toObject(),
      isFavorited: !!isFavorited
    };
  }));

  res.render('creators', { creators: creatorsWithStatus });
});
Quick Tips for Polish
  • Styling: Add CSS to the .favorited class (e.g., color: #ff4444;) to make the active state obvious.
  • Performance: If you have a lot of creators, use a bulk query (like $in in MongoDB) to fetch all favorites at once instead of checking each individually.
  • Error Handling: Replace alert boxes with a custom toast component for a smoother user experience.

内容的提问来源于stack exchange,提问作者Shooting Stars

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:20:03