Converting string to DOM node (ul/li) in JavaScript with appendchild - javascript

I am trying to make use of createElement, createTextNode and appendChild to rewrite an outdated simple to-do list code example.
The code example requires the use of the array join() method so, unfortunately, this can't be removed. The old version just wrote the HTML for the ul list in code fragments.
I am unsure how to proceed where I have entered to comment at line 30 of the js: " need to render the tasks (from stringToInsert) as a list to div id="output" here "
I have referred to the following stackoverflow articles to help me rewrite the code:
js-how-to-concatenate-variables-inside-appendchild -This example uses join() and appendChild but not list items.
create-ul-and-li-elements-in-javascript-
From that one, I copied the code from a Fiddle and put it into function createul() in my example codepen
Once I have the function addTask() working createul() and it's associated HTML elements (such as the render list button) will be removed.
// tasks.js #2
// This script manages a to-do list.
// Need a global variable:
var tasks = [];
function addTask() {
'use strict';
console.log("addTask started");
// Get the task:
var task = document.getElementById('task');
// Reference to where the output goes:
var output = document.getElementById('output');
if (task.value) {
tasks.push(task.value);
// Update the page:
//var message = '<h2>To-Do</h2>';
var stringToInsert = tasks.join(' : ');
console.log(stringToInsert);
var taskUl = document.createElement('ul');
taskUl.setAttribute('id', 'autoTask');
document.getElementById('output').appendChild(taskUl);
/* need to render the tasks (from stringToInsert) as a list to div id ="output" here */
document.getElementById("task").value = '';
}
// Return false to prevent submission:
return false;
} // End of addTask() function.
function createul() {
var ul = document.createElement('ul');
ul.setAttribute('id', 'proList');
var t, tt;
productList = ['Electronics Watch', 'House wear Items', 'Kids wear', 'Women Fashion'];
document.getElementById('renderList').appendChild(ul);
productList.forEach(renderProductList);
function renderProductList(element, index, arr) {
var li = document.createElement('li');
li.setAttribute('class', 'item');
ul.appendChild(li);
t = document.createTextNode(element);
li.innerHTML = li.innerHTML + element;
}
}
function init() {
document.getElementById('renderbtn').addEventListener("click", createul);
document.getElementById('theForm').onsubmit = addTask;
}
window.addEventListener('load', init);
/* css (I have simplified this a little for this example and I am sorry I haven't cut it down further) */
form {
margin: 0 auto;
width: 400px;
padding: 14px;
background-color: #ffffff;
border: solid 2px #425955;
}
/* ----------- stylized ----------- */
h1 {
font-size: 14px;
font-weight: bold;
margin-bottom: 8px;
}
p {
font-size: 11px;
color: #666666;
margin-bottom: 20px;
border-bottom: solid 1px #BFBD9F;
padding-bottom: 10px;
}
label {
display: block;
font-weight: bold;
text-align: right;
width: 140px;
float: left;
}
select {
float: left;
font-size: 12px;
padding: 4px 2px;
border: solid 1px #BFBD9F;
width: 200px;
margin: 2px 0 20px 10px;
}
input {
float: left;
font-size: 12px;
padding: 4px 2px;
border: solid 1px #BFBD9F;
width: 200px;
margin: 2px 0 20px 10px;
}
#submit {
clear: both;
margin-left: 150px;
width: 125px;
height: 31px;
background: #F1F2D8;
text-align: center;
line-height: 20px;
color: #000000;
font-size: 12px;
font-weight: bold;
}
#output {
clear: both;
margin-bottom: 10px;
color: blue;
}
<form action="#" method="post" id="theForm">
<div><label for="task">Task</label><input type="text" name="task" id="task" required></div>
<input type="submit" value="Add It!" id="submit"><br>
<button type="button" id="renderbtn">render list</button>
<div id="renderList"></div>
<div id="output"></div>
edit: I can just convert it back to an array with something like the following if there is no other way of doing it.
var ar = stringToInsert.split(' : ');
or something based on:
stringToInsert.split(' : ').forEach ... or if that doesn't work I could try map()

