Javascript- Creating To Do list not working - javascript

I deleted the button part in my script but not even the first part of my function is working where I type in input box and suppose to be added to the ...I don't understand why. When I run the code without the buttons code which is titled " //BUTTON creation " I get no error but no item is being added to the list. So I have two problems Items aren't being added to my list and aren't displaying and also if I include the button part its saying an error "list.appendChild is not a function"
<input type="text" placeholder="Enter an Activity" id="textItem">
<img src="images/add-button.png" id="addButton">
<div id="container">
<ul class="ToDo">
<!--
<li>
This is an item
<div id="buttons">
<button ></button>
<img src="images/remove-icon.png"id="remove">
<button id="complete"></button>
<img src="images/complete-icon.jpg" id="complete">
</div>
</li>
!-->
</ul>
</div>
<script type="text/javascript">
//Remove and complete icons
var remove = document.createElement('img').src =
"images/remove-icon.png";
var complete = document.createElement('img').src = "images/complete-icon.jpg";
//user clicks add button
//if there is text in the item field we grab the item into var text
document.getElementById("addButton").onclick = function()
{
//value item is the text entered by user
var value = document.getElementById("textItem").value;
//checks if there is a value typed
if(value)
{
addItem(value);
}
//adds a new item to the ToDo list
function addItem(text)
{
var list = document.getElementsByClassName("ToDo");
//created a varibale called item that will create a list item everytime this function is called
var item = document.createElement("li");
//this will add to the innerText of the <li> text
item.innerText = text;
//BUTTON creation
var buttons = document.createElement('div');
buttons.classList.add('buttons');
var remove = document.createElement('buttons');
buttons.classList.add('remove');
remove.innerHTML = remove;
var complete = document.createElement('buttons');
buttons.classList.add('complete');
complete.innerHTML = complete;
buttons.appendChild(remove);
buttons.appendChild(complete);
list.appendChild(buttons);
list.appendChild(item);
}
}
</script>

The problem is in the line:
var list = document.getElementsByClassName("ToDo");
list.appendChild(item);
The line var list = document.getElementsByClassName("ToDo"); will provide a collection, notice the plural name in the api.
You need to access it using :
list[0].appendChild(item);
There are other problems too in the code but hopefully this gets you going!

There are a couple of issues in your code that need to be addressed to get it to work properly.
1) You are creating your image elements and then setting the variables to the src name of that image and not the image object itself. When you use that reference later on, you are only getting the image url and not the element itself. Change var remove = document.createElement('img').src = "images/remove-icon.png" to this:
var removeImg = document.createElement('img')
removeImg.src = "images/remove-icon.png";
2) As #Pankaj Shukla noted, inside the onclick function, getElementsByClassName returns an array, you will need to address the first item of this array to add your elements. Change var list = document.getElementsByClassName("ToDo") to this:
var list = document.getElementsByClassName("ToDo")[0];
3) For your buttons, you are trying to creating them using: var remove = document.createElement('buttons'). This is invalid, buttons is an not the correct element name, its button. Additionally, you are re-declaring the variables remove and complete as button objects, so within the onclick function it reference these buttons, not the images you defined earlier. So when you assign the innerHTML to remove and complete, you are assigning the buttons innerHTML to itself. The solution is to change the image variables to something different.
4) Finally, also relating to the buttons, you are assigning the innnerHTML to an image object, that's incorrect. You can either insert the html text of the img directly, or append the image object as a child of the button, similar to how the button is a child of the div.
The updated code with all these changes looks like this:
//Remove and complete icons
var removeImg = document.createElement('img');
removeImg.src = "images/remove-icon.png";
var completeImg = document.createElement('img');
completeImg.src = "images/complete-icon.jpg";
//user clicks add button
//if there is text in the item field we grab the item into var text
document.getElementById("addButton").onclick = function() {
//value item is the text entered by user
var value = document.getElementById("textItem").value;
//checks if there is a value typed
if (value) {
addItem(value);
}
//adds a new item to the ToDo list
function addItem(text) {
var list = document.getElementsByClassName("ToDo")[0];
//created a varibale called item that will create a list item everytime this function is called
var item = document.createElement("li");
//this will add to the innerText of the <li> text
item.innerText = text;
//BUTTON creation
var buttons = document.createElement('div');
buttons.classList.add('buttons');
var remove = document.createElement('button');
remove.classList.add('remove');
remove.appendChild(removeImg);
var complete = document.createElement('button');
complete.classList.add('complete');
complete.appendChild(completeImg);
buttons.appendChild(remove);
buttons.appendChild(complete);
list.appendChild(buttons);
list.appendChild(item);
}
}

