如何为循环渲染的多按钮绑定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); }; });
问题根源
- 重复ID冲突:HTML中所有帖子的编辑按钮、内容容器、表单都使用了相同的
id(如edit、content、edit_post),但DOM要求id必须唯一,document.querySelector()只会返回第一个匹配的元素,导致无论点击哪个按钮,都只会操作第一个帖子的元素。 - JS逻辑未绑定当前帖子:代码未关联按钮与所属帖子的关系,无法定位到点击按钮对应的目标内容和表单。
- 表单提交未同步后端:原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
相关产品推荐
相关产品推荐

