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

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;

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.

How to show javascript console output into an alert()

Everyday my teacher looks across the internet to find random facts for us then she writes on the board and we get to see it each day we go in her class. I'm trying to make a super simple website that once a button is clicked an alert at the top of the page appears. Whenever the fact I cant figure out how to make the console go to the alert. I've looked at a different other Stack question but i didn't really understand it. Here is my complete code. I'm sorry i just started javascript and literally couldn't be more new to it. Thanks in advance!
const express = require("express");
const app = express();
app.use(express.static("public"));
app.get("/", (request, response) => {
response.sendFile(__dirname + "/facts.html");
});
const listener = app.listen(process.env.PORT, () => {
console.log("Your app is listening on port " + listener.address().port);
});
var fact = require('random-fact');
fact();
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Generate Random Facts</title>
</head>
<body>
<h1>Fact Generator</h1>
<br><br><br><br>
<script>
function test() {
alert('fact');
}
</script>
<input type="button" onclick="test()" value="Generate Random Fact!">
</body>
</html>
I'm sorry if I'm not understanding your code properly but from my understanding the first code block, the NodeJS is sending a file on the root url. That root file is what you want to alert this "random fact"
However, the random fact data is stored in var fact = require("random-fact")
NodeJS is a JavaScript runtime environment. Meaning a place to execute JavaScript code, OUTSIDE the browser and in the server. NodeJS isn't like vanilla JS where you have access to the BOM and DOM.
However, with express you can set up what's called an endpoint. That endpoint will send the data on each request to each route which you can set up on the front end.
First off, I'm assuming the fact() function returns some data. We'll set up an endpoint:
app.get('/random-facts', (req, res) => res.send(fact()));
whenever we send a get request to /random-facts it'll execute the function and hopefully get what you want
On the frontend we need to do what's called an AJAX request. Where you send a request to the API. Like you do in your browser when you type www.google.com you send a get request. AJAX request can get more sophisticated with just GET requests but that's for something else.
We'll do that with JS using the fetch API
On your html you can do
<script>
fetch('http://localhost:3000/random-facts')
.then(response => response.json())
.then(data => alert(data));
</script>
This is probably a bit more advanced than what you know but I hope this helps
Like someone else said, the JavaScript you have up there is server side NodeJS.
Instead, just use your HTML and statically host it on GitHub or something.
Here is an example of a random fact from a list of facts:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Generate Random Facts</title>
</head>
<body>
<h1>Fact Generator</h1>
<p id="fact"> </p>
<script>
// populate with multiple facts
var facts = ["fact one", "fact two", "fact three", "fact four"];
function alertFact() {
const fact = returnFact()
document.getElementById('fact').innerText = fact
alert(fact);
}
function returnFact() {
var fact = facts[Math.floor(Math.random() * facts.length)];
return fact;
}
</script>
<input type="button" onclick="alertFact()" value="Generate Random Fact!" />
</body>
</html>
or you can view it here: https://jsfiddle.net/utdoLbj2/

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

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);

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>

Refresh iFrame (Cache Issue)

We are getting a weird issue on which we are not sure what exactly cause it. Let me elaborate the issue. Suppose, we have two different html pages a.html and b.html. And a little script written in index.html:
<html>
<head>
<script>
function reloadFrame(iframe, src) {
iframe.src = src;
}
</script>
</head>
<body>
<form>
<iframe id="myFrame"></iframe>
<input type="button" value="Load a.html" onclick="reloadFrame(document.getElementById('myFrame'), 'a.html')">
<input type="button" value="Load b.html" onclick="reloadFrame(document.getElementById('myFrame'), 'b.html')">
</form>
</body>
</html>
A server component is continuously updating both files a.html and b.html. The problem is the content of both files are successfully updating on the server side. If we open we can see the updated changes but client getting the older content which doesn't show the updated changes.
Any idea?
Add this in a.html and b.html
<head>
<meta http-Equiv="Cache-Control" Content="no-cache" />
<meta http-Equiv="Pragma" Content="no-cache" />
<meta http-Equiv="Expires" Content="0" />
</head>
To force no cache checks
If you can add server-side instructions to those HTML files, you could send the appropriate headers to prevent caching:
Making sure a web page is not cached, across all browsers (I think the consensus is that the 2nd answer is best, not the accepted one)
Simone's answer already deals with Meta tags.
A cheap quick trick is to add a random number as a GET parameter:
page_1.html?time=102398405820
if this changes on every request (e.g. using the current time), reloading wil get forced every time, too.
Try something like the following:
<script>
var frameElement = document.getElementById("frame-id");
frameElement.contentWindow.location.href = frameElement.src;
</script>
This will force the iframe to be reloaded even if it was cached by the browser
I want to put Vishwas comment as a separate answer, extending
Pekka’s answer
//ensure iframe is not cached
function reloadIframe(iframeId) {
var iframe = document.getElementById(iframeId);
var d = new Date();
if (iframe) {
iframe.src = iframe.src + '?ver=' + d.getTime();
//alternatively frameElement.contentWindow.location.href = frameElement.src; //This will force the iframe to be reloaded even if it was cached by the browser
}
}
reloadIframe('session_storage_check');
Homero Barbosa's Solution worked like a charm. In my case, I had a varying number of iframes on the page, so I did the following:
$('.some_selector').each(function () {
var $randid = Math.floor(Math.random() * 101);
$(this).attr({'id': 'goinOnaSafari-' + $randid});
var $frame = document.getElementById('goinOnaSafari-' + $randid);
$frame.contentWindow.location.href = $frame.src;
});
I could not get the HTML to work.
<head>
<meta http-Equiv="Cache-Control" Content="no-cache" />
<meta http-Equiv="Pragma" Content="no-cache" />
<meta http-Equiv="Expires" Content="0" />
</head>
For development in chrome I checked the console Network tab and found where the iframe is loaded.
I confirmed that it was loaded with a 304 response wich means it loads from cache.
Right click -> clear browser cache.
Will not work in production, but at least helps with development.
For one possible solution to this, pass a "cache parameter" to your calls to a.html and b.html. For example
HTML
<input type="button" value="Load a.html" onclick="cacheSafeReload('a.html');">
Javascript
function cacheSafeReload(urlBase) {
var cacheParamValue = (new Date()).getTime();
var url = urlBase + "?cache=" + cacheParamValue;
reloadFrame(document.getElementById('myFrame'), url);
}

Categories