I'm going to show you a different approach that may help clear things up -
function ul (nodes)
{ const e = document.createElement("ul")
for (const n of nodes)
e.appendChild(n)
return e
}
function li (text)
{ const e = document.createElement("li")
e.textContent = text
return e
}
function onSubmit (event)
{ event.preventDefault()
tasks.push(f.taskInput.value)
f.taskInput.value = ""
render()
}
function render ()
{ const newList = ul(tasks.map(li))
f.firstChild.replaceWith(newList)
}
const tasks = [ "wash dishes", "sweep floors" ] // <- initial tasks
const f = document.forms.main // <- html form
f.addButton.addEventListener("click", onSubmit) // <- button listener
render() // <- first render
<h3>todo list</h3>
<form id="main">
<ul></ul>
<input name="taskInput" placeholder="example: paint fence">
<button name="addButton">Add Task</button>
</form>
And here's a more modern approach using a DOM library like React -
const { useState, useRef } = React
const { render } = ReactDOM
function TodoList ({ initTasks = [] })
{ const [ tasks, updateTasks ] =
useState(initTasks)
const inputEl =
useRef(null)
function onSubmit () {
updateTasks([ ...tasks, inputEl.current.value ])
inputEl.current.value = ""
}
return <div>
<h3>todo list</h3>
<ul>{tasks.map(t => <li children={t} />)}</ul>
<input ref={inputEl} placeholder="ex: paint fence" />
<button onClick={onSubmit}>add</button>
</div>
}
render
( <TodoList initTasks={[ "wash dishes", "sweep floors" ]}/>
, document.body
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>

Related

Add and remove classes from Node in Javascript

I have a slider in my page and slider's indicators are dynamic, It bases on slider's elements' number and width of body.
My code block is:
function setIndicators(){
const indicator = document.createElement("div");
indicator.className = "indicator active";
indicatorContainer.innerHTML = "";
for(let i = 0;i <= maxIndex; i++){
indicatorContainer.appendChild(indicator.cloneNode(true));
}
updateIndicators();
}
which is working fine. But I want to show active indicator but I cannot manipulate elements' classes.
I tried this:
function updateIndicators(index) {
indicators.forEach((indicator) => {
indicator.classList.remove("active");
});
let newActiveIndicator = indicators[index];
newActiveIndicator.classList.add("active");
}
And I am not able to reach every indicators using index or anything I know/find. Also, it seems like NodeList not a HTML element.
Other things you may need:
const indicatorContainer = document.querySelector(".container-indicators");
const indicators = document.querySelectorAll(".indicator");
let maxScrollX = slider.scrollWidth - body.offsetWidth;
let baseSliderWidth = slider.offsetWidth;
let maxIndex = Math.ceil(maxScrollX / baseSliderWidth);
A better one I would suggest using the indicators in a different way. Since your HTML isn't shared, I have to assume a few things:
function clearAll() {
const activeOnes = document.querySelectorAll(".active");
activeOnes.forEach(function(activeOne) {
activeOne.classList.remove("active");
});
}
function chooseOne(index) {
clearAll();
const indicators = document.querySelectorAll(".indicator");
indicators[index].classList.add("active");
}
* {
font-family: 'Operator Mono', consolas, monospace;
}
.indicators {
border: 2px solid #ccc;
display: inline-block;
width: auto;
margin: 15px;
}
.indicators .indicator {
padding: 15px;
line-height: 1;
background-color: #fff;
flex-grow: 1;
text-align: center;
display: inline-block;
}
.indicator.active {
background-color: #f90;
}
<div class="indicators"><div class="indicator">I1</div><div class="indicator">I2</div><div class="indicator">I3</div><div class="indicator">I4</div><div class="indicator">I5</div></div>
<button onclick="chooseOne(2); return false">Select I3</button>
<button onclick="chooseOne(3); return false">Select I4</button>
I would have done this differently this way.
Preview

One event handler function not getting triggered after the execution of another event handler function defined in the same js file

I am trying to build a static todo app, this app stores the todo list in localStorage (browser memory).
there are 3 elements related to the error - 1 element is causing the error and the other 2 being affected by it.
a hide complete checkbox - hides the completed tasks when checked. (element causing the error)
complete todo checkbox - mark a todo as completed when checked.
delete note button - deletes a note.
I have attached the code files and screenshot, to get an overall idea about the problem and the code.
along with this, I have stored my code in this repo: https://github.com/AbhishekTomr/Todo
Problem - Once the hide completed checkbox is triggered, the complete todo and delete todo mechanism stops working.
Can someone please help me in fixing the code or let me know what is causing the issue?
Screenshot:
//js file : todoFunctions.js
//function for getting value stored in todoList
let getTodo = function(){
return Boolean(localStorage.getItem("todo"))?JSON.parse(localStorage.getItem("todo")):[];
}
//function to add or render any list on the screen
let render = function(list){
document.querySelector("#td").innerHTML = "";
list.forEach(function(item){
newTask(item);
}
)
}
//function for saving the task in the local storage
let saveTask = function(todo){
let tdList = JSON.stringify(todo);
localStorage.setItem("todo",tdList); //setting value for the first time or updating it
}
let newTask = function(node)
{
let li = document.createElement("li");
let status = document.createElement("input");
let remove = document.createElement("button");
li.textContent = node.td;
remove.setAttribute("class","remove");
remove.textContent="Delete";
status.setAttribute("type","checkbox");
status.setAttribute("class","status");
status.checked = node.status;
li.style.textDecoration = (node.status)?"line-through":"none";
let div = document.createElement("div");
div.setAttribute("class","task");
div.setAttribute("id",node.index);
div.appendChild(status);
div.appendChild(li);
div.appendChild(remove);
document.querySelector("#td").appendChild(div);
document.getElementById("new-task").value = ""; //clearing the input feild
}
//function for adding a new task
let addTask = function(todo){
let td = document.getElementById("new-task").value;
let status = false;
let node = {td : td,status : status};
todo.push(node);
saveTask(todo); // saving it to local storage
newTask(node);
}
// function for searching out task
let searchTask = function(todo,e){
let searchList = todo.filter(function(item){
return item.td.includes(e.target.value);
})
render(searchList); // showing the searched terms on the go..
}
//funtion to delete task
let deleteTodo=function(e,index,todo){
e.target.parentElement.remove();
todo.splice(index,1);
saveTask(todo);
}
//funtion for completing and undoing a task
let changeStatus = function(e,index,todo){
let state = e.target.checked;
let td = e.target.parentElement.children[1];
td.style.textDecoration = (state)?"line-through":"none";
todo[index].status = state;
saveTask(todo);
}
//function for hiding complete task
let hideCompleted = function(e,todo){
if(e.target.checked)
{
let filterLst = todo.filter(function(item){
return !item.status;
})
render(filterLst);
}else{
render(todo);
}
}
//js file :main.js
let todo = getTodo(); // get the todo List from storage
render(todo); // display the initial todo List
//functionality for the different events
document.getElementById("add-task").addEventListener("click",function(e){ //event when the add new task button is pressed
addTask(todo); //funtion for adding new task and displaying it on page
})
document.getElementById("search-txt").addEventListener("input",function(e){ //event for text typed in seach bar
searchTask(todo,e); //funtion for searching the tasks and displaying it on page
})
//event to delete todo
let btns = document.querySelectorAll(".remove");
btns.forEach(function(item,index){
item.addEventListener("click",function(e){
deleteTodo(e,index,todo);
})
})
//event for complete/uncomplete task
let check = document.querySelectorAll(".status");
check.forEach(function(item,index){
item.addEventListener("change",function(e){
changeStatus(e,index,todo);
console.log("i am triggered");
})
})
document.querySelector("#hide-check").addEventListener("change",function(e){ //event when hide completed is checked and unchecked
hideCompleted(e,todo);
})
/* css file : main.css */
*{
margin: 0;
padding: 0;
box-sizing: border-box;
/* border: 1px solid black; */
font-family: monospace;
}
main{
margin: 5px;
}
h1{
font-size: 3rem;
font-weight: bold;
font-variant: small-caps;
display: inline-block;
width: 250px;
text-align: center;
}
#td-options{
display: flex;
flex-flow: column nowrap;
justify-content:space-around;
align-items: flex-start;
font-size: 1.2rem;
width: 250px;
}
#search,#search input{
width: 100%;
height: 100%;
text-align: center;
border: 2px solid black;
}
#hide-completed{
width: 100%;
display: flex;
justify-content: flex-start;
align-items: center;
font-size: 1.2rem;
margin: 5px 0;
}
#hide-check{
margin: 10px;
}
#todo{
/* border: 3px solid pink; */
background-color: pink;
margin: 1rem 0;
display: inline-block;
padding: 10px;
}
.task{
border: 1px solid green;
display: flex;
justify-content: flex-start;
align-items: center;
font-size: 1.2rem;
}
.task li{
list-style-position: inside;
margin: .5rem;
text-transform: capitalize;
}
.task button{
padding: 0 .5rem;
border-radius: 2px;
background-color: gray;
color: whitesmoke;
}
.task button:hover{
cursor: pointer;
}
#add-todo{
display: flex;
flex-flow: row nowrap;
width: 255px;
justify-content: space-between;
height: 25px;
}
#add-todo button{
padding: 0 .5rem;
border-radius: 3px;
}
<!-- html file: index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="main.css">
<title>Todo</title>
</head>
<body>
<main>
<!-- mainheading -->
<h1>My Todo App</h1>
<!--todoOptions-->
<div id="td-options">
<div id="search">
<input type="text" id="search-txt" placeholder="search your Todo">
</div>
<div id="hide-completed">
<label for="hide-check">
Hide Completed Tasks
</label>
<input type="checkbox" id="hide-check" name="hide">
</div>
</div>
<!-- todo list -->
<div id="todo">
<ol id="td"></ol>
</div>
<!-- add todo -->
<div id="add-todo">
<input type="text" placeholder="Add A New To Do Task" id="new-task">
<button id="add-task">Add ToDo</button>
</div>
<!-- js files -->
<script src="todoFunctions.js"></script>
<script src="main.js"></script>
</main>
</body>
</html>
The function render goes through a list and creates the relevant elements for a task to show it on the screen and then appends them.
At the start a list is collected from localstorage and then all the required event listeners are added.
When hideCompleted is called it creates a list from the remaining elements that are not completed, or just uses the complete todo list, and re-renders it. This creates all the elements OK so everything looks alright on the screen.
BUT no event listeners are added so the delete button and so on do not do anything.
My suggestion would be to make the event listener creation code for .remove etc into a function. Call that on start up and when you recreate the list on screen.

