diff --git a/projects/landing-page/README.md b/projects/landing-page/README.md index 3282de7b9..e4bd7c46d 100755 --- a/projects/landing-page/README.md +++ b/projects/landing-page/README.md @@ -2,12 +2,25 @@ ## Table of Contents -* [Instructions](#instructions) +- [Landing Page Project](#landing-page-project) + - [Table of Contents](#table-of-contents) + - [Instructions](#instructions) + - [Usage](#usage) + - [Description](#description) + ## Instructions -The starter project has some HTML and CSS styling to display a static version of the Landing Page project. You'll need to convert this project from a static project to an interactive one. This will require modifying the HTML and CSS files, but primarily the JavaScript file. +Add some sections in the html `index.html` file and assign each of them an `id` and a `data-nav` attribute +then you can start the app by opening `index.html` in your preferable browser. +Hit: browser should support javascript and be relatively modern, examples `chrome` or `Edge` + +## Usage + +This Project is to be used as a landing page where the user adds sections and the app will calculate +how many are added and build a navbar based on the sections count -To get started, open `js/app.js` and start building out the app's functionality +## Description -For specific, detailed instructions, look at the project instructions in the Udacity Classroom. +A cool landing page with dynamic navbar based on sections count which could be extended by adding more +cool features and maybe new pages and link them to some kind of dropdowns or links \ No newline at end of file diff --git a/projects/landing-page/css/styles.css b/projects/landing-page/css/styles.css index 16145e98c..58c77f386 100755 --- a/projects/landing-page/css/styles.css +++ b/projects/landing-page/css/styles.css @@ -67,7 +67,7 @@ section { .navbar__menu ul { padding-left: 0; margin: 0; - text-align: right; + text-align: left; } .navbar__menu li { @@ -75,11 +75,11 @@ section { } .navbar__menu .menu__link { - display: block; padding: 1em; font-weight: bold; text-decoration: none; color: #000; + cursor: pointer; } .navbar__menu .menu__link:hover { @@ -88,6 +88,29 @@ section { transition: ease 0.3s all; } +.active { + background: #333; + color: #fff; + transition: ease 0.3s all; +} + +.active-default { + background: #333; + color: #fff; + transition: ease 0.3s all; +} + +.scroll-top { + width: 80px; + height: 40px; + border: none; + cursor: pointer; + display: none; + position: fixed; + right: 50px; + bottom: 50px; +} + /* Header Styles */ .page__header { background: #fff; diff --git a/projects/landing-page/index.html b/projects/landing-page/index.html index baadb068a..260a38cee 100755 --- a/projects/landing-page/index.html +++ b/projects/landing-page/index.html @@ -18,7 +18,7 @@ -
+

Landing Page

@@ -47,6 +47,14 @@

Section 2

Section 3

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi fermentum metus faucibus lectus pharetra dapibus. Suspendisse potenti. Aenean aliquam elementum mi, ac euismod augue. Donec eget lacinia ex. Phasellus imperdiet porta orci eget mollis. Sed convallis sollicitudin mauris ac tincidunt. Donec bibendum, nulla eget bibendum consectetur, sem nisi aliquam leo, ut pulvinar quam nunc eu augue. Pellentesque maximus imperdiet elit a pharetra. Duis lectus mi, aliquam in mi quis, aliquam porttitor lacus. Morbi a tincidunt felis. Sed leo nunc, pharetra et elementum non, faucibus vitae elit. Integer nec libero venenatis libero ultricies molestie semper in tellus. Sed congue et odio sed euismod.

+

Aliquam a convallis justo. Vivamus venenatis, erat eget pulvinar gravida, ipsum lacus aliquet velit, vel luctus diam ipsum a diam. Cras eu tincidunt arcu, vitae rhoncus purus. Vestibulum fermentum consectetur porttitor. Suspendisse imperdiet porttitor tortor, eget elementum tortor mollis non.

+ + +
+
+

Section 4

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi fermentum metus faucibus lectus pharetra dapibus. Suspendisse potenti. Aenean aliquam elementum mi, ac euismod augue. Donec eget lacinia ex. Phasellus imperdiet porta orci eget mollis. Sed convallis sollicitudin mauris ac tincidunt. Donec bibendum, nulla eget bibendum consectetur, sem nisi aliquam leo, ut pulvinar quam nunc eu augue. Pellentesque maximus imperdiet elit a pharetra. Duis lectus mi, aliquam in mi quis, aliquam porttitor lacus. Morbi a tincidunt felis. Sed leo nunc, pharetra et elementum non, faucibus vitae elit. Integer nec libero venenatis libero ultricies molestie semper in tellus. Sed congue et odio sed euismod.

+

Aliquam a convallis justo. Vivamus venenatis, erat eget pulvinar gravida, ipsum lacus aliquet velit, vel luctus diam ipsum a diam. Cras eu tincidunt arcu, vitae rhoncus purus. Vestibulum fermentum consectetur porttitor. Suspendisse imperdiet porttitor tortor, eget elementum tortor mollis non.

