Why will fetch only execute when it's not in a function? - javascript

I'm just learning Javascript/API call & I'm having trouble with fetch. When I call the fetch function from myFunction it doesn't do anything (that I can see), but if I take it out of myFunction it will display the error message that I expect.
I've tried to Google this but I haven't found anything that has helped me. I've added the console.log statements to make sure that the function is being called, and it is.
I've tried taking the fetch statement out of the function, and instead just putting it as a script in the html file. It works fine then. However I want to call it using the function name of a function that's in a javascript file & I'd like to understand why it's not working correctly right now. I think it has something to do with fetch not returning before the function exits, but I'm not sure.
javascript function:
function myFunction(){
fetch('https://sickw.com/api.php?format=html&key=SOME_KEY&imei=12908438754328705&service=999999')
.then(response => response.text())
.then(data => {
//document.write(data)
console.log(data)
});
//make sure the function was called
console.log("hiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii");
}
html code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Ghibli App</title>
<link href="https://fonts.googleapis.com/css?family=Dosis:400,700" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<div id="root"></div>
<form>
<select id="carrier">
<option value="carrier">Carrier</option>
<option value="icloud">iCloud</option>
</select>
<button onClick="myFunction('carrier');">Submit</button>"
</form>
<script src="scripts.js"></script>
</body>
</html>
I'm expecting to get a page to come up that says "Error S01: Service ID is wrong!" however the page isn't changing at all when I call the code from myFunction().

Why you don't see anything:
The problem isn't that you're calling fetch from within a function, but that you're calling that function within the onclick of a button in a form.
When you place a <button> element inside of a <form> element, its default behavior is to submit the form (posting the results to a different web page and loading that new page in the browser). Since your form doesn't have an action attribute, it will just reload the current page. This reloading of the current page is causing you to not see any results.
Fixing the first issue:
To prevent the form from returning, you need to call preventDefault() on the event passed to your function:
function myFunction(event){
event.preventDefault();
fetch('https://sickw.com/api.php?format=html&key=SOME_KEY&imei=12908438754328705&service=999999')
.then(response => response.text())
.then(data => {
//document.write(data)
console.log(data)
});
//make sure the function was called
console.log("hiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii");
}
The second issue
However because of the way you're calling this function (passing it to the onclick method of an HTML element) you don't have access to that event. To rectify this, you should use the addEventListener method like so:
document.getElementsByTagName("button")[0].addEventListener("click", myFunction);
If you give the button an ID, this can be replaced with:
document.getElementById("button-id").addEventListener(...)
The third issue
You're passing 'carrier' directly to your function, but my guess is that you want to read the value from the <select> element. To do this, you should use document.getElementById and .value to read the select:
var carrier = document.getElementById("carrier").value;
Once you've done that, you can utilize carrier in your fetch request
The fourth issue
As others have already mentioned in the comments, you should avoid calls to document.write. There are books written about why it's bad, but the proper answer is to use the DOM API to modify elements on the page. For instance:
var p = document.createElement("p");
p.innerHTML = data;
document.appendChild(p);
The final code
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Ghibli App</title>
<link href="https://fonts.googleapis.com/css?family=Dosis:400,700" rel="stylesheet" />
<link href="style.css" rel="stylesheet" />
</head>
<body>
<div id="root"></div>
<form>
<select id="carrier">
<option value="carrier">Carrier</option>
<option value="icloud">iCloud</option>
</select>
<button id="button-id">Submit</button>"
</form>
<script src="scripts.js"></script>
</body>
</html>
script.js
function myFunction(event){
event.preventDefault();
var carrier = document.getElementById("carrier").value;
fetch('https://sickw.com/api.php?format=html&key=SOME_KEY&imei=12908438754328705&service=999999&carrier=' + carrier)
.then(response => response.text())
.then(data => {
var p = document.createElement("p");
p.innerHTML = data;
document.appendChild(p);
});
}
document.getElementById("button-id").addEventListener("click", myFunction);

Related

How do I store data from form and display it on a new page using javascript?