deleting an element onClick from an array of objects

i am doing the library project from "odin project" website and i am having trouble completing it. my idea is to access the cards particular index in the "library" array of objects, but i am having trouble doing so. my idea is to have a function that creates some type of id from its place in the array ( such as its index ) and use that as access for my delete button. any suggestions? i appreciate your time here is my codepen link
//constructor to add a book to
function Book(title, author, pages) {
this.title = title;
this.author = author;
this.pages = pages;
}
//array of books
const library = [];
//hides and unhides forms
const hide = () => {
var form = document.querySelector("#hide");
if (form.style.display === "none") {
form.style.cssText =
"display: block; display: flex; justify-content: center; margin-bottom: 150px";
} else {
form.style.display = "none";
}
};
//creates form, takes input,creates card, resets and runs hide function when done
const addBookCard = () => {
const bookName = document.querySelector('input[name="bookName"]').value;
const authorName = document.querySelector('input[name="authorName"]').value;
const numPages = document.querySelector('input[name="numPages"]').value;
library.push(new Book(bookName, authorName, numPages));
//just stating variables used within my function
const container = document.querySelector(".flex-row");
const createCard = document.createElement("div");
const divTitle = document.createElement("p");
const divAuthor = document.createElement("p");
const divPages = document.createElement("p");
const deleteBtn = document.createElement("button");
//using a class from my css file
createCard.classList.add("card");
createCard.setAttribute("id","id_num")
deleteBtn.setAttribute("onclick", "remove()")
deleteBtn.setAttribute('id','delBtn')
//geting all info from library
divTitle.textContent = "Title: " + bookName
divAuthor.textContent = "Author: " + authorName
divPages.textContent = "Number of Pages: " + numPages
deleteBtn.textContent = "Delete This Book";
//adding it all to my html
container.appendChild(createCard);
createCard.appendChild(divTitle);
createCard.appendChild(divAuthor);
createCard.appendChild(divPages);
createCard.appendChild(deleteBtn);
document.getElementById("formReset").reset();
hide()
return false
};
var btn = document.querySelector('#newCard');
btn.onclick = addBookCard;
You can change library declaration from const to let.
Then you can push books together with their corresponding deleteBtn, that way you will be able to easily remove an entry that corresponds to the clicked deleteBtn
library.push([new Book(bookName, authorName, numPages), deleteBtn]);
And then you can add event listener on deleteBtn like this
deleteBtn.addEventListener('click', event => {
event.target.parentNode.remove();
library = library.filter(v => v[1] !== event.target);
});
Where the first line removes the element from the DOM, and the second line creates new library array without the removed entry.
function Book(title, author, pages) {
this.title = title;
this.author = author;
this.pages = pages;
}
//array of books
let library = [];
//hides and unhides forms
const hide = () => {
var form = document.querySelector("#hide");
if (form.style.display === "none") {
form.style.cssText =
"display: block; display: flex; justify-content: center; margin-bottom: 150px";
} else {
form.style.display = "none";
}
};
//creates form, takes input,creates card, resets and runs hide function when done
const addBookCard = () => {
const bookName = document.querySelector('input[name="bookName"]').value;
const authorName = document.querySelector('input[name="authorName"]').value;
const numPages = document.querySelector('input[name="numPages"]').value;
//just stating variables used within my function
const container = document.querySelector(".flex-row");
const createCard = document.createElement("div");
const divTitle = document.createElement("p");
const divAuthor = document.createElement("p");
const divPages = document.createElement("p");
const deleteBtn = document.createElement("button");
library.push([new Book(bookName, authorName, numPages), deleteBtn]);
deleteBtn.addEventListener('click', event => {
event.target.parentNode.remove();
library = library.filter(v => v[1] !== event.target);
});
//using a class from my css file
createCard.classList.add("card");
createCard.setAttribute("id","id_num")
deleteBtn.setAttribute('id','delBtn')
//geting all info from library
divTitle.textContent = "Title: " + bookName
divAuthor.textContent = "Author: " + authorName
divPages.textContent = "Number of Pages: " + numPages
deleteBtn.textContent = "Delete This Book";
//adding it all to my html
container.appendChild(createCard);
createCard.appendChild(divTitle);
createCard.appendChild(divAuthor);
createCard.appendChild(divPages);
createCard.appendChild(deleteBtn);
document.getElementById("formReset").reset();
hide()
return false
};
var btn = document.querySelector('#newCard');
btn.onclick = addBookCard;
function hello (){
for (var i = 0; i < library.length ;i++) {
console.log(library[i]);
}
}
body {
margin: 0 auto;
width: 960px;
//background: cyan;
}
.flex-row {
display: flex;
flex-wrap: wrap;
}
.flex-column {
display: flex;
flex-direction: column;
}
.flex-row-form {
display: flex;
justify-content: center;
}
.flex-column-form {
display: flex;
flex-direction: column;
background: purple;
width: 45%;
padding: 20px;
border-radius: 5px;
border: 2px solid black;
color: white;
font-weight: 300;
font-size: 24px;
}
.card {
width: 33.33%;
text-align: center;
height: 200px;
border: 1px solid black;
padding: 20px;
margin: 10px;
border-radius: 10px;
}
.text {
padding-bottom: 20px;
font-weight: 300;
font-size: 20px;
}
p {
font-size: 20px;
font-weight: 400;
}
#newBook {
margin: 30px;
padding: 10px 20px;
cursor: pointer;
font-size: 16px;
color: #dff;
border-radius: 5px;
background: black;
}
#delBtn{
padding:10px;
border-radius:5px;
background:red;
color:white;
font-size:14px;
cursor: pointer;
}
<div id="display"></div>
<button id="newBook" onclick="hide()">New Book</button>
<div class="flex-row-form" id="hide" style= "display:none">
<form class="flex-column-form" id="formReset">
Book Name: <input type="text" name="bookName" value="Book Name" id="title"><br>
Author Name: <input type="text" name="authorName" value="Author Name " id="author"<br>
Number of Pages: <input type="text" name="numPages" value="# of Pages" id="pages" ><br>
<button id="newCard"> Add Book to Library</button>
</form>
</div>
<div class="flex-row">
</div>
And I have removed this line
deleteBtn.setAttribute("onclick", "remove()")
you don't need it anymore since I have added event listener for that button, and it was throwing an error because you didn't define remove function in your code.

