I'm attempting to create a simple to-do list and I've encountered two problems:
After refreshing the page, all the created elements are no longer visible on the page despite being in local storage.
After refreshing the page and submitting new values to the input, localStorage overwrites itself.
Despite that, the items displayed from the input fields are from the previous localStorage, which no longer exists (I really hope this makes sense).
const inputEl = document.getElementById("inputEl")
const submitBtn = document.getElementById("submit")
const clearBtn = document.getElementById("clearBtn")
const todoListContainer = document.getElementById("todoList")
const taskContainer = document.querySelector(".task")
const cancelBtn = document.querySelector(".cancelBtn")
const doneBtn = document.querySelector(".doneBtn")
const errorMsg = document.querySelector(".error")
let localStorageContent = localStorage.getItem("tasks")
let tasksItem = JSON.parse(localStorageContent)
let tasks = []
function createTask() {
if (inputEl.value.length != 0) {
const newDiv = document.createElement("div")
newDiv.classList.add("task")
const newParagraph = document.createElement("p")
const newCancelBtn = document.createElement("button")
newCancelBtn.classList.add("cancelBtn")
newCancelBtn.textContent = "X"
const newDoneBtn = document.createElement("button")
newDoneBtn.classList.add("doneBtn")
newDoneBtn.textContent = "Done"
todoListContainer.appendChild(newDiv)
newDiv.appendChild(newParagraph)
newDiv.appendChild(newCancelBtn)
newDiv.appendChild(newDoneBtn)
//^^ Creating a container for a new task, with all its elements and assigning the classes^^
tasks.push(inputEl.value)
inputEl.value = ""
for (let i = 0; i < tasks.length; i++) {
localStorage.setItem("tasks", JSON.stringify(tasks))
newParagraph.textContent = JSON.parse(localStorageContent)[i]
}
errorMsg.textContent = ""
} else {
errorMsg.textContent = "You have to type something in!"
errorMsg.classList.toggle("visibility")
}
}
submitBtn.addEventListener("click", () => {
createTask()
})
clearBtn.addEventListener("click", () => {
localStorage.clear()
})
HTML code below:
<!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" href="/style.css">
<script src="/script.js" defer></script>
<title>To-do list</title>
</head>
<body>
<h2 class="error visibility"></h2>
<div id="todoList">
<h1>To-Do List</h1>
<input type="text" name="" id="inputEl" placeholder="Add an item!">
<button type="submitBtn" id="submit">Submit</button>
<button id="clearBtn">Clear list</button>
<div class="task">
</div>
</div>
</body>
</html>
After refreshing the page, all the created elements are no longer visible on the page despite being in local storage
That is because you are rendering the HTML only after the click event and not on page load. To render the HTML for existing tasks stored in the localStorage you have to write a code that loops over your existing tasks in the tasksItem and applies the rendering logic to it.
I would suggest splitting the rendering code from your createTask() function and create a new function for it (for example renderTask()), then you can use it inside a loop on page load and also call the function once a new task is created in the createTask() function.
window.addEventListener('load', (event) => {
// Your read, loop and render logic goes here
})
After refreshing the page and submitting new values to the input, localStorage overwrites itself.
That's because you are actually overriding the tasks in the localStorage. To keep existing tasks, you have to use your tasksItem variable instead of the blank tasks array to create your tasks in and save them to the localStorage.
So, instead of:
tasks.push(inputEl.value)
You would use:
tasksItem.push(inputEl.value)
The same goes for:
for (let i = 0; i < tasksItem.length; i++) {
localStorage.setItem("tasks", JSON.stringify(tasksItem))
// …
}
Related
I was unsure how exactly to phrase this in the title. I've made a todo list and I'm working on making the todo items editable. Through the displayTodo function, I've been able to make the li items editable in the DOM, but I would like this change to be reflected in the todoList array as well when I hit the save button. I'm unsure of how exactly I would be able to make this work. I was thinking of the splice method, but I don't know how that would work in this situation since I would need to pass in the index.
// Global Variables
const input = document.querySelector('.input');
const addBtn = document.querySelector('.add-btn');
const removeBtn = document.querySelector('.remove-btn');
const todos = document.querySelector('.todos');
// Event Listeners
addBtn.addEventListener('click', addTodo);
removeBtn.addEventListener('click', removeTodo);
const todoList = [
]
function addTodo() {
// Push user input to array
let inputValue = input.value;
todoList.push(inputValue);
displayTodo();
input.value = '';
console.log(todoList);
}
function removeTodo() {
let listItems = document.querySelectorAll('.todos li');
// Remove last todo from array
todoList.splice(-1, 1);
// Remove last todo from ul
todos.removeChild(listItems[listItems.length - 1]);
//console.log(todoList);
}
function displayTodo() {
// Create li and display it
let newTodo = document.createElement('li');
newTodo.textContent = input.value;
todos.appendChild(newTodo);
// Create edit button and display it
let editButton = document.createElement('button');
editButton.textContent = 'Edit';
newTodo.appendChild(editButton);
editButton.addEventListener('click', function() {
// Create edit input and save button
editButton.style.opacity = 0;
editButton.style.visibility = 'hidden';
let editInput = document.createElement('input');
newTodo.appendChild(editInput);
let saveButton = document.createElement('button');
newTodo.appendChild(saveButton);
saveButton.textContent = 'Save';
saveButton.addEventListener('click', function() {
newTodo.textContent = editInput.value;
console.log(todoList);
})
});
}
<!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>Todo List</title>
</head>
<body>
<div class="container">
<h1>Todo List</h1>
<input class="input" type="text" placeholder="Add A Task" autocomplete="off" required>
<button class="add-btn">Add Task</button>
<button class="remove-btn">Remove Task</button>
<ul class="todos">
</ul>
</div>
</body>
</html>
First of all, In editButton.addEventListener('click', function() {}, get the text content of the <li> element that has to be edited. The text content has the word 'Edit' appended to the list element and hence removing it in the second line. Get the index of the array element whole value is liValue using indexOf property.
let liValue = newTodo.textContent;
liValue = liValue.replace('Edit', '');
let liIndex = todoList.indexOf(liValue);
In saveButton.addEventListener('click', function () {}, after updating the DOM, use splice() to update the array list.
todoList.splice(liIndex, 1, editInput.value);
I have added the function in which the changes are done. The parts of the code that has to be added are commented down below.
editButton.addEventListener('click', function () {
// Create edit input and save button
editButton.style.opacity = 0;
editButton.style.visibility = 'hidden';
/* first part of the code starts here */
let liValue = newTodo.textContent;
liValue = liValue.replace('Edit', '');
let liIndex = todoList.indexOf(liValue);
/* first part of the code ends here */
let editInput = document.createElement('input');
newTodo.appendChild(editInput);
let saveButton = document.createElement('button');
newTodo.appendChild(saveButton);
saveButton.textContent = 'Save';
saveButton.addEventListener('click', function () {
newTodo.textContent = editInput.value;
/* second part of the code starts here */
todoList.splice(liIndex, 1, editInput.value);
/* second part of the code ends here */
})
});
Link to codepen: https://codepen.io/geekyquentin/pen/LYQdjXw
I have a problem. I want to make page like this
https://www.idntimes.com/korea/kpop/matthew-suharsono/rekomendasi-lagu-dreamcatcher-untuk-pengantar-tidur-c1c2/5
I already can add the page number at the end of the URL. But when I'm in testing.html/4 and I want to refresh it, the page does not appear and shows the error "Cannot get testing.html/4". How to make it can refresh like usual?
Here's my 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">
<title>Document</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<style>
.spinner {
display: none;
}
</style>
</head>
<body style="font-size: 60px;">
<div class="news-content">
</div>
<div class="loading">
<p>Loading Please Wait</p>
</div>
<script>
function loadData(count) {
fetch('/index.json')
.then(res => res.json())
.then(json => {
if (count < json.length) {
let text = document.createElement('p');
text.innerText = json[count].text;
document.querySelector('.news-content').append(text);
if (count > 0) {
history.pushState(null, null, `/testing.html/${count}`)
}
}
});
}
let count = 0
window.addEventListener('load', loadData(count));
window.addEventListener('scroll', () => {
if ((window.innerHeight + window.pageYOffset) >= document.body.offsetHeight) {
count += 1;
loadData(count)
}
})
</script>
</body>
</html>
It seems to me that you are using pure HTML files in an HTTP/HTTPS local server. When you are having this kind of instance of the server you are not dynamically generating pages because you don't have any server side setup behind the HTML file.
You can do this using queries and since your app doesn't contain any server backend use client Javascript to create a pagination concept.
Instead of having a route type system ( which is usually handled by controller on the backend ) use query system:
Instead of:
/testing.html/{PAGE_NUMBER}
Use:
/testing.html?page={PAGE_NUMBER}
To get page query in Javascript, use the following function:
function getPageNumber() {
const urlParams = new URLSearchParams(window.location.search);
const page = urlParams.get('page');
return page;
}
Then create a function where you would paginate the data ( assuming the data is an array ):
function paginateData(data, resultsPerPage, pageNumber) {
// Chunk the data based on the limit
let result = data.reduce((rows, key, index) => (index % resultsPerPage == 0 ? rows.push([key]) : rows[rows.length-1].push(key)) && rows, []);
// Return the current page with index calculation
return result[pageNumber - 1];
}
And the final code should be something like this:
function getData(data) {
const RESULTS_PER_PAGE = 2;
const currentPageNumber = Number(getPageNumber());
const paginatedData = paginateData(data, RESULTS_PER_PAGE, currentPageNumber);
// If paginated data is undefined return first page
if (!paginatedData) {
/*
You can even redirect to /testing.html?page=1
*/
return paginateData(data, RESULTS_PER_PAGE, 1);
}
return paginatedData;
}
All you are left with is to provide the function getData with an data parameter resembling an array type.
I have 2 html files connected to one js file. When I try to access a html element in the second html file using js it doesn't work saying that is is null. I did
let elementname = document.getElementById("element") for a element in the second html page then
console.log(elementname) and it says it is null. When I do it for a element in the first html page it says HTMLButtonElement {}
Here is the html for the first Page
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Not Quuuuiiiizzzz</title>
<link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h1>Not Quuuuiiiizzzz</h1>
<h2>Join a quiz</h2>
<!--Buttons -->
<div style="text-align: center;">
<button id="btnforquiz1" onclick="gotoquiz()"></button>
<button id="btnforquiz2" onclick="gotoquiz1()"></button>
<button id="btnforquiz3" onclick="gotoquiz2()"></button>
</div>
<h2 id="h2">Create a Quuuuiiiizzzz</h2>
<script src="script.js"></script>
</body>
</html>
For the second page
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Not Quuuuiiiizzzz</title>
<link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body onload="quizLoad()">
<h1 id="question">Hello</h1>
<button id="answer1"></button>
<button id="answer2"></button>
<button id="answer3"></button>
<button id="answer4"></button>
<script src="script.js"></script>
</body>
</html>
And Finally for the js file :
//setting global variables
let btn1 = document.getElementById("btnforquiz1") //getting button with id of btnforquiz1 repeat below
correct = 0
let btn2 = document.getElementById("btnforquiz2")
let btn3 = document.getElementById("btnforquiz3")
let question = document.getElementById("question")
let answer1 = document.getElementById("answer1")
let answer2 = document.getElementById("answer2")
let answer3 = document.getElementById("answer3")
let answer4 = document.getElementById("answer4")
quizNameRel = -1;
cosnole.log(question)
console.log(answer1)
//Quiz Data
Quiz_1 = {
"What is the capital of buffalo":["Idk", "Yes", "No",0],
"What is the smell of poop": ["Stinky"]
};
Quiz_2 = [
"What is wrong with you"
];
Quiz_3 = [
"What is wrong with you #2"
]
let quiz = {
name: ["History Test", "Math Practice", "ELA Practice"],
mappingtoans: [0,1,2],
QA: [Quiz_1, Quiz_2, Quiz_3]
}
//quiz data
//when body loades run showQuizzs function
document.body.onload = showQuizzs()
function showQuizzs() {
//loops throo the vals seeting the text for the btns
for (let i = 0; i < quiz.name.length; i++) {
btn1.textContent = quiz.name[i-2]
btn2.textContent = quiz.name[i-1]
btn3.textContent = quiz.name[i]
}
}
//leads to the showQuizzs
function gotoquiz() {
location.href = "quiz.html"
quizNameRel = quiz.name[0]//I was trying to create a relation so we could knoe which quiz they wnt to do
startQuiz()
}
function gotoquiz1() {
location.href = "quiz.html"
quizNameRel = quiz.name[1]
startQuiz()
}
function gotoquiz2() {
location.href = "quiz.html";
quizNameRel = quiz.name[2];
startQuiz();
}
function answerselect(elements){
whichone = Number(elements.id.slice(-2,-1))
if(Quiz_1[whichone]==Quiz_1[-1]){
correct+=1;
NextQuestion();
}else{
wrong+=1;
}
}
//gets the keys and puts it into an array
function getkeys(dictionary){
tempdict = [];
for(i in dictionary){
tempdict.push(i);
}
return tempdict;
}
function setQuestion() {
let tempdict = getkeys(Quiz_1)
console.log(tempdict, getkeys(Quiz_1));
//question.innerHTML = tempdict;
}
// startQuiz
function startQuiz() {
switch (quizNameRel){
case quiz.name[0]:
//case here
setQuestion()
break
case quiz.name[1]:
//case here
break
case quiz.name[2]:
//case here
break
}
}
//TO DO:
// Set the question
// Set the answer
// Check if correct button
This is happening because at a time you have rendered only one html file. For example if you render index1.html(first file) then your js will look for rendered element from first file only but here index2.html(second file) is not rendered so your js script is unable to find elements of that file that's the reason it shows null.
If you try to render now index2.html rather than index1.html then you will find now elements from index2.html are detected by js script but elements from index1.html are null now.
I have a difficult question, I am trying to get the input value of an input field, however, I need this to happen within another function.
I already have code that works outside of this other function but I need to refactor it to work inside another function that I am calling.
Examples of working code and non-working code are below.
Here is the HTML where I am getting the input:
<!DOCTYPE HTML>
<HTML>
<head>
<title></title>
<meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="css/styles.css" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script type="text/javascript" src="js/require.js"></script>
<script type="text/javascript">
(function () {
var config = {
baseUrl: "js",
};
var dependencies = ["otherFile"];
require(config, dependencies);
})();
</script>
</head>
<body>
<div>
<label>Input URL</label>
<input type="url" />
<p id="targetInput"></p>
</div>
</body>
</html>
Here is the non-working JS that I am trying to call within another function:
function someOtherFunction() {
var getCurrentInput = function() { };
var input = document.querySelector("input");
var log = document.getElementById("targetInput");
input.addEventListener("input", getCurrentInput);
var getCurrentInput = function (e) {
log.currentInput = e.target.value;
};
}
});
Lastly here is the working code that works outside of the scope of someOtherFunction
var getCurrentInput = "";
var input = document.querySelector("input");
var log = document.getElementById("targetInput");
input.addEventListener("input", getCurrentInput);
function getCurrentInput(e) {
log.currentInput = e.target.value;
}
Now you may notice that there isn't a form being submitted here, the reason for this is because this code is running on an iframe that is being called into another app. The submit is happening there but requires me to call a function to make it happen and technically isn't a submit, meaning I don't have control over it like a regular submit. This is why I need to call the current input value inside someOtherFunction.
Any help would be greatly appreciated here! Essentially I want to get the value inside the input and update my API with the value as a JSON string. There must be a better way!
Was a bit difficult to follow at first given the nesting, but something like this?
const doThing = (e) => {
let input = document.getElementById("input");
let log = document.getElementById("targetInput");
log.textContent = input.value;
}
<div>
<label>Input URL</label>
<input type="url" id="input"/>
<p id="targetInput"> </p>
</div>
<button onclick="doThing()">Click</button>
Essentially an external submit that takes an internal input value, and injects it into another internal element?
When making a new XMLHttpRequest, like so
let http = new XMLHttpRequest();
There is a method (property ?) called onload. My question is, why we can assign a function to it? like this
http.onload = function(){};
If onload is a property in XMLHttpRequest, the value of it will change when we assign a new value to it, like a function, right?
What is the XMLHttpRequest.onload constructor looks like?
I am really confused about this.
When we first started writing HTML event handlers, we would write
<button onclick="alert('Hey you')"> Say Hi</button>
When you translate the HTML into the DOM, you get a reference to an object and you can also set that event handler by setting a property to a function. The browser will call that function for you when the user clicks the button. This is a poor man's event system using properties as callbacks.
Those were the two ways to set an event handler:
XML attribute
DOM element reference by setting a property
After a while, they realized that multiple listeners may need to be set (overwriting a property would override the previous listener). We created hacks to allow setting multiple handlers by creating function that called other functions and there was a need for a real event system. The DOM element became an EventTarget, and you can now use a more common Event interface.
buttonEl.addEventListener('click', () => console.log('clicked'));
buttonEl.addEventListener('click', () => console.log('second handler'));
So we have three different ways of setting events. XmlHttpRequest also implements its own XMLHttpRequestEventTarget event target, so you can set "events" in both ways, through callback properties and addEventListener.
In your question, you said: "What is the XMLHttpRequest.onload constructor looks like?" You probably meant how can we override the onload property because a property doesn't have a constructor, its value has a constructor, but we typically assign it using a literal. It's initially unset (undefined), if it's never set, it won't get called.
It uses XMLHttpRequest() in its program;
Everything works on the ChromeDev per-browser. However, when I try to run the page in other browsers this method is blocked.
[enter image description here][1]
[1]: https://i.stack.imgur.com/mfE0k.png
In the MDN documentation it is written that currently the method is supported by all browsers.
Does anyone know why I can't run my script in other browsers?
function loadDocumentCsv() {
const xhttp = new XMLHttpRequest();
xhttp.onload = function () {
const request = this.responseText.toString();
const allRecords = downloadElement("#allRecords");
const isofixTrue = downloadElement("#isofixTrue");
const brandSort = downloadElement("#brandSort");
const sortMileageAndIsofixFalse = downloadElement("#sortMileageAndIsofixFalse");
allRecords.innerHTML = "";
isofixTrue.innerHTML = "";
brandSort.innerHTML = "";
sortMileageAndIsofixFalse.innerHTML = '';
const arrayData = request.replace(/\n/g, ",").split(",");
const newArray = arrayData.map((element, index) => {
element.trim();
if (index > 4) {
if (index % 5 === 2) {
const cutLetters = element.replace(/[^0-9]+/, "");
element = Number(cutLetters.substring(cutLetters.length - 4, cutLetters.length));
if (element == 0) element = 2007;
} else if (index % 5 === 3) {
element = Number(element.replace(/[^0-9]+/, ""));
} else if (index % 5 === 4) {
element = element.trim() === "TRUE";
}
}
return element;
});
const arrayOfArrays = [];
while (newArray.length) {
const addArrayToArrays = newArray.splice(0, 5);
arrayOfArrays.push(addArrayToArrays);
}
//header table
const header = arrayOfArrays.splice(0, 1);
const firstRow = (table) =>
header.forEach((el) => {
const item = templateHtml(el, "font-weight-bold");
table.appendChild(item);
});
//ALL CARS
firstRow(allRecords);
arrayOfArrays.forEach((element, index) => {
const item = templateHtml(element);
allRecords.appendChild(item);
const item2 = templateHtml(element);
element[4] === true || index === 0 ? isofixTrue.appendChild(item2) : "";
});
//isofix == TRUE
firstRow(isofixTrue);
//sort Brand
firstRow(brandSort);
arrayOfArrays.sort(sortedBrand);
arrayOfArrays.forEach((element) => {
const item = templateHtml(element, "font-weight-normal");
brandSort.appendChild(item);
});
// sortMileageAndIsofixFalse
firstRow(sortMileageAndIsofixFalse);
arrayOfArrays.sort((prev, next) => prev[3]- next[3]);
arrayOfArrays.forEach((element) => {
const item = templateHtml(element, "font-weight-normal");
element[4] === false ? sortMileageAndIsofixFalse.appendChild(item) : "";
});
};
xhttp.open("GET", "Cars.csv");
xhttp.send();
}
<!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">
<title>Document</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#4.6.0/dist/css/bootstrap.min.css"
integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://unicons.iconscout.com/release/v4.0.0/css/solid.css">
<link rel="stylesheet" href="./css/style.css">
</head>
<body id="body">
<section class="container loadClick mb-5">
<button type="button" onclick="loadDocumentCsv()" class="btn loadDocument font-weight-bold">Wyświetl wszystkie tabele</button>
</section>
<section class="container">
<h2>1. Wszystkie samochody</h2>
<div id="allRecords"></div>
</section>
<section class="container mt-5">
<h2>2. Samochody gdzie Isofix === TRUE</h2>
<div id="isofixTrue"></div>
</section>
<section class="container mt-5">
<h2>3. Posortowane samochody wg Marki</h2>
<div id="brandSort"></div>
</section>
<section class="container mt-5">
<h2>4. Samochody gdzie Isofix === FALSE, posortowane wg przebiegu od najmniejszego do największego</h2>
<div id="sortMileageAndIsofixFalse"></div>
</section>
<div id="fixed">
<i class="uis uis-arrow-circle-up"></i>
</div>
<script src="./index.js"></script>
</body>
</html>
The CSV file looks like this:
Brand,Model,Year of production,Mileage,Isofix
Alfa Romeo,156,1998,600 000,TRUE
Chrysler,Voyager,2007,342 121,FALSE
Smart,Cabrio,2005,422122,TRUE
Fiat,500,2001 unkown,121333,FALSE
Chrysler,Voyager,2005,421 325,FALSE
Smart,Liftback,2015,534531,TRUE