I am creating a simple todo app.
When I add a todo and list them, I want to show delete and change status button with every todo,
But only one button appears,
See, first button(delete button) is very tiny and doesn't even function. But it works perfectly when I add only 1 button.
Here is the js for adding the list item and button,
var view = {
displaytodos: function(){
var todoul = document.querySelector('ul');
todoul.innerHTML = '';
for (var i=0;i < todolist.todos.length;i++){
var newli = document.createElement('li');
newli.setAttribute('class','text-success');
var todotextwithstatus = '';
if (todolist.todos[i].completed === true){
todotextwithstatus = '(X) - ' + todolist.todos[i].todotext + ' --> ';
}
else{
todotextwithstatus = '( ) - ' + todolist.todos[i].todotext + ' --> ';
}
newli.id = i;
newli.textContent = todotextwithstatus;
delbtn = this.createdeletebutton();
statusbtn = this.createstatusbutton();
newli.appendChild(statusbtn);
newli.appendChild(delbtn);
todoul.appendChild(newli);
}
},
createdeletebutton: function(){
var delbtn = document.createElement('button');
delbtn.textContent = 'Delete';
delbtn.className = 'DeleteElement';
return delbtn;
},
createstatusbutton: function(){
var statusbtn = document.createElement('button');
delbtn.textContent = 'Change Status';
delbtn.className = 'changestatusbutton';
return statusbtn;
},
This is the html,
<div class='col-xs-4'>
<h3>Your todos will appear here.</h3>
<ul>
<li>You have not added any todos yet.</li>
</ul>
<button onclick='handlers.toggle_all()'>Change status of all Todos</button>
</div>
What is the solution for this? Why is this happening?
Related
I have a problem with the function below. It's taking the data from JSON and it's creating a menu item. The problem is when there are more than 2 menu items, and I try to increase the quantity of the first item then the value of the second item is increasing.
function ShowTheMenu(theCategoryId) {
var parentEl = document.getElementById("itemlist");
ClearMenu();
for (let i = 0; i < data.length; i++) {
if (data[i].KategorijaBroj == theCategoryId) {
// MAIN PARENT
var itemBox = document.createElement("div");
itemBox.classList.add("itembox");
var itemImage = document.createElement("img");
itemImage.classList.add("itemimage");
itemImage.src = "/menuitemsimages/" + data[i].Image;
var itemContent = document.createElement("div");
itemContent.classList.add("itemcontent");
var itemTitle = document.createElement("h3");
itemTitle.classList.add("itemtitle");
itemTitle.innerHTML = data[i].Title;
var itemPrice = document.createElement("p");
itemPrice.classList.add("itemprice");
itemPrice.innerHTML = "$" + data[i].Price;
var itemQnt = document.createElement("p");
itemQnt.classList.add("quantity");
itemQnt.innerHTML = "Quantity";
var buttonsBox = document.createElement("div");
buttonsBox.classList.add("divcontrolbtns");
var itemQuantity = 0;
var quantityValue = document.createElement("div");
quantityValue.innerHTML = itemQuantity;
var increaseBtn = document.createElement("div");
increaseBtn.classList.add("controlbtns");
increaseBtn.innerHTML = "+";
increaseBtn.addEventListener("click", function () {
if(itemQuantity < 10) {
itemQuantity++;
}
quantityValue.innerHTML = itemQuantity;
})
var decreaseBtn = document.createElement("div");
decreaseBtn.classList.add("controlbtns");
decreaseBtn.innerHTML = "-";
decreaseBtn.addEventListener("click", function () {
if(itemQuantity > 0) {
itemQuantity--;
}
quantityValue.innerHTML = itemQuantity;
})
var itemAddToCart = document.createElement("button");
itemAddToCart.classList.add("btn-add-to-cart");
itemAddToCart.textContent = "Add to cart";
var itemDesc = document.createElement("p");
itemDesc.classList.add("itemdesc");
itemDesc.innerHTML = data[i].Description;
itemBox.appendChild(itemImage);
itemContent.appendChild(itemTitle);
itemContent.appendChild(itemDesc);
itemContent.appendChild(itemPrice);
itemContent.appendChild(itemAddToCart);
itemContent.appendChild(itemQnt);
buttonsBox.appendChild(increaseBtn);
buttonsBox.appendChild(quantityValue);
buttonsBox.appendChild(decreaseBtn);
itemContent.appendChild(buttonsBox);
itemBox.appendChild(itemContent);
parentEl.appendChild(itemBox);
}
}
}
IMAGE
What should I do in order for the chosen menu item value to be changed?
Try to do something like this bellow. I try to use same HTML structure that you use but to be honest, I suggest that you change a little bit ;)
<!DOCTYPE html>
<html>
<head>
<script>
// Qt
var quantity = new Array();
function ShowTheMenu(theCategoryId) {
// Clear menu
// ClearMenu();
// bt+
increaseBtn = (i) => {
// Item target
let item = document.getElementById('item_' + i);
// Qt target
let qtSpan = item.getElementsByClassName('qt');
// Qt
let qt = parseInt(qtSpan[0].innerHTML);
// Fix some errors
if (qt === undefined || !qt) qt = 0;
// Increase
if (qt < 10) qt++;
// Update
qtSpan[0].innerHTML = qt;
};
// bt-
decreaseBtn = (i) => {
// Item target
let item = document.getElementById('item_' + i);
// Qt target
let qtSpan = item.getElementsByClassName('qt');
// Qt
let qt = parseInt(qtSpan[0].innerHTML);
// Fix some errors
if (qt === undefined || !qt) qt = 0;
// Decrease
if (qt > 0) qt--;
// Update
qtSpan[0].innerHTML = qt;
};
//
var data = new Array();
data[0] = {
Image:
'https://s2.glbimg.com/WcYUQNaattnUf7d8U8MUBfk7loU=/620x430/e.glbimg.com/og/ed/f/original/2015/10/30/pizza.jpg',
KategorijaBroj: 1,
Title: 'Delicious Pizza',
Price: 10,
Description: 'Description test',
};
for (let i = 0; i < data.length; i++) {
if (data[i].KategorijaBroj == theCategoryId) {
// Img
let img = data[i].Image; // '/menuitemsimages/' + data[i].Image;
// Title
let title = data[i].Title;
// Price
let price = '$' + data[i].Price;
// Description
let desc = data[i].Description;
// Qtd
let qt = 2;
// Matriz
let newItem = `<div id="item_${i}" class="itembox">
<div class="itemcontent">
<img src="${img}" border=0 width=100/>
<h3 class="itemtitle">${title}</h3>
<p class="itemprice">${price}</p>
<div class="quantity">
<span>Quantity : </span>
<span class="qt">${qt}</span>
</div>
<div class="controlbtns">
<button class="addbtn" onClick="increaseBtn(${i})">+</button>
<button class="removebtn" onClick="decreaseBtn(${i})">-</button>
</div>
<button class="btn-add-to-cart">Add to cart</button>
<p class="description">${desc}</p>
</div>
</div>`;
// Get the menulist itens
let parentEl = document.getElementById('itemlist');
// Add item
parentEl.insertAdjacentHTML('beforeend', newItem);
}
}
}
</script>
</head>
<body>
<div id="itemlist"></div>
<script>
ShowTheMenu(1);
</script>
</body>
</html>
It's because both items are sharing the same variable, in this case, itemQuantity.
Option 1
If they all should have their own counter I would recommend using an object for tracking this.
const itemQuantity = {
'item1': 2,
'item2': 5
}
if you add some unique class or id to the element you can use this in your onclick event to use as a key. (Where I used 'item1' and 'item2')
Option 2
If you create a function that does everything that's inside your for loop and then just call that function it should also work. This works because every function then creates its own scoped variable of itemQuanity.
Go with whatever options feel best for now. How to manage data in your frontend has a lot of different ways and opinions. You'll quickly discover what works best in what scenario.
What Olavo Mello is mentioning in his answer could still make your code better. using string literals for small HTML snippets is often more readable than using document.createElement(). I would recommend fixing your counter issue first and then look if you could improve your code with Olavo Mello's answer in mind. Good luck :)
The first part of the code is working correctly, but now that each button appears, how do i add functionality to each of them? currently the only button which does something when pressed is always the last one, the rest do nothing.
Change it to
{
var output = "";
var data = JSON.parse(e.target.responseText);
for(var i=0; i<data.length; i++)
{
output = data[i].title + ' ';
var p = document.createElement("p");
var div = document.getElementById("response");
var textNode = document.createTextNode(output);
p.appendChild(textNode);
var button = document.createElement("button");
button.innerHTML = "Download";
p.appendChild(button);
div.appendChild(p);
button.addEventListener ("click", () =>
{
alert("Test");
});
}
}
You are adding the below code out side the for loop
button.addEventListener ("click", () =>
{
alert("Test");
} );
Keep the above code inside the for loop. So that for each button the event listener will be added.
Another way to approach this would be to add the callback function to the onclick variable of the elements prototype:
function doStuff() {
var output = "";
var data = JSON.parse(e.target.responseText);
for (var i = 0; i < data.length; i++) {
output = data[i].title + ' ';
var p = document.createElement("p");
var div = document.getElementById("response");
var textNode = document.createTextNode(output);
p.appendChild(textNode);
var button = document.createElement("button");
button.innerHTML = "Download";
// Adds the callback function here
button.onclick = () => {
// fill in your arrow function here...
alert("Test");
};
p.appendChild(button);
div.appendChild(p);
};
}
doStuff();
Here is a jsFiddle
You should use event delegation for dynamically added elements
// sample data
var data = [{
title: 'one'
}, {
title: 'two'
},{
title: 'three'
}];
var output = "";
for (var i = 0; i < data.length; i++) {
var output = data[i].title + " ";
var p = document.createElement("p");
var div = document.getElementById("response");
var textNode = document.createTextNode(output);
p.appendChild(textNode);
var button = document.createElement("button");
// added output to button text for identification
button.innerHTML = output + " Download";
p.appendChild(button);
div.appendChild(p);
}
// Get the parent element, add a click listener
document.getElementById("response").addEventListener("click", function(e) {
// e.target is the clicked element!
// If it was a button
if (e.target && e.target.nodeName == "BUTTON") {
// Button found! Output the identifying data!
// do other work on click
document.getElementById("display").innerHTML = e.target.innerHTML + " Clicked";
}
});
<div id="response"></div>
<div id="display">Display</div>
I'm trying to dynamically create a div for each user's campaign.
What I'd really like to see is a "show details" button inside the div that redirects to the campaign details. I tried to run this code, but it only takes the last index.
for (var i = utente.campagne.length - 1; i >= 0; i--) {
var appended = document.createElement("DIV");
appended.className = "jumbotron";
var nome = document.createElement("p");
nome.innerHTML = "Campaign Name: " + utente.campagne[i].nome;
appended.appendChild(nome);
var button = document.createElement("INPUT");
button.type = "button";
var index = i;
button.addEventListener('click', function () {
window.location = "ShowDetails.jsp?indexcampagna=" + index;
});
button.value = "show details";
button.className = "btn btn-primary btn-sm";
appended.appendChild(button);
var col = document.createElement("DIV");
col.className = "col-lg-4 col-sm-6 text-center";
col.appendChild(appended);
document.getElementById("boxes").appendChild(col);
}
I used this code:
var list = was_talkWindows.querySelectorAll('.msg:not(.was_added)');
var i;
for (i = 0; i < list.length; i++)
{
list[i].className += cssClass;
var btn = document.createElement('button');
btn.setAttribute('type', 'button');
btn.setAttribute('class', 'was_addButton');
btn.addEventListener('click', function() {
was_button_act(this.parentElement);
});
btn.innerHTML = buttonName;
list[i].appendChild(btn);
}
however, friend told me that .msg:not(.was_added) is too slow, so doing it in the opposite way:
var cssClass = ' was_added';
var buttonName = 'start waiting';
if (was_set_standby_auto == true)
{
cssClass += ' was_standby';
buttonName = 'cancel';
}
try {
var currectMSG = was_talkWindows.querySelector('.msg:last-child');
while (currectMSG.classList.contains('was_added') == false)
{
currectMSG.className += cssClass;
var btn = document.createElement('button');
btn.setAttribute('type', 'button');
btn.setAttribute('class', 'was_addButton');
btn.addEventListener('click', function() {
was_button_act(this.parentElement);
});
btn.innerHTML = buttonName;
currectMSG.appendChild(btn);
currectMSG = currectMSG.previousElementSibling;
}
} catch (err) {}
but the code add the button twice each time for the last one.
not really understand this behavor.
Use previousElementSibling instead of previousSibling, so that it skips over text nodes.
And add the was_added class to the element so it won't be processed again later.
var was_talkWindows = document;
var cssClass = " newclass";
var buttonName = "Click me";
try {
var currectMSG = was_talkWindows.querySelectorAll('.msg:last-child')[0];
while (currectMSG.classList.contains('was_added') == false) {
currectMSG.className += cssClass;
currectMSG.classList.add('was_added');
var btn = document.createElement('button');
btn.setAttribute('type', 'button');
btn.setAttribute('class', 'was_addButton');
btn.addEventListener('click', function() {
was_button_act(this.parentElement);
});
btn.innerHTML = buttonName;
currectMSG.appendChild(btn);
currectMSG = currectMSG.previousElementSibling;
}
} catch (err) {}
<ol>
<li class="msg was_added">Text</li>
<li class="msg was_added">Text</li>
<li class="msg">Text</li>
<li class="msg">Text</li>
<li class="msg">Text</li>
</ol>
The problem with a site that is not under your control, it does not always know what is running, and it took me a long time to realize that the problem is not adding buttons twice (against logic), but anyone who does not I edit the code at the same time.
so declare just once:
var was_msgList = was_talkWindows.getElementsByClassName('msg');
and the loop (every 2 seconds):
for ( var i=was_msgList.length; i-- && was_msgList[i].getAttribute('data-status') == null; )
{
was_load_addButton(was_msgList[i],attStatus,buttonText);
}
any ideas for change are welcome.
So I'm trying to to basically dynamically create li's inside an array, and I would like to create a 'delete' button within each li, so that when I click that li, I can delete that specific li.
I know this seems very basic, but I've been looking at JS for hours now, and am starting to really confuse myself here.
I keep getting errors like addChild() is not a function... I feel like I'm close, but no cigar. Thanks in advance!
Anyway, here's my add function:
function add(){
var deleteBtn = document.createElement('input');
deleteBtn.type = 'submit';
deleteBtn.name = 'addButton';
deleteBtn.className = 'deleteButton';
for(i=0;i<1;i++){
id_number[i] = i+1;
var newSong = '<li class="li_test" id="' + id_number[i] + '">' + "<span>" + "</span>" + '</li>';
// $(newSong).appendChild(deleteBtn);
$(deleteBtn).appendTo("#playlist-1");
$(newSong).appendTo("#playlist-1");
showList.push(newSong);
deleteBtn.addEventListener('click', function(evt) {
deleteFromPlaylist(newSong);
});
}
}
Here's my delete function
function deleteFromPlaylist(newSong){
var deleteBtn = document.getElementsByTagName('deleteButton');
// var deleteMe = deleteBtn.parentNode;
alert(deleteBtn);
for(i=0;i<showList.length;i++){
if(newSong === showList[i]){
showList.splice(i,1);
// var pp = p.parentNode;
// pp.removeChild (p);
deleteMe = deleteMe.parentNode.remove("li_test");
deleteMe.removeChild(deleteBtn);
}
// console.log(deleteMe);
}
}
EDIT: 1 More Related Question
I would like to only add an item if it doesn't exist already in the array. Here is what I have so far. Any tips on where I'm going wrong?
for (i = 0; i < showList.length; i++) {
if (newSong !== showList[i]){
ul_list.innerHTML = newSong;
container_div.appendChild(ul_list); //append the info
container_div.appendChild(deleteBtn);
document.getElementById('playlist-1').appendChild(container_div); //finally add it to the playlist div
showList.push(newSong);
deleteBtn.addEventListener('click', function(evt) {
deleteFromPlaylist(evt, newSong);
});
inc++;
alert("It IS in the Array!");
}else{
alert("This already exists!");
}
}
You seem to have a strange mix of code. Forget the jQuery stuff until you know javascript.
> function add(){
> var deleteBtn = document.createElement('input');
> deleteBtn.type = 'submit';
I don't think that's a good idea. Much better to use type button or a button element.
> deleteBtn.name = 'addButton';
> deleteBtn.className = 'deleteButton';
>
> for(i=0;i<1;i++){
Presumably i will go a bit higher in future. ;-)
> id_number[i] = i+1;
Where did id_number come from?
>
> var newSong = '<li class="li_test" id="' + id_number[i] + '">' + "<span>" + "</span>" + '</li>';
> // $(newSong).appendChild(deleteBtn);
Stick to one method of creating elements. Consider using a document fragment to hold the parts.
> $(deleteBtn).appendTo("#playlist-1");
> $(newSong).appendTo("#playlist-1");
> showList.push(newSong);
Where did showList come from?
> deleteBtn.addEventListener('click', function(evt) {
> deleteFromPlaylist(newSong);
> });
Not all browsers support addEventListener. Since you are only adding one listener, consider just assigning to the button's onclick property. Note that newSong is just a string.
> }
> }
In the other function:
> function deleteFromPlaylist(newSong){
> var deleteBtn = document.getElementsByTagName('deleteButton');
There is no HTML "deleteButton" element, so that will return an empty collection.
> // var deleteMe = deleteBtn.parentNode;
> alert(deleteBtn);
> for(i=0;i<showList.length;i++){
> if(newSong === showList[i]){
> showList.splice(i,1);
> // var pp = p.parentNode;
>
> // pp.removeChild (p);
> deleteMe = deleteMe.parentNode.remove("li_test");
Where did deleteMe come from? You commented out where it was declared and it hasn't been assigned a value, so deleteMe.parentNode will throw an error.
> deleteMe.removeChild(deleteBtn);
> }
> // console.log(deleteMe);
> }
> }
> }
Anyhow, here's some working code, it's still pretty awful but I'll leave it to you go improve it.
<script>
var showList = [];
function add(){
var id_number = [];
var deleteBtn = document.createElement('input');
deleteBtn.type = 'button';
deleteBtn.name = 'addButton';
deleteBtn.className = 'deleteButton';
deleteBtn.value = 'Delete Button';
for (i=0; i<1; i++) {
id_number[i] = i + 1;
// '<li class="li_test" id="' + id_number[i] + '">' + "<span>" + "</span>" + '</li>';
var newSong = document.createElement('li');
newSong.className = 'li_test';
newSong.id = id_number[i];
newSong.appendChild(document.createElement('span').appendChild(document.createTextNode('song')));
showList.push(newSong);
deleteBtn.onclick = (function(id) {
return function(){deleteFromPlaylist(id);}
}(newSong.id));
newSong.appendChild(deleteBtn);
document.getElementById('playlist-1').appendChild(newSong);
}
}
function deleteFromPlaylist(id) {
var song = document.getElementById(id);
if (song) {
song.parentNode.removeChild(song);
}
}
window.onload = function() {
add();
}
</script>
<ul id="playlist-1">
<li>Original
</ul>
I've altered your code and functions to purely use javascript, instead of a mixture containg jquery. I've added comments in the code to explain my actions. If you have any questions, feel free to ask.
var showList = [];
var inc = 1;
function add() {
//create the container element. If we do this, keeping track of all elements
//becomes easier, since we just have to remove the container.
var container_div = document.createElement('div');
container_div.id = "cont_" + inc;
var ul_list = document.createElement('ul');
var deleteBtn = document.createElement('input');
deleteBtn.type = 'button';
deleteBtn.value = 'remove song';
deleteBtn.name = 'addButton';
deleteBtn.className = 'deleteButton';
var id_number = [];
var newSong = "";
for (i = 0; i < 1; i++) {
id_number[i] = i + 1;
newSong += '<li class="li_test" id="cont_' + inc + '_song_id_' + id_number[i] + '">' + "<span>test " + inc + "</span>" + '</li>\n'; //all ids must be unique, so we construct it here
}
ul_list.innerHTML = newSong;
container_div.appendChild(ul_list); //append the info
container_div.appendChild(deleteBtn);
document.getElementById('playlist-1').appendChild(container_div); //finally add it to the playlist div
showList.push(newSong);
deleteBtn.addEventListener('click', function(evt) {
deleteFromPlaylist(evt, newSong);
});
inc++;
}
function deleteFromPlaylist(evt, newSong) {
var deleteBtn = evt.target; //target the button clicked, instead of a list of all buttons
var container_div = deleteBtn.parentNode; //get the parent div of the button
var cont_parent = container_div.parentNode; //and the parent of the container div
for (i = 0; i < showList.length; i++) {
if (newSong === showList[i]) {
showList.splice(i, 1);
}
}
cont_parent.removeChild(container_div); //finally, remove the container from the parent
}
Update:
I've modified the above function to strictly use objects, rather than strings, because it is easier to extract relevant information from objects, than strings.
I've added in comments to assist with understanding the code. Again, if you have any questions, feel free to ask.
function add() {
var list_bool;
//create the container element. If we do this, keeping track of all elements
//becomes easier, since we just have to remove the container.
var container_div = document.createElement('div');
container_div.id = "cont_" + inc;
var ul_list = document.createElement('ul');
var deleteBtn = document.createElement('input');
deleteBtn.type = 'button';
deleteBtn.value = 'remove song';
deleteBtn.name = 'addButton';
deleteBtn.className = 'deleteButton';
var list_item = document.createElement("li"); //create list element
list_item.className = "li_test"; //set element class
var list_span = document.createElement("span"); //create span element
list_span.innerHTML = "test"; //set span text
list_item.appendChild(list_span); //append span to list element
ul_list.appendChild(list_item); //append list element to un-ordered list element
var list_bool = false; //create local boolean variable
if (showList.length > 0) { // loop through showList if it isn't empty
for (var i = 0; i < showList.length; i++) {
if (showList[i].innerText !== list_item.innerText) {
list_bool = true; //if song exists(comparing text values, set bool to true
} else if (showList[i].innerText === list_item.innerText) {
list_bool = false; //else, set it to false
break; //break out of loop.. we don't want it becoming true again, now do we?
}
}
} else {
list_bool = true; //showList is empty, set to true
}
if (list_bool) { //if true, do action of appending to list
container_div.appendChild(ul_list); //append the info
container_div.appendChild(deleteBtn);
document.getElementById('playlist-1').appendChild(container_div); //finally add it to the playlist div
showList.push(list_item);
deleteBtn.addEventListener('click', function(evt) {
deleteFromPlaylist(evt, newSong);
});
inc++;
}
}
DEMO, notice that add() is executed twice, but because the song 'test' already exists, it only executes the end action once.