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?
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>
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...
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>
Related
i am using storelocater.js for multiple location in google map and show the information according to the location with image. i can show only one image but i want to show multiple images inside the information panel. link this
Here is my code
var panelDiv = document.getElementById('panel');
storeLocator.Panel.NO_STORES_IN_VIEW_HTML_ = '<li class="no-stores">The nearest outlet:</li>';
var Store = storeLocator.Store;
Store.prototype.generateFieldsHTML_ = function(fields) {
var html = '';
html += '<div class="store-data">';
if(this.props_['title']){
html += '<div class="title"><div class="img-list clearfix">' +
for (var i = 0; i <= this.props_[images].length; i++) {
console.log(this.props_[images[i]]);
// <img src=' + this.props_['images'] + '>
}
+ '</div></div>'
}
html += '</div>';
return html;
}
var data = new storeLocator.StaticDataFeed;
data.setStores([
new storeLocator.Store('store02', new google.maps.LatLng(27.67663,85.31093), null, {images: ["img/thapathalil.jpg","img/thapathalil.jpg","img/thapathalil.jpg"]})
]);
and it shows:
Uncaught SyntaxError: Unexpected token for...
how can i solve this?? how can i fetch location inside of "images"
THANKS in advance
Actually you got Uncaught SyntaxError: Unexpected token for... because you used the for..loop in the string concatenation statement, directly after the + sign.
Change this code :
html += '<div class="title"><div class="img-list clearfix">' +
for (var i = 0; i <= this.props_[images].length; i++) {
console.log(this.props_[images[i]]);
// <img src=' + this.props_['images'] + '>
}
+ '</div></div>'
To the following:
html += '<div class="title"><div class="img-list clearfix">';
for (var i = 0; i <= this.props_['images'].length; i++) {
console.log(this.props_['images'][i]);
html += '<img src=' + this.props_['images'][i] + '>';
}
html += '</div></div>'
Note:
You should separate the concatenation of strings to the html
variable and the for loop logic, using html += instead of just using concatenation with + sign on multiple lines.
Make sure to wrap the properties names between two '' while accessing your objects, like in this.props_[images] where it should be this.props_['images'] and in this.props_[images[i]] where it should be this.props_['images'][i].
And the first 2 lines of your html variable decalaration and the concatenation, var html = ''; html += '<div class="store-data">'; can be shortened to just var html = '<div class="store-data">';.
I think there is a typo. Change this:
console.log(this.props_[images[i]])
to
console.log(this.props_['images'][i])
And you should use
i < this.props_['images'].length
So try this:
for (var i = 0; i < this.props_['images'].length; i++) {
console.log(this.props_['images'][i]);
}
I am trying to allow clients to create a list of students then view more info by simply clicking on the button with the students name. I've got it to create the button and display the students name in the button but it only calls the function when I click submit to add the student to the list, the actual student button doesn't seem to function.
function updateStudentList() {
var html = "";
for (var i = 0; i < students.length; i++) {
html += "<li><button type='button' class='studentButton'" + "id=" + students[i].name +">" + students[i].name + "</button></li>";
}
$('#studentList').html(html);
for (var i = 0; i < students.length; i++) {
document.getElementById(students[i].name).addEventListener('click', openStudentInfo(students[i].name));
}
}
function openStudentInfo(studentName) {
console.log("Opening " + studentName + " info.");
var studentInfo = requestStudentByName(studentName);
if (studentInfo != null) {
var studentInfoForm = $("#studentInfoForm");
var html = "";
html += "<h3>Student Name: " + studentInfo.name + "</h3>";
html += "<h3>Student ID: " + studentInfo.studentID + "</h3>";
studentInfoForm.html(html);
$("#studentInfoModal").show();
}
}
HTML:
<ul data-role="listview" id="studentList"> </ul>
Note: I can't use the onclick tag in HTML, it causes security issues. Cordova also blocks this.
The way you binding the event is not ok. Try binding this way:
$(document).ready(function() {
$("#studentList").on("click", ".studentButton", function() {
var studentId = $(this).data("studentid");
openStudentInfo(studentId);
});
});
And in your HTML generation:
html += "<li><button type='button' class='studentButton' data-studentid='" + students[i].studentID +"'>" + students[i].name + "</button></li>";
This kind of event delagation works not metter how you create the elements inside the root element(studentList in this case), because the event was bound in it, and not on the dynamic elements.
no jquery version of DontVoteMeDown's answer
document.getElementById('studentList').addEventListener('click', function(event) {
var clickedEl = event.target;
if(clickedEl.className === 'studentButton') {
var studentId = clickedEl.dataset.studentId;
openStudentInfo(studentId);
}
});
I am building a cat clicker game. Where I Have five cat and each one has click function which increase count on every click.
I have created a DOM from javascript function, I have create image from document.create method so that I can attached event to it. So my javascript code contains both pure HTML and HTMLElementObj. fiddle.
But innerHTML is not paring the element which is creating by the document.createElement
function fac(name) {
this.name = name;
this.count = 0;
}
var cats = ['cat1', 'cat2', 'cat3', 'cat4', 'cat5']
function $(name) {
return document.querySelector(name);
}
var html = "";
for (var i = 0; i < cats.length; i++) {
var cat = new fac(cats[i])
html += "<div>";
html += "<div>" + cat.name + "</div>";
var elem = document.createElement('img');
elem.src = 'https://c2.staticflickr.com/2/1126/625069434_db86b67df8_n.jpg';
html += elem;
html += "<div class='count " + i + "'></div>";
html += "</div>";
elem.addEventListener('click', function() {
$('.count' + i).innerText = ++cat.count;
}, false);
$('#getClicked').innerHTML = html
}
You are mixing strings and dom elements and that won't work. Just do everything as strings
html += "<div>";
html += "<div>" + cat.name + "</div>";
var elem = "<img src='https://c2.staticflickr.com/2/1126/625069434_db86b67df8_n.jpg'>";
html += elem;
html += "<div class='count " + i + "'></div>";
or as dom elements (a bit more learning needed to start with but better in the long run)
I'm trying to make an application with JSON data for a frisbee tournament. I'm now working on a page where you can view and edit the scores from a match. It should be possible to increase or decrease the score of either of the two teams. It looks like this:
I skipped some parts from the code to make it easier to read. This is the relevant code:
gamePage: function(data){
var score1 = parseInt(data.team_1_score),
score2 = parseInt(data.team_2_score);
var html = '',
html_pool_open = '<section class="new_page">';
html = html + html_pool_open;
var html_pool_top = '<div class="game">';
html = html + html_pool_top;
var html_team_1 = '<div class="name">'+data.team_1.name+'</div>'
+ '<div class="score score_team1">'
+ '<a href="#" onclick="" ><img src="/images/plus.png"/></a>'
+ '<span>'+score1+'</span>'
+ '<img src="/images/minus.png"/>'
+ '</div></div>';
The score between the span must be increased or decreased onclick
html = html + html_team_1;
x$('#content').html(html);
}
I'm not allowed to do it with jQuery, so vanilla JavaScript only please.
I would do somethig like this:
LIVE DEMO
var data = {
team_1_score: 42,
team_2_score: 6,
team_1 : {name:'Beast Amsterdam'},
team_2 : {name:'Amsterdam Money Gang'}
};
var SCORE_APP = {
tools : {
setScore : function( i, pm ){
var currScore= parseInt( data['team_'+ i +'_score'] , 10);
if(currScore=='0' && pm=='dn') return; // prevent -1 score
var newScore = data['team_'+ i +'_score'] += (pm=='up'? 1 : -1);
document.getElementById('team_'+ i +'_score').innerHTML = newScore;
}
},
game : {
gamePage : function(data) {
var html = '<section class="new_page">';
for(var i=1; i<3; i++){
html += '<div class="game"><div class="name">'+ data['team_'+i].name +'</div>'+
'<div class="score score_team1">'+
'<a href="javascript:;" onclick="SCORE_APP.tools.setScore(\''+i+'\',\'up\')">'+
'<img src="http://i.imgur.com/axk6J7M.jpg"/></a>'+
'<span id="team_'+i+'_score">'+ data['team_'+i+'_score'] +'</span>'+
'<a href="javascript:;" onclick="SCORE_APP.tools.setScore(\''+i+'\',\'dn\')">'+
'<img src="http://i.imgur.com/movjGkd.jpg"/></a>'+
'</div></div>';
}
html += '</section>';
document.getElementById('content').innerHTML = html;
}
},
init : function(){
this.game.gamePage(data);
}
};
SCORE_APP.init();
Add an ID to the span holding the score, and onclick send to the method setScore two arguments:
the team number (i = 1||2)
and the type of math we need to apply to the current score (I used a string representation "up" and "dn").
This two arguments are all you need to immediately keep up to date the data Object (holding the game stats) and apply the changes on screen to the targeted SPAN ID.
Set the onclick attribute within the <a href="#" onclick="" ></a> tag to calling a function such as increaseScore:
onclick="increaseScore()"
And give the span element an id:
<span id="myScore">
Then write a function which adds the score:
function increaseScore()
{
score1++;
document.getElementById("myScore").innerHTML=score1;
}
I creating jquery code to generate specific html-blocks that contains html-controls (e.g. textbox, textarea, button) with two buttons - add new block and delete current block:
$(document).ready(function () {
// Constants
var SEPARATOR = "`";
var SEPARATOR_START = "<span class='Hidden'>";
var SEPARATOR_END = "</span>";
var SEPARATOR_BLOCK = SEPARATOR_START + SEPARATOR + SEPARATOR_END;
var CONTAINER = "#weContainer";
// Initialize container
InitializeDataContainer(CONTAINER);
// Buttons events
$(".AddBlock").click(function () {
AddBlock(CONTAINER);
});
$(".DeleteBlock").click(function () {
DeleteBlock(CONTAINER, GetParentId(this));
});
// Functions
function GetParentId(container) {
var id = ($(container).parent().attr("id")).replace(new RegExp("_", 'g'), "");
return id;
}
function Template() {
var uniqueId = Math.random() * 10000000;
var template = "<div class='weBlock' id='_" + uniqueId + "_'>";
template += "<input type='button' value='add' class=\"AddBlock\" />";
template += "<input type='button' value='del' class=\"DeleteBlock\" />";
template += "<br/>";
template += "<input type='text' class='weStartDate weTextbox' />";
template += " ";
template += "<input type='text' class='weEndDate weTextbox' />";
template += "<br/>";
template += "<input type='text' class='weCompany weTextbox' />";
template += " ";
template += "<input type='text' class='weJobTitle weTextbox' />";
template += "<br/>";
template += "<input type='text' class='weClients weTextbox' />";
template += " ";
template += "<input type='text' class='weProjectName weTextbox' />";
template += "<br/>";
template += "<textarea type='text' rows='4' cols='40' class='weProjectDesc weTextarea'></textarea>";
template += "<br/>";
template += "<textarea type='text' rows='6' cols='40' class='weActivities weTextarea'></textarea>";
template += "<br/>";
template += "<textarea type='text' rows='4' cols='40' class='weToolsTech weTextarea'></textarea>";
template += "</div>";
template += SEPARATOR_BLOCK;
return template;
}
function GetIdFromTemplate(template) {
var array = template.split('_');
return array[1];
}
function AddBlock(container) {
$(container).append(Template());
}
function DeleteBlock(container, id) {
var content = $(container).html();
content = content.replace(new RegExp("\<span class='Hidden'\>", "g"), "")
.replace(new RegExp("\</span\>", "g"), "");
var blocks = content.split(SEPARATOR);
content = "";
var index;
for (var i = 0; i < blocks.length; i++) {
if (GetIdFromTemplate(blocks[i]) != id && !IsNullOrEmpty(blocks[i])) {
content += blocks[i] + SEPARATOR_BLOCK;
}
else {
index = i;
}
}
$(container).html(content);
}
function IsNullOrEmpty(string) {
if (string == null || string == 'undefined' || string.length == 0) {
return true;
}
else {
return false;
}
}
function InitializeDataContainer(container) {
$(container).html(Template());
}
});
For the first time (when page load) i created first block:
function InitializeDataContainer(container) {
$(container).html(Template());
}
My problem is next - buttons Add and Delete work only for this first html-block that i created when page load, but if i add new blocks in page using Add button (from this first block that works) then buttons Add and Delete from this new blocks doesn't work!
Sorry for may be not good code, im not javascript engineer:)
Use the .live() instead:
$(".AddBlock").live("click", function () {
AddBlock(CONTAINER);
});
And same for the other class - the .click() is "static" only for the elements that exists in the time of calling it, while .live() should work for any existing or future elements.
Your jQuery code adds event handlers to the Add/Delete buttons you create uses click to do so:
$(".AddBlock").click(function () {
AddBlock(CONTAINER);
});
This only affects HTML elements that are already in the page.
One solution would be to change it to
$(".AddBlock").live('click', function () {
AddBlock(CONTAINER);
});
to make it also work for elements that get added to the page later.
Another solution would be to manually add the click event handlers to any elements you add to the page dynamically:
function AddBlock(container) {
var $template = $(Template());
$(container).append($template);
$template.find(".AddBlock").click, function () {
AddBlock(CONTAINER);
});
$template.find(".DeleteBlock").click, function () {
DeleteBlock(CONTAINER, GetParentId(this));
});
}