0

I'm having trouble with a jQuery script for voting on posts. I have a "frontpage" with a list of posts from a mysql db. On each post there is a little votebox you can either vote up or down.

jQuery/ajax script:

<script type="text/javascript">
$(document).ready(function() {

    $('.plus-button').click(function(){
        var postid = <?php echo $postloopid; ?>;
        $('.minus-button').removeClass('disliked');    
        $(this).toggleClass('liked');
        alert(postid);

        $.ajax({
            type:"POST",
            url:"php/votesystem.php",
            dataType : 'html',
            data:'act=like&postid='+postid,
            success: function(data){
                $('.plus-button').html(data);
            }
        });
    });
    $('.minus-button').click(function(){
        var postid = $(this).attr('id');
        $('.plus-button').removeClass('liked');    
        $(this).toggleClass('disliked');

        $.ajax({
            type:"POST",
            url:"php/votesystem.php",
            dataType : 'html',
            data:'act=dislike&postid='+postid,
            success: function(data){
                $('.minus-button').html(data);
            }
        });
    });
});
</script>

Votebox.php

<?php
// If postid is from frontpage use $postloopid as $postid
if(isset($postloopid)){
    $postid = $postloopid;
}
$postid = htmlentities($postid, ENT_QUOTES);;

include("connect.php");


//For test purposes
echo $postid;



// If user logged in show votebox
if(isset($_SESSION['username'])){

    $userid = $_SESSION['userid'];

    $sql2 = mysqli_query($connect,"SELECT * FROM posts WHERE id='$postid' AND deleted=0");

    if($sql2){

        $voterow = mysqli_fetch_assoc($sql2);



         $checkupvote = $voterow['upvoters'];

         $checkdownvote = $voterow['downvoters'];

         $checkupvote = explode(" ",$checkupvote);

         $checkdownvote = explode(" ",$checkdownvote);

         if($checkupvote = array_search($userid,$checkupvote) == true){

            echo '<div class="plus-button liked" name="like">+ ' . $voterow['totalupvotes'] . '</div>';

            echo '<div class="minus-button" name="dislike">- ' . $voterow['totaldownvotes'] . '</div>';


         }

         elseif($checkdownvote = array_search($userid,$checkdownvote) == true){

            echo '<div class="plus-button" name="like">+ ' . $voterow['totalupvotes'] . '</div>';

            echo '<div class="minus-button disliked" name="dislike">- ' . $voterow['totaldownvotes'] . '</div>';


         }

         else{

            echo '<div class="plus-button" name="like">+ ' . $voterow['totalupvotes'] . '</div>';

            echo '<div class="minus-button" name="dislike">- ' . $voterow['totaldownvotes'] . '</div>';

         }

    }
    else {
        echo 'No result <br />';

    }

}
else {
    echo 'Cant find user';
}









?>

Frontpage:

<?php
$sql1 = mysqli_query($connect,"SELECT * FROM posts WHERE totalupvotes < $trendmin AND deleted=0 ORDER BY added DESC LIMIT 0,10");
if($sql1){
    while($row = mysqli_fetch_array($sql1)){
        $postloopid = $row['id'];


        ?>
        <div id="postlist">

        <div style="width:400px; font-size:18px; font-weight:bold;">
        <a target="_blank" href="post.php?id=<?php echo $row['id']; ?>"><?php echo $row['title']; ?></a>
        </div><br />
        <article class="slide"><?php echo nl2br($row['post']); ?></article>





        <br />
        <?php include("php/votebox.php"); ?>
        <br />
        by <a style="font-size:18px;" href="profile.php?id=<?php echo $row['submittedby']; ?>"><?php echo $row['submitteduser']; ?></a>
         at <span style="font-size:12px;"><?php echo $row['added']; ?></span><span style="float:right; margin-right: 10px;"><a target="_blank" href="post.php?id=<?php echo $row['id']; ?>#commentfield"><?php echo $row['totalcomments']; ?> comments</a></span>



         </div>


<?php
    }
}
?>

The problem now is that when I click the votebox buttons it turns out I only get the postid from the first loaded post from the while loop. Another problem I have is that my total up- and downvotes changes in all the posts on the list, not the specific post.

