So I'm making a holiday finder "app" and I want to have buttons where when someone clicks the name of their country it inputs the country's value into the api string.
What I did was loop over all the buttons and save the target value to a variable which I then concatenated into the api. The thing is when I look in the console at the api fetched, where the country code is supposed to be it says "undefined".
I'm a bit confused on why so if you find the solution please explain it.
let countrySelect = document.getElementById('country-select');
let holidayName = document.getElementById('holiday-name');
let holidayDesc = document.getElementById('holiday-desc');
let holidayDate = document.getElementById('holiday-date');
let holidayType = document.getElementById('holiday-type');
let info = document.querySelector('.cell');
let buttonValue;
// get button values
const button = document.querySelectorAll("button").forEach(
button => button.addEventListener('click', function(e) {
buttonValue = e.target.value;
console.log(buttonValue)
})
);
// api url
const api = `https://calendarific.com/api/v2/holidays?&api_key=<api key>&country=${buttonValue}&year=2020`;
// When the button is clicked fetch results
countrySelect.addEventListener('click', function() {
fetch(api)
.then(res => res.json())
.then(data => {
var apiResponse = data.response;
console.log(apiResponse);
}, networkError => {
alert(networkError)
})
})
You need to define / redefine your api variable within the countrySelect event listener.
At the moment it is being defined before any buttons are clicked, so buttonValue is undefined. So even if your buttonValue changes in response to buttons being clicked, the api variable stays how it was, ie. with country=undefined.
let countrySelect = document.getElementById('country-select');
let buttonValue;
const button = document.querySelectorAll("button").forEach(
button => button.addEventListener('click', function(e) {
buttonValue = e.target.value;
console.log(buttonValue);
})
);
// When the button is clicked fetch results
countrySelect.addEventListener('click', function() {
const api = `https://calendarific.com/api/v2/holidays?&api_key=<api key>&country=${buttonValue}&year=2020`;
console.log(api);
});
#country-select {
border: 1px solid green;
color: green;
display: inline-block;
cursor: pointer;
}
<button value='uk'>
UK
</button>
<button value ='us'>
US
</button>
<div id='country-select'>
Select Country
</div>
Related
I'm using the very famous StarWars API and I should extrapolate the data from the first page of the planets (and so far I have no problems). Sure, I had to do some research to figure out how to do it, but I finally succeeded. But now there is another problem.
I would have to create buttons that allow the user to go to the second page of the planets and get data from that page. And here I am stuck, because I don't know how to do it, because then, using the Next Button, the user must be able to go to the third,, fourth, fifth and sixth page, and then returning back to the first page of planets.
So I looked at the URL and thought we could bind a variable to it that is itself bound to a value provided by the buttons. In this way, if the user clicks on the button the chosen value should be given to the variable located after the URL. To do this I used the ++ value and --value patterns, which do what they should: increase and decrease the value.
However, this is not transmitted to the URL and the new fetch process does not take place to get the info from the pages.
But I did something wrong and I don't know what.
Could you please help me? ..
This is the problematic code:
value = 1
const prevButton = document.createElement("button");
prevButton.innerText = 'Prev Page'
prevButton.setAttribute("id", "prev-button")
document.body.append(prevButton);
prevButton.style.cssText = "background-color: violet; width: 150px; height: 45px;
transform: translateY(-725%); margin-left: -10px;"
prevButton.addEventListener('click', () => {
return --value
})
const nextButton = document.createElement("button");
nextButton.innerText = 'Next Page'
nextButton.setAttribute("id", "next-button")
document.body.append(nextButton);
nextButton.style.cssText = "background-color: violet; width: 150px; height: 45px; transform: translateY(-825%); margin-left: 90.5%;"
nextButton.addEventListener('click', () => {
return ++value
})
const response = await fetch ('https://swapi.dev/api/planets/?page=' + value);
const planetsData = await response.json().then(planetsData => {
let results = planetsData.results;
results.forEach(planet => {
const wrapper = document.createElement("wrapper")
square.append(wrapper);
wrapper.style.cssText = 'background-color: red; width: 200px; height: 250px; margin: auto; margin-top: 1.5%; margin-left: 50px; display: flex; flex-wrap: nowrap"; flex-direction: row;'
wrapper.innerHTML = '<div><h1>Name: ' + planet.name + '</h1>' +
'<h3><p>Climate: ' + planet.climate + '</p></h3>' +
'<p>Population: ' + planet.population +
'<p>Terrain: ' + planet.terrain + '</p></div>';
});
})
Here's an example that embellishes on my comment. I've hardcoded the buttons here for convenience but the key points are:
getting that fetch process in its own function so that it can be called whenever a button is clicked
Using the next and previous properties of the returned data to guide which page should be displayed next.
// Cache the elements
const planets = document.querySelector('.planets');
const buttons = document.querySelector('.buttons');
const previous = document.querySelector('[data-id="previous"]');
const next = document.querySelector('[data-id="next"]');
// Add one listener to the button container
buttons.addEventListener('click', handleClick);
// Initialise the base endpoint, and `data`
const base = 'https://swapi.dev/api/planets/?page=1';
let data = {};
// Fetch the first page
fetchData();
// When a button is clicked grab its
// data id (which will be either "previous" or "next"
// and use that to fetch the next page
function handleClick(e) {
if (e.target.matches('.button')) {
const { id } = e.target.dataset;
fetchData(id);
}
}
// Pass in the value of the data attribute as `id`
// If the data has a previous/next property
// use its value to get the previous/next page, otherwise
// use the base endpoint
// Then create some HTML from the data and add it to
// the page, finally updating the disabled state of
// each button
async function fetchData(id) {
const endpoint = data[id] || base;
const res = await fetch(endpoint);
data = await res.json();
const html = createHTML(data.results);
planets.innerHTML = html;
previous.disabled = !data.previous ? true : false;
next.disabled = !data.next ? true : false;
}
// Pass in the new data, `map` over it, and return
// a string of HTML
function createHTML(data) {
return data
.map(planet => {
return `
<section class="planet">
<h4>${planet.name}</h4>
<p>Population: ${planet.population}</p>
<p>Terrain: ${planet.terrain}</p>
</section>
`;
})
.join('');
}
.planet { border: 1px solid #efefef; padding: 0.4em; }
.button:hover { cursor: pointer; }
<section class="buttons">
<button data-id="previous" class="button">Prev</button>
<button data-id="next" class="button">Next</button>
</section>
<section class="planets"></section>
Additional documentation
querySelector
map
join
Destructuring assignment
matches
Template/string literals
Data attributes
I am a student and currently learning jquery <--
I am trying to make the function that will take input from the inputbox and then add it to the unordered list as an EXTRA
Using jQuery create an input and a button. When clicking on the button it should invoke a function addToList that will use the input's value to add it to the toDos variable. Make sure to render it on the screen as a new list item in the unordered list.
const body = $("body");
const header = $("<header>Todo List</header>");
const unorderedList = $("<ul>unorderedlist</ul>");
const testButton = $("<button>testButton</button>")
const inputBox = $("<input></input>")
var toDos = ["wake up", "eat breakfast", "code"];
$("<ul>")
.append(toDos.map((text) => $("<li>", { text })))
.appendTo(document.body);
testButton.on("click", () => {
console.log("hello");
.append(inputBox.text) => $("<li>", { text })
.appendTo(document.body);
});
body.append(header);
body.append(unorderedList);
body.append(inputBox);
body.append(testButton);
You had several problems which I fixed:
const body = $("body");
const header = $("<header>Todo List</header>");
const unorderedList = $("<ul></ul>");
const testButton = $("<button>testButton</button>")
const inputBox = $("<input></input>")
var toDos = ["wake up", "eat breakfast", "code"];
// Add toDos to the <ul>
unorderedList.append(toDos.map((text) => $("<li>", { text })))
// Add click handler to button:
testButton.on("click", () => {
console.log("hello");
unorderedList.append($("<li>", { text: inputBox.val() }))
});
body.append(header);
body.append(unorderedList);
body.append(inputBox);
body.append(testButton);
I am working on an app which fetch the data from API and create a list of the books based on the data received from API. it's an API which gives book titles and information. I generated dynamic li elements and generated a button inside the li. each button has a hidden input element which keep book titles in it. the problem is I'm trying to define a onclick event listener for buttons, since buttons are generated dynamically they don't have id. I want to create an event listener for buttons so that once one specific button is clicked the value of hidden input element that is defined inside the button is passed. I couldn't figure out a way to do that. how to make it to understand which specific button has been clicked so it return the input value that is attached to it.
any help would be really appreciated.
here is a portion of my code.
async function overViewMaker(){
const response = await fetch(api_url_overview.concat(api_key));
let data = await response.json();
data = data.results.lists;
data.forEach(book => {
let mybook = book.books;
mybook.forEach(eachbook => {
var book_div = document.getElementById('book_list');
var liTag = document.createElement("li");
var aTag = document.createElement("buttom");
var inpuHidden = document.createElement("input");
inpuHidden.setAttribute("type", "hidden");
inpuHidden.value = eachbook.title;
aTag.appendChild(inpuHidden);
liTag.appendChild(aTag);
book_div.appendChild(liTag);
});
});
}
Each button is already an element object and so you can use addEventListener directly on the element.
async function overViewMaker() {
const response = await fetch(api_url_overview.concat(api_key));
let data = await response.json();
data = data.results.lists;
// moved book_div out of for loop so it doesn't need to be re-queried for every book
var book_div = document.getElementById("book_list");
data.forEach((book) => {
let mybook = book.books;
mybook.forEach((eachbook) => {
var liTag = document.createElement("li");
var aTag = document.createElement("button");
var inpuHidden = document.createElement("input");
inpuHidden.setAttribute("type", "hidden");
inpuHidden.value = eachbook.title;
aTag.appendChild(inpuHidden);
aTag.addEventListener("click", (ev) => {
// you don't need to get book title from the hidden input element since it is in the scope
// inputHidden.value is also accessible from inside of here
const title = eachBook.title;
console.log(title);
});
liTag.appendChild(aTag);
book_div.appendChild(liTag);
});
});
I have four buttons, each of these is different than others.
What I want:
When I click on the button I want to add to this button a class named 'togglemath' and exactly in the same time I want to remove class from the three left buttons ( of course if the buttons has a classname 'togglemath')
I know that the code below is bad, but I put this here to understand what I mean.
const sumButton = document.getElementById('add');
const substractButton = document.getElementById('subtract');
const multipleButton = document.getElementById('multiple');
const divideButton = document.getElementById('divide');
const mathButtons = [sumButton, substractButton, multipleButton, divideButton];
const activeClass = () => {
mathButtons.forEach(el => {
el.addEventListener('click', e => {
[e.target, ...rest] = mathButtons;
e.target.classList.add('togglemath');
rest.classList.remove('togglemath');
});
});
};
activeClass();
Lets make for example 3 buttons:
<button class="toggle">1</button>
<button class="toggle">2</button>
<button class="toggle">3</button>
<button class="toggle">4</button>
As far as I understand you need to remove class togglemath from all the buttons except the one that you clicked on. Then we can do something like this:
const TOGGLE_CLASS_NAME = 'togglemath';
const $buttons = document.getElementsByClassName('toggle');
function toggleClass(event) {
for (const $button of $buttons) {
$button.classList.remove(TOGGLE_CLASS_NAME);
}
event.target.classList.add(TOGGLE_CLASS_NAME);
}
for (const $button of $buttons) {
$button.addEventListener('click', toggleClass);
}
First remove the class from all the elements when a button is clicked and then add the class to the clicked button:
const sumButton = document.getElementById('add');
const substractButton = document.getElementById('subtract');
const multipleButton = document.getElementById('multiple');
const divideButton = document.getElementById('divide');
const mathButtons = [sumButton, substractButton, multipleButton, divideButton];
mathButtons.forEach(button => button.addEventListener('click', activeClass))
function activeClass() {
mathButtons.forEach(button => button.classList.remove('togglemath'));
event.target.classList.add('togglemath');
}
.togglemath {
color: red;
}
<button id="add">add</button>
<button id="subtract">subtract</button>
<button id="multiple">multiple</button>
<button id="divide">divide</button>
You are not far from the actual solution. The problem seems to be with the understanding of the following line:
[e.target, ...rest] = mathButtons;
This doesn't search the e.target out of mathButtons and assigns the other elements to rest. But instead assigns e.target to the first element in mathButtons and rest to the second, third end fourth. This produces the follow-up problem that e.target.classList.add('togglemath') will always add the class to the first button.
rest.classList.remove('togglemath') has a somewhat other issue, namely that you try to access classList on an array. Instead you should access it for element in the array.
Without changing your code much you could be looking at something like this:
const sumButton = document.getElementById('add');
const substractButton = document.getElementById('subtract');
const multipleButton = document.getElementById('multiple');
const divideButton = document.getElementById('divide');
const mathButtons = [sumButton, substractButton, multipleButton, divideButton];
const activeClass = () => {
mathButtons.forEach(el => {
el.addEventListener('click', e => {
mathButtons.forEach(mathButton => {
mathButton.classList.remove('togglemath');
});
e.target.classList.add('togglemath');
});
});
};
activeClass();
This solution first removes all togglemath classes from all buttons. Then adds it (back) to the clicked target.
Strafing somewhat from what you've provided, you could opt to save the active button in a variable. This way you only have to remove the class from the current active button, add it to the event target, and replace the active variable with the clicked target.
const sumButton = document.getElementById('add');
const substractButton = document.getElementById('subtract');
const multipleButton = document.getElementById('multiple');
const divideButton = document.getElementById('divide');
const mathButtons = [sumButton, substractButton, multipleButton, divideButton];
const activeClass = () => {
let active;
mathButtons.forEach(button => {
button.addEventListener('click', event => {
if (event.target === active) return;
if (active) active.classList.remove('togglemath');
event.target.classList.add('togglemath');
active = event.target;
});
});
};
activeClass();
button {
border: 1px solid #33497b;
padding: 0.25em 1em;
background-color: white;
color: #33497b;
}
button.togglemath {
background-color: #33497b;
color: white;
}
<button id="add" type="button">+</button>
<button id="subtract" type="button">−</button>
<button id="multiple" type="button">×</button>
<button id="divide" type="button">÷</button>
Program starts with displaying all notes from localstore, but when I click the addButton it display my current note only.
I want to show all the notes and after click event new note will add with previous notes.
let addButton = document.querySelector(".addBtn");
let userNotes = [];
displayNotes();
//addButton Event Listner
addButton.addEventListener("click", function(e) {
e.preventDefault();
//get the user inputs
let userInputs = {
title: document.getElementById("title_input").value,
description: document.getElementById("des_input").value
};
//push user inputs to arrays
userNotes.push(userInputs);
document.querySelector("form").reset();
//store to the localstorage
localStorage.setItem("Notes", JSON.stringify(userNotes));
//display to the user
displayNotes();
});
function displayNotes() {
let gettingNotes = localStorage.getItem("Notes");
let allNotes = JSON.parse(gettingNotes);
let html = "";
allNotes.forEach(element => {
html += `
<div class="single-item">
<h2 class="single-item-title">
${element.title}
</h2>
<p class="single-item-description">
${element.description}
</p>
</div>
`;
});
document.querySelector(".item-list").innerHTML = html;
}
It happens because you set userNotes to be an empty array and you add only your current note. Try to initialize userNotes with a value from localStorage, i.e
let userNotes = JSON.parse(localStorage.getItem('Notes')) || []
Also, then you are able to use userNotes variable in displayNotes function, instead of allNotes.