Dynamically Change ID of an HTML Element with Javascript - javascript

Alright.
I have created a custom object that I am using to build a list of things. One of the functions for this custom object involves adding the object to a list. In this list, there is a value I assign to each different element that keeps track of how many of that item have been added to the list. Such as:
(3) Object#1
(2) Object#2
3 and 2 of course being the 'count' value of that object. My problem is that I am creating the list dynamically by calling:
function Thing(count, value)
{
this.count=count;
this.value=value;
}
Thing.prototype.addToList = function()
{
if (this.count == 0)
{
this.count++;
var list = document.getElementById("blah");
var li = document.createElement("li");
var interior = "<span id='counter'>("+this.count+")</span>";
li.innerHTML = interior;
list.appendChild(li);
}
else
{
this.count++;
var countInc = document.getElementById("counter");
countInc.innerHTML = "("+this.count+")";
}
}
This works fine, but if I am to add multiple Objects with separate count values, there is no way to distinguish between them, and as a result, the first 'counter' span in the list is altered with the the count value of the most recently added object. All other count values remain the same (1). I have tried:
var interior = "<span id='counter"+this.value+"'>("+this.count+")</span>";
To try and create a unique id each time, but this isn't working. Basically, I am wondering how I can create new ID values each time I instantiate a new Object.

try this
var list = document.getElementById("blah");
var li = document.createElement("li");
var interior = "<span id='counter-"+this.count+"'>("+this.count+")</span>";
li.innerHTML = interior;

Related

Page refresh displays entire localstorage object