Related

Creating a packing list using Javascript, DOM, and the replaceChild() method

I am making a packing list for my assignment and we choose any place and any 3 items to bring. What is supposed to happen is the user presses the 1, 2, or 3 buttons and a prompt will appear telling the user what they want to swap the chosen item with. Then, once they hit OK, the new item will appear in the list. So, if they press 1 and type an item, the new item will be in the 1. The same should happen if the user wanted to swap the item in 2 or the item in 3. However, the problem I am having is that it does not show the new item. It allows me to press 1, 2, or 3 and then type what I want to swap whichever item with, but then adds to the list with a 4 appearing at the end of the list. To swap the items, the assignment says to use the replaceChild() method. Did I put in something wrong in the coding or forget something? Help is appreciated.
<!DOCTYPE html>
<html lang="en">
<head>
<title>CIS 223 Chapter 10 Program</title>
<meta charset="utf-8">
<script>
var list;
function createDOM()
{
list = document.getElementById("items");
var newElement = document.createElement("LI");
var nodeText = document.createTextNode("Sunscreen");
newElement.appendChild(nodeText);
list.appendChild(newElement);
var newElement = document.createElement("LI");
var nodeText = document.createTextNode("Water");
newElement.appendChild(nodeText);
list.appendChild(newElement);
var newElement = document.createElement("LI");
var nodeText = document.createTextNode("Swim suits");
newElement.appendChild(nodeText);
list.appendChild(newElement);
}
//Swap items function.
function registerKey(keyEvent)
{
var keyValue = keyEvent.key;
if (keyValue < "1" || keyValue > "3")
{
alert("Only press 1, 2, or 3");
return;
}
var userInput;
var newInput;
var newElement = document.createElement("LI");
//Prompt user for new entry.
userInput = prompt("Enter a new item for slot "+keyValue,"");
//Check input for valid entry.
if (userInput != null && userInput != "")
{
//Write Your code to Pass string input to Text Node.
// .......
newInput = document.createTextNode("");
//Write Your code to Attach Text Node to Element.
// .......
newElement.appendChild(newInput);
list.appendChild(newElement);
var whichNode = parseInt(keyValue); // which item to be replaced
//Write Your code to Replace existing node with new node.
// .......
nodeText.replaceChild(newInput,nodeText.childnodes[LI]);
}
}
</script>
</head>
<body onload=createDOM() onkeypress=registerKey(event)>
<div id="wrapper">
<header>
<h1>Summer Vacation</h1>
</header>
<main>
<h2>We're going to Cancun, Mexico!</h2>
<p>Let's pack a few <em>essentials</em> for the trip.</p>
<ol id="items">
</ol>
<p>These provisions were carefully selected for this trip.<br><br>
However, you can swap out any of the three items by pressing:<br>
<strong>1</strong>, <strong>2</strong>, or <strong>3</strong> on your keyboard.</p>
</main>
</div>
</body>
</html>
As this is an assignment, I won't give you the fixed code, but here are the main issue that need to be fixed:
You need to create the text node using the input that the user entered. Currently newInput = document.createTextNode(""); creates a text node with an empty string "". You need to pass your user's input into this.
nodeText doesn't exist inside of your registerKey function. The variable defined within a function body {} only exist within that function body (scope). So anywhere you're trying to access nodeText inside of your registerKey function you'll get an error - you can read more about scope here.
You don't need to use list.appendChild(newElement);. This appends/adds your new item to your list at the end of the list. This isn't the behavior you want. Instead, your script will add the item to your list using .replaceChild(). So you can remove this line.
nodeText.replaceChild(newInput,nodeText.childnodes[LI]); isn't correct either. The idea of .replaceChild() is to pass in the new element you want to replace the old element with. The use case is as so:
parentNode.replaceChild(newChild, oldChild);
In your case the new element is newElement, which is the <li> element you've created containing the user's input value, and not the text node newInput. Again, here nodeText doesn't exist, so you need to use list instead of nodeText as that is the (parent) element that contains your list items/<li> elements as children.
nodeText.childnodes[LI] is also going to give you issues. As established above, nodeText needs to be list. Moreover, childnodes needs to be changed to childNodes. This will give you a NodeList of child items in your list, which is similar to an array. Recall that the item you need to pass as the second argument to replaceChild() needs to be the item you want to replace/remove from your list, so this item needs to be obtained from the node list using the whichNode number. You can take a look at NodeList.item() to help you achieve this. Note that you have created your HTML element with a space:
<ol id="items">
</ol>
meaning that using .childNodes on your empty <ol> will give you an element at index 0 of the NodeList. I suggest you remove this blank space and use <ol id="items"></ol> instead:
var items = document.getElementById("items1");
console.log(items.childNodes); // empty NodeList
var items = document.getElementById("items2");
console.log(items.childNodes); // NodeList with 1 item (undesirable)
<ol id="items1"></ol>
<ol id="items2">
</ol>

