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

如何为循环渲染的多按钮绑定addEventListener?解决编辑帖子异常

社交媒体帖子编辑功能故障排查与修复

我开发了一个展示用户社交媒体帖子的页面,所有帖子均带有Edit按钮,点击该按钮会显示预填当前内容的textarea和提交按钮的表单。但目前存在问题:无论点击哪个帖子的Edit按钮,始终只有第一个帖子的内容被修改。我猜测需要添加id来跟踪待编辑的帖子,但不知如何实现,是否有其他解决方案?


现有代码

views.py

def index(request):
    post_list = AllPost.objects.all().order_by("date").reverse()
    paginator = Paginator(post_list, 10) # Show 10 posts per page.
    page_number = request.GET.get('page')
    page_obj = paginator.get_page(page_number)
    return render(request, "network/index.html", {
        "posts": post_list,
        "page_obj": page_obj
    })
def edit(request, id):
    if request.method == "POST":
        new_content = request.POST["new_content"]
        updated_post = AllPost.objects.filter(id = id)
        updated_post.update(content = new_content)
        return JsonResponse({}, status = 201)
    return JsonResponse({"error": "Bad Request"}, status = 400)

index.html

{% for post in page_obj %}
    {{ post.full_name|upper }}<br>
    
        <div class="frame">
            <h4><a href="{% url 'profile' post.user.username %}" style="color: black;">{{post.user.username}}</a></h4>
            {% if post.user == user %}
                <button class="btn btn-sm btn-outline-primary" id="edit">Edit</button>
            {% endif %}
            <div id="content">{{post.content}}</div>
            <form action="{% url 'edit' post.id %}" method="post" id="edit_post" style="display: none;">
                {% csrf_token %}
                <div class="form-group"><textarea id="new_content" name="new_content" cols="30"></textarea></div>
                <input class="btn btn-sm btn-primary" id="save" type="submit" value="Save">
            </form>
            
            <div class="grey" id="timestamp">{{post.date}}</div>
            <div class="grey">{{post.likes}}</div>
            <a href="#" style="color: grey;">Comment</a>
        </div>
{% endfor %}

network.js

document.addEventListener("DOMContentLoaded",  () => {
    document.querySelector("#content").innerHTML = localStorage.getItem("content");
    document.querySelectorAll("#edit").forEach(button => {
        button.onclick = () => {
            const content = document.querySelector("#content").innerHTML;
            document.querySelector("#content").style.display = "none";
            document.querySelector("#edit_post").style.display = "block";
            document.querySelector("#new_content").value = content;
        }
    });
    document.querySelector("#edit_post").onsubmit = (event) => {
        let content = document.querySelector("#new_content").value;
        document.querySelector("#content").innerHTML = content;
        document.querySelector("#content").style.display = "block";
        document.querySelector("#edit_post").style.display = "none";
        event.preventDefault();
        localStorage.setItem("content", content);
    };
});

问题根源

  1. 重复ID冲突:HTML中所有帖子的编辑按钮、内容容器、表单都使用了相同的id(如edit、content、edit_post),但DOM要求id必须唯一,document.querySelector()只会返回第一个匹配的元素,导致无论点击哪个按钮,都只会操作第一个帖子的元素。
  2. JS逻辑未绑定当前帖子:代码未关联按钮与所属帖子的关系,无法定位到点击按钮对应的目标内容和表单。
  3. 表单提交未同步后端:原JS的表单提交仅修改了前端显示和localStorage,未实际发送请求到后端edit视图,数据库内容不会更新。

修复方案

步骤1:修改HTML,为元素添加唯一标识

将重复的id替换为class,通过父容器绑定帖子关联关系:

{% for post in page_obj %}
    {{ post.full_name|upper }}<br>
    
        <div class="frame" data-post-id="{{ post.id }}">
            <h4><a href="{% url 'profile' post.user.username %}" style="color: black;">{{post.user.username}}</a></h4>
            {% if post.user == user %}
                <button class="btn btn-sm btn-outline-primary edit-btn">Edit</button>
            {% endif %}
            <div class="post-content">{{post.content}}</div>
            <form action="{% url 'edit' post.id %}" method="post" class="edit-form" style="display: none;">
                {% csrf_token %}
                <div class="form-group"><textarea name="new_content" cols="30"></textarea></div>
                <input class="btn btn-sm btn-primary" type="submit" value="Save">
            </form>
            
            <div class="grey">{{post.date}}</div>
            <div class="grey">{{post.likes}}</div>
            <a href="#" style="color: grey;">Comment</a>
        </div>
{% endfor %}

步骤2:修改JS,绑定当前帖子的元素交互

通过按钮找到所属的.frame容器,在容器内定位元素,避免全局查询:

document.addEventListener("DOMContentLoaded",  () => {
    // 绑定编辑按钮点击事件
    document.querySelectorAll('.edit-btn').forEach(button => {
        button.onclick = () => {
            const frame = button.closest('.frame');
            const contentDiv = frame.querySelector('.post-content');
            const editForm = frame.querySelector('.edit-form');
            const textarea = editForm.querySelector('textarea');
            
            textarea.value = contentDiv.textContent;
            contentDiv.style.display = 'none';
            editForm.style.display = 'block';
        }
    });

    // 绑定表单提交事件
    document.querySelectorAll('.edit-form').forEach(form => {
        form.onsubmit = async (event) => {
            event.preventDefault();
            const frame = form.closest('.frame');
            const contentDiv = frame.querySelector('.post-content');
            const textarea = form.querySelector('textarea');
            const newContent = textarea.value;

            // 发送请求更新后端数据
            const response = await fetch(form.action, {
                method: 'POST',
                headers: {
                    'X-CSRFToken': form.querySelector('[name=csrfmiddlewaretoken]').value,
                    'Content-Type': 'application/x-www-form-urlencoded'
                },
                body: `new_content=${encodeURIComponent(newContent)}`
            });

            if (response.ok) {
                contentDiv.textContent = newContent;
                contentDiv.style.display = 'block';
                form.style.display = 'none';
            }
        }
    });
});

步骤3:优化后端(可选)

添加权限验证和错误处理,避免越权操作和无效请求:

def edit(request, id):
    if request.method == "POST":
        new_content = request.POST.get("new_content")
        if not new_content:
            return JsonResponse({"error": "Content cannot be empty"}, status=400)
        try:
            updated_post = AllPost.objects.get(id=id)
            # 验证用户是否为帖子作者
            if updated_post.user != request.user:
                return JsonResponse({"error": "Permission denied"}, status=403)
            updated_post.content = new_content
            updated_post.save()
            return JsonResponse({}, status=201)
        except AllPost.DoesNotExist:
            return JsonResponse({"error": "Post not found"}, status=404)
    return JsonResponse({"error": "Bad Request"}, status=400)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 23:10:52