UI not being updated by the result of fetch - javascript

I am trying to load search results based on what the user typed (responsive search), I thought it was my event as I wanted it more responsive e.g keyup but it didn't work. Here is working codepen
and following in my code. I had been struggling for hours now. What am I doing wrong?
function generateHTML(el,toggleClass){
const div = document.createElement("div");
const card = div.classList.add("card","col-sm-6", "col-md-6", "col-lg-3", "m-2",toggleClass);
div.innerHTML = `
<a href='#' class="products">
<img src="${el.image}" class="card-img-top rounded" alt="${el.title}">
<div class="card-body">
<h5 class="card-title">${(slicer(el.title))}</h5>
</div>
</a>
`
grid.appendChild(div);
}

This should do what you actually want
//getting the reference to elements
const row = document.querySelector("#search");
const grid = document.querySelector("#grid");
document.addEventListener("DOMContentLoaded", function () {
const div = document.createElement("div");
const formGroup = div.classList.add("form-group");
div.innerHTML = `
<div class="alert alert-primary">
<label for="searchField">Search</label>
<input type="text" class="form-control" id="searchField" placeholder="Type in what your looking for">
</div>
`;
row.appendChild(div);
//fetchData()
//get the reference to form
var input = '';
const myInput = document.getElementById("searchField");
myInput.addEventListener("keyup", function (e) {
input = myInput.value;
console.log(input);
fetch('https://fakestoreapi.com/products/')
//convert the response to JSON
.then(res => res.json())
//use the data to filter if it matches or display all
.then(data => {
grid.innerHTML = '';
data.forEach(el => {
if (el.title.toLowerCase().includes(input.toLowerCase())) {
console.log('h')
generateHTML(el, "block")
}
})
})
});
//get the reference to input
//const input = form.searchField.value.trim();
//add a event to listen on key up
})
function generateHTML(el, toggleClass) {
const div = document.createElement("div");
const card = div.classList.add("card", "col-sm-6", "col-md-6", "col-lg-3", "m-2", toggleClass);
div.innerHTML = `
<a href='#' class="products">
<img src="${el.image}" class="card-img-top rounded" alt="${el.title}" width="100" height="auto">
<div class="card-body">
<h5 class="card-title">${(slicer(el.title))}</h5>
</div>
</a>
`;
grid.appendChild(div);
}
function slicer(str) {
return str.slice(0, 10);
}
<div class="container">
<div class="" id="search">
</div>
<div class="row" id="grid">
</div>
</div>

Related

Can't delete div's with new added buttons

