Kolejny ciekawy temat, który musimy przerobić, aby dobrze poznać frontend i JavaScript. Do dzieła.
Ok, będziemy analizować kod z poniższym markupem (przykład z githuba):
<body>
<nav>
<ul>
<li class="active">
<a href="#home">Home</a>
</li>
<li>
<a href="#about-me">About me</a>
</li>
<li>
<a href="#my-projects">My projects</a>
</li>
<li>
<a href="#testimonials">Testimonials</a>
</li>
<li>
<a href="#contact">Contact</a>
</li>
</ul>
</nav>
To jest część nav, teraz część druga:
<main>
<section id="home">
<h1>Home</h1>
<p>
Lorem (...)
</p>
</section>
<section id="about-me">
<h1>About Me</h1>
<p>
Lorem (...)
</p>
</section>
<section id="my-projects">
<h1>My projects</h1>
<p>
Lorem (...)
</p>
</section>
<section id="testimonials">
<h1>Testimonials</h1>
<p>
Lorem (...)
</p>
</section>
<section id="contact">
<h1>Contact</h1>
<p>
Lorem (...)
</p>
</section>
</main>
<script src="script.js"></script>
Mamy też jakieś CSSy, nas interesuje właściwość scroll-behavior:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
scroll-behavior: smooth;
}
Reszta to bardzo prosty flex-box, gdzie na lewo mamy navbar w starym stylu (guzik pod guzikiem), na prawo reszta strony:
body {
display: flex;
flex-wrap: wrap;
font-family: Verdana, Geneva, Tahoma, sans-serif;
}
body nav {
display: block;
width: 150px;
}
body nav ul {
position: fixed;
margin: 0;
padding: 0;
width: 150px;
height: 100vh;
list-style: none;
background: #17653a;
}
body nav ul li {
transition: 0.2s;
}
body nav ul li.active {
background: #f44336;
}
body nav ul li.active a {
color: white;
}
body nav ul li a {
display: inline-block;
width: 100%;
padding: 5px 10px;
color: white;
text-decoration: none;
}
body main {
width: 100%;
margin-left: 150px;
text-align: justify;
}
body main section {
margin: 50px;
line-height: 30px;
}
body main section h1 {
text-align: center;
margin-bottom: 10px;
}
Tu nie ma co rozkminiać, aby poznać te CSSy to inne przykłady robiliśmy, tutaj przeklejamy, bo chcemy nie tracąc czasu poznać coś innego.
Ok, script.js:
window.addEventListener("scroll", (event) => {
let menuItems = document.querySelectorAll("nav li");
let sections = document.querySelectorAll("main section");
sections.forEach( (section, i) => {
let top = section.getBoundingClientRect().top;
if (top < window.innerHeight) {
menuItems.forEach( i => i.classList.remove("active") );
menuItems[i].classList.add("active");
}
} );
} );
O co chodzi – wyjaśniam:
- ilość sekcji i ilość przycisków jest jednakowa
- scroll event, odpalany, gdy currentTarget jest scrollowany (jest nim okno)
- zakładam, że nie propaguje i nie bąbelkuje, ale mniejsza o to, mam tylko nadzieję, że eventy jeszcze pamiętamy
- chcemy sprawdzać scroll sekcji, zaś klasy dodawać i odejmować nav-itemom
- getboundingclientrect znamy, top to odległość od góry (można zamiast top dać y)
- dalej sprawdzamy, czy element został przescrollowany i nadajemy/odejmujemy odpowiednie klasy
Dodam, że my się tak bawić nie będziemy, bo od takich rzeczy są intersection observery. Ale w ramach nauki i te metody musieliśmy poznać.
Dobra, teraz event scrollend, sam go sobie dopisałem:
window.addEventListener("scrollend", (event) => {
alert("scrollend" + window.scrollY)
});
Swoją drogą limitowana dostępność + słabo na razie działa. Odpala się po dojechaniu do końca jak i dojechaniu do początku. Można to poprawić:
window.addEventListener("scrollend", (event) => {
if(window.scrollY < 100)
return;
alert("scrollend" + window.scrollY)
});
Teraz działa jako-tako poprawnie…
Ok, wykonajmy sobie jakiś scroll do góry, po potwierdzeniu:
window.addEventListener("scrollend", (event) => {
if(window.scrollY < 100)
return;
let scrollToTop = confirm("Scroll to top?");
if(scrollToTop)
window.scroll(window.scrollX, 0);
});
Działa, choć zepsuliśmy event scroll, teraz active jest zablokowany na ostatnim elemencie, JSowe przeskrolowanie się nie zarejestrowało jako event…
To może tak:
window.addEventListener("scrollend", (event) => {
if(window.scrollY < 100)
return;
let scrollToTop = confirm("Scroll to top?");
if(scrollToTop)
document.querySelector("main").scrollIntoView({behavior: 'smooth'});
});
Też nie bardzo, cóż, musimy poznać intersection observera, ale dobrze, abyśmy te bardziej klasyczne metody pracy z JS też poznali…