I have built a todo list using Vanilla Javascript and localstorage. The todo list has the following key, value:
key: todolist
value: [[\"id:0\",\"title:buy groceries\",\"done:false\"],
[\"id:1\",\"title:pick up dry cleaning\",\"done:false\"],
[\"id:2\",\"title:walk dog\",\"done:false\"]]
The values display just great on my website (only the title is displaying) but when I refresh the page, the whole object is displaying.
Before page refresh:
buy groceries
pick up dry cleaning
walk dog
After page refresh:
id:0,title:buy groceries,done:false
id:1,title:pick up dry cleaning,done:false
id:2,title:walk dog,done:false
Obviously, after a page refresh I only want the title to display on the list inside the li tag. It's a head scratcher because it only does this after a page refresh.
How do I get only the title to display after page refresh?
I'm somewhat of a newbie to Javascript and can't quite figure out how to make this happen. I've been Googling for almost two days and ready to tear my hair out!!
// set up some variables for elements on the page
const form = document.querySelector('form');
const ul = document.querySelector('ul');
const button = document.querySelector('button');
const input = document.getElementById('item');
// Fix empty array when script runs by making a conditional statement that
checks if localStorage already exists
//let itemsArray = localStorage.getItem('todolist') ?
JSON.parse(localStorage.getItem('todolist')) : [];
let todolist;
if (localStorage.getItem('todolist')) {
itemsArray = JSON.parse(localStorage.getItem('todolist'));
} else {
itemsArray = [];
}
localStorage.setItem('todolist', JSON.stringify(itemsArray));
const data = JSON.parse(localStorage.getItem('todolist'));
//alert(typeof(data));
// function that creates an li element, sets the text of the element to the
parameter, and appends the list item to the ul.
const liMaker = (text) => {
const li = document.createElement('li');
li.textContent = text;
ul.appendChild(li);
// Create a "close" button and append it to each list item
var span = document.createElement("SPAN");
var txt = document.createTextNode("🗑️");
span.className = "close";
span.appendChild(txt);
li.appendChild(span);
for (i = 0; i < close.length; i++) {
close[i].onclick = function() {
var div = this.parentElement;
div.style.display = "none";
}
}
}
// Event listener that submits the value of the input
form.addEventListener('submit', function (e) {
e.preventDefault();
var id = "id:" + itemsArray.length;
var title = "title:" + input.value;
var done = "done:" + "false";
itemsArray.push([id, title, done]);
//itemsArray.push(input.value);
localStorage.setItem('todolist', JSON.stringify(itemsArray));
liMaker(input.value);
input.value = "";
});
data.forEach(item => {
liMaker(item);
});
// clear items from todolist
button.addEventListener('click', function () {
localStorage.removeItem("todolist");
while (ul.firstChild) {
ul.removeChild(ul.firstChild);
}
itemsArray = [];
});
One thing I should note, the page refresh issue doesn't happen when I change the following:
itemsArray.push([id, title, done]);
to the following:
itemsArray.push(input.value);
The main reason you are having this problem is because your JSON is not formatted properly.
The reason you are only seeing the problem on page refresh is because at this point local storage contains the "todolist" key with your improperly formed JSON. This JSON value is then stored in your data variable and output to your list items in an undesired way (as you described).
Otherwise (without page refresh) the text of your list items is coming directly from the text in the input field.
If you make the following changes to your code it will work properly (I have tested it). Hope it helps.
JavaScript comments
Firstly i'm not sure if this just happened when you posted your code here but if your comments in JS extend across two lines or more then you need to put // on all lines.
For example in your code you have:
//function that creates an li element, sets the text of the element to the
parameter, and appends the list item to the ul.
and it should be:
//function that creates an li element, sets the text of the element to the
//parameter, and appends the list item to the ul.
The format of your JSON
Secondly I see a problem with the way the JSON is formatted.
It should look something like the following (before slashes are added).
[{"id":0,"title":"buy groceries","done":false},
{"id":1,"title":"pick up dry cleaning","done":false},
{"id":2,"title":"walk dog","done":false}]
Note each property name (i.e "id", "title" and "done") should have double quotes and each property value (e.g "buy groceries") should have double quotes (unless its an int or a boolean etc).
You can use a tool called JSLint to check your JSON is valid.
So in order to create your JSON in the right format (when the form is submitted)
change these lines of code:
var id = "id:" + itemsArray.length;
var title = "title:" + input.value;
var done = "done:" + "false";
itemsArray.push([id, title, done]);
to the following:
var idValue = itemsArray.length;
var titleValue = input.value;
var doneValue = false;
itemsArray.push({"id": idValue, "title": titleValue, "done" : doneValue});
Iterating through the array
Your data variable will contain the array of todolist objects (from local storage).
So therefore the item you have in the following code will contain the full object i.e {"id":0,"title":"buy groceries","done":false}.
So in order to get the title here you need to say item.title. (This will work now that the JSON will be properly formatted):
data.forEach(item => {
//log the item to check it.
console.log(item);
liMaker(item.title);
});

Problem with Looping through over a newly created list element by js

I am building a Todo-List Project and i am stuck at looping through my newly created list items.
This is what i am doing:
Created an array.
Made li items for array's each element through looping so that array appears in a list manner.
And then looping through newly created li section to addEventListener on each of li's ( But this one is not working).
var arrList = ["play","learn","walk"];
var list = document.querySelectorAll("li");
var done = false;
//printing array in list manner
for(let i = 0; i < arrList.length; i++){
let el = document.createElement("li")
el.textContent = arrList[i];
document.querySelector("ul").appendChild(el);
}
//looping through each li's to apply if else statement
for(let i = 0; i < list.length; i++){
list[i].addEventListener("click",function(){
if(!done){
this.style.textDecoration = "line-through";
done = true;
}else{
this.style.textDecoration = "none";
done = false;
}
})
}
You're code is mostly correct, however there are a few issues that need to be addressed. First, consider replacing your for loop with iteration based on forEach() as shown below. Using forEach() in this way allows you to leverage "closure" which in this case will greatly simplify your code. For instance, you can use the closure feature to store the done state of each item in your list, rather than storing that state explicitly in an array.
The other issue I noticed was var list = document.querySelectorAll("li"); queries the document for li elements before any are added to your document - later in your script it seems you're iterating that empty query result and expecting it to contain the added li elements.
Here's a working snippet - hope this helps!
var arrList = ["play", "learn", "walk"];
// Iterate the list via forEach
arrList.forEach(function(arrItem) {
// We're now in a new "closure" for this list item
// so we can define some state like "done" that will
// be used exclusively for this list item
var done = false;
// Create li element for this list item as before
var el = document.createElement("li")
el.textContent = arrItem;
// Configure click event
el.addEventListener("click", function() {
// Notice we're able to use the done variable
// in this closure for this list item? The key
// thing to understand is that each list item
// will have it's own unique "done" variable
if (!done) {
el.style.textDecoration = "line-through";
done = true;
} else {
el.style.textDecoration = "none";
done = false;
}
})
document.querySelector("ul").appendChild(el);
});
<ul></ul>
It seems like you only have one done variable that is shared for every item on the todo list. Therefore if you click one of the items all of the items will be crossed out. You will need a boolean variable for every item in your to do list.
Add this line just above the second for loop and remove from the top.
var list = document.querySelectorAll("li");
You are assigning list the values even before they are created.
from the source code I see that the list li item is initialized before new li item been created,
it will cause the list li item not contains the new one,
due to that addEventListener will not working for the new item.
to fix this, just need move init list li item code after creation part :
var arrList = ["play","learn","walk"];
var done = false;
//printing array in list manner
for(let i = 0; i < arrList.length; i++){
let el = document.createElement("li")
el.textContent = arrList[i];
document.querySelector("ul").appendChild(el);
}
var list = document.querySelectorAll("li");
//looping through each li's to apply if else statement
for(let i = 0; i < list.length; i++){
list[i].addEventListener("click",function(){
if(!done){
this.style.textDecoration = "line-through";
done = true;
}else{
this.style.textDecoration = "none";
done = false;
}
})
}
Please, be simple...
var
arrList = ["play","learn","walk"],
UL_arrList = document.querySelector("ul")
;
arrList.forEach (arrItem => {
let el = document.createElement("li");
el.textContent = arrItem;
UL_arrList.appendChild(el);
el.onclick = function(e){
let deco = this.style.textDecoration || 'none';
this.style.textDecoration = (deco==='none') ? 'line-through': 'none';
}
});
<ul></ul>

Failed to execute 'appendChild' error when appending text content to items in list

I'm trying to add text items to a list from an array of values held in an object.
My JS looks like this -
var MiFiveLog = function(person) {
var ul = document.querySelector('.logged-interactions-list');
for (i = 0; i < person.loggedMessages.length; i++) {
var li = document.createElement('li');
var textNode = document.createTextNode(person.loggedMessages[i]);
ul.appendChild(textNode);
}
}
Person constructor -
Person = function(name) {
this.name = name,
this.loggedMessages = []
}
Looks simple enough but the script is giving me the following console error
Uncaught TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.
Bit confused - would appreciate any help.
Thanks,
Your code should work, but you haven't shown us what your person object looks like, so that is most likely the culprit. If you look at my sample below, you'll see the structure that the object needs to be set up with in order to use the syntax: person.loggedMessages[i] in a loop.
Also, you should be appending your text nodes to the list items, not the list and then the list items should be appended to the list.
Finally, you can skip the explicit construction of a text node (if you like) and just use the textContent property.
See comments below:
// Here's your object structure, which is OK, but you have to instantiate it
var Person = function(name) {
this.name = name,
this.loggedMessages = []
}
// Instantiate the object and populate the array property:
var p2 = new Person("John Doe");
p2.loggedMessages.push("message 1");
p2.loggedMessages.push("message 2");
p2.loggedMessages.push("message 3");
p2.loggedMessages.push("message 4");
var MiFiveLog = function(person) {
var ul = document.querySelector('.logged-interactions-list');
for (i = 0; i < person.loggedMessages.length; i++) {
var li = document.createElement('li');
// You can accomplish this the way you were attempting except that the
// text nodes should be appended to the list items, not the list:
//var textNode = document.createTextNode(person.loggedMessages[i]);
//li.appendChild(textNode);
// Or, you can skip the text node and appending it to the list item completely
// and just insert the text into the list item:
li.textContent = person.loggedMessages[i]
ul.appendChild(li); // The list items should be appended to the unordered list
}
}
MiFiveLog(p2); // Invoke the function and pass it the instance
<ul class="logged-interactions-list"></ul>

Javascript For loop appending child only appends first element, then throws error

I'm looping through a js object with a nested for loop, stated below, it appends the first element correctly, but then throws the following error:
Can't set the property className of an undefined reference or empty reference. (not sure if exact error, translating from Dutch...)
function allVideos() {
var sql = "SELECT videos.VideoName, videos.VideoPath FROM videos";
var resultSet = db.query(sql, {json:true}); //returns: [{"VideoName":"timelapse aethon2","VideoPath":"videos\\Roermond Papier\\160424 Time laps Aethon2.avi"},{"VideoName":"timelapse aethon3","VideoPath":"videos\\Roermond Papier\\160424 Time laps Aethon2.avi"}]
var parsed = JSON.parse(resultSet);
var parsedlength = arrLenght(parsed);
//alert(resultSet);
for(var i = 0; i < parsedlength; i++) {
var obj = parsed[i];
//alert(i);
var videoElement = document.getElementById("allVideos");
for (var key in obj) {
if(obj.hasOwnProperty(key)) {
videoElement.appendChild(document.createElement('div'));
videoElement.children[i].id='allVid' + i;
videoElement.children[i].className='col-md-4 col-xs-12';
//alert(typeof key)
var card = document.getElementById('allVid' + i);
alert(i);
card.appendChild(document.createElement('div'));
card.children[i].className='card card-block';
card.children[i].innerHTML = "<h3 class='card-title'>" + obj['VideoName'] + "</h3><button class='btn btn-primary'>Selecteren</button>"
}
}
}
}
[EDIT] added screenshot of how it looks
Your code has some significant logic issues. You're using nested loops, but appending to an element assuming that the outer loop counter will let you index into that element's children to get the element you just appended. Later, you try to get that same element again using getElementById. Then, you append a new element to your newly-created element, but try to access that new element using children[i] on the one you just created — at that point, the card element will only have a single child, so as of the second outer loop, it will fail.
createElement returns the element to you, so there's no reason at all to try to access it via children[i] (either time) or getElementById.
See comments:
function allVideos() {
var sql = "SELECT videos.VideoName, videos.VideoPath FROM videos";
var resultSet = db.query(sql, {json:true});
var parsed = JSON.parse(resultSet);
var parsedlength = arrLenght(parsed);
for(var i = 0; i < parsedlength; i++) {
var obj = parsed[i];
//alert(i);
var videoElement = document.getElementById("allVideos");
for (var key in obj) {
if(obj.hasOwnProperty(key)) {
// Create the card, give it its id and class
var card = document.createElement('div');
card.id='allVid' + i;
card.className='col-md-4 col-xs-12';
// Create the div to put in the card, give it its class and content
var div = document.createElement('div');
card.appendChild(div);
div.className='card card-block';
div.innerHTML = "<h3 class='card-title'>" + obj['VideoName'] + "</h3><button class='btn btn-primary'>Selecteren</button>"
// Append the card
videoElement.appendChild(card);
}
}
}
}
Side note: arrLenght looks like a typo (it should be th, not ht), but moreover, there's no reason to use a function to get the length of an array; it's available via the array's length property: parsedLength = parsed.length.
Side note 2: You may find these ways of looping through arrays useful.
Your problem is the if within the nested for:
if(obj.hasOwnProperty(key)) { ...
The variable i is increased even if the property is not "owned" (when the if condition returns false), so next time that the condition is true, i is out of bounds.

Dexie.js iterating a dynamic list

I am using dexie.js, which is an indexDB wrapper. Anywhoo, I have an array that is called from the user's local storage, and my function is supposed to iterate through every list item in the DB and show it. However, upon clicking on my Butane it only shows the most recent input of name.
Note: You can see the entire database by adding a few values in and checking your local storage.
My JsFiddle:
https://jsfiddle.net/enzp3zws/1/
my html:
<ul id="mane"></ul>
my js:
var db = new Dexie("TestDatabase");
db.version(1).stores({
friends: '++id, name, age'
});
var collection = db.friends;
var placement = document.getElementById('rainman');
var jacement = document.getElementById('rainboy');
var stacement = document.getElementById('mane');
var listed = document.createElement('li');
function addrain(){
collection.each(function(friend){
var element = [friend.name];
for (var i = 0; i < element.length; i++){
listed.textContent = element[i];
document.getElementById('mane').appendChild(listed);
//alert(element); <-- this call alerts all names in database.
}
});
}
Please excuse the randomness of some of these variable names. I don't know what sleep is anymore.
You need to create a new 'li' element each time:
//Remove var listed = ... line in header, then:
function addrain(){
collection.each(function(friend){
var element = [friend.name];
for (var i = 0; i < element.length; i++){
var listed = document.createElement('li');
listed.textContent = element[i];
document.getElementById('mane').appendChild(listed);
}
//alert(element); <-- this call alerts all names in database.
});
}
The reason your code did not work before is that you only created one li element, and repeatedly changed its text and re-inserted it at different locations.

Categories

Resources