-
-
Save atelierbram/bb6b69eb99591a37ea0295cb6a928574 to your computer and use it in GitHub Desktop.
Code for youtube video on popstate, hashchange, and history.state
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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 popstate and hashchange Events</title> | |
<link rel="stylesheet" href="./main.css" /> | |
</head> | |
<body> | |
<header> | |
<h1><code>popstate </code>AND<code> hashchange </code>Events</h1> | |
</header> | |
<nav> | |
<a href="#a" data-name="James Holden" data-home="Earth">James Holden</a> | |
<a href="#b" data-name="Bobbie Draper" data-home="Mars">Bobbie Draper</a> | |
<a href="#c" data-name="Josephus Miller" data-home="Belt" | |
>Josephus Miller</a | |
> | |
</nav> | |
<main class="all"> | |
<div class="box earth">Earth</div> | |
<div class="box mars">Mars</div> | |
<div class="box belt">Belt</div> | |
<div class="box mars">Mars</div> | |
<div class="box earth">Earth</div> | |
<div class="box belt">Belt</div> | |
<div class="box mars">Mars</div> | |
<div class="box earth">Earth</div> | |
<div class="box mars">Mars</div> | |
<div class="box earth">Earth</div> | |
<div class="box mars">Mars</div> | |
<div class="box belt">Belt</div> | |
<div class="box earth">Earth</div> | |
<div class="box belt">Belt</div> | |
<div class="box mars">Mars</div> | |
</main> | |
<script src="main.js" defer></script> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
:root { | |
--base-orbit: 4; | |
} | |
* { | |
padding: 0; | |
margin: 0; | |
box-sizing: border-box; | |
} | |
html { | |
font-size: 24px; | |
font-weight: 300; | |
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, | |
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; | |
line-height: 1.5; | |
color: #eee; | |
background-color: #333; | |
} | |
body { | |
background-color: #333; | |
min-height: 100vh; | |
} | |
header, | |
main { | |
padding: 1rem 2rem; | |
} | |
nav { | |
background-color: orange; | |
color: #333; | |
display: flex; | |
flex-direction: row; | |
justify-content: space-around; | |
align-items: center; | |
} | |
nav a, | |
nav a:visited { | |
font-size: 1.2rem; | |
font-weight: 500; | |
line-height: 3.6rem; | |
letter-spacing: 3px; | |
padding: 1.2rem 0; | |
color: #444; | |
text-shadow: 1px 1px 2px white; | |
cursor: pointer; | |
text-decoration-line: overline underline; | |
position: relative; | |
z-index: 10; | |
} | |
nav a:hover, | |
nav a:active, | |
nav a:focus { | |
color: #000; | |
} | |
nav a:hover::before, | |
nav a:active::before, | |
nav a:focus::before { | |
animation-play-state: paused; | |
} | |
nav a::before { | |
content: attr(data-home); | |
text-decoration-style: none; | |
text-shadow: none; | |
position: absolute; | |
z-index: -1; | |
color: white; | |
border-radius: 50%; | |
width: 5ch; | |
height: 5ch; | |
text-align: center; | |
opacity: 0.6; | |
top: 2rem; | |
left: 0; | |
transform-origin: 5ch 0; | |
animation-name: orbit; | |
animation-duration: 2.4s; | |
animation-iteration-count: infinite; | |
animation-timing-function: linear; | |
box-shadow: 1px 1px 2px #333; | |
} | |
nav a[data-home='Earth']::before { | |
background-color: cornflowerblue; | |
color: white; | |
width: 7ch; | |
height: 7ch; | |
animation-duration: calc(365ms * var(--base-orbit)); | |
} | |
nav a[data-home='Mars']::before { | |
background-color: orangered; | |
color: white; | |
animation-duration: calc(687ms * var(--base-orbit)); | |
} | |
nav a[data-home='Belt']::before { | |
background-color: #888; | |
border-top-right-radius: 40% 60%; | |
border-top-left-radius: 30% 70%; | |
border-bottom-left-radius: 80% 80%; | |
border-bottom-right-radius: 70% 30%; | |
height: 4ch; | |
width: 5ch; | |
color: white; | |
animation-duration: calc(2000ms * var(--base-orbit)); | |
} | |
@keyframes orbit { | |
0% { | |
transform: rotate(0deg); | |
} | |
50% { | |
transform: rotate(180deg); | |
} | |
100% { | |
transform: rotate(359deg); | |
} | |
} | |
main { | |
display: flex; | |
flex-direction: row; | |
flex-wrap: wrap; | |
justify-content: center; | |
align-items: flex-start; | |
gap: 1rem; | |
} | |
.box { | |
width: 18vw; | |
height: 18vw; | |
padding: 1rem; | |
border-radius: 1rem; | |
opacity: 0; | |
} | |
.box.earth { | |
background-color: cornflowerblue; | |
color: cornsilk; | |
} | |
.box.mars { | |
background-color: orangered; | |
color: #222; | |
} | |
.box.belt { | |
background-color: #888; | |
color: #222; | |
} | |
main.earth .box.earth { | |
opacity: 1; | |
} | |
main.mars .box.mars { | |
opacity: 1; | |
} | |
main.belt .box.belt { | |
opacity: 1; | |
} | |
main.all .box.earth, | |
main.all .box.mars, | |
main.all .box.belt { | |
opacity: 1; | |
} | |
h1 { | |
color: orange; | |
} | |
h2 { | |
color: orangered; | |
} | |
p { | |
font-size: 1.2rem; | |
text-align: center; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const APP = { | |
init() { | |
//when the page loads | |
//check the state or hash value or both | |
APP.checkState(); //when the page loads | |
//add listeners for nav bar | |
//add listeners for popstate OR hashchange | |
APP.addListeners(); | |
//APP.updateLayout('earth'); | |
}, | |
addListeners() { | |
document.querySelector('nav').addEventListener('click', APP.nav); | |
window.addEventListener('popstate', APP.checkState); | |
//when the user clicks back or forward | |
//window.addEventListener('hashchange', APP.tempHC); | |
}, | |
// tempPop(ev) { | |
// console.log('popstate'); | |
// console.log(history.state); | |
// }, | |
// tempHC(ev) { | |
// console.log('hashchange'); | |
// console.log(history.state); | |
// }, | |
checkState() { | |
//do we want to drive our app by state or fragment-identifier(hash) or query? | |
//called when page loads AND after a popstate event | |
console.log(location); | |
console.log(history); | |
if (!location.hash) { | |
//default first load | |
history.replaceState( | |
{ home: 'Earth', name: 'James Holden' }, | |
'', | |
'#earth' | |
); | |
document.title = 'Earth'; | |
APP.updateLayout('earth'); | |
} else { | |
let hash = location.hash.replace('#', ''); | |
APP.updateLayout(hash); | |
document.title = hash; //first letter to uppercase needed | |
} | |
}, | |
nav(ev) { | |
ev.preventDefault(); | |
let anchor = ev.target; | |
let home = anchor.getAttribute('data-home'); | |
let name = anchor.getAttribute('data-name'); | |
let state = { | |
home, | |
name, | |
}; | |
let hash = `#${home.toLowerCase()}`; | |
history.pushState(state, '', hash); | |
document.title = home; | |
APP.updateLayout(home.toLowerCase()); | |
}, | |
updateLayout(place) { | |
//accept a className and update the interface based on that | |
let main = document.querySelector('main'); | |
main.className = place; | |
}, | |
}; | |
document.addEventListener('DOMContentLoaded', APP.init); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Practical Navigation with popstate and hashchange events by Steve Griffith