I need to make a form with 2 input fields. When you press submit it should take you to a new page and display the input data.
This is my html code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Film survey</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<form id="form" action="./Success.html">
<h1>Movie survey</h1>
<p>Thank you for participating in the film festival!</p>
<p>Please fill out this short survey so we can record your feedback</p>
<div>
<label for="film">What film did you watch?</label>
</div>
<div>
<input type="text" id="film" name="film" required />
</div>
<div>
<label for="rating"
>How would you rate the film? (1 - very bad, 5 - very good)
</label>
</div>
<input type="text" id="rating" name="rating" required />
<div><button class="submit">Submit</button></div>
</form>
<script src="script.js"></script>
</body>
</html>
This is my js code
const formEl = document.querySelector("#form");
formEl.addEventListener("submit", (event) => {
event.preventDefault();
const formData = new FormData(formEl);
fetch("https://reqres.in/api/form", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
film: formData.get("film"),
rating: formData.get("rating"),
}),
})
.then((response) => {
window.location.href = "./Success.html";
})
.catch((error) => {
console.error(error);
});
});
I have a second html page called Success.html which I want to display tha data given in the form. I need to make a mock API so I tried using reqres.
This is my Success.html page
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Success</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1>Thank you for your feedback!</h1>
<p>You watched: <span id="film"></span></p>
<p>You rated it: <span id="rating"></span></p>
<script>
const formData = JSON.parse(request.body);
const filmEl = document.querySelector("#film");
const ratingEl = document.querySelector("#rating");
filmEl.textContent = formData.film;
ratingEl.textContent = formData.rating;
</script>
</body>
</html>
I thought this would work but I get this error after pressing submit:
`` Success.html:16 Uncaught ReferenceError: request is not defined
at Success.html:16:35
The line const formData = JSON.parse(request.body); in your success.html is trying to reference a variable named request, which isn't defined in the scope of the <script> tag that it's contained in - that's why you're getting the ReferenceError: request is not defined error.
This other Stackoverflow question seems similar to yours - I have linked you to an answer that I think would be helpful. In short, you could pull the values you care about out of your response, then pass them via query parameters to be displayed in your success.html.
Along those same lines, it might make sense for your mock API to respond with a 302 status code and location header including film and rating query parameters that points to your success page.
You can pass along data between web pages in several ways, using JSON data { foo: 'bar' } as an example:
1. Use URL parameter:
Stringify and URL encode your JSON response (full or needed data only), and pass along via a URL parameter
Example: 'data=' + encodeURIComponent(JSON.stringify(data)), resulting in data=%7B%20foo%3A%20'bar'%20%7D
Keep in mind that there is a limit to URL length, varying from 2k to 64k depending on browser
2. Use browser local storage:
The quota is browser specific, in range of 5-10MB, e.g. much larger than URL length
To set in the form page: localStorage.setItem('myFormData', JSON.stringify(data));
To read in success page: let data = JSON.parse(localStorage.getItem('myFormData'); (you likely want to add some error handling)
3. Temporarily POST data
You post the JSON data back to the server to /success
Your success page is delivered dynamically via a server side script that receives the data via a POST, and regurgitates it as JSON or in any other preferred format
Extra work, so not recommended
Having listed that, why not show the content in the form page itself? You can easily update the DOM using native JavaScript or jQuery.

previous HTML won't render with back button after history.pushState()

I have the below HTML which is served from GET /home and uses Javascript to hijack the form submit event in order to change the method from POST to DELETE. The server listening for requests to DELETE /delete which actually return a 303 redirect to GET /new which serves some HTML. The Javascript updates the address bar to example.com/new using the history API, and renders the HTML using using window.document.documentElement.innerHTML = text. This all works find, and when I click the back button the address bar DOES change back to the previous address (example.com/home) but the HTML is not rendered. Why is this? I've read the Mozilla docs but I can't find anything about not rendering HTML, surely if the back button updates the address bar, the page should be updated to that URL? I also tried using history.replaceState() but it didn't work.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<form action="/delete" method="POST">
<input type="submit" value="submit" />
<input name="key" value="val" />
</form>
</body>
<script>
const form = document.querySelector("form");
form.addEventListener("submit", async e => {
e.preventDefault();
console.log(new URLSearchParams(new FormData(e.currentTarget)));
const response = await fetch(form.action, {
body: new URLSearchParams(new FormData(e.currentTarget)),
method: "DELETE",
redirect: "follow",
});
const text = await response.text();
history.pushState({}, "", response.url);
// history.replaceState({}, "", response.url);
window.document.documentElement.innerHTML = text;
});
</script>
</html>
As per your code, you are replacing innerHTML, which includes the script part as well. Please move your script to head tag (if it is possible) or instead replacing innerHTML of the whole document, make a parent div and replace the content of that div.
So instead of this -
window.document.documentElement.innerHTML = text;
Do this -
documemt.getElementById('SomeDIV').innerHTML = text;

How to make a network call to an endpoint each time a page loads

I'm running website on the local server at (http://127.0.0.1:8080).
I have a file named track located at api/track where api is simply a folder.
I have a file name main.js located at js/main.js.
Finally, I have my index.html file located at the same level with /api and /js.
I would like to make a network call to this endpoint (api/track) each time a page index.html loads.
Also, I'd like to include a timestamp ts (in milliseconds since EPOCH). An Example of the url would be /api/track?ts=1594280202864. I don't need to use the results of this call.
Here is what I did, but I'm not sure it's doing what I want especially the network call part. Can somebody help, please? Thanks
const update = () => {
var dt = new Date();
document.getElementById("date").innerHTML = dt;
//date in millisecodn since EPOCH
var dtInMilSec = dt.getTime();
fetch('/api/track/dtMilSec')
.then(response => response.json())
.then(data => {
console.log(data)
});
};
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>The Date</title>
<link href="style/main.css" rel="stylesheet" />
</head>
<body onload="update()">
<h1>What's today's date?</h1>
<p>Today is: <span id="date"></span></p>
<script src="js/main.js"></script>
</body>
https://stackoverflow.com/a/25984032/14649968
Maybe take a look at this answer on a related thread, and call your update() function from within the event listener for DOMContentLoaded within the main.js script, rather calling it from the onload attr in the html itself?
document.addEventListener('DOMContentLoaded', () => update(), false);
Seems like you want your fetch URL to be something like
fetch('/api/track?ts=' + dtInMilSec)

Javascript ES6: setting <h1> with local storage and form submission

I am trying to set the element #welcome_banner by using a function in javascript that takes a form submission and sets it to localStorage and then pulls that info and sets it onto an <h1> tag. Apparently it works but only changes the tag for a millisecond and then it disappears! I have tried doing the .innerHTML setting in various places inside and outside of the function clickHandler() and in the main body of the script. I am certain this is something superbasic I am missing.
<!DOCTYPE html>
<html>
<head>
</head>
<script >
//set display name to form submission, set welcome banner to display name
function clickHandler () {
document.querySelector('#display_name').onsubmit = function() {
localStorage.setItem('dn', dn);
document.querySelector('#welcome_banner').innerHTML=changeWelcomeBanner;
}
};
function changeWelcomeBanner () {
var dn = localStorage.getItem('#dn').value;
var welcomeBanner = document.getElementById('#welcome_banner');
welcomBanner.innerHTML = `Hello ${dn}`;
}
</script>
<title>Project 2</title>
<body style="background-color:#ff3300;">
<h1 id="welcome_banner"></h1>
<form id="display_name">
<input id="dn" autocomplete="off" autofocus placeholder="" type="text">
<button>set display name</button>
</body>
</html>
The steps to do this are as follows:
Store the typed text in localStorage, you're doing this in the event handler for the form submission: localStorage.setItem('dn', dn);
When you submit the form, the page is going to refresh from the server. This is why you're only seeing the text briefly, then the page reloads from the server with no knowledge of what was there before.
Look for information about page events and write a handler for the DOMContentLoaded event like you did for your submit event handler. DOMContentLoaded is well supported these days. Something like: document.addEventListener("DOMContentLoaded", function(event) {
// code to check local storage goes here...
});
In that handler you want to check localStorage like you're doing here: var dn = localStorage.getItem('dn').value; and if there is a value there, set the innerHtml of the <h1> to that value, like you're doing here: welcomBanner.innerHTML = 'Hello ${dn}';
I think you might have had a stray # character in your localStorage.getItem call that I removed in the steps above. You might also want to have default text you post if there's nothing found in localStorage when you check.
Here's a simplified example:
If you are calling a function to get the value from Local Storage, be sure you have a return
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<h1 id="welcome"></h1>
<input type="text" id="something">
<button type="button" id="click">CLICK ME</button>
<script>
document.getElementById("click").addEventListener("click", function(){
var x=document.getElementById("something").value;
document.getElementById("welcome").innerHTML=x;
localStorage.setItem('x', JSON.stringify(x));
document.getElementById("welcome").innerHTML = getData();
});
function getData(){
var retrieve=localStorage.getItem('x');
return JSON.parse(retrieve); //Now return the value
}
</script>
</body>
</html>

How can I make a JavaScript function wait for input or value?

As you can see below, I have a function,createCharacter(), that calls another function, getUserInput(). This function is intended to grab the value of a text input element and return that value to be stored in the "name" variable within the createCharacter. However, if you run this code. It completely runs through both functions, never giving the opportunity for the user to input a value. Perhaps a more specific question is, how can I make this function wait for the variable to be defined before returning it to createCharacter? I've tried to wrap the code in a while loop that will run for as long as value is undefined. Didn't work, created an infinite loop and crashed. ANY solution to this problem will be greatly appreciated. I feel like the solution is so simple, but I just can't figure it out for the life of me. Thanks.
var messageDisplay = document.querySelector(".message-display");
var menuInput = document.querySelector(".menu-input");
var playerInput = document.querySelector(".player-text")
function createCharacter() {
messageDisplay.textContent = "Welcome! What is your name?";
var name = getUserInput();
messageDisplay.textContent = "Hello " + name + "!";
}
function getUserInput() {
var textValue = playerInput.value;
return textValue;
}
createCharacter();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div class="message-display"></div>
<div class="menu-input">
<form class="menu-input-content">
<input class="player-text" type="text">
<input class="submit-button" type="submit">
</form>
</div>
<script type="text/javascript" src="app.js"></script>
</body>
</html>
I think you have a misunderstanding of how the DOM and the user interact. The DOM is event based. You can start by add an change event listener to your input element (or on the submit button):
menuInput.onchange = createCharacter;
And then remove the call to createCharacter, the last line in the code you posted.
This will then call the createCharacter() method when you change the text in the input at all, which is probably not what you want. You could also try:
var menuSubmit = document.querySelector(".submit-button");
menuSubmit.onclick = createCharacter;
And that is probably more on the right track.
However, given your misunderstanding in the first place, perhaps you need to reconsider how you approach your design?
The reason it runs through the code immediately is because of the last line. The browser loads the JS and executes everything in the global scope. Your query selectors are run and stored in those variables, the functions defined, and then on the last line you call one of the defined functions.
To fix this you need to redesign your app to be event based. Keep defining needed variables and functions in the global scope, as you are doing here, but then change your execution to be in response to events.
I think you are looking for something like this. You should be using the events to get what you wanted. You are executing createCharacter() before even the user clicked the Submit button. Hence you see "Hello !" as there is no user input initially.
function submitClicked(event) {
var messageDisplay = document.querySelector(".message-display");
var playerInput = document.querySelector(".player-text");
messageDisplay.innerHTML = "Hello " + playerInput.value;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div class="message-display"></div>
<div class="menu-input">
<input class="player-text" type="text">
<input class="submit-button" onclick="submitClicked()" type="submit">
</div>
<script type="text/javascript" src="app.js"></script>
</body>
</html>

Categories