user17470872
user17470872

Reputation: 55

How can I addEventListener to multiple buttons which are displayed via a for loop?

I have a page that displays social media posts from users and all posts have an Edit button. When the Edit button is clicked on, a form with a textarea pre-filled with the current content and a submit input is displayed.

The problem is that regardless of which post's Edit button I click, always the first post is changing. I guess I should add an "id" somewhere to track which post is being edited but I couldn't figure out how to do that. Or is there another solution?

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);
    };
});

Upvotes: 0

Views: 356

Answers (1)

Franco Torres
Franco Torres

Reputation: 1106

Just remove the absolute id-selectors because with an absolute id-selector you just get always the first element with that id. So change it for a relative class-selector instead. Also, I recommend you to use addEventListener instead of on<event>.

Change the .html to the following:

{% 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 edit">Edit</button>
            {% endif %}
            <div class="content">{{post.content}}</div>
            <form action="{% url 'edit' post.id %}" method="post" class="edit_post" style="display: none;">
                {% csrf_token %}
                <div class="form-group"><textarea class="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 %}

Change the .js to the following:

document.addEventListener("DOMContentLoaded", () => {
  const content = localStorage.getItem("content")
  if (content) document.querySelector(".content").textContent = JSON.parse(content)
  Array.from(document.querySelectorAll(".edit")).forEach(button => {
    button.addEventListener('click', event => {
      const target = event.target
      const frame = target.closest('.frame')
      const content = frame.querySelector('.content')
      const edit_post = frame.querySelector('.edit_post')
      edit_post.style.display = 'block'
      const new_content = frame.querySelector('.new_content')
      new_content.value = content.textContent
      content.style.display = 'none'
    })
    const form = button.closest('.frame').querySelector('.edit_post')
    form.addEventListener('submit', event => {
      const target = event.target
      const frame = target.closest('.frame')
      const content = frame.querySelector('.content')
      const new_content = frame.querySelector('.new_content')
      content.textContent = new_content.value
      localStorage.setItem('content', JSON.stringify(new_content.value))
      content.style.display = 'block'
      const edit_post = frame.querySelector('.edit_post').style.display = 'none'
      event.preventDefault()
    })
  });
});

Upvotes: 1

Related Questions