Untitled_.Jun.2.2022.1_06.AM.mp4
"Drones ply the liminal space between the physical and the digital -- pilots fly them, but aren't in them. They are versatile and fascinating objects -- the things they can do range from the mundane (aerial photography) to the spectacular
-- John Batelle
This is an example for a Product Landing Page for the QUADRAStalkr, an imaginary drone from an imaginary company (that I have all the rights to ๐ ๐ ๐ ). Includes use of video media, navigation elements, smart use of CSS animation to create an interactive design, and a form element that submits to a static page.
To use the app simply click on the View Project
button or visit https://trrapp12.github.io/QUADRA-STALKR/.
-
A ๐ฅ๐ฅ๐ฅlittle animation to intro the page.
-
Javascript closures.
-
Creating a throttler for a scroll event.
-
User Story #1: My product landing page should have a header element with a corresponding id="header".
-
User Story #2: I can see an image within the header element with a corresponding id="header-img". A company logo would make a good image here.
-
User Story #3: Within the #header element I can see a nav element with a corresponding id="nav-bar".
-
User Story #4: I can see at least three clickable elements inside the nav element, each with the class nav-link.
-
User Story #5: When I click a .nav-link button in the nav element, I am taken to the corresponding section of the landing page.
-
User Story #6: I can watch an embedded product video with id="video".
-
User Story #7: My landing page has a form element with a corresponding id="form".
-
User Story #8: Within the form, there is an input field with id="email" where I can enter an email address.
-
User Story #9: The #email input field should have placeholder text to let the user know what the field is for.
-
User Story #10: The #email input field uses HTML5 validation to confirm that the entered text is an email address.
-
User Story #11: Within the form, there is a submit input with a corresponding id="submit".
-
User Story #12: When I click the #submit element, the email is submitted to a static page (use this mock URL: https://www.freecodecamp.com/email-submit).
-
User Story #13: The navbar should always be at the top of the viewport.
-
User Story #14: My product landing page should have at least one media query.
-
User Story #15: My product landing page should utilize CSS flexbox at least once.
So while I was using this I needed to create an eventlistener that would change the size and color of the text once the navbar got a certain distance down. This created a number of interesting issues.
- How do you prevent an expensive DOM traversal every time the smallest pixel is scrolled?
- to fix this I had to come up with code that would throttle the the event calls to every 200ms and I had to use a closure so I could access the scope of a parent function to do it. See below:
(() => {
// set constants
const elementHeight = `${document.body.clientHeight}px`;
const scrollableElement = window;
const elementToChange = document.querySelectorAll('.nav-link-2')
const elementColor = 'var(--prussian-blue)';
const logo = document.getElementById('header-img');
// check to see if object is window object
function isElementWindow(element) {
console.log(`isElementWindow() fired. Element is ${element}, logical comparison returns ${element === window}`)
return element === window;
}
// define scroll event handler
function scrollHandler(height, scrollableEl, element, color) {
// when the top of the window is equal to height of the header, turn header background opaque
for (const item in element) {
if (isElementWindow(scrollableEl)) {
// future improvement would take the window height/ window width to get an aspect ratio and then times that by the height to get a more dynamic response, instead of guesstimating 10% scroll down
if (scrollableEl.scrollY >= (parseInt(height) - (parseInt(height) * .9))) {
element[item].style.backgroundColor = `${color}`;
element[item].style.letterSpacing = "2px";
element[item].style.borderRadius = "0px";
logo.style.display = "none"
}
// had to create a cumbersome else if instead of just a else statement because the logo would come back after CSS had removed it if you scrolled back up to the top
else if (window.innerWidth >= 950 && scrollableEl.scrollY <= (parseInt(height) - (parseInt(height) * .9))) {
console.log('entering logo resize else if')
element[item].style.backgroundColor = 'transparent';
element[item].style.letterSpacing = "";
element[item].style.borderRadius = "8px";
logo.style.display = "block"
}
}
}
}
// create throttle handler
function throttleHandler(func, delay) {
let lastCall = 0;
return function(...args) {
let currentTime = Date.now();
if (lastCall - currentTime >= delay) {
func(...args);
lastCall = currentTime;
}
}
}
// call scroll eventhandler in throttle handler
window.addEventListener('scroll', () => {
// tableData is only for use of console.table
let tableData = [
{
name: "elementHeight",
value: elementHeight
},
{
name: "scrollableElement",
value: scrollableElement
},
{
name: "elementToChange",
value: elementToChange
},
{
name: "elementColor",
value: elementColor
}
];
console.table(tableData)
// attach handler to window.scroll event
throttleHandler(scrollHandler(elementHeight, scrollableElement, elementToChange, elementColor))
})
})();
- I decided I wanted to create an animation
(() => {
window.addEventListener('load', () => {
console.log('window loaded');
const lineObject = document.getElementsByClassName('line');
const secondLineObject = document.getElementsByClassName('straight');
const displayWindow = document.getElementById('count');
for (const [key, value] of Object.entries(lineObject)) {
value.classList.add('spin-animation')
}
for (const [key, value] of Object.entries(secondLineObject)) {
value.classList.add('spin-animation')
}
function counter() {
count = 10;
setInterval(depricateTime, "1000")
function depricateTime() {
if (count >= 0) {
displayTime(count);
count--;
} else {
clearInterval(depricateTime);
closeWindow();
}
}
function displayTime(count) {
displayWindow.innerHTML = count;
}
function closeWindow() {
document.getElementById('counter-container').style.display = "none"
}
}
counter();
})
})()
- 100% of the work on this project was my own. The original requirements of the project, as listed in the user stories, were provided for direction as part of the Free Code Camp course.
Alien icons created by Freepik - Flaticon
Photos and other asset credits:
- Callum Hilton
- Dids
- Dominika Roseclay
- Mudassir Ali
- Petar Avramoski