const addToList = function ({ name, id }) {
let html1 = `
<li class="list-item" data-id="${id}" id="${id}">
${name}
<span> </span>
<button class="btn-remove">remove</button>
</li>
`;
pokemonList.insertAdjacentHTML('beforeend', html1);
};
const displayListItem = function () {
favourite.forEach((listItem) => {
addToList(listItem);
});
};
let favourite = getStorageItem('pokemon');
pokemonContainer.addEventListener('click', function (e) {
if (e.target.classList.contains('btn-favourites')) {
favourite = getStorageItem('pokemon');
let eTargetID = parseInt(e.target.dataset.id);
let found = favourite.some((el) => el.id === eTargetID);
console.log(found);
console.log(eTargetID);
if (!found) {
favourite.push(pokemonArray[e.target.dataset.id - 1]);
setStorageItem('pokemon', favourite);
// behöver lösa duplcates i favorit listan
displayListItem(favourite);
}
}
});
How do I check if a child element with a dataset id exists? I want to prevent duplicate from appearing in my favourite list when I get them from local storage when I click on a button, if a li tag already exists with a specific dataset id.
Right now when I click let's say bulbasaur, I get bulbasaur in my list. If i then click ivysaur, I get bulbasaur, bulbasaur, ivysaur.
Thanks!
You can use querySelector, that dynamically accepts an id to search, for example:
document.querySelector(`[data-id="${datasetId}"]`)
Returns the element if exists, if not returns null.
Related
I am a newbie at web development. I am trying to display data that is being stored in javascript array, inside an unordered list as list elements.
I have tried looping through the elements in the array and display them one at a time using for loops but its not working
let linksArray = [] // array to store link leads while browsing
let inputEl = document.getElementById("input-el") // to get a hold on the input element from the dom
let displayEl = document.getElementById("display_link-el")
//function to save links inputs into linksArray
function save(params) {
linksArray.push(inputEl.value)
console.log(linksArray);
localStorage.setItem("mylinks", JSON.stringify(linksArray))
inputEl.value = ""
displayEl.textContent = linksArray
}
//function to conver linksArray to a list element in the DOM
function convetToListli(linksArray) {
for (let i = 0; i < linksArray.length; i++) {
let links = "<li>"
linksArray[i]
" </li>"
}
}
<input id="input-el" title="lead" placeholder="input weblink" type="text">
<button id="input-btn" onclick="save()">SAVE </button>
<button id="del-btn" ondblclick="delLeads()">DELETE</button>
<div>
<div>
<ul id="display_link-el"></ul>
</div>
<div>
<ul id="log_link-el"></ul>
</div>
</div>
I have added small correction to your script
function save(params) {
linksArray.push(inputEl.value)
console.log(linksArray);
inputEl.value=""
displayEl.innerHTML = convetToListli(linksArray)
}
//function to conver linksArray to a list element in the DOM
function convetToListli(linksArray) {
var links = ""
for (let i = 0; i < linksArray.length; i++) {
links= links + "<li>"+linksArray[i]+" </li>"
}
return links
}
This is just for example purchase
NOTE: Make sure to filter the input field while using innerHTML because it will leads to XSS
You should learn basic DOM manipulations. Here's one possible solution for your question
const list = document.querySelector('.list');
const links = ['link 1', 'link 2', 'link 3'];
links.forEach(link => {
let item = document.createElement('li');
item.textContent = link;
list.appendChild(item)
})
<ul class="list"></ul>
I'm trying to solve this challenge from TestDome and i need some help. I dont really understand how i must toggle the email and how to append the item to DOM...
!!Please without Vanilla JS only!!
Implement the showCustomers function so that it renders customers as list items. The first argument to the function, customers, is an array of objects with the name and email properties. The second argument to the function, targetList, is an unordered HTML list to which each customer should be added as a separate list item.
The name and email properties should be added as two paragraphs inside the list item. At first, the email paragraph element should not be present in the DOM. The email paragraph element should be added to the DOM after the name is clicked, and it should be removed from the DOM when the name is clicked again.
For example, the following code:
document.body.innerHTML = `
<div>
<ul id="customers">
</ul>
</div>
`;
let customers = [{name: "John", email: "john#example.com"},
{name: "Mary", email: "mary#example.com"}];
showCustomers(customers, document.getElementById("customers"));
let customerParagraph = document.querySelectorAll("li > p")[0];
if(customerParagraph) {
customerParagraph.click();
}
console.log(document.body.innerHTML);
Should render:
<div>
<ul id="customers">
<li>
<p>John</p>
<p>john#example.com</p>
</li>
<li>
<p>Mary</p>
</li>
</ul>
</div>
THIS IS MY CODE
function showCustomers(customers, targetList) {
customers.forEach(item =>{
let res = `<li>
<p> ${item.name}</p>;
<p> ${item.email}</p>;
</li>;
targetList.innerHTML = targetList.appendChild(res);
})
}
https://www.testdome.com/questions/javascript/customer-list/49798?visibility=3&skillId=2
Replace the line
targetList.innerHTML = targetList.appendChild(res);
with
targetList.innerHTML += res;.
You basically have two ways for adding elements:
increasing innerHTML contents with raw strings
appending children to DOM element
In your case res is a string so you can't use targetList.appendChild
Since you asked :
'The email paragraph element should be added to the DOM after the name is clicked, and it should be removed from the DOM when the name is clicked again'.
create list el,create p el, create event listener on p el, append email to a child element
Replace your code to
customers.forEach((item) => {
const li = document.createElement("li");
const name = document.createElement("p");
name.textContent = item.name;
name.style.cursor = "pointer";
name.addEventListener("click", (event) => {
const parent = event.target.parentElement;
if (parent.children.length == 1) {
const email = document.createElement("p");
email.textContent = item.email;
parent.appendChild(email);
} else {
parent.lastChild.remove();
}
});
li.appendChild(name);
targetList.appendChild(li);
});
function showCustomers(customers, targetList) {
customers.forEach((customer, index) => {
const node = document.createElement('li');
let nameEl = document.createElement('p');
let emailEl = document.createElement('p');
nameEl.innerText = customer.name;
emailEl.innerText = customer.email;
node.appendChild(nameEl);
nameEl.addEventListener('click', function() {
node.contains(emailEl) ? node.removeChild(emailEl) :
node.appendChild(emailEl);
});
targetList.appendChild(node)
});
}
<?php foreach ($communities as $community) { ?>
<div class="community-container">
<p><?php echo $community->Title ?></p>
<button class="del-btn" data-id="<?php echo $community->ID ?>">Delete</button>
</div>
<?php } ?>
<Script>
const deleteBtns = document.getElementsByClassName('del-btn');
var deleteBtnsArray = Array.from(deleteBtns);
deleteBtnsArray.map(deleteBtn => {
deleteBtn.addEventListener('click', () => {
const delRequest = new XMLHttpRequest();
let params = [];
params = `deleteCommunity=true&communityID=${deleteBtn.dataset.id}`;
delRequest.open('POST', '/ajax/delete-community');
delRequest.onreadystatechange = function() {
if (delRequest.responseText === 'success') {
deleteBtn.parentElement.remove();
}
}
delRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
delRequest.send(params);
})
})
</Script>
I have communities list on my page and each community item has a delete button. Now when I click on a delete button I want to delete related community item.
But...
I can inspect these delete buttons from the browser and change it's data-id value to something else(if a data-id of a delete button is 10, I can change it to 1000). Then when I click on that delete button after changing its data-id value from the client side, it will delete some other community instead correct one. (If a community exists with that changed data-id value) because buttons data-id is changed and JavaScript code takes that value to make AJAX request. I can't stop user changing data-id from the client-side. Therefore, How can I handle this situation if user changed data attributes from client side?
Extra information
$communities is a array of community objects and each community item has a Name and ID.
You could read the data- prop(s) after the page loads. Store them in an array or whatever and then delete the data- prop(s) from the elements.
Be aware that validation should probably happen somewhere on the server side instead of client side
// Wrap everything in an IIFE so we don't create global variables
(() => {
const ID_MAP = new WeakMap();
const onClickAction = ({ currentTarget }) => {
// Exit if there is no ID stored
if(!ID_MAP.has(currentTarget)) return;
// Retrieve and log ID
const id = ID_MAP.get(currentTarget);
console.log(id);
}
const btns = document.querySelectorAll('button');
for(const btn of btns) {
// Skip if it doesn't have an ID
if(!btn.dataset.id) continue;
// Store and hide `data-id` attribute
ID_MAP.set(btn, btn.dataset.id);
btn.removeAttribute('data-id');
// Add event listener
btn.addEventListener('click', onClickAction, false);
}
})();
<button data-id="001">id: 001</button>
<button data-id="002">id: 002</button>
<button data-id="003">id: 003</button>
<button data-id="004">id: 004</button>
<button data-id="005">id: 005</button>
EDIT: using suggestions from comments (event delegation)
// Wrap everything in an IIFE so we don't create global variables
(() => {
const ID_MAP = new WeakMap();
const onClickAction = ({ target }) => {
// Exit if it's not a button
if(target.nodeName !== 'BUTTON') return;
// Exit if there is no ID stored
if(!ID_MAP.has(target)) return;
// Retrieve and log ID
const id = ID_MAP.get(target);
console.log(id);
}
const btns = document.querySelectorAll('button');
for(const btn of btns) {
// Skip if it doesn't have an ID
if(!btn.dataset.id) continue;
// Store and hide `data-id` attribute
ID_MAP.set(btn, btn.dataset.id);
btn.removeAttribute('data-id');
}
// Add event listener, instead of `document` you can also use a common parent container
document.addEventListener('click', onClickAction, false);
})();
<button data-id="001">id: 001</button>
<button data-id="002">id: 002</button>
<button data-id="003">id: 003</button>
<button data-id="004">id: 004</button>
<button data-id="005">id: 005</button>
Why when you are searching for something else is deleting the previous contents ?For example first you search for egg and show the contents but then when you search for beef the program deletes the egg and shows only beef.Thank you for your time code:
const searchBtn = document.getElementById('search-btn');
const mealList = document.getElementById('meal');
const mealDetailsContent = document.querySelector('.meal-details-content');
const recipeCloseBtn = document.getElementById('recipe-close-btn');
// event listeners
searchBtn.addEventListener('click', getMealList);
mealList.addEventListener('click', getMealRecipe);
recipeCloseBtn.addEventListener('click', () => {
mealDetailsContent.parentElement.classList.remove('showRecipe');
});
// get meal list that matches with the ingredients
function getMealList(){
let searchInputTxt = document.getElementById('search-input').value.trim();
fetch(`https://www.themealdb.com/api/json/v1/1/filter.php?i=${searchInputTxt}`)
.then(response => response.json())
.then(data => {
let html = "";
if(data.meals){
data.meals.forEach(meal => {
html += `
<div class = "meal-item" data-id = "${meal.idMeal}">
<div class = "meal-img">
<img src = "${meal.strMealThumb}" alt = "food">
</div>
<div class = "meal-name">
<h3>${meal.strMeal}</h3>
Get Recipe
</div>
</div>
`;
});
mealList.classList.remove('notFound');
} else{
html = "Sorry, we didn't find any meal!";
mealList.classList.add('notFound');
}
mealList.innerHTML = html;
});
}
Beacuse you are using innerHTML , if you want to save the previous contents you should use append or innerHTML + = .
Because everytime you make a search, the html var is populated with new data.
if you move the 'html' variable to the root scope, this should get you there:
// get meal list that matches with the ingredients
let html = ""; // <-- place it outside the function body
function getMealList(){
let searchInputTxt = document.getElementById('search-input').value.trim();
fetch(`https://www.themealdb.com/api/json/v1/1/filter.php?i=${searchInputTxt}`)
.then(response => response.json())
.then(data => {
// let html = ""; // <-- remove it from here
if(data.meals){
data.meals.forEach(meal => {
I can't seem to get local storage to work. The goal is to keep the todo list items on the page upon refresh. Every time I refresh the page it goes poof. The syntax seems right.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TODO LIST</title>
<link rel="stylesheet" href="./styles/style.css">
</head>
<body>
<main id="main">
<h1>THE TO-DO LIST:</h1>
<form action="" id="add-task">
<label for="todo">Add Task:</label>
<input type="text" id="todo">
<button>Add Task</button>
</form>
<p class="center">To complete task, click on text.</p>
<ul id="task-list">
<li class="task-complete">example_1 <button>Remove Task</button></li>
</ul>
</main>
<script src="./script/index.js"></script>
</body>
</html>
const form = document.querySelector('#add-task');
const input = document.querySelector('#todo');
const taskList = document.querySelector('#task-list');
let taskID = 0;
taskList.addEventListener('click', function(e) {
if (e.target.tagName === 'BUTTON') {
e.target.parentElement.remove();
let inputTask = document.getElementById('todo');
localStorage.setItem('email', inputTask.value);
} else if (e.target.tagName === 'LI') {
e.target.classList.toggle('task-complete');
}
});
form.addEventListener('submit', function(e) {
e.preventDefault();
console.log(input.value);
const newTask = document.createElement('li');
const removeBtn = document.createElement('button');
let savedInput = input.value;
removeBtn.innerText = 'Remove Task';
newTask.innerText = input.value;
newTask.appendChild(removeBtn);
taskList.appendChild(newTask);
input.value = '';
console.log(localStorage);
});
.task-complete {
text-decoration: line-through;
}
Joshua, here are a few things from looking at your sample:
First, you're setting the localStorage to a single item, with the current input value, not a collection of tasks like an array
It also seems that you're not getting the saved data on page reload, that's why nothing happens when page reloads
Remember that you can only save strings to localStorage, in a todo list you might want to save an array (a collection of todos), but since you can't do it you need to convert it to a string while saving (JSON.stringify(yourArray) will help you with that), and parse it back to an Array when loading (JSON.parse)
const form = document.querySelector('#add-task');
const input = document.querySelector('#todo');
const taskList = document.querySelector('#task-list');
let taskID = 0;
let tasks = [] // here will hold your current todos collection
// a function that will retrieve the saved todos from local storage
//
// note that 'tasks' can be any string identifier that you want — 'todos'
// would also work — but you need to use the same for localStorage.getItem
// and localStorage.setItem
function getTasksFromLocalStorage(){
// it will return `null` if nothing's there
tasks = localStorage.getItem('tasks') || []
if (tasks) {
// convert it to an array so you can loop over it
tasks = JSON.parse(tasks)
}
}
function addTask(text) {
// CREATE DOM ELEMENTS
const newTask = document.createElement('li');
const removeBtn = document.createElement('button');
removeBtn.innerText = 'Remove Task';
// set the text to the provided value
newTask.innerText = text;
// append the remove button
newTask.appendChild(removeBtn);
// append it to the dom so we can see it
taskList.appendChild(newTask)
}
// on page load get tasks from local storage
// then loop over it, create the DOM elements and append them to
// the taskList
document.addEventListener('DOMContentLoaded', function() {
getTasksFromLocalStorage()
// if we have saved tasks, loop over them and render to the dom
tasks.forEach(function(savedTaskText) {
addTask(savedTaskText)
})
})
// then on your code, you need to update to push
// the current inputed `task` to the `tasks` collection (Array)
// then save the entire collection to the local storage
// then add the new task to the DOM
// and finally reset the input
form.addEventListener('submit', function(e) {
e.preventDefault();
console.log(input.value);
// save it to the current holding list
tasks.push(input.value)
// save a copy of the updated list to the localStorage, so when you
// reload the page you get saved items!
localStorage.setItem('tasks', tasks)
// add it to DOM
addTask(input.value);
// reset the input
input.value = '';
});
There's more things you need to do, if you want tasks to have unique ids (since, so you can remove them later), but the code was simplified for brevity of explanation (and yet you got a long answer anyways).
Here's so docs and suggested reading:
MDN Docs for LocalStorage
MDN Docs for JSON (parse and stringify)
There's plenty vanilla javascript tutorials (written and youtube) for "creating a todo lists using localStorage", that go into more detail than we can go in a SO answer, I suggest you skim through those as well!
Good luck and Happy coding ✌️
There are 2 problems with your code.
First, you are not saving each to-do task entered by user upon form submit. If you want to save each to-do task entered by user in localStorage, then modify the form submit handler as below:
form.addEventListener('submit', function(e) {
e.preventDefault();
const newTask = document.createElement('li');
const removeBtn = document.createElement('button');
let savedInput = input.value;
removeBtn.innerText = 'Remove Task';
newTask.innerText = input.value;
newTask.appendChild(removeBtn);
taskList.appendChild(newTask);
localStorage.setItem('Task'+taskID, input.value);
taskID++;
input.value = '';
});
Second, you are not utilizing the previously saved data in localStorage to show the list of to-dos that were entered by user before the page was loaded. You can achieve that by using below function code:
function showSavedToDos() {
const keys = Object.keys(localStorage);
let i = keys.length;
while (i--) {
const newTask = document.createElement('li');
const removeBtn = document.createElement('button');
removeBtn.innerText = 'Remove Task';
newTask.innerText = localStorage.getItem(keys[i]);
newTask.appendChild(removeBtn);
taskList.appendChild(newTask);
}
}
showSavedToDos();
You are not using de localStorage API, please take a look to this example. here I am using template to display the tasks. In the html file is the only change
<main id="main">
<h1>THE TO-DO LIST:</h1>
<form action="" id="add-task">
<label for="todo">Add Task:</label>
<input type="text" id="todo" />
<button>Add Task</button>
</form>
<p class="center">To complete task, click on text.</p>
<ul id="task-list">
<li class="task-complete">example_1 <button>Remove Task</button></li>
</ul>
</main>
<template id="task">
<li class="task-complete">
<span></span>
<button>Remove task</button>
</li>
</template>
In JavaScript I create a render function that will collect the task stored in localstorage. Populated when calling store(input.value) in the submit handler
const form = document.querySelector("#add-task");
const input = document.querySelector("#todo");
const taskList = document.querySelector("#task-list");
let taskID = 0;
taskList.addEventListener("click", function (e) {
if (e.target.tagName === "BUTTON") {
e.target.parentElement.remove();
let inputTask = document.getElementById("todo");
localStorage.setItem("email", inputTask.value);
} else if (e.target.tagName === "LI") {
e.target.classList.toggle("task-complete");
}
});
form.addEventListener("submit", function (e) {
e.preventDefault();
console.log(input.value);
const newTask = document.createElement("li");
const removeBtn = document.createElement("button");
let savedInput = input.value;
removeBtn.innerText = "Remove Task";
newTask.innerText = input.value;
newTask.appendChild(removeBtn);
taskList.appendChild(newTask);
store(input.value);
input.value = "";
console.log(localStorage);
});
function getTasks() {
return localStorage.tasks ? JSON.parse(localStorage.tasks) : [];
}
function store(task) {
const tasks = getTasks();
tasks.push(task);
localStorage.setItem("tasks", JSON.stringify(tasks));
}
function render() {
const tasks = getTasks();
tasks.forEach((task) => {
const newTask = createTask(task);
taskList.appendChild(newTask);
});
}
function createTask(task) {
const template = document.querySelector("#task");
const taskNode = template.content.cloneNode(true);
taskNode.querySelector("span").innerText = task;
return taskNode;
}
render();
The render function run every first render of the page, so tasks list will be populated