after I keep creating new DIVs with my javascript functions, the new ones don't delete themselves.
My codes creates a post it div which gives info about title, author, and pages, and a X button that when you press it, it deletes it.
It works for my first 4 HTML examples, but not with the newly added divs from javascript.
const container = document.querySelector('.right');
let removeButtons = document.querySelectorAll('.remove');
let removeButtonsArray = Array.prototype.slice.call(removeButtons);
let bookNum = 4;
function addBook() {
const titleAdd = document.getElementById('title-add').value;
const readAdd = document.getElementById('read-add').value;
const totalAdd = document.getElementById('total-add').value;
if (titleAdd == '' || readAdd == '' || totalAdd == '') {
alert('Complete all of the boxes');
return;
} else if (readAdd > totalAdd) {
alert('why are you lying? =[[');
return;
}
const book = document.createElement('div');
book.classList.add('book');
container.appendChild(book);
const xButton = document.createElement('button');
xButton.classList.add('remove');
book.appendChild(xButton);
xButton.innerHTML = 'X';
const title = document.createElement('h2');
book.appendChild(title);
title.innerHTML = titleAdd;
const author = document.createElement('h3');
book.appendChild(author);
author.innerHTML = document.getElementById('author-add').value;
const pages = document.createElement('h3');
book.appendChild(pages);
const pagesNumber = readAdd + '/' + totalAdd;
pages.classList.add('page-num')
pages.innerHTML = pagesNumber;
bookNum++;
removeButtons = document.querySelectorAll('.remove');
removeButtonsArray = Array.prototype.slice.call(removeButtons);
}
const button = document.querySelector('.add');
button.onclick = addBook;
removeButtonsArray.forEach(removeButton => removeButton.addEventListener('click', function() {
const remove = removeButtonsArray.indexOf(removeButton);
removeButtonsArray.splice(remove, 1);
document.querySelector('.book').remove();
bookNum--;
}));
<div class="container">
<div class="left">
<div class="title-add">
<h2>Title:</h2>
<input type="text" id="title-add" name="title-add" max="30">
</div>
<div class="author-add">
<h2>Author:</h2>
<input type="text" id="author-add" name="author-add" max="30">
</div>
<div class="pages">
<div class="pages-read">
<h3>Pages read:</h3>
<input type="number" id="read-add" min="0" name="read">
</div>
<div class="pages-total">
<h3>Total pages:</h3>
<input type="number" id="total-add" min="1" name="total">
</div>
</div>
<button class="add">Add book</button>
</div>
<div class="right">
<div class="book">
<button class="remove">X</button>
<h2 class="title">muie</h2 >
<h3 class="author">Csokmai Robert123</h3>
<h3 class="page-num">31/316</h3>
</div>
<div class="book">
<button class="remove">X</button>
<h2>muie</h2 >
<h3>Csokmai Robert</h3>
<h3 class="page-num">31/316</h3>
</div>
<div class="book">
<button class="remove">X</button>
<h2>muie</h2 >
<h3>Csokmai Robert</h3>
<h3 class="page-num">31/316</h3>
</div>
<div class="book">
<button class="remove">X</button>
<h2>muie</h2 >
<h3>Csokmai Robert</h3>
<h3 class="page-num">31/316</h3>
</div>
</div>
</div>
I tried everything but I just don't know how to fix it
Problems:
There are a few Problems with your code:
Delete function doesn't actually work, because every time you are just removing the first element which has book class. document.querySelector('.book').remove();
The reason the Delete button for Dynamically added books was not working is because this bit of code removeButtonsArray.forEach(removeButton => removeButton.addEventListener was executed only on first execution. So, newly added buttons weren't getting the Event Listener registered for them.
Solutions:
Delete functionality can be easily handled by targeting the parentElement of the Delete Button which was clicked.
For the Dynamically added Books' Delete Functionality, We need to register the click Event Listener for each one of them during creation.
const container = document.querySelector('.right');
let removeButtons = document.querySelectorAll('.remove');
let bookNum = 4;
function addBook() {
const titleAdd = document.getElementById('title-add').value;
const readAdd = document.getElementById('read-add').value;
const totalAdd = document.getElementById('total-add').value;
if (titleAdd == '' || readAdd == '' || totalAdd == '') {
alert('Complete all of the boxes');
return;
} else if (readAdd > totalAdd) {
alert('why are you lying? =[[');
return;
}
const book = document.createElement('div');
book.classList.add('book');
container.appendChild(book);
const xButton = document.createElement('button');
xButton.classList.add('remove');
book.appendChild(xButton);
xButton.innerHTML = 'X';
const title = document.createElement('h2');
book.appendChild(title);
title.innerHTML = titleAdd;
const author = document.createElement('h3');
book.appendChild(author);
author.innerHTML = document.getElementById('author-add').value;
const pages = document.createElement('h3');
book.appendChild(pages);
const pagesNumber = readAdd + '/' + totalAdd;
pages.classList.add('page-num')
pages.innerHTML = pagesNumber;
bookNum++;
// Adding Click Event to new Books Delete Button
xButton.addEventListener("click", removeBook);
}
const button = document.querySelector('.add');
button.onclick = addBook;
// Function for Removing Book
function removeBook(e) {
// Since we are getting the Delete Buttons event as Frunction Argument
// We can get access to it's parentElement i.e., the target Book Div
e.target.parentElement.remove();
bookNum--;
}
removeButtons.forEach(removeButton => removeButton.addEventListener('click', removeBook));
<div class="container">
<div class="left">
<div class="title-add">
<h2>Title:</h2>
<input type="text" id="title-add" name="title-add" max="30">
</div>
<div class="author-add">
<h2>Author:</h2>
<input type="text" id="author-add" name="author-add" max="30">
</div>
<div class="pages">
<div class="pages-read">
<h3>Pages read:</h3>
<input type="number" id="read-add" min="0" name="read">
</div>
<div class="pages-total">
<h3>Total pages:</h3>
<input type="number" id="total-add" min="1" name="total">
</div>
</div>
<button class="add">Add book</button>
</div>
<div class="right">
<div class="book">
<button class="remove">X</button>
<h2 class="title">muie</h2>
<h3 class="author">Csokmai Robert123</h3>
<h3 class="page-num">31/316</h3>
</div>
<div class="book">
<button class="remove">X</button>
<h2>muie</h2>
<h3>Csokmai Robert</h3>
<h3 class="page-num">31/317</h3>
</div>
<div class="book">
<button class="remove">X</button>
<h2>muie</h2>
<h3>Csokmai Robert</h3>
<h3 class="page-num">31/315</h3>
</div>
<div class="book">
<button class="remove">X</button>
<h2>muie</h2>
<h3>Csokmai Robert</h3>
<h3 class="page-num">31/314</h3>
</div>
</div>
</div>

Adding div container with class name and html content with JavaScript

I'm looking for a way to add a div container using template literals. for example.
I have a div in my index.html
<div class="bag">
</div>
Every time the user adds a new item to the bag the following divs' get added inside the bag like so...
<div class="bag">
<div class="bag__item"> // <--- added here
<div class="wrapper--within">
<img src="./assets/images/cookie5-small.jpg" alt="" />
<h3 class="bag__unit-price">$5</h3>
<div class="bag__quantity-container">
<div class="bag__minus-sign"></div>
<h3 class="bag__quantity-container__quantity">2</h3>
<div class="bag__plus-sign-middle"></div>
<div class="bag__plus-sign-center"></div>
</div>
<div class="bag__total-price-container">
<img
class="bag__trash-icon"
src="./assets/images/trash-small.png"
alt=""
/>
<h2 class="bag__total-price">$10</h2>
</div>
</div>
</div> // <-- to here
</div>
In my javascript I target my bag container
class Cart {
constructor() {
this.cartContainer = document.querySelector(".bag");
this.events();
}
events() {
this.updateCart();
}
updateCart() {
let newItemDiv = document.createElement("div")
newItemDiv.className = "bag__item"
newItemDiv.createElement("div")
}
}
export default Cart;
I was originally planning to add each div individually but i would like a way where i can do something like..
updateCart() {
let newItemDiv = document.createElement("div")
add `<div class="bag__item"> // <--- added here
<div class="wrapper--within">
<img src="./assets/images/cookie5-small.jpg" alt="" /> // <---image will change depending on item added
<h3 class="bag__unit-price">$5</h3> // price will change depending on item added..
<div class="bag__quantity-container">
<div class="bag__minus-sign"></div>
<h3 class="bag__quantity-container__quantity">2</h3>
<div class="bag__plus-sign-middle"></div>
<div class="bag__plus-sign-center"></div>
</div>
<div class="bag__total-price-container">
<img
class="bag__trash-icon"
src="./assets/images/trash-small.png"
alt=""
/>
<h2 class="bag__total-price">$10</h2>
</div>
</div>
</div> `
}
Is this something that can be done?
In your updateCart() method you can write
updateCart() {
let newItemDiv = document.createElement("div")
newItemDiv.className = "bag__item"
newItemDiv.innerHTML = `your markup here with the whole div hierarchy`;
}
You can do this.
If you already added the div.bad
document.getElementsByClassName("bag").innerHTML = `<div> </div>`
or
var elemt = document.createElement("div")
elemt.innerHTML = `<div> </div>`
You can do it like this: (I implemented below example by Cart class because in your question you've been using Cart class to create new Item and I consider that using this class is mandatory, there are other ways to acheive below result with less lines of code)
class Cart {
constructor(img, price) {
this.img = img;
this.price = price;
this.cartContainer = document.querySelector('.bag');
this.events();
}
getTemplate() {
const template = `<div class="bag__item">
<div class="wrapper--within">
<img src="${this.img}" alt="" />
<h3 class="bag__unit-price">${this.price}</h3>
<div class="bag__quantity-container">
<div class="bag__minus-sign"></div>
<h3 class="bag__quantity-container__quantity">2</h3>
<div class="bag__plus-sign-middle"></div>
<div class="bag__plus-sign-center"></div>
</div>
<div class="bag__total-price-container">
<img
class="bag__trash-icon"
src="./assets/images/trash-small.png"
alt=""
/>
<h2 class="bag__total-price">$10</h2>
</div>
</div>
</div> `;
return template;
}
events() {
this.updateCart();
}
updateCart() {
const template = this.getTemplate();
let newItemDiv = document.createElement('div');
this.cartContainer.append(newItemDiv);
newItemDiv.outerHTML = template;
}
}
// sample img, price (You can set different values for img, price when you want to create new one, this static content is just for example)
const img = 'https://fakeimg.pl/350x200/000000/?text=Image1';
const price = '100$';
function addBag(){
new Cart(img, price);
}
<button onClick="addBag()">click me</button>
<div class="bag">ba continaer:</div>

Loop over API data and display multiple search items in the DOM with JavaScript

I'm trying to set up a search API app, using the https://restcountries.eu/ API and axios.
I've managed to display everything in the DOM, as I want. However, if I search for "United", or something with only part of the name. It either displays undefined or if I loop though it'll only display the last search item. I'd like to bring all the search items into the DOM, under any search criteria I input.
Below is the HTML:
<body>
<div class="search-container">
<form id="form" class="form">
<input type="text" id="search" placeholder="Search for country..." />
</form>
</div>
<main id="main">
<div id="map" class="google-map"></div>
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js"></script>
<script src="script.js"></script>
</body>
</html>
And here's the JS:
const APIURL = 'https://restcountries.eu/rest/v2/name/'
// const GOOGLE_MAPS_API = 'https://maps.googleapis.com/maps/api/js?key=AIzaSyDhlU1KMTlTh4C__bTJBxbVA-s7wvQbO9E&callback=initMap'
const main = document.getElementById('main')
const form = document.getElementById('form')
const search = document.getElementById('search')
async function getCountryData(name) {
try {
const { data } = await axios.get(APIURL + name)
data.forEach(res => {
let countryArr = res
createCountryCard(countryArr)
} )
} catch (err) {
if(err.response.status == 404) {
createErrorCard('No countries found')
setTimeout(() => {
main.innerHTML = ''}
, 1500);
}
}
}
function createCountryCard(country) {
const cardHTML = `
<div class="content-container">
<div class="card">
<div class="wrapper">
<div class="card-title">
<h2>${country.name}</h2>
<h4>Capital: ${country.capital}</h4>
<h5>Population: ${country.population.toLocaleString('en')}</h5>
</div>
<div class="card-image">
<img
src="${country.flag}"
alt="${country.name +'-flag'}"
/>
</div>
</div>
<div class="wrapper">
<div class="map-content">
<div id="map" class="google-map"></div>
</div>
<div class="card-content">
<ul class="card-list">
<li><strong>Region:</strong> ${country.region}</li>
<li><strong>Subregion:</strong> ${country.subregion}</li>
<li><strong>Currency:</strong> ${country.currencies[0].name}<span> ${country.currencies[0].symbol}</span></li>
<li><strong>Spoken Language:</strong> ${country.languages[0].name}</li>
<li><strong>Timezone:</strong> ${country.timezones}</li>
</ul>
</div>
</div>
</div>
</div>
`
main.innerHTML = cardHTML
console.log(country)
}
function createErrorCard(msg) {
const cardHTML = `
<div class="card">
<h1>${msg}</h1>
</div>
`
main.innerHTML = cardHTML
}
// Search Input
form.addEventListener('submit', (e) => {
e.preventDefault()
const countryName = search.value
if(countryName) {
getCountryData(countryName)
search.value = ''
}
})
Any help would be much appreciated.
Thanks
main.innerHTML = cardHTML line in createCountryCard() function overwrites the content of the #main element with each loop iteration. That's why you are only getting the last item from your search results. Try changing it to main.innerHTML += cardHTML.

Append HTML in DOM, not seen by selectors

Using innerHTML when I add a new item, the query selector does not seem to add it.
So when I try to calculate my items. I only am getting the original ones to calculate, but not the newly generated ones.
Even when you console.log() the elements by the variable it will only show the original elements.
All these elements have the same class name as the original element.
Just cannot get them to be seen or added on the generated items.
Fiddle code snippet.
const total = document.querySelectorAll(".tot")
const price = document.querySelectorAll(".cost");
let textval = document.querySelectorAll('.qty-item');
const cal = document.getElementById("calc");
const errorMessage = document.querySelectorAll('.error');
//
let theform = document.querySelector(".theform");
let newitem = document.querySelector('.new-item');
let createBtn = document.getElementById("create");
let theItem = document.querySelector(".newStuff");
//
form.addEventListener("click",function(e){
let theHtml = `
<div>
<span class="cost">${newitem.value}</span>
</div>
<div class="qty">
<label>QTY:</label><input placeholder="0" class="qty-item">
</div>
<div class="tot">
<span><label>TOTAL</label> $0.0</span>
</div>
`
});
cal.addEventListener('mouseover', function(e) {
console.log('total', total);
for (var i = 0; i < price.length; i++) {
let xPrice = price[i].innerHTML.split("$");
let parsePrice = parseFloat(xPrice[1]);
if (textval[i].value === "" || isNaN(textval[i].value)) {
console.log("No Good");
} else {
let x = parseFloat(textval[i].value);
let y = parsePrice;
let z = x * y;
total[i].innerText = z.toFixed(2);
total[i].innerText = z;
for (let k = 0; k < total.length; k++) {
let j = parseFloat(total[k].innerHTML);
console.log(j);
}
}
}
});
<body>
<div class="main">
<span class="title">A Title</span>
</div>
<div class="content">
<div class="item">
<span>Item 1</span>
</div>
<div>
<span class="cost">$100.00</span>
</div>
<div id="qty">
<label>QTY:</label><input placeholder="0" class="qty-item">
<p class="error"></p>
</div>
<div class="tot">
<span><label>TOTAL</label> $0.0</span>
</div>
</div>
<br><br>
<div class="main">
<span class="title">A Title</span>
</div>
<div class="content">
<div class="item">
<span>Item 2</span>
</div>
<div>
<span class="cost">$50.00</span>
</div>
<div class="qty">
<label>QTY:</label><input placeholder="0" class="qty-item">
<p class="error"></p>
</div>
<div class="tot">
<span><label>TOTAL</label> $0.0</span>
</div>
</div>
<form class ='theform'>
<label>NewItem</label><input placeholder="0" class="new-item">
<button id="create">create</button>
</form>
<span class ="newStuff"></span>
<div class="calc-button">
<button id="calc">Calculate Prices</button>
</div>
</body>
#Phil is right on this. You are running the query selectors at the start of your script and will therefore run only once. So when the user clicks a button and you dynamically add new html to the page, the query selectors will not fire again.
You can initialize those queries at the top of your script just like you have them now, but you will need to re-assign their values to new queries inside your event listener something like the following:
const total = document.querySelectorAll(".tot")
const price = document.querySelectorAll(".cost");
let textval = document.querySelectorAll('.qty-item');
let cal = document.getElementById("calc");
const errorMessage = document.querySelectorAll('.error');
//
let theform = document.querySelector(".theform");
let newitem = document.querySelector('.new-item');
let createBtn = document.getElementById("create");
let theItem = document.querySelector(".newStuff");
form.addEventListener("click",function(e){
let theHtml = `
<div>
<span class="cost">${newitem.value}</span>
</div>
<div class="qty">
<label>QTY:</label><input placeholder="0" class="qty-item">
</div>
<div class="tot">
<span><label>TOTAL</label> $0.0</span>
</div>
`
//append your HTML to the correct target element then update your query inside the event listener
textval = document.querySelectorAll('.qty-item');
cal = document.getElementById("calc");
});
Given all your querySelectorAll() queries are using simple, single-class-name selectors, you can replace them all with document.getElementsByClassName(), ie
const total = document.getElementsByClassName('tot')
const price = document.getElementsByClassName('cost');
const textval = document.getElementsByClassName('qty-item');
const errorMessage = document.getElementsByClassName('error');
This behaves almost the same as querySelectorAll() with one difference...
elements is a live HTMLCollection of found elements.
The elements referenced in these collections will update automatically as elements are added or removed from the document.

How to repeat xml element?

I have created the code to load elements from a xml file. But it does not load repeated values in xml file. It only loads one element, others are not loading.
Herewith attached an example.
$(document).ready(function(){
$.ajax({
type:"GET",
url:"data.xml",
dataType:"xml",
success:showdata
});
});
function showdata(xml){
xml = $(xml).children();
$(xml).children().each(function () {
let tag = $(this).prop("tagName");
let to = $(this).find("to").text();
let from =$(this).find("from").text();
let heading = $(this).find("heading").text();
let list = $(this).find("list").text();
let body =$(this).find("body").text();
let html = `<div class="col-md-4" id="random">
<div class="thumbnail">
<a href="#${tag}"><p>${to}</p>
<p>${from}</p>
<p>${heading}</p>
<p>${body}</p></a>
</div>
</div>`;
let popup = `<div id="${tag}" class="overlay">
<div class="popup">
<h6>${heading}</h6>
<a class="close" href="#">×</a>
<ul><li>${list}</li></ul>
</div>
</div>`;
$("#xmldata").append(html);
$("#popup").append(popup);
});
}
http://next.plnkr.co/edit/MVwJnBHypBFvg2Lk?
I saw in your code you are using anchor tag and it is always pointing to #note. I have just append index number and Now it is perfect.
Please Change this line
$(xml).children().each(function (index,item) {
let tag = $(this).prop("tagName")+index;
I have also updated list which was not binding as a li. Now check it will work for you
Complete Code is as follows
function showdata(xml){
xml = $(xml).children();
$(xml).children().each(function (index,item) {
let tag = $(this).prop("tagName")+index;
let to = $(this).find("to").text();
let from =$(this).find("from").text();
let heading = $(this).find("heading").text();
let list = $(this).find("list");
let body =$(this).find("body").text();
let html = `<div class="col-md-4" id="random">
<div class="thumbnail">
<a href="#${tag}"><p>${to}</p>
<p>${from}</p>
<p>${heading}</p>
<p>${body}</p></a>
</div>
</div>`;
var listStr="";
list.each((index,item)=>{
listStr+="<li>"+item.innerHTML+"</li>"
})
let popup = `<div id="${tag}" class="overlay">
<div class="popup">
<h6>${heading}</h6>
<a class="close" href="#">×</a>
<ul>`+listStr+`</ul>
</div>
</div>`;
$("#xmldata").append(html);
$("#popup").append(popup);
});
}

Categories

Resources