toothie
toothie

Reputation: 1029

jquery .click() event issue (django)

I have created a view that generates posts and each post have a comments_set which produces all the comments made by user. When new comment is posted, below function is executed.

$("#comment-post-button").click(function(){ 
    var event_id = document.getElementById('event-id').value;
    var url= '/post-comments/'+event_id +'/';

    var data = {csrfmiddlewaretoken: document.getElementsByName('csrfmiddlewaretoken')[0].value,
              content:document.getElementsByName('comment-post-content')[0].value
             }
    $.post(url , data, function(){
      $("#refresh-comments").load("/comments/" + event_id + '/', function(){
        $("#comment-post-content").val(""); 
      });
    });
    $("#comment-post-content").val("");  
    return false;
  });

The problem is that the page contains multiple posts and each comment submission button has the same id "comment-post-button". So the above function works only for the top post and not for the rest. I can see what the problem is but don't know how to solve this. Please help.

Here is the html markup:

{% for event in events %}
    <div class="post">
      <div class="post-right">
        <div class="post-author"><a href="/{{ event.author.username }}/">{{ event.author.first_name }}</a></div>
        <div class="post-content">{{ event.description }}</div> 
        <div class="post-bar">
          <div class="post-likes">{{ event.up_votes }}<a href="#"><img src="/site-media/static/images/like.png" /></a></div>
          <div class="post-dislikes">{{ event.down_votes }}<a href="#"><img src="/site-media/static/images/dislike.png" /></a></div> 
          <div class="post-timestamp">{{ event.pub_date }}</div>
          <a href="#" class="post-comment-link">Comment</a>
        </div>
        <div class="post-comment">
          <form method="post" action="/post-comments/{{ event.id }}/">
            {% csrf_token %}
            <input type="text" id="comment-post-content" name="comment-post-content" maxlength="200" placeholder="Add a comment..." />
            <input type="hidden" id="event-id" value="{{ event.id }}">
            <input type="submit" id="comment-post-button" class="comment-post-button" value="Post comment" />
          </form>
        </div>
        <div id="refresh-comments" class="comments">
          {% include "comments.html" %}
        </div>
      </div>
      <div class="post-left">
        <a href="#"><img src="../FestJanta/site-media/static/images/post.jpg" /></a>
      </div>
    </div>
    {% endfor %}

comments.html:

{% for comment in event.comment_set.all|slice:"3" %}
<div class="comments-right">
    <a href="/{{ name }}/" class="first_name">{{ comment.author.first_name }}</a>
    {{ comment.content }}<br>
    <div class="comment-timestamp">{{ comment.pub_date }}</div>
</div>
<div class="comments-left"><a href="#"><img src="../FestJanta/site-media/static/images/comment.jpg" /></a></div>
{% endfor %}

Final working solution:

$(".comment-post-button").click(function(){ 
    var btn = $(this);
    var currentPost = btn.parents('.post');
    var event_id = currentPost.find('.event-id').val();
    var url= '/post-comments/'+event_id +'/';
    var data = {csrfmiddlewaretoken: document.getElementsByName('csrfmiddlewaretoken')[0].value,
              content:currentPost.find('input[name="comment-post-content"]').val()
             }
    $.post(url , data, function(){
      $(currentPost.find('.refresh-comments')).load("/comments/" + event_id + '/', function(){
        $(currentPost.find('.comment-post-content')).val(""); 
      });
    }); 
    return false;
  });

Upvotes: 1

Views: 1375

Answers (3)

Markus
Markus

Reputation: 652

You could do the following:

  • Identify all post buttons, e.g. by a class like .comment-button
  • Use the .on() notation of jQuery
  • Pass the event and use its target property to identify the DOM element that triggered the event
  • Use traversion to get the correct DOM element of the post

The result should look something like this (untested):

Markup (I basically just got rid of the IDs; not sure how/if django handles this automatically):

{% for event in events %}
<div class="post">
  <div class="post-right">
    <div class="post-author"><a href="/{{ event.author.username }}/">{{ event.author.first_name }}</a></div>
    <div class="post-content">{{ event.description }}</div> 
    <div class="post-bar">
      <div class="post-likes">{{ event.up_votes }}<a href="#"><img src="/site-media/static/images/like.png" /></a></div>
      <div class="post-dislikes">{{ event.down_votes }}<a href="#"><img src="/site-media/static/images/dislike.png" /></a></div> 
      <div class="post-timestamp">{{ event.pub_date }}</div>
      <a href="#" class="post-comment-link">Comment</a>
    </div>
    <div class="post-comment">
      <form method="post" action="/post-comments/{{ event.id }}/">
        {% csrf_token %}
        <input type="text" name="comment-post-content" maxlength="200" placeholder="Add a comment..." />
        <input type="hidden" name="event-id" value="{{ event.id }}">
        <input type="submit" class="comment-post-button" value="Post comment" />
      </form>
    </div>
    <div class="comments">
      {% include "comments.html" %}
    </div>
  </div>
  <div class="post-left">
    <a href="#"><img src="../FestJanta/site-media/static/images/post.jpg" /></a>
  </div>
</div>
{% endfor %}

Javascript:

$("body") // Could be any ancestor, body is not the best option
.on('click', '.comment-post-button' function(ev){ 
    var clickTarget = ev.target, // The element clicked on
    postElement = $(clickTarget).closest('.post'), // the div enclosing a post
    commentSection = $(postElement).find(".comments"), // the div enclosing the comments
    commentInputField = $(clickTarget).siblings("[name='comment-post-content']"),
    event_id = $(clickTarget).siblings("[name='event-id']").val(),
    url= '/post-comments/'+event_id +'/';

    // Not sure what this token is, so I will not change that part
    var data = {csrfmiddlewaretoken: document.getElementsByName('csrfmiddlewaretoken')[0].value,
          content: commentInputField.val()
         }
    $.post(url , data, function(){
        $(commentSection).load("/comments/" + event_id + '/', function(){
            $(commentInputField ).val("").prop('disabled', false); // In the callback, empty and reenable
        });
    });
    $(commentInputField ).prop('disabled', true); // I guess disabling the field makes sense
    return false;
});

An additional advantage is that you will end up with only one click handler. Note that the solution could be optimized (e.g. by improving the selectors). In addition, jslint will give some warnings.

Upvotes: 1

cytofu
cytofu

Reputation: 903

to give each post & post_Comment_button a unique id, as suggested by someone, change the markup as follows:

{% for event in events %}
    <div class="post" id="post_{{forloop.counter}}">

    [...]

    <input type="submit" id="comment-post-button_{{forloop.counter}}" class="comment-post-button" value="Post comment" />

then change the js function as follows:

$("#comment-post-button").click(function(event){ 
    var buttonNr = event.target.id.replace('comment-post-button_', ''); 
    var postId = 'post_' + buttonNr;

    $("#" + postId)... --> and do whatever with it..

Upvotes: 0

karaxuna
karaxuna

Reputation: 26940

Remove id and add class:

<input type="hidden" class="event-id" value="{{ event.id }}">

Do something like this:

$('.comment-post-button').click(function(){
    var $btn = $(this);
    var $currentPost = $btn.parents('.post');
    var event_id = $currentPost.find('.event-id').val();
    //...
});

Find each element in $currentPost scope: Instead of this:

content: document.getElementsByName('comment-post-content')[0].value

Do this:

content: $currentPost.find('input[name="comment-post-content"]').val()

Upvotes: 1

Related Questions