I keep having the same issue again and again : When I add a task, it gets added to the localStorage correctly. But once I have multiple tasks and I click complete on a random one, sometimes it happens that the LocalStorage removes the task before it or after it. I can't figure out a way how to let it work.
let addBtn = document.getElementById("addButton");
let textInput = document.getElementById("textInput");
let mainList = document.getElementById("mainList");
let storage = JSON.parse(localStorage.getItem("toDos")) || [];
if (storage.length != 0){
for (let i=0; i<storage.length;i++){
addTask(mainList, storage[i]);
};
};
function addTask(list,task){
var newTask = document.createElement("li");
newTask.textContent=task
newTask.className="task d-flex justify-content-between list-group-item bg-primary mt-2";
addCompleteBtn(newTask);
list.appendChild(newTask);
}
function addCompleteBtn (newTask){
let completeBtn = document.createElement("button");
completeBtn.className="btn btn-success completeBtn";
completeBtn.textContent="Completed";
newTask.appendChild(completeBtn);
};
addBtn.addEventListener("click", ()=>{
if(textInput.value === ""){
alert("Error: The field is either empty or you entered more than 100 characters!");
}else{
storage.push(textInput.value);
addTask(mainList, textInput.value);
textInput.value="";
localStorage.setItem("toDos",JSON.stringify(storage));
};
});
mainList.addEventListener("click",(evt)=>{
if (evt.target.className==="btn btn-success completeBtn") {
let listItem = evt.target.parentNode;
let mainUl = listItem.parentNode;
let toBeRemoved = listItem.textContent;
storage.splice(storage.indexOf(toBeRemoved),1);
mainUl.removeChild(listItem);
localStorage.setItem("toDos",JSON.stringify(storage));
};
});
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<link rel="stylesheet" href="style.css">
<title>To-do-list App</title>
</head>
<body>
<div class="jumbotron bg-info">
<div class="container">
<h1 class="display-4 text-center">Task List</h1>
<p class="lead text-center">Click on the Add button to add a task</p>
</div>
</div>
<div class="container">
<div class="firstInput input-group mb-5">
<div class="input-group-prepend">
<button class="btn btn-primary" type="button" id="addButton">Add Task</button>
</div>
<input type="text" title='Enter a Task' class="form-control" id="textInput" maxlength="200">
</div>
</div>
<ul class='tasksList container' id="mainList">
</ul>
<script src="script.js"></script>
</body>
</html>
Shows all the values in the LocalStorage
Once task 2 is removed, task 3 is removed from the Storage instead
this is the specific part that causes me issues :
mainList.addEventListener("click",(evt)=>{
if (evt.target.className==="btn btn-success completeBtn") {
let listItem = evt.target.parentNode;
let mainUl = listItem.parentNode;
let toBeRemoved = listItem.textContent;
/* This is the part of code causing me issues
storage.splice(storage.indexOf(toBeRemoved),1);
*/
mainUl.removeChild(listItem);
localStorage.setItem("toDos",JSON.stringify(storage));
};
});
The problem is here let toBeRemoved = listItem.textContent;,this will return the text content of all elements inside that li. So if you add a task named 'Task 1', when you press Completed the value of toBeRemoved will be Task 1Completed since this will return the text of the button as well since your button is a child of the li.
So change let toBeRemoved = listItem.textContent; to let toBeRemoved = listItem.childNodes[0].nodeValue; and you should be fine, this will get the text of the li only.
Related
I want to add li to the ul element with the value entered by user in the input area.
This is the error I am getting in console:
[Error] TypeError: document.getElementsByClassName("list-group").appendChild is not a function. (In 'document.getElementsByClassName("list-group").appendChild("li")', 'document.getElementsByClassName("list-group").appendChild' is undefined)
additem (script.js:7)
//this is my javascript code-->
function additem() {
var task = document.getElementsByClassName("input-group-text").value;
var li = document.createElement("li");
li.classList.add("list-group-item");
li = "task";
document.getElementsByClassName("list-group").appendChild("li");
}
document.getElementById("button").addEventListener("click",additem);
//<--!this is my html code-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>LearnCodeOnline</title>
<link rel="stylesheet" href="./style.css" />
<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
/>
</head>
<body class="bg-dark">
<div class="container bg-warning p-4">
<h1 class="text-center">LearnCodeOnline</h1>
<div class="input-group">
<span class="input-group-text">todo</span>
<textarea class="form-control" aria-label="With textarea"></textarea>
</div>
<button id="button" class="float-right">Add</button>
<ul class="list-group">
<li class="list-group-item">An item</li>
</ul>
<!-- <button
type="button"
class="btn btn-success btn-lg mt-4 mx-auto d-block sort-btn"
>
Sort courses
</button> -->
</div>
</body>
</html>
JS:-
function additem() {
var task = document.getElementsByClassName("form-control")[0].value;
var li = document.createElement("li");
li.classList.add("list-group-item");
li.innerText = task;
document.getElementsByClassName("list-group")[0].appendChild(li);
}
document.getElementById("button").addEventListener("click",additem);
Instead of doing .appendChild("li"), we'll give it an element and not string as it takes the element and not string
getElementsByClassName() returns an array so we will take the item at 0th index of the array in both the functions where it is there
also instead of setting li = "task" like this, we'll have to set its text by li.innerText = task
i started an app todoslist , after creating first code simply of adding new todos in DOM
now my task is this :
addtodo :
// grab todo value
// pu tit in the array
// tell the draw method to redraw the todos
drawtodo :
grab the array
for each text add a todo entry in the documen
the array
my html code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="style.css">
<title>TodoList</title>
</head>
<body>
<div class="todolist_box">
<h3> To Do List </h3>
<div class="input">
<input type="text" id ="inp" placeholder="Add new Task">
<button onclick="newTodo()" ><i> enter </i></button>
<button onclick="newTodo()" ><i> save </i></button>
<button onclick="drawtodo()" ><i> load </i></button>
</div>
<ul id="myUL">
</ul>
</div>
<script src="script.js" type="application/javascript"></script>
</body>
</html>
and this is my javascript code
function newElement() {
// this code doesn't work, but it gives you an idea
const li = document.createElement("li")
const newEntry = document.getElementById("inp").value
const u = document.createTextNode(newEntry)
li.appendChild(u)
document.getElementById("myUL").appendChild(li)
document.getElementById("inp").value = "Nothing"
// something like thi
let todos = []
function newTodo() {
let inpvalue = document.getElementById('inp').value
todos.push(inpvalue)
// trigger draw event
}
function drawtodo() {
for (var i = todos.length - 1; i >= 0; i--) {
let li = document.createElement('li')
let newlist = li.appendChild(document.createTextNode(todos[i]))
inpvalue.appendChild(newlist)
}
}
document.onload = function() {
// this will excute when the document loads
}
}
Try using Javascript event listener instead of onclick attribute in html.
HTML:
<button id="load" ><i> load </i></button> // Removed onclick attribute
JS:
document.getElementById("load").addEventListener("click", drawtodo, false);
same with enter and save buttons, when click triggers the newTodo function.
I can't clear up my input field even tho i followed a tutorial step by step , i'm making this to do list and I want the input field to be clear anytime i submit a new to do . so what is wrong with my code ?
ps : i tried to clear the cache and nothing
let addButton=document.getElementById('addButton');
let toDoContainer = document.getElementById('ToDoContainer');
let inputField = document.getElementById('text');
//event listeners
addButton.addEventListener('click',addTodo);
//functions
function addTodo(event,title){
event.preventDefault();
//create the to do
let toDoDiv = document.createElement('div');
toDoDiv.classList.add('todo');
const newToDo =document.createElement('p');
newToDo.innerHTML=inputField.value;
newToDo.classList.add('todo-item');
toDoDiv.appendChild(newToDo);
toDoContainer.appendChild(toDoDiv);
//check mark button
const completedButton = document.createElement('button');
completedButton.innerHTML="success";
completedButton.classList.add("complete-btn");
toDoDiv.appendChild(completedButton);
//check delete button
const trashButton = document.createElement('button');
trashButton.innerHTML="delete";
trashButton.classList.add("complete-btn");
toDoDiv.appendChild(trashButton);
//append todo
toDoContainer.appendChild(tododiv);
inputField.value= "";
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>to do list </title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<div class="container">
<header>
<h1>This is your to do list </h1>
<div class="input">
<input type="text" placeholder="what to do ...?" id="text">
<input type="button" value="add" id="addButton">
</div>
</header>
<div id="ToDoContainer">
</div>
</div>
<script src="./script.js"></script>
</body>
</html>
here is a screenshot
sonEtLumiere is right, you have a typo. It should be:
toDoContainer.appendChild(toDoDiv);
Your variable is called toDoDiv, you have an error in this line (penultimate line):
toDoContainer.appendChild(tododiv);
This will work:
toDoContainer.appendChild(toDoDiv);
I try to build a browser-based text RPG as a project to improve my JavaScript. It looks like this:
Browser based Text RPG.
Now I have one Button with a click EventListener. When the button is clicked, I store the input of the form into a variable called playerName and return the player Name and some text by modifying the innerHTML of an empty paragraph.
JS:
let input = document.querySelector("#input");
let button = document.querySelector("#mainButton");
let textView = document.querySelector("#textView");
let buttonPress = function() {
button.addEventListener("click", function() {
if (input.value.length === 0) {
alert("Please enter a valid Name!");
//Evaluate Player Name
} else if (input.value.length >= 1) {
let playerName = input.value;
input.value = "";
textView.innerHTML = `Hi there ${playerName}, your adventure begins shortly! Where do you come from?`;
input.placeholder = "Origin";
}
});
};
buttonPress();
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<title>A Tale in Space</title>
<!-- Required meta tags -->
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<!-- Bootstrap CSS -->
<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
crossorigin="anonymous"
/>
<link rel="stylesheet" href="app.css" />
</head>
<body>
<div class="container d-flex justify-content-center align-items-center">
<div class="row justify-content-center align-items-center">
<div
class="col-6 flex-column d-flex justify-content-center align-items-center"
>
<h1 class="text-center">Welcome to A Tale in Space</h1>
<input type="text" name="" id="input" placeholder="Enter your name" />
<button id="mainButton" type="submit" class="btn btn-danger mt-3">
Submit
</button>
<p id="textView" class="text-center mt-5"></p>
</div>
</div>
</div>
<!-- Optional JavaScript -->
<script src="app.js"></script>
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script
src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
crossorigin="anonymous"
></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
crossorigin="anonymous"
></script>
<script
src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
crossorigin="anonymous"
></script>
</body>
</html>
I try to figure out what I need to do to basically continue on the same page by pressing the same button. So after the name was returned I would like to change the text, asking the origin of the player by pressing the SAME button. It somehow breaks my brain.
If someone has a better suggestion of displaying my text, I am all ears.
TL;DR: How to use the same button for multiple functions?
Try it to use multiple functions for one button:
button.addEventListener("click", function(){alert("click1")}, false);
button.addEventListener("click", function(){alert("click2")}, false);
I hope this works for you:
add name to input name
<input type="text" name="name" id="input" placeholder="Enter your name" />
Js file :
let button = document.querySelector("#mainButton");
let playerName = "";
let playerOrigin = "";
(function() {
button.addEventListener("click", function() {
let input = document.querySelector("#input");
let textView = document.querySelector("#textView");
if (input.value.length === 0) {
alert("Please enter a valid Name!");
//Evaluate Player Name
} else if (input.value.length >= 1) {
if (input.name === 'name') {
playerName = input.value;
input.value = "";
textView.innerHTML = `Name : ${playerName}, Origin : ${playerOrigin}`;
input.placeholder = "Origin";
input.name = "origin";
} else if (input.name === 'origin') {
playerOrigin = input.value;
input.value = "";
textView.innerHTML = `Name : ${playerName}, Origin : ${playerOrigin}`;
input.placeholder = "Enter your name";
input.name = "name";
}
}
});
})();
I have the following html page that seeks to check for keywords in text input and return a score depending on how many keywords are entered. For instance, if two keywords are returned, the score would be 2, and if the answer contains no keywords, then a score of 0 would be returned.
In the example below two accepted keywords have been used (e.g. good and eternal) and therefore the user would receive a score of 2.
I've made an attempt (a beginner to JS) but would appreciate some help.
UPDATE: The javascript i have used nearly works (to search for multiple keywords) but doesnt display the showscore variable correctly each time.
This is what I have so far:
<script>
document.getElementById('longanswer').addEventListener('input', function(e){
let keyword = e.target.value;
document.getElementById('greeting').innerHTML = "Just write this";
});
function displayScore() {
var showscore = 0;
var answer = document.getElementById('longanswer').value;
var keyword1="eternal";
var keyword2="good";
var keyword3="true";
if (answer.indexOf(keyword1)!=-1){
showscore=showscore+1
document.getElementById("displayscore").innerHTML = showscore;
} else if (answer.indexOf(keyword1)!=-1 & answer.indexOf(keyword2)!=-1){
showscore=showscore+2
} else if (answer.indexOf(keyword1)!=-1 & answer.indexOf(keyword2)!=-1 & answer.indexOf(keyword3)!=-1){
showscore=showscore+3
} else {
showscore=0
}
document.getElementById("displayscore").innerHTML = showscore;
}
</script>
The interface is below:
Whole code that can be easily re-created below:
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<title>Long Answer Question & Answers</title>
</head>
<body>
<form>
<h1> Q and A </h1>
<div class="form-group">
<label for="longanswer">What is the meaning of life?:</label><br>
<textarea rows="4" cols="100" id="longanswer">
</textarea>
<br>
<button onclick="displayScore()" type="button" class="btn btn-success">Submit</button>
</div>
</form>
<div class="card border-primary mb-3" style="max-width: 18rem;">
<div class="card-header">Score</div>
<div class="card-body text-primary">
<h5 class="card-title">Generating a score for your answer</h5>
<p class="card-text" id="displayscore">Once you've clicked submit we will display your score for this answer here.</p>
</div>
</div>
<script>
document.getElementById('longanswer').addEventListener('input', function(e){
let keyword = e.target.value;
document.getElementById('greeting').innerHTML = "Just write this";
});
function displayScore() {
var showscore;
var answer = document.getElementById('longanswer').value;
var keyword1="eternal";
var keyword2="good";
if (answer.indexOf(keyword1)!=-1)
{
showscore="found"
}
document.getElementById("displayscore").innerHTML = showscore;
}
</script>
To have it more dynamic, I'd use a new RegExp in which you can check the text; the length of match' result will be the score
let keywords = new RegExp("(" + ["foo", "bar", "baz"].join('|') + ")", 'gi');
document.getElementById('btn').addEventListener('click', e => {
document.getElementById('score').innerHTML =
document.getElementById('foo').value.match(keywords).length;
});
<textarea id="foo"></textarea>
<button id="btn">
Check
</button>
<div id="score">
</div>