onclick inside forEach, value parameter only refers to last element

I have a list of anchor in my html, I want to make their href editable.
Everything fine, but the validation step (last onclick) refers to the last anchor instead of the current one
var anchors = document.querySelectorAll('.home-content a');
var col = document.querySelectorAll('.home-content > article');
anchors.forEach((k)=> {
let linkpanel = document.getElementById('link-edit-panel'); //This element is a single div in my html
let linkpanelvalidate = document.getElementById('validate-link'); //the button inside the said div
let editinput = linkpanel.querySelector('input'); //the input inside this div
//For each anchors, I add a button that will let user show the "linkpanel" div to edit the href of this anchor
let editbut = document.createElement('div');
let linktxt = k.href;
editbut.classList.add('edit-but','toremove');
editbut.innerHTML = "<i class='fas fa-link'></i>";
//I put this new element to the current anchor
k.appendChild(editbut);
console.log(k); // this to show me the full list of anchors
/* PROBLEM START HERE */
//click on the "edit" button
editbut.onclick = ()=>{
console.log(k); //Here, it shows the good anchor!
}
//click on the "validate" button
linkpanelvalidate.onclick = ()=>{
console.log(k); //Here, it shows the very last anchor...
}
});
I tried to put the element inside a constant
const ttt = k;
It does not change a thing.
Thank you for your help
We are facing here a classical forEach bubbling misunderstand (and I was blind not to see it)
When the click on the validate button occures, the call is made from the "main" bubble (outside the loop function if you need to picture it) so naturaly, it returns the last occurrence of the loop when we print the value in the console for example.
Solution
There is many solutions, you can store these values in an array to use each of them later
var arr = [];
node.forEach((v)=>{
arr.push(v);
});
Or, you don't want to deal with an array and want to keep it simple, like me, and you create your button during the forEach loop event, like this
node.forEach((v)=>{
let btn = document.createElement('button');
document.body.appendChild(btn);
btn.onclick = ()=> {
console.log(v); //it is the current value, not the last one
//you can create another button here and put his onclick here, the value will still remains etc
}
});

How to save data in local storage