Get several JSON object printed to the page

I'm making a site where the user will be able to search for a country and the city or cities in that country will show on the page. I'm able to show one city now for each country but if the country have two or more cities only one of the cities shows. I tried the "+=" to create several cards that will show on the page. That created some issues for me. I'm thinking that I have to use the "appendChild()" function to append each city card to a new div in the DOM. But i'm not 100% sure how to do that, with this code.
If I type in "USA" in the searchfield and USA both have LA and NY as cities. The first one shows now, but I want both to show. I've tried using document.createElement('cityCard') and append cityCard to the container where the cards show. But I did not get it to work as I wanted, I might have done some syntax mistake.
Is this the rigth mindset for this task? Or is it a better way?
Don't mind the CSS, its not done.
Link to a fiddle where all the code is.
https://jsfiddle.net/uzfb852g/12/
added the code under aswell(its the same as in the fiddle)
HTML CODE:
<!DOCTYPE html>
<html>
<head>
<link href="https://fonts.googleapis.com/css?family=Martel:400,700,900"
rel="stylesheet">
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<h1>FINN DITT FERIESTED!</h1>
<form id="inputForm">
<input type="text" id="sokFelt">
<button id="btn">Search</button>
<button id="allBtn">Alle steder</button>
</form>
<div id="break"></div>
<div id="searchWord"></div>
<div id="cardContainer">
<div id="cityCards">
<h2 id="country"></h2>
<h4 id="city"></h4>
<img id="cityImg">
</div>
</div>
<button id="btnTwo"></button>
<script src="content.js"></script>
</body>
</html>
CSS CODE:
body{
margin: auto;
width: 100%;
height: 100%;
}
h1{
text-align: center;
margin: 25px;
color: tomato;
font-family: 'Martel', serif;
text-shadow: 1px 2px #333;
font-weight: 900;
}
#inputForm{
text-align: center;
margin: 25px;
}
#break{
width: 80%;
margin: auto;
height: 1px;
text-align: center;
background-color: #333;
}
#btn{
padding: 5px 15px;
}
#sokFelt{
padding: 5px 15px;
}
#searchWord{
font-size: 24px;
margin: 40px;
color: #333;
font-weight: bold;
}
#cardContainer{
width: 100%;
margin: auto;
display: flex;
flex-direction: column;
flex-wrap: wrap;
}
#cityCards{
padding: 12px 22px;
background-color: aqua;
border-radius: 5px;
width: 20%;
height: 250px;
}
#cityImg{
width: 100%;
height: 200px;
}
#allBtn{
padding: 5px 15px;
}
JS CODE:
var form = document.getElementById('inputForm');
var input = form.querySelector('input');
var country = document.getElementById("country");
var city = document.getElementById("city");
var cityImg = document.getElementById("cityImg");
var searchWord = document.getElementById("searchWord");
/*IMAGES*/
var place = [
{land: 'Norge', by: 'Oslo', img: 'img/Oslo.jpg'},
{land: 'USA', by: 'Los Angeles', img: "img/LA.jpg"},
{land: 'USA', by: 'New York', img: "img/NewYork.jpg"},
{land: 'Tyskland', by: 'Berlin', img: 'img/berlin.jpg'},
{land: 'Frankrike', by: 'Paris', img:'img/berlin.jpg'}
];
form.addEventListener('submit', (e) => {
e.preventDefault();
for(var i = 0; i < place.length; i += 1){
if(input.value === place[i].land) {
searchWord.innerHTML = input.value;
document.createElement('cityCards');
country.innerHTML = place[i].land;
city.innerHTML = place[i].by;
cityImg.src = place[i].img;
}
}
});
document.getElementById("btnTwo").addEventListener("click", function(){
document.createElement("")
});
Try this to see the problem:
country.innerHTML += place[i].land;
city.innerHTML += place[i].by;
citycards is not an HTML element
You must use an array with divĀ“s (array[i]=document.createElement('div'))
Then create images with img[i] = document.createElement('img')
Set the img.src, and append this with appendChild to cardcontainer.
Using createElement and appendChild would be the right way to go, but you could also use a template tag instead.
Then you could just fill the template with the filtered information and import the template to the DOM.
Here is an example on how this could look like. You may want to take a look at the array function filter, map and forEach.
var form = document.getElementById('inputForm');
var input = form.querySelector('input');
var searchWord = document.getElementById("searchWord");
var template = document.querySelector('#cardContainer');
var getAllBtn = document.getElementById('allBtn');
var place=[{land:"Norge",by:"Oslo",img:"http://www.telegraph.co.uk/travel/destination/article137625.ece/ALTERNATES/w460/oslocityhall.jpg"},{land:"USA",by:"Los Angeles",img:"http://www.zocalopublicsquare.org/wp-content/uploads/2015/10/DistantLASkylineBIG-levinson.jpg"},{land:"USA",by:"New York",img:"https://www.city-journal.org/sites/cj/files/New-York.jpg"},{land:"Tyskland",by:"Berlin",img:"http://www.telegraph.co.uk/content/dam/Travel/Destinations/Europe/Germany/Berlin/Berlin%20cathedral-xlarge.jpg"}];
form.addEventListener('submit', (e) => {
e.preventDefault();
});
getAllBtn.addEventListener("click", function() {
// clear your container div to empty all previous added elements.
searchWord.innerHTML = "";
// filter your place data on the land property of each item.
place.filter( item => item.land === input.value )
// set the img src attr and text content for the elements in the template.
.map( item => {
template.content.getElementById("country").textContent = item.land;
template.content.getElementById("city").textContent = item.by;
template.content.getElementById("cityImg").src = item.img;
return document.importNode(template.content, true);
})
// append them to your container element.
.forEach( item => {
searchWord.appendChild(item)
})
});
body{margin:auto;width:100%;height:100%}h1{text-align:center;margin:25px;color:tomato;font-family:'Martel',serif;text-shadow:1px 2px #333;font-weight:900}#inputForm{text-align:center;margin:25px}#break{width:80%;margin:auto;height:1px;text-align:center;background-color:#333}#btn{padding:5px 15px}#sokFelt{padding:5px 15px}#searchWord{font-size:24px;margin:40px;color:#333;font-weight:700}#cardContainer{width:100%;margin:auto;display:flex;flex-direction:column;flex-wrap:wrap}#cityCards{padding:12px 22px;background-color:aqua;border-radius:5px;width:20%;height:250px}#cityImg{width:100%;height:200px}#allBtn{padding:5px 15px}
<h1>VACATION</h1>
<form id="inputForm">
<input type="text" id="sokFelt">
<button id="btn">Search</button>
<button id="allBtn">All places</button>
</form>
<div id="break"></div>
<div id="searchWord"></div>
<template id="cardContainer">
<div id="cityCards">
<h2 id="country"></h2>
<h4 id="city"></h4>
<img id="cityImg">
</div>
</template>

Reloading of deleted list items

I have an input box which you can enter items, submit it, and create a box with it's own delete button to remove it. Problem is, after deleting a number of boxes, and then entering something new in input, all the previous items that were deleted get reloaded, including the new item.
How can I prevent reloading of already removed boxes?
Fiddle (Stacksnippets do not allow submit)
This is my Html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Shopping List Example</title>
<link rel="stylesheet" type="text/css" href="css-list.css">
</head>
<div id="centerPanel">
<form class="my-list-form">
<input type="text" class="input" name="add-input" id="add-input">
<button class="add-button" id="submitBtn">Add</button>
</form>
<ul class="my-list-ul"></ul>
</div>
<script src="https://code.jquery.com/jquery-2.2.3.min.js"></script>
<script src="js-list.js"></script>
</html>
JS:
var state = {items:[]};
var addItem = function(state, item)
{
state.items.push(item);
}
var displayItem = function(state, element){
var htmlItems = state.items.map(function(item){
return '<li class="box">' + item + '</br><button class="divBtns" id="deleteBtn">Delete</button>' + '</li>';
});
element.html(htmlItems);
}
//After deleting items, this button again loads all items that have been created since
//the page loaded up, including the new item.
//Needs to be fixed to not reload the deleted items
$('.my-list-form').submit(function(event){
event.preventDefault();
addItem(state, $('.input').val());
displayItem(state, $('.my-list-ul') );
/* alert(state.items[1]); shows that the items array holds everything that is turned into a div*/
})
$(document).ready(function(e){
$('ul').on('click', '#deleteBtn', function(event){
var rmvButton = $(this).closest('li');
rmvButton.remove();
});
})
css:
* {
font-family: sans-serif;
font-weight: normal;
}
#centerPanel {
margin-left: 50px;
margin-top: 50px;
padding-left: 10px;
}
h1 {
font-size: 34px;
}
.font-size {
font-size: 17px;
}
#add-input {
height:25px;
width: 190px;
font-size: 16px;
}
button {
font-size: 17px;
}
#submitBtn {
height: 30px;
width: 85px;
}
.divBtns {
margin-top: 10px;
}
.box {
border: 1px solid black;
border-color: grey;
width: 153px;
height: 65px;
padding: 20px;
font-style: italic;
font-size: 22px;
margin-bottom:10px;
margin-right: 10px;
}
ul {
list-style-type: none;
margin-left:-40px;
color: grey;
}
li {
float: left;
}
It appears you never remove anything from the state object, which is added to every time you run addItem().
You'd need a way to remove a specific item from this array, probably by getting the index of the li to delete and doing
state.items.splice(index, 1);
Store the index as a data attribute on the button:
var displayItem = function(state, element){
var i = 0;
var htmlItems = state.items.map(function(item){
return '<li class="box">' + item + '</br><button class="divBtns" ' +
'id="deleteBtn" data-index="' + (i++) + '">Delete</button>' + '</li>';
});
element.html(htmlItems);
}
Then you can get it in the click callback
var index = $(this).data('index');
You can update state to solve this problem.
It's my code:
...
var deleteItem = function(state, itemId) {
var index = 0;
var isFind = state.items.some(function(item, i) {
if (item.id == itemId) {
index = i;
return true;
} else {
return false;
}
});
if (isFind) {
state.items.splice(index, 1);
}
}
...
$(document).ready(function(e){
$('ul').on('click', '#deleteBtn', function(event){
...
// update state
deleteItem(state, $(this).parent().data('id'));
});
})
https://jsfiddle.net/q483cLp9/

Categories