I am new in JavaScript. I wrote function to save data to local storage, but every time I try to save a data,it doesnt work and it always get this error: push is not a function.and this error just appears a second in console.log and dissappear by itself. apart from this my problem is it doesn't save the data to the local storage How can I fix this? (the function,not working is "addTodoToStorage"
const form =document.querySelector("#todo-form");
const todoInput = document.querySelector("#todo");
const todolist= document.querySelector(".list-group");
const firstCardBody= document.querySelectorAll(".card-body")[0];
const secondCardBody= document.querySelectorAll(".card-body")[1];
const filter = document.querySelector("#filter");
const clearButton = document.querySelector("clear-todos");
eventListeners();
function eventListeners() {
form.addEventListener("submit",addTodo);
}
function addTodo(e) {
const newTodo = todoInput.value.trim();
if (newTodo === ""){
showAlert("danger","please type something");
}
else{
addTodoToUI(newTodo);
addTodoToStorage(newTodo);
showAlert("success","successfully added");
}
e.preventDefault();
}
function getTodosFromStorage() {
let todos;
if(localStorage.getItem("todos") === null){
todos = [];
}
else{
todos= JSON.parse(localStorage.getItem("todos"));
}
return todos;
}
function addTodoToStorage(newTodo) {
let todos = getTodosFromStorage();
todos.push(newTodo);
localStorage.setItem("todos",JSON.stringify(todos));
}
function showAlert(type,message) {
const alert = document.createElement("div");
alert.className= `alert alert-${type}`;
alert.textContent= message;
firstCardBody.appendChild(alert);
//set time out
setTimeout(function () {
alert.remove();
},2000);
}
function addTodoToUI(newTodo) {
const listItem = document.createElement("li");
const link = document.createElement("a");
link.href="#";
link.className="delete-item";
link.innerHTML='<i class = "fa fa-remove"></i>';
listItem.className="list-group-item d-flex justify-content-between";
listItem.appendChild(document.createTextNode(newTodo));
listItem.appendChild(link);
todolist.appendChild(listItem);
todoInput.value = "";
}
As for the error msg disappearing after a while, it's because you haven't prevented the default behavior of the form. Or rather, you have prevented it at the bottom, which is fine if you want the form to prevent GET-ing(its default method) to the URL(in action attribute).
But if an error occurs inside the submit handler (in your case, it probably does), the JavaScript stops the execution, and the form continues its default behavior, because there's is nothing to tell it that it can't do that. It can, so it will. HTML forms be ratchet like that. Due to which the page reloads.
For the part why there's an error, I couldn't find any errors here. If this is the same code you're using, there shouldn't be a problem.
EDIT
I tried to reproduce your example as follows and it works. It adds items to my localStorage successfully.
<form id="todo-form">
<input id="todo" type="text" />
</form>
<script>
const form =document.querySelector("#todo-form");
const todoInput = document.querySelector("#todo");
eventListeners();
function eventListeners() {
form.addEventListener("submit",addTodo);
}
function addTodo(e) {
e.preventDefault();
const newTodo = todoInput.value.trim();
if (newTodo === ""){
showAlert("danger","please type something");
}
else{
addTodoToStorage(newTodo);
showAlert("success","successfully added");
}
}
function getTodosFromStorage() {
let todos;
if(localStorage.getItem("todos") === null){
todos = [];
}
else{
todos= JSON.parse(localStorage.getItem("todos"));
}
return todos;
}
function addTodoToStorage(newTodo) {
let todos = getTodosFromStorage();
todos.push(newTodo);
localStorage.setItem("todos",JSON.stringify(todos));
}
function showAlert(type,message) {
const alert = document.createElement("div");
alert.className = `alert alert-${type}`;
alert.textContent = message;
document.body.appendChild(alert);
//set time out
setTimeout(function () {
alert.remove();
}, 2000);
}
</script>
Related
I want the function home(); to execute only once. When the player chooses their weapon it creates a homepage button every time they click one of three buttons.
how can it run once so only one homepage button is created regardless of how many times the game is played again?
https://codepen.io/hamisakim/full/XWjVEbx
function home(){ const button = document.createElement("button");
button.innerHTML = "Homepage";
button.setAttribute("id",'homebutton')
const postDiv = document.querySelector('#choices');
postDiv.appendChild(button);
function buttonClick(e) {
home();
const choices = ["lapis", "papyrus", "scalpellus"];
const randomIndex = Math.floor(Math.random() * choices.length);
computer.currentChoice = choices[randomIndex];
document.querySelector("#homepageText").innerHTML = '';
document.querySelector('h3').innerHTML = 'choose below to play again!';
document.getElementById('choices').removeEventListener('click',null);
Add a check if it has run. A simple boolean
var hasHomeRun = false;
function home(){
if (hasHomeRun) return;
hasHomeRun = true;
...
Other option would be to check to see if the element exists
function home(){
if (document.querySelector('#choices')) return;
...
Since your home() functions adds a <button id='homebutton'>, you could use JS to check if that ID is already exists in the DOM
function home() {
if (!document.getElementById("homebutton")) {
// Create button, since it's not yet there
}
}
i think you need this:
window.runHome = false;
const home = function() {
console.log('run home');
}
const buttonClick = function() {
if(!window.runHome) {
window.runHome = 1;
home();
}
}
<button type="button" onclick="buttonClick()">Click</button>
As you can see in the image, the warning shows up whenever I trigger the click eventListener, naturally. But how can I make it show up only once. Thanks in advance.
function eventListeners() {
form.addEventListener("submit", addTodo)
}
function showAlert(type, message) {
const alert = document.createElement("div")
alert.className = `alert alert-${type} d-inline p-1`
alert.setAttribute("style", "border-radius: 2rem")
alert.textContent = message
form.appendChild(alert)
setTimeout(function () {
alert.remove()
}, 1000)
}
// Not a good solution.
Try this :
let isAlertBeingShown = false
function eventListeners() {
form.addEventListener("submit", addTodo)
}
function showAlert(type, message) {
if( isAlertBeingShown ) {
//Do not show alert. Simply return.
return;
// or you can do alert.remove here instead of return.
}
const alert = document.createElement("div")
alert.className = `alert alert-${type} d-inline p-1`
alert.setAttribute("style", "border-radius: 2rem")
alert.textContent = message
form.appendChild(alert);
isAlertBeingShown = true;
setTimeout(function () {
alert.remove();
isAlertBeingShown = false;
}, 1000)
}
Note: Instead of creating div and adding to form and then removing it again and again you can just add single div with id message in html for message and hide it using its css display property to none and then use javascript function to change message in div and show/hide it.
let alert = document.querySelector('#message');
function showAlert(type, message) {
alert.textContent = message;
alert.display = 'block';
setTimeout(function () {
alert.display = 'none';
alert.textContent = '';
}, 1000)
}
I want to save a bool value for when you refresh the page, the value is true (if you want the color theme to be default, white) and false (if you want the color theme to be dark, black). Local storage is saving the last value correctly, and loading the correct value, but it is still not working, I don't know why. Here is my code, any help would be grateful.
var link = document.getElementById("color-mode");
var button = document.getElementById("theme-button");
var searchButton = document.getElementById("search-button");
var userSettings = document.getElementById("user-settings");
var siteLogo = document.getElementById("site-logo");
var isDefault = false;
button.onclick = function() {
if(isDefault == true) {
DarkTheme();
}
else {
DefaultTheme();
}
}
function DefaultTheme() {
link.href = "../static/CSS/default.css";
isDefault = true;
searchButton.src = "../static/Media/SearchIconDefault.png";
userSettings.src = "../static/Media/UserIconDefault.png";
siteLogo.src = "../static/Media/LogoDefault.png";
window.localStorage.setItem("saveTheme", isDefault);
}
function DarkTheme() {
link.href = "../static/CSS/dark.css";
isDefault = false;
searchButton.src = "../static/Media/SearchIconDark.png";
userSettings.src = "../static/Media/UserIconDark.png";
siteLogo.src = "../static/Media/LogoDark.png";
window.localStorage.setItem("saveTheme", isDefault);
}
function load() {
isDefault = window.localStorage.getItem("saveTheme");
console.log("Val: " + isDefault);
if(isDefault == false) {
DarkTheme();
}
else {
DefaultTheme();
}
}
load();
Read your code:
load() initially had isDefault as false
it calls DarkTheme, which sets isDefault also to false
then user click button
isDefault is false - then DarkTheme is called, which test isDefault also to false.
Application basically can't enter other theme, except the case where you change something manually in localStorage.
Change code to :
button.onclick = function() {
if(isDefault == true) {
DefaultTheme();
}
else {
DarkTheme();
}
}
I'm currently working on a Library app where a user can track unread and read books. I have it setup where when you click a book a modal pops up with the title, author, and buttons allowing you to mark the book as read or completely delete it.
I'm trying to fix a problem where opening and closing more than one modal and then clicking the delete button will delete all the items you previously clicked.
Here's the delete function -
Book.prototype.delete = function() {
myLibrary = myLibrary.filter((e) => {
return e !== this;
});
};
Here's how I'm opening each modal -
const render = () => {
const booksUnreadList = document.getElementById('unread');
const booksReadList = document.getElementById('read');
booksUnreadList.innerHTML = 'Unread';
booksReadList.innerHTML = 'Read';
myLibrary.forEach((book) => {
const li = document.createElement('li');
li.className = 'book';
book.read === 'Read'
? booksReadList.appendChild(li)
: booksUnreadList.appendChild(li);
li.innerHTML = book.info();
li.addEventListener('click', function handler() {
openBookModal(book);
});
});
And then the modal itself -
function openBookModal(book) {
document
.getElementById('book-modal-mark-complete')
.removeEventListener('click', markReadHandler);
document
.getElementById('book-modal-delete')
.removeEventListener('click', deleteHandler);
bookForm.style.display = 'none';
toggleForm.style.backgroundColor = '#978de0';
toggleForm.innerHTML = 'Add Book';
const bookModal = document.getElementById('book-modal');
bookModal.style.display = 'grid';
document.getElementById('book-modal-title').innerHTML = book.title;
document.getElementById('book-modal-author').innerHTML = 'By ' + book.author;
document
.getElementById('book-modal-mark-complete')
.addEventListener('click', markReadHandler);
function markReadHandler() {
book.read = 'Read';
render();
bookModal.style.display = 'none';
}
document
.getElementById('book-modal-delete')
.addEventListener('click', deleteHandler);
function deleteHandler() {
book.delete();
render();
bookModal.style.display = 'none';
}
document.getElementById('book-modal-close').addEventListener('click', () => {
bookModal.style.display = 'none';
});
}
Here's a jsfiddle of everything, to recreate the problem just open and close 2+ books and then delete 1 of them.
https://jsfiddle.net/Spawn_Bot/w6j4b8Lh/4/
Thanks!
You could store the currently selected book in the currentBook variable, move the modal event handlers outside the openBookModal() function and in the event handlers you could delete the currentBook:
let currentBook = {};
...
function openBookModal(book) {
currentBook = book;
...
}
document
.getElementById('book-modal-mark-complete')
.addEventListener('click', markReadHandler);
function markReadHandler() {
currentBook.read = 'Read';
render();
bookModal.style.display = 'none';
}
document
.getElementById('book-modal-delete')
.addEventListener('click', deleteHandler);
function deleteHandler() {
currentBook.delete();
render();
bookModal.style.display = 'none';
}
Here's the demo: https://jsfiddle.net/k6z08m2q/
I am a beginner in Javascript development and I have to do the classical to-do app. It has to be object-oriented and my program has two classes: Task and Tag.
A task contains some tags.
When the user clicks on a tag, he can modify its name. First, I did wrote an anonymous callback function which was listening to the modification form submission and it worked well. But, I have to create a named function declared somewhere else instead of my existing listener. However, I need to access to some of the properties of my object (which is edited) and I have absolutely no idea how to do a thing like that.
Here is a small part of my code:
module.Tag = class Tag {
constructor(name = 'untitled', parent = null) {
this.name = name;
this.parentTask = parent;
}
//Method which displays the tag name
display_name() {
return $('<li>').addClass('tag').text(this.name);
}
//Method which displays the tag
display() {
let tag_item = this.display_name();
let field = $('<input>').prop('type', 'text').prop('value', this.name);
let button = $('<button>').addClass('validationButton').prop('type', 'submit').text('✓');
let removeButton = $('<button>').addClass('removeButton').text('X');
let form = $('<form>').append(field).append(button).append(removeButton);
let in_edit = false;
tag_item.click((event) => {
event.stopPropagation();
event.preventDefault();
let target = $(event.target);
if (target.is('li') && !in_edit) {
tag_item.empty();
tag_item.append(form);
in_edit = true;
}
if (target.is('button') && target.prop('type') === 'submit') {
if(field.val() !== '') {
this.name = field.val();
module.StorageManager.storeTasks();
}
tag_item.empty();
tag_item.text(this.name);
field.val(this.name);
in_edit = false;
}
if (target.is('button') && target.hasClass('removeButton')) {
if(confirm('Voulez-vous vraiment supprimer ce tag ?')) {
tag_item.remove();
this.removeTagFromParent();
module.StorageManager.storeTasks();
}
}
});
return tag_item;
}
//Method which removes the tag from the parent task
removeTagFromParent() {
this.parentTask.removeTag(this);
}
};
My listener is in the display method and it uses Tag.name property and some of the variables created in the method body. I can't see how to write this function somewhere else and Google didn't help me.
I hope my problem is clear, English is not my native language.
Some advices?
You can extract your anonymouse function to be another class method. It is an event handler so in order to correctly access the defined object you'll have to bind it correctly.
Here is an example of the modified script:
module.Tag = class Tag {
constructor(name = 'untitled', parent = null) {
this.name = name;
this.parentTask = parent;
}
//Method which displays the tag name
display_name() {
return $('<li>').addClass('tag').text(this.name);
}
//Method which displays the tag
display() {
let tag_item = this.display_name();
let field = $('<input>').prop('type', 'text').prop('value', this.name);
let button = $('<button>').addClass('validationButton').prop('type', 'submit').text('✓');
let removeButton = $('<button>').addClass('removeButton').text('X');
let form = $('<form>').append(field).append(button).append(removeButton);
let in_edit = false;
tag_item.click(this.handleClick.bind(this));
// this is where you invoke the function and
//bind it to the context of the class
return tag_item;
}
//Method which removes the tag from the parent task
removeTagFromParent() {
this.parentTask.removeTag(this);
}
// extracted method defined here:
handleClick(event) {
let tag_item = this.display_name();
let field = $('').prop('type', 'text').prop('value', this.name);
event.stopPropagation();
event.preventDefault();
let target = $(event.target);
if (target.is('li') && !in_edit) {
tag_item.empty();
tag_item.append(form);
in_edit = true;
}
if (target.is('button') && target.prop('type') === 'submit') {
if(field.val() !== '') {
this.name = field.val();
module.StorageManager.storeTasks();
}
tag_item.empty();
tag_item.text(this.name);
field.val(this.name);
in_edit = false;
}
if (target.is('button') && target.hasClass('removeButton')) {
if(confirm('Voulez-vous vraiment supprimer ce tag ?')) {
tag_item.remove();
this.removeTagFromParent();
module.StorageManager.storeTasks();
}
}
}
};