@@ -54,6 +62,6 @@

Section 3

© Udacity

- + diff --git a/projects/landing-page/js/app.js b/projects/landing-page/js/app.js index f52ee8672..eb12c9a4b 100755 --- a/projects/landing-page/js/app.js +++ b/projects/landing-page/js/app.js @@ -22,15 +22,125 @@ * Define Global Variables * */ - +const sectionsInThePage = [...document.querySelectorAll('[data-nav]')]; +const sectionsTitles = sectionsInThePage.map((section) => section.dataset.nav); +const navBarContainer = document.querySelector('#navbar__list'); /** * End Global Variables * Start Helper Functions * */ - - +const navBarLinksCreator = (linkTitlesArray=[]) => { + /** map through the existing sections and create li for each section */ + const linksToBeAppended = linkTitlesArray.map((link, index) => { + // get the section number to use it as a part of an id + const sectionNumber = link.split(' ')[1]; + // create li + const listItem = document.createElement('li'); + // set the innerHTML of the li to the relevant section title + listItem.innerHTML = link; + // if first li set an extra class to highlight it by default + index == 0 ? + listItem.setAttribute('class','menu__link active-default') : + listItem.setAttribute('class','menu__link'); + listItem.setAttribute('id',`link${sectionNumber}`); + // add event listener to handle the click events + listItem.addEventListener('click', () => scrollToHandler(`section${sectionNumber}`)); + // return the new created li element + return listItem; + }); + + // call `navBarLinksAppendHandler` to append the li elements list + navBarLinksAppendHandler(linksToBeAppended); +} + +const scrollTopBtnCreator = () => { + // create a btn and append it to the main + const btn = document.createElement('button'); + btn.innerHTML = 'Scroll Top'; + btn.setAttribute('id','scroll-top'); + btn.setAttribute('class','scroll-top'); + btn.addEventListener('click', () => scrollTopHandler()); + document.querySelector('#main').appendChild(btn); +} + +const showScrollTopBtn = () => { + // if the user scroll more than 500px y axis then show the scroll-top btn + const btn = document.querySelector('#scroll-top'); + window.scrollY > 500 ? btn.style.display = 'block' : btn.style.display = 'none'; +} + +const scrollToHandler = (elementID) => { + // handles the click events of the nav bar li elements and scroll to the correct relevant position + const element = document.querySelector(`#${elementID}`); + const positionToScrollTo = element.getBoundingClientRect().top + window.scrollY; + window.scroll({ + top: positionToScrollTo, + // scroll smoothly + behavior: 'smooth' + }); +} + +const scrollTopHandler = () => { + // scroll to top when scroll-top btn is clicked + window.scroll({ + top: 0, + behavior: 'smooth' + }); +} + +const navBarLinksAppendHandler = (linkElementsArray=[]) => { + // append the li elements list inside `navBarContainer` + navBarContainer.append(...linkElementsArray); +} + +const isInViewport = (elements) => { + // loop throw all existing sections to see which is in viewport + const elementsVisibleState = elements.map((el) => { + // convert section height to number + const elementHeight = parseInt(window.getComputedStyle(el).height); + /** calculate the visibility of the section based on its top and bottom position relative to viewport*/ + const elementVisibleState = el.getBoundingClientRect().top < 400 && + el.getBoundingClientRect().top > - (elementHeight - 400) && + el.getBoundingClientRect().bottom <= elementHeight + 400; + + /** get the relevant nav bar link */ + const relevantLink = document.querySelector(`#link${el.id.slice(-1)}`); + /** add class when in viewport and remove it when out of it */ + elementVisibleState ? + ( + // if the section is in viewport set to active + el.classList.add('your-active-class'), + relevantLink.classList.add('active') + ) : + ( + // if it is not in viewport remove the extra classes + el.classList.remove('your-active-class'), + setTimeout(() => relevantLink.classList.remove('active'),0) + ); + + return elementVisibleState; + }); + + // if no section is in viewport set the first to be active by default + !elementsVisibleState.some((state) => state) ? + (elements[0].classList.add('your-active-class'), document.querySelector(`#link${elements[0].id.slice(-1)}`).classList.add('active-default')) : + document.querySelector(`#link${elements[0].id.slice(-1)}`).classList.remove('active-default'); +} + +document.addEventListener('scroll', () => { + // listen on scroll to see which section is in viewport + isInViewport(sectionsInThePage); + // listen if the user scrolls on y axis to display scroll-top btn + showScrollTopBtn(); +}, {passive: true}); + +document.addEventListener('DOMContentLoaded', () => { + // activate and call the creators functions on document ready + navBarLinksCreator(sectionsTitles); + scrollTopBtnCreator(); +}); /** * End Helper Functions