Skip to content

Instantly share code, notes, and snippets.

@prof3ssorSt3v3
Last active July 2, 2024 11:44
Show Gist options
  • Save prof3ssorSt3v3/f7c23df64a9cb9285c5d6b20e860cf01 to your computer and use it in GitHub Desktop.
Save prof3ssorSt3v3/f7c23df64a9cb9285c5d6b20e860cf01 to your computer and use it in GitHub Desktop.
Using History.state and popstate to manage calls.
const APP = {
init: () => {
console.log('init');
document.querySelector('footer').innerHTML = `<p>popstate with ${JSON.stringify(history.state)}</p>`;
if (!history.state) {
console.log('clear url');
history.replaceState(null, null, './index.html'); //default value
} else {
APP.readState();
}
//moved from one history entry to another
window.addEventListener('popstate', APP.pop);
document.querySelector('form').addEventListener('submit', APP.submit);
},
pop: (ev) => {
//popstate event
console.log('popstate', history.state);
document.querySelector('footer').innerHTML = `<p>popstate with ${JSON.stringify(history.state)}</p>`;
APP.readState();
},
readState: () => {
console.log('read state and call search');
let state = history.state;
if (state && 'key' in state) {
console.log(state, 'had state');
//Eg: {key:keyword, type:'movie', something: 'else'}
document.getElementById('key').value = state.key; //the input element
APP.processSearch();
} else {
//no state value for key
console.log('no state or no key');
//clear the form
document.getElementById('key').value = '';
//clear any old results
document.querySelector('main h2 span').textContent = '---';
document.querySelector('main p').textContent = 'No search keyword.';
// At this point we could choose to read values from a querystring or a hash value.
// Adding this support we could now have external links to URLs
}
},
readURL: () => {
//get the location.search and the location.hash and search based on values there...
},
submit: (ev) => {
//form submitted
//stop the page from loading
ev.preventDefault();
//run the fake search
APP.processSearch();
},
processSearch: (key) => {
let keyword = document.getElementById('key').value;
console.log('processSearch', keyword);
if (keyword) {
document.querySelector('main').classList.add('active');
APP.doSearch(keyword)
.then((results) => {
//update URL
console.log(`Search results ${results}.`);
let state = { key: keyword };
if (history.state && history.state.key && history.state.key === keyword) {
console.log('state is same so call replaceState...');
history.replaceState(state, null, `./index.html`);
} else {
console.log(`Call pushState with the new state.`);
history.pushState(state, null, `./index.html`);
}
return keyword;
})
.then((keyword) => {
document.querySelector('main span').textContent = keyword.toUpperCase();
document.querySelector('main p').textContent = `Found ${keyword}`;
document.querySelector('main').classList.remove('active');
})
.catch((err) => {
console.warn(err.message);
document.querySelector('main span').textContent = keyword.toUpperCase();
document.querySelector('main p').textContent = `${keyword} NOT Found`;
document.querySelector('main').classList.remove('active');
let state = { type: 'movie', key: keyword };
history.replaceState(state, null, `./index.html`);
});
} else {
console.log('Keyword empty, so no search');
document.querySelector('main span').textContent = '---';
document.querySelector('main p').textContent = `Nothing to search`;
document.querySelector('main').classList.remove('active');
}
},
doSearch: (keyword) => {
return new Promise((resolve, reject) => {
console.log('fake api searching....');
setTimeout(resolve, 1400, keyword);
});
},
};
document.addEventListener('DOMContentLoaded', APP.init);
<!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>History Nav using State</title>
<script src="./app.js" defer></script>
<link rel="stylesheet" href="./main.css" />
<!-- https://gist.github.com/prof3ssorSt3v3/f7c23df64a9cb9285c5d6b20e860cf01 -->
</head>
<body>
<header>
<h1>
Using
<code>`history.state`</code>
Instead of
<code>`location.hash`</code>
</h1>
</header>
<form action="#">
<label for="key">Keyword</label>
<input type="text" id="key" name="key" />
<button id="btnSearch">Search</button>
</form>
<main>
<h2>Title about <span>keyword</span></h2>
<p>No search keyword.</p>
<div class="loader">Loading...</div>
</main>
<footer></footer>
</body>
</html>
* {
box-sizing: border-box;
padding: 0;
margin: 0;
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans',
'Helvetica Neue', sans-serif;
}
html {
font-size: 20px;
font-weight: 100;
color-scheme: dark light;
}
header {
padding: 1rem 3rem;
}
main,
footer {
padding: 1rem 3rem;
}
code {
display: inline;
font-family: monospace;
color: cadetblue;
}
label {
margin: 0 1rem 0 3rem;
font-size: 1rem;
padding: 0.25 1rem;
}
input {
margin: 0 1rem;
font-size: 1rem;
padding: 0.25rem 1rem;
}
button {
margin: 0 1rem;
font-size: 1rem;
padding: 0.25rem 1rem;
border: none;
cursor: pointer;
}
button:hover {
background-color: coral;
}
.loader {
display: none;
font-size: 4rem;
color: coral;
}
main.active .loader {
display: block;
animation-name: pulse;
animation-duration: 0.8s;
animation-direction: alternate;
animation-iteration-count: infinite;
}
h2 {
margin: 1rem 0;
font-size: 2rem;
}
main h2 span {
color: coral;
}
p {
margin: 1rem 0;
color: coral;
font-size: 1.5rem;
}
main.active h2 {
opacity: 0.5;
}
main.active p {
opacity: 0.5;
}
@keyframes pulse {
0% {
opacity: 1;
}
100% {
opacity: 0.1;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment