How can I make this click counter work properly?

I'm making a Marvel API project. I added a Like button so it could count the likes, but everytime I click any like button the only counter adding numbers is the first one. Can anyone tell me how I do this properly?

enter image description here

The problem is because the ID is being made by a for, so how could I solve this?

This is my JS code (on the js file of my project) :

success: function(data)
{
    footer.innerHTML = data.attributionHTML;
    var string = "";
    string += "<div class='row'>";

    for (var i = 0; i < data.data.results.length; i++)
    {
        var element = data.data.results[i];                   
        string += '<div class="col-md-3" align="center">';                 
        string += "<img src='" + element.thumbnail.path + "/portrait_fantastic." + element.thumbnail.extension + "'/>";                       
        string += '<button class="btn btn-success" onClick="testo()"><i class="fas fa-thumbs-up"></i> | <a id="likes">0</a></button>';
        string += "<h3>" + element.title + "</h3>";                                                  
        string += "</div>";

        if ((i + 1) % 4 == 0) 
        {
            string += "</div>";
            string += "<div class='row'>";
        }
    }

    marvelContainer.innerHTML = string;
}

And this is my onclick function (It is on my html file because it wont work on my js file)

<script>
var likes=0;
function testo()
{
    likes += 1;
    document.getElementById("likes").innerHTML = likes;
}
</script>

Upvotes: 1

Views: 109

Answers (2)

Adriano Marra
Adriano Marra

Reputation: 129

That is because all your buttons are being generated with the same id="likes" and then you are changing the HTML with document.getElementById("likes").innerHTML = likes; for your code to work properly you will need to use a different approach, maybe adding a data-* attribute to your buttons and then change the likes by the data-* atribute using .getAttribute('data-id-something').innerHTML instead of document.getElementById("likes").innerHTML. Or even better in this case you can give the buttons a class name and handle it with: document.getElementsByClassName("like-btn")

You can check the last option in this example:

 	var init = function(data){
                var string ="";
                string += "<div class='row'>";
                for(var i = 0; i<4; i++)
                {
                    // var element = data.data.results[i];                   
                    string += '<div class="col-md-3" align="center">';                 
                    string += "<img src='/portrait_fantastic.jgeg'/>";                       
                    string += '<button class="btn btn-success" onClick="testo('+i+')"><i class="fas fa-thumbs-up"></i> | <span class="like-btn">0</span></button>';
                    string += "<h3>Element title</h3>";                                                  
                    string += "</div>";                   
                    if((i+1) % 4 ==0) 
                    {
                        string += "</div>";
                        string += "<div class='row'>";
                    }
                }
                document.getElementById("marvelContainer").innerHTML = string;
            }
            init();
<script>
var likes=0;
function testo(id) {      
        var btn = document.getElementsByClassName("like-btn");
        likes = parseFloat(btn[id].innerHTML);
        likes += 1;
        btn[id].innerHTML = likes;
    }
</script>

<div id="marvelContainer"></div>

I hope it will help you...

Upvotes: 4

Taplar
Taplar

Reputation: 24965

  • Gave the buttons a class, as that is what is going to be clicked.
  • Changed the link element to a span. Having a link in a button doesn't make much sense, as you can't "not" click the button and click the link.
  • Removed the inline onclick for the link and added an event listener logically on all the buttons.
  • The click logic finds the nested span in the button
    • It takes the data attribute on the span, turns it into an integer, and increments it
    • It then updates the data attribute value for the next click
    • And finally it updates the visible text that the user can see

EDIT

  • Changed it to bind the click event listener on the span as well and stop propagation on the click event. Actually clicking the span was causing the click event to register for the span, and not the button.

// fake out some data for the element generation
var data = { data: {
  results: [
    { thumbnail: { path: '/path1', extension: 'jpg', title: 'Element1' } }
    ,{ thumbnail: { path: '/path2', extension: 'png', title: 'Element2' } }
    ,{ thumbnail: { path: '/path3', extension: 'gif', title: 'Element3' } }
  ]
} };
// fake out the container the elements are built to
var marvelContainer = document.querySelector('#container');

var string = "<div class='row'>";

for (var i = 0; i < data.data.results.length; i++) {
  var element = data.data.results[i];
  string += '<div class="col-md-3" align="center">';
  string += "<img src='" + element.thumbnail.path + "/portrait_fantastic." + element.thumbnail.extension + "'/>";
  // put a class on the button
  // also removed the id and inline onclick
  // change the id on the link to a class
  // also initialized the data-likes on the link to zero
  string += '<button class="btn btn-success likes-button"><i class="fas fa-thumbs-up"></i> | <span class="likes" data-likes="0">0</span></button>';
  string += "<h3>" + element.title + "</h3>";
  string += "</div>";

  if ((i + 1) % 4 == 0) {
    string += "</div>";
    string += "<div class='row'>";
  }
}

marvelContainer.innerHTML = string;

document.querySelectorAll('.likes-button, .likes').forEach(function(likeButton){
  likeButton.addEventListener('click', incrementLikes);
});

function incrementLikes (e) {
  e.stopPropagation();
  
  // find the inner likes element of the button
  var likes = e.target.querySelector('.likes') || e.target;

  // increment the likes
  var incrementedLikes = parseInt(likes.dataset.likes) + 1;

  // update the data attribute for next click, and update the text
  likes.dataset.likes = incrementedLikes.toString();
  likes.innerText = incrementedLikes;
}
<div id="container">
</div>

Upvotes: 1

Related Questions