I am creating a to-do-list in javascript and everything was good until I had to deal with the local storage. I want to be able to refresh the page without losing what I put in my list just before.
I searched in many forums but I didn't find a similar case.
Here is a part of my HTML code : Just an ul and an input
Here is a part of my JS code : My function which creates li inside my ul
And here is a preview of my to do list : Hope it helps
So if I didn't explain my problem well enough, let me know and I will bring more precisions.
Thank for reading !
PS : I should specify that I already read the documentation on MDN and others websites and I understood the principle of localStorage but I'm struggling with the integration of this in my code. I saw lot of examples of its use but they are often too simple or on the contrary too different/hard to understand.
This is why I ask your help to have a little bit more personal response.
window.addEventListener('load', function()
{
var yourToDo = document.getElementById('myInput');
yourToDo.addEventListener('keydown', myFunction);
function myFunction(e)
{
if (e.keyCode == 13)
{
var line = document.createElement('div'); //This is my div which contains the 3 items which constitute a line
line.classList.add('myLine');
document.getElementById('myUl').appendChild(line);
var circle = document.createElement('i'); //The first item is a circle which can be check or unchecked
circle.id = 'myCircle';
document.querySelector('.myLine:last-child').appendChild(circle);
circle.addEventListener('click', function()
{
this.classList.toggle('fas');
this.classList.toggle('fa-check-circle');
this.classList.toggle('unstyled');
task.classList.toggle('crossedOut')
});
var task = document.createElement('li'); //The second item is a <li> which contains the value of the input
task.innerHTML = yourToDo.value.charAt(0).toUpperCase() + yourToDo.value.slice(1);
document.querySelector('.myLine:last-child').appendChild(task);
var trash = document.createElement('i'); //The third item is a trash which suppresses the whole line
trash.classList.add('fas');
trash.classList.add('fa-trash-alt');
document.querySelector('.myLine:last-child').appendChild(trash);
trash.addEventListener('click', function()
{
this.parentNode.remove();
});
yourToDo.value = '';
}
}
<section>
<ul id="myUl"></ul>
<label>
<input id="myInput" type="text" placeholder="Add your to do task" onfocus="this.placeholder = ''" onblur="this.placeholder = 'Add your to do task'">
</label>
</section>
I don't think that localStorage works in the code-snippets but try this in your own code.
I added a function to your code that gets all localStorage items and creates a list item for each on page load. Also added where you should save it to localStorage in your original function.
Comments are added throughout.
Hope this helps
window.addEventListener('load', function() {
var yourToDo = document.getElementById('myInput');
yourToDo.addEventListener('keydown', myFunction);
function myFunction(e) {
if (e.keyCode == 13) {
var line = document.createElement('div'); //This is my div which contains the 3 items which constitute a line
line.classList.add('myLine');
document.getElementById('myUl').appendChild(line);
var circle = document.createElement('i'); //The first item is a circle which can be check or unchecked
circle.id = 'myCircle';
document.querySelector('.myLine:last-child').appendChild(circle);
circle.addEventListener('click', function() {
this.classList.toggle('fas');
this.classList.toggle('fa-check-circle');
this.classList.toggle('unstyled');
task.classList.toggle('crossedOut')
});
var task = document.createElement('li'); //The second item is a <li> which contains the value of the input
task.innerHTML = yourToDo.value.charAt(0).toUpperCase() + yourToDo.value.slice(1);
//Set loacl storage item
localStorage.setItem(yourToDo.value.charAt(0).toUpperCase() + yourToDo.value.slice(1), yourToDo.value.charAt(0).toUpperCase() + yourToDo.value.slice(1));
document.querySelector('.myLine:last-child').appendChild(task);
var trash = document.createElement('i'); //The third item is a trash which suppresses the whole line
trash.classList.add('fas');
trash.classList.add('fa-trash-alt');
document.querySelector('.myLine:last-child').appendChild(trash);
trash.addEventListener('click', function() {
this.parentNode.remove();
});
yourToDo.value = '';
}
}
});
function load() {
//Create each item (as you did above)
function create(item) {
var yourToDo = document.getElementById('myInput');
var line = document.createElement('div'); //This is my div which contains the 3 items which constitute a line
line.classList.add('myLine');
document.getElementById('myUl').appendChild(line);
var circle = document.createElement('i'); //The first item is a circle which can be check or unchecked
circle.id = 'myCircle';
document.querySelector('.myLine:last-child').appendChild(circle);
circle.addEventListener('click', function() {
this.classList.toggle('fas');
this.classList.toggle('fa-check-circle');
this.classList.toggle('unstyled');
task.classList.toggle('crossedOut')
});
var task = document.createElement('li'); //The second item is a <li> which contains the value of the input
//Set innerHTML to item that you ar passing in for loop below
task.innerHTML = item;
document.querySelector('.myLine:last-child').appendChild(task);
var trash = document.createElement('i'); //The third item is a trash which suppresses the whole line
trash.classList.add('fas');
trash.classList.add('fa-trash-alt');
document.querySelector('.myLine:last-child').appendChild(trash);
trash.addEventListener('click', function() {
this.parentNode.remove();
});
yourToDo.value = '';
}
//Create an array to store all local storage items
var values = [],
keys = Object.keys(localStorage),
a = keys.length;
//Push items to array
while (a--) {
values.push(localStorage.getItem(keys[a]));
}
//Create a for loop and loop through all array items and pass each item value to the create funtion above
for (let i = 0; i < Object.keys(localStorage).length; i++) {
create(values[i])
}
}
//Call load on page load up (it would actually be better if you add this in your window "load" listener above )
load();
//Hope this helps!
<section>
<ul id="myUl"></ul>
<label>
<input id="myInput" type="text" placeholder="Add your to do task" onfocus="this.placeholder = ''" onblur="this.placeholder = 'Add your to do task'">
</label>
</section>

Creating favourites list - how to copy item from one list to another on click?

(New to Javascript)
I have created a input box which turns the input value into a list item, and adds a star
I am trying to create a new list whereby when the star is clicked on, it will copy into the 'favourites' section
I have tried this, but it is not working
let newStarSpan = document.querySelector("span")
newStarSpan.addEventListener('click', function(e){
//on click, get the parent node of the item clicked on
//i.e., want to return the individual <li> list item for the starSpan that was cliked on
let favNode = this.parentNode
//create a copy of this list item (the parent node)
let nodeCopy = favNode.cloneNode(true)
//add it to list B - favourites list
listB.appendChild(nodeCopy)
https://codepen.io/jemimacholzer/pen/dyYMKxj
You have few issues in your code, the first issue is related to the selector
const listB = document.querySelector('listB')
update it with
Fix:
const listB = document.getElementById('listB')
the other issue is related to newSpan event declaration & its scope, the newSpan can't be accessed outside of btn.onclick function.
You also assigned click listener to newSpan which seems to declare & bind on page when no newSpan is present in Dom. So instead of defining the listener you need create a function which triggers on newSpan click and bind that function during newSpan creation.
const listA = document.getElementById("listA")
const input = document.querySelector('input')
const btn = document.querySelector('button')
const listB = document.getElementById('listB')
btn.onclick = function() {
let Shopitem = input.value //store current value of input element in variable
input.value = ' ' //empty input element
const listItem = document.createElement('li') //creating a list item for the shopping item
listItem.setAttribute('id', Shopitem)
const span = document.createElement('span') //list text
const newBtn = document.createElement('button') //creating button to delete item
var starSpan = document.createElement("span")
var text = document.createTextNode("\u2B50")
starSpan.appendChild(text)
//making span and button children of the list item (containing within each list item)
listItem.appendChild(starSpan)
listItem.appendChild(span) //add list text to list item
listItem.appendChild(newBtn) //add list remove button to list item
newBtn.textContent = 'Delete'
span.textContent = Shopitem
listA.appendChild(listItem) //add li to the ul list
newBtn.onclick = function(e) {
list.removeChild(listItem)
}
starSpan.onclick = function(e) {
cloneListItem(listItem)
}
input.focus(); //focus the input method ready for entering the next shopping list item
}
//need to create new reference to star span as old reference is inside eother function
function cloneListItem(favNode){
//create a copy of this list item (the parent node)
let nodeCopy = favNode.cloneNode(true)
//add it to list B - favourites list
listB.appendChild(nodeCopy)
}
https://codepen.io/jemimacholzer/pen/dyYMKxj

Firebase, finding the key of parent based on child's value

essentially what I am trying to do is to create a button on a created text node in js. Then find the value of spmet and remove the question (spmet) from the database.
However I can't figure out how to properly reference it, and find the specific value that I want deleted. (so other picture, remove that question from database when I press the "x")
this is the the way to remove questions
This is the firebase layout
var btn = document.createElement("BUTTON");
var btnText = document.createTextNode("x"); //create button
btn.appendChild(btnText);
tekst.appendChild(btn);
btn.id = "questionBtn";
//bytter enter som gir linjeskift til <br>
tekst.innerHTML = tekst.innerHTML.replace(/\n/g, '<br>');
chat.appendChild(bubble);
setTimeout(function(){
chat.classList.add('visible')
}, 1);
chat.scrollTop = chat.scrollHeight;
console.log(bubble);
// Function to remove the question on the button generated
tekst.onclick = function removeQ(){
window.alert("Knapp funker");
var ref = database.ref();
ref.child('spm')
.orderByChild('spmet')
.equalTo(spmet)
.once('value', function(snap) {
//remove the specific spmet parent
window.alert(snap.val());
});
document.getElementById("cont1").removeChild(bubble); // removes text from page
var spmRef = ??
spmRef.remove(); //can't reference properly
}
When you generate the HTML element for each question, keep the ID of that question as an attribute on that HTML element:
ref.child("spm").on("child_added", function(snapshot) {
var div = document.createElement("div");
div.id = snapshot.key;
div.innerText = snapshot.child("spmet").val();
div.onclick = onSpmClick;
questionContainer.appendChild(div);
});
Now when the user clicks on one of the questions, you can get the key from that div and remove it:
function onSpmClick(e) {
var key = e.target.id;
ref.child("spm").child(key).remove();
}

Categories

Resources