I wanted to train classes in Java Script with To Do List but I had some errors.
I want to make it by 4 classes (in separate files) - only for training.
Main.js - to coordinate rest of classes
AddTask.js
RemoveTask.js
SearchTask.js
I have an error with pushing tasks to toDoList in class AddTask.js.
This is code in HTML:
<!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>
</head>
<body>
<h1>ToDoList</h1>
<section>
<input type="text">
<button id="start">Add task</button>
</section>
<input type="text" class="search">
<h2>Number of tasks: <span>0</span></h2>
<ul></ul>
<script src="AddTask.js"></script>
<script src="RemoveTask.js"></script>
<script src="SearchTask.js"></script>
<script src="Main.js"></script>
</body>
</html>
This is code in Main.js
class Main {
constructor(){
this.addTask = new AddTask()
this.button=document.getElementById('start')
this.button.addEventListener('click', this.addTask.startGame.bind(this))
this.taskNumber = document.querySelector('h2 span')
this.ul = document.querySelector('body ul')
}
}
const start = new Main()
This is code in AddTask.js
class AddTask {
constructor() {
this.toDoList = []
}
startGame() {
const input = document.querySelector('section input')
const titleTask = input.value;
if (titleTask === "") return alert ('You have to write a task!');
const task = document.createElement('li');
task.innerHTML = titleTask + "<button>Delete</button>";
this.toDoList.push(task)
input.value = ""
return toDoList;
}
}
Now the error is:
AddTask.js:14 Uncaught TypeError: Cannot read properties of undefined (reading 'push') at Main.startGame (AddTask.js:14:19).
Could you tell me what is the problem in that case?
Remove .bind from Main Class and add
this.startGame = this.startGame.bind(this); to the constructorof AddTask
class AddTask {
constructor() {
this.toDoList = [];
this.startGame = this.startGame.bind(this);
}
startGame() {
const input = document.querySelector("section input");
const titleTask = input.value;
if (titleTask === "") return alert("You have to write a task!");
const task = document.createElement("li");
task.innerHTML = titleTask + "<button>Delete</button>";
this.toDoList.push(task);
input.value = "";
return this.toDoList;
}
}
Related
I want the button with the id of number1 to display the value of 1 on to the input box which has the id of quest which is short for question.I also want to know if my code can be made more readable.
<!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>Calucator</title>
<style>
body{
text-align: center;
}
</style>
<script>
const quest = document.getElementById("quest");
const data = quest.value;
const yourElement = document.createElement("div");
function nums(){
const num1 = document.getElementById('number1').innerText = 1;
data.textContent = num1;
}
function run() {
nums()
yourElement.textContent = data
quest.appendChild(yourElement);
}
</script>
</head>
<body>
<h1>Calucator</h1>
<input type="number" placeholder="Enter now" name="" id="quest">
<button onclick="run()">=</button>
<br>
<button onclick="" id="number1">1</button>
</body>
</html>
<script>
const quest = document.getElementById("quest");
const data = quest.value;
const yourElement = document.createElement("div");
//PROBLEM 1: You are not attaching yourElement to the DOM. See Element.insertBefore / Element.appendChild
function nums(){
const num1 = document.getElementById('number1').innerText = 1;
data.textContent = num1;
}
function run() {
nums()
yourElement.textContent = data
quest.appendChild(yourElement);
}
</script>
And
<button onclick="run()">=</button>
Problem 2: Don't use inline element event handling. It isn't safe and Content-Security-Policy won't allow it. Instead, use JavaScript Element.addEventListener(...)
Im still relatively new to JS. I know i probably shouldnt write my code the way i have done here in the real world, but im only doing this to test my knowledge on for loops and pulling JSON data.
My question is, with the way i have structured my code, is it possible for me to add classnames/Id's to the elements i have made using doc.createElement? for example if i wanted to add custom icons or buttons to each element? I cant seem to think of a way to add them other than having to write out all the HTML and do it that way. Here's my code :
HTML
<!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="./styles.css">
<title>Document</title>
</head>
<body>
<section>
</section>
<script src="./app.js"></script>
</body>
</html>
JS
const allCustomers = document.querySelector("section");
let custName = "";
let username = "";
let email = "";
let id = "";
const requestURL = "https://jsonplaceholder.typicode.com/users";
fetch(requestURL)
.then((response) => response.text())
.then((text) => DisplayUserInfo(text));
function DisplayUserInfo(userData) {
const userArray = JSON.parse(userData);
for (i = 0; i < userArray.length; i++) {
let listContainer = document.createElement("div");
let myList = document.createElement("p");
let myListItems = document.createElement("span");
myList.textContent = `Customer : ${userArray[i].name}`;
myListItems.innerHTML =`<br>ID: ${userArray[i].id} <br>Email: ${userArray[i].email} <br>Username: ${userArray[i].username}`;
myListItems.appendChild(myList);
listContainer.appendChild(myListItems);
allCustomers.appendChild(listContainer);
}
}
DisplayUserInfo();
Any pointers would be greatly appreciated as well as any constructive feedback. Thanks
Yes, for sure you can add any attribute for a created element. element.classList.add('class-name-here') for adding class, element.id = 'id-name-here' for adding id.
const allCustomers = document.querySelector("section");
let custName = "";
let username = "";
let email = "";
let id = "";
const requestURL = "https://jsonplaceholder.typicode.com/users";
fetch(requestURL)
.then((response) => response.text())
.then((text) => DisplayUserInfo(text));
function DisplayUserInfo(userData) {
const userArray = JSON.parse(userData);
for (i = 0; i < userArray.length; i++) {
let listContainer = document.createElement("div");
let myList = document.createElement("p");
myList.classList.add('active');
myList.id = 'paragraph'
let myListItems = document.createElement("span");
myList.textContent = `Customer : ${userArray[i].name}`;
myListItems.innerHTML =`<br>ID: ${userArray[i].id} <br>Email: ${userArray[i].email} <br>Username: ${userArray[i].username}`;
myListItems.appendChild(myList);
listContainer.appendChild(myListItems);
allCustomers.appendChild(listContainer);
}
}
DisplayUserInfo();
.active {
color: red;
}
#paragraph {
font-size: 24px;
}
<!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="./styles.css">
<title>Document</title>
</head>
<body>
<section>
</section>
<script src="./app.js"></script>
</body>
</html>
is it possible for me to add classnames/Id's to the elements i have
made using doc.createElement
Yes possible with classList for adding class and setAttribute to add id
let listContainer = document.createElement("div");
// To add class
listContainer.className = 'your-class'; //if you have just one
listContainer.classList.add("my-class");//if you want to add multiple
// To add id
listContainer.setAttribute("id", "your_id");
When you use document.createElement it returns an Element. You can use Element attributes and methods to reach what you need. There are some docs for this class on MDN.
This means you can:
> myDiv = document.createElement("div")
<div></div>
> myDiv.id = "test"
'test'
> myDiv
<div id="test"></div>
For classes you can use the attributes className or classList.
I'm creating a to-do list and I want my CLEAR ITEMS button to be deleted once I delete the last item on the list (now it doesn't disappear unless I refresh the page). How can I achieve this? Any suggestions? I've tried different solutions but I really don't know how to make it work. I'm really stuck at this.
JavaScript code:
const toDoItems = document.getElementsByClassName("to-do-items")[0];
const input = document.getElementById("input");
const trashIcon = document.getElementById("trash");
const delItems = document.getElementsByClassName("clear-items")[0];
input.addEventListener("keydown", function (event) {
if (event.key === "Enter") {
addItem();
clearItems();
}
});
function addItem() {
let divParent = document.createElement("div");
let divChild = document.createElement("div");
let checkIcon = document.createElement("i");
let trashIcon = document.createElement("i");
divParent.className = "item";
divParent.innerHTML = "<div>" + input.value + "</div>";
checkIcon.className = "fas fa-check-square";
checkIcon.style.color = "lightgray";
checkIcon.addEventListener("mouseenter", function () {
checkIcon.style.color = "limegreen";
});
checkIcon.addEventListener("mouseleave", function () {
checkIcon.style.color = "lightgray";
});
checkIcon.addEventListener("click", function () {
checkIcon.style.color = "green";
divParent.style.textDecoration = "line-through";
checkIcon.addEventListener("mouseleave", function () {
checkIcon.style.color = "green";
});
});
divChild.appendChild(checkIcon);
trashIcon.className = "fas fa-trash";
trashIcon.style.color = "darkgray";
trashIcon.addEventListener("mouseenter", function () {
trashIcon.style.color = "rgb(182, 71, 71)";
});
trashIcon.addEventListener("mouseleave", function () {
trashIcon.style.color = "darkgray";
});
trashIcon.addEventListener("click", function () {
divParent.remove();
if (toDoItems == 1) {
delItems.remove();
}
});
divChild.appendChild(trashIcon);
divParent.appendChild(divChild);
toDoItems.appendChild(divParent);
input.value = "";
}
let clearButton = false;
function clearItems() {
let clear = document.createElement("button");
if (clearButton === false) {
clear.innerHTML = "Clear Items";
clear.className = "btn";
delItems.appendChild(clear);
input.removeEventListener("click", clearItems);
}
clearButton = true;
document
.getElementsByTagName("button")[1]
.addEventListener("click", function () {
window.location.reload();
});
}
Here's the 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" href="style.css" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css"
integrity="sha512-KfkfwYDsLkIlwQp6LFnl8zNdLGxu9YAA1QvwINks4PhcElQSvqcyVLLD9aMhXd13uQjoXtEKNosOWaZqXgel0g=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
/>
<title>To Do List</title>
</head>
<body>
<div class="container">
<div class="nav">
<h2><i class="fa-solid fa-clipboard-check"></i> To-do List</h2>
<div class="user-input">
<input id="input" type="text" /><button
onclick="addItem(), clearItems()"
>
Submit
</button>
</div>
</div>
</div>
<div class="container">
<div class="to-do-items"></div>
<div class="clear-items"></div>
</div>
<script src="script.js"></script>
</body>
</html>
Every time you delete something. Check how many items are left. If that amount is 0 remove the clear all button. If you press the clear all button. Make sure at the end of the code that clears all the items you also remove the button.
you can use element.remove() to get rid of the button. More info here.
I would also use document.getElementById() to get the button (maybe items too) itself. More info on that here.
I'm a beginner when it comes to coding and the biggest issue I have is to understand WHY something doesn't work (how to diagnose an error). I tried to combine what I learned from Colt Steele on Udemy with fetch API and so far, I've managed to make it work to list the NAMES of the movies when you search, but when I try to display the IMAGES, they seem to not work and it seems like it's trying to load them from my PC rather than from the TVMaze API. Here's my code:
function searchShow(query) {
const url = `https://api.tvmaze.com/search/shows?q=${query}`;
fetch(url)
.then(response => response.json())
.then((jsonData) => {
const resultsNames = jsonData.map(element => element.show.name);
const resultsImages = jsonData.map(e => e.show.image);
console.log(resultsNames);
renderResults(resultsNames);
console.log(resultsImages);
renderImages(resultsImages);
document.getElementById("errorMessage").innerHTML = "";
})
.catch((error) => {
document.getElementById("errorMessage").innerHTML = error;
})
}
function renderResults(resultsNames) {
const list = document.getElementById("resultsList");
list.innerHTML = "";
resultsNames.forEach(result => {
const element = document.createElement("li");
element.innerText = result;
list.appendChild(element);
});
}
function renderImages(resultsImages) {
const list2 = document.getElementById("imagesDisplay");
list2.innerHTML = "";
resultsImages.forEach(result => {
const imgShow = document.createElement("IMG");
imgShow.src = result;
list2.appendChild(imgShow);
})
}
let searchTimeoutToken = 0;
window.onload = () => {
const searchFieldElement = document.getElementById("searchField")
searchFieldElement.onkeyup = (event) => {
clearTimeout(searchTimeoutToken);
searchTimeoutToken = setTimeout(() => {
searchShow(searchFieldElement.value);
}, 250);
};
}
<!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>TV Show</title>
</head>
<body>
<h1>TV Search</h1>
<input type="text" id="searchField" placeholder="Search a TV Show...">
<ul id="resultsList"></ul>
<ul id="imagesDisplay"></ul>
<div id=" errorMessage">
</div>
<script src="script.js"></script>
</body>
</html>
Can you please help me understand why is this not working and also, how can I make it display in a list like this:
-Name of the show
-Image of the show
-2nd name of the 2nd show
-2nd image of the 2nd show
etc.
Thank you in advance!
If you look at your images, you will see the src as [object Object] instead of the url to your image. You need to access the property of your object, in this case there's a few to choose from that represent different sized images.
I've modified your snippet to get what you want.
function searchShow(query) {
const url = `https://api.tvmaze.com/search/shows?q=${query}`;
fetch(url)
.then(response => response.json())
.then((jsonData) => {
let shows = jsonData.map(element => element.show);
renderShows(shows);
document.getElementById("errorMessage").innerHTML = "";
})
.catch((error) => {
document.getElementById("errorMessage").innerHTML = error;
})
}
function renderShows(shows) {
const list = document.getElementById("resultsList");
list.innerHTML = "";
shows.forEach(show => {
const element = document.createElement("li");
const img = document.createElement("img");
const text = document.createElement("span");
img.src = show.image.original;
text.innerText = show.name;
element.appendChild(text);
element.appendChild(img);
list.appendChild(element);
});
}
let searchTimeoutToken = 0;
window.onload = () => {
const searchFieldElement = document.getElementById("searchField")
searchFieldElement.onkeyup = (event) => {
clearTimeout(searchTimeoutToken);
searchTimeoutToken = setTimeout(() => {
searchShow(searchFieldElement.value);
}, 250);
};
}
img {
max-width: 100px;
}
<!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>TV Show</title>
</head>
<body>
<h1>TV Search</h1>
<input type="text" id="searchField" placeholder="Search a TV Show...">
<ul id="resultsList"></ul>
<ul id="imagesDisplay"></ul>
<div id="errorMessage">
</div>
<script src="script.js"></script>
</body>
</html>
I need to create a form for comments with the ability to dynamically add them to the list. Each comment should have an assigned ID in consecutive order. The newest comment should be at the very bottom. Comments should be stored in the comments array. Each comment should have properties such as id (number) and text (string). Comments array must be empty when loaded initially. Each click on the "Add" button should create a new object inside the array and create element in the DOM tree.
let nextId = 1;
const comments = [];
const commentForm = document.querySelector('[data-id="comment-form"]');
const commentInput = commentForm.querySelector('[data-input="comment"]');
const button = commentForm.querySelector('[data-action="add"]');
const commentList = commentForm.querySelector('[data-id="comment-list"]');
button.addEventListener('click', () => {
const object = {};
if (commentInput.value != '') {
comments.map(() => ({ id: 'nextId++', text: commentInput.value }));
}
createElement();
});
function createElement() {
const newComment = document.createElement('li');
newComment.setAttribute('data-comment-id', comments.id);
newComment.textContent = comments.text;
commentList.appendChild(newComment);
}
<!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></title>
<link rel="stylesheet" href="./css/styles.css" />
</head>
<body>
<div id="root">
<form data-id="comment-form">
<textarea data-input="comment"></textarea>
<button data-action="add">Add</button>
</form>
<ul data-id="comment-list"></ul>
</div>
<script src="./js/app.js"></script>
</body>
</html>
There are some issues in your code:
You are trying to access commentList from commentForm, but that element is outside of the commentForm. Use document object to access the element.
comments is an array from which you are trying to access text property, there is text property on comments.
You should pass the current input value to the function so that you can set the newly created LI's text with the value.
You should use push() instead of map() to push an item into the array. nextId is a variable but you are using that as if it is a string, you should remove the quotes around it.
For the better user experience, I will suggest you to clear the value of the input after creating the item.
Demo:
let nextId = 1;
const comments = [];
const commentForm = document.querySelector('[data-id="comment-form"]');
const commentInput = commentForm.querySelector('[data-input="comment"]');
const button = commentForm.querySelector('[data-action="add"]');
const commentList = document.querySelector('[data-id="comment-list"]');
button.addEventListener('click', () => {
const object = {};
if (commentInput.value != '') {
comments.push({ id: nextId++, text: commentInput.value });
}
createElement(commentInput.value);
commentInput.value = '';
});
function createElement(ci) {
const newComment = document.createElement('li');
newComment.setAttribute('data-comment-id', comments.id);
newComment.textContent = ci;
commentList.appendChild(newComment);
}
<!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></title>
<link rel="stylesheet" href="./css/styles.css" />
</head>
<body>
<div id="root">
<form data-id="comment-form">
<textarea data-input="comment"></textarea>
<button type="button" data-action="add">Add</button>
</form>
<ul data-id="comment-list"></ul>
</div>
<script src="./js/app.js"></script>
</body>
</html>