Any ideas?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
  • 1
    It looks like you're using html_entities to "escape" your $postid, use mysql_real_escape to do this, html_entities is by no means secure. – LJᛃ Aug 29 '14 at 01:20
  • You're selecting all rows from `posts`, then selecting a single row from `posts`. That's an additional 10 more queries that don't need to be made (considering the `LIMIT` clause). Just check if `$row` is set at the top of `Votebox.php`, or do this in a single file. – mstrthealias Aug 29 '14 at 01:24
  • @LJ_1102 Thnx for the advice. – Sindre Sørensen Aug 29 '14 at 01:26
  • @myninjaname I'm using another infinite scroll script not included in this problem. – Sindre Sørensen Aug 29 '14 at 01:27
  • Maybe create a function that accepts a `posts` row and returns the Votebox HTML; call this from your other location and inside your `posts` fetch loop. – mstrthealias Aug 29 '14 at 01:29
  • 1
    In addition to @LJ_1102 's comment, see http://stackoverflow.com/questions/60174/how-can-i-prevent-sql-injection-in-php?rq=1 – mstrthealias Aug 29 '14 at 01:30

2 Answers2

1

Alright lets go through your jQuery code(just the like function):

Bind click handler to all elements with the class plus-button

$('.plus-button').click(function(){
    var postid = <?php echo $postloopid; ?>;

Remove disliked class from all elements with class minus-button

    $('.minus-button').removeClass('disliked');    
    $(this).toggleClass('liked');
    alert(postid);

    $.ajax({
        type:"POST",
        url:"php/votesystem.php",
        dataType : 'html',
        data:'act=like&postid='+postid,
        success: function(data){

Set inner html of all elements with class plus-button

            $('.plus-button').html(data);
        }
    });
});

What you want is to store the postid as an attribute on the posts, then use

var postid = $(this).attr('postid')

only remove the disliked class from the current element

$(this)
    .parents('{element containing plus and minus button}')
    .find('.minus-button')
        .removeClass('disliked')
;

store reference to the clicked element

var $this = $(this); // Cool guys do this right in the beginning and use only this variable instead of $(this)

then in your ajax success handler you want to set only the html of the button you clicked, so you may use the reference you stored in the parent function scope

$this.html(data);

EDIT in regards to infinite scroll script

When parts of your page are dynamic, you want to bind the click handler to a static parent element of the dynamic content using:

$("#postsParent").on('click', '.plus-button', function () {/*handler*/})
LJᛃ
  • 7,655
  • 2
  • 24
  • 35
  • Uhm... I see where this is going and I like it, but what's a click handler to all elements with the class plus-button? You mean like
    ? And what would that look like?
    – Sindre Sørensen Aug 29 '14 at 01:43
  • the "click handler" is the method discussed above, your function handling the click event. No special markup required, also be sure to only include the script once not for every row. – LJᛃ Aug 29 '14 at 01:47
1

The Javascript code isn't in the loop, it can't reference postloopid. Or if it is, you're binding all buttons of the class every time through the loop, and clicking on them will run all the handlers, incrementing all the posts.

You should put the post ID as a data field in the button, and then access that from the Javascript:

echo '<div class="plus-button liked" data-postid="'+$postid+'" name="like">+ ' . $voterow['totalupvotes'] . '</div>';

echo '<div class="minus-button" data-postid="'+$postid+'" name="dislike">- ' . $voterow['totaldownvotes'] . '</div>';

Then your Javascript can do:

$('.plus-button').click(function(){
    var postid = $(this).data('postid');
    $(this).siblings('.minus-button').removeClass('disliked');    
    $(this).toggleClass('liked');
    alert(postid);

    $.ajax({
        type:"POST",
        url:"php/votesystem.php",
        dataType : 'html',
        data:'act=like&postid='+postid,
        context: this,
        success: function(data){
            $(this).html(data);
        }
    });
});

I made the following changes to the JS:

  1. Use $(this).data('postid') to get postid.
  2. Only remove the disliked class from the sibling minus button, not all minus buttons on the page.
  3. Put the returned HTML just in this element, not in all plus buttons. I pass this in the context: parameter, so that the success function can refer to it.
Barmar
  • 741,623
  • 53
  • 500
  • 612