I have a site and I want each section to take up the screen and as you scroll down the page the sections fade out as the next section comes into view. I'm struggling on how this math needs to work.
For one section it's easy which is basically the height of the element and it's distance from the top to get the opacity percentage. However, I just can't quite figure out the math for other sections. I need section 2 to start fading in as section 1 is fading out.
I do not want to scrolljack. I just want a scrolling effect. Rather than override default scrolling behavior I just want to animate in/out as you scroll where a certain Y scroll value = a certain CSS value rather than a sort of "breakpoint" where it animates to a new "slide".
For the proper effect, for example, my math divides the height of the row by 2 so that it's 50% faded out as the next section should be fading in 50%. But, as of now, the math just doesn't work for the other sections.
This site is using vanilla JS as it's supposed to be a simple one pager.
Example below:
(function () {
window.addEventListener('scroll', () => {
const rows = document.querySelectorAll('.row');
rows.forEach(function (row, index) {
const distanceToTop = window.pageYOffset;
const elementHeight = row.offsetHeight * (index + 1) / 2;
row.style.opacity = ((elementHeight - distanceToTop) / elementHeight * 100) / 100;
});
});
})();
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0 0 0 0;
font-family: sans-serif;
font-family: 'Manrope', helvetiva, sans-serif;
transition: background-color 1s;
color:#fff;
background: linear-gradient(0deg, #100521 0%, #50357c 50%, #878290 100%);
}
header {
position: fixed;
top: 0;
width: 100%;
background: #fff;
z-index: 999;
}
header h1 {
font-family: 'Lack';
font-size: 4em;
text-align: center;
padding: 10px;
color: #100521;
}
.row-container .row:first-child {
opacity: 1;
}
.row {
opacity: 0;
height: 100vh;
display: flex;
flex-direction:column;
}
.row-inner {
display: grid;
grid-template-columns: 1fr 1fr;
position: sticky;
top: 0;
/* border: 1px solid yellow; */
padding: 0;
height: 100vh;
}
.column {
display: flex;
flex-direction: column;
justify-content: center;
align-content: center;
flex: 50%;
padding: 10px;
}
.left-column {
background-color: transparent;
}
.right-column {
background-color: transparent;
}
h1 {
font-family: 'Unbounded', helvetica, sans-serif;
}
h1 {
margin: 0;
font-size: 36px;
}
h2 {
margin: 0;
font-size: 24px;
}
p {
margin: 10px 0;
font-size: 18px;
line-height: 1.5;
}
img {
width: 100%;
height: auto;
}
<!DOCTYPE html>
<html>
<head>
<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Manrope&family=Unbounded&family=Lack&display=swap" rel="stylesheet">
</head>
<body>
<header>
<h1>My App</h1>
</header>
<!-- First row -->
<div class="row-container">
<div class="row section-1">
<div class="row-inner">
<div class="column left-column">
<div class="column-inner">
<h1>Headline 1</h1>
<h2>Subhead 1</h2>
<p>Lorem Ipsum dolar Gamet</p>
</div>
</div>
<div class="column right-column">
<div class="column-inner">
<img src="https://avatars.dicebear.com/api/adventurer/team-member-1.jpg" alt="Image 1">
</div>
</div>
</div>
</div>
<!-- Second row -->
<div class="row section-2">
<div class="row-inner">
<div class="column left-column">
<div class="column-inner">
<h1>Headline 2</h1>
<h2>Subhead 2</h2>
<p>Lorem Ipsum dolar Gamet</p>
</div>
</div>
<div class="column right-column">
<div class="column-inner">
<img src="https://avatars.dicebear.com/api/adventurer/team-member-2.jpg" alt="Image 2">
</div>
</div>
</div>
</div>
<!-- Third row -->
<div class="row section-3">
<div class="row-inner">
<div class="column left-column">
<div class="column-inner">
<h1>Headline 3</h1>
<h2>Subhead 3</h2>
<p>Lorem Ipsum dolar Gamet</p>
</div>
</div>
<div class="column right-column">
<div class="column-inner">
<img src="https://avatars.dicebear.com/api/adventurer/team-member-3.jpg" alt="Image 3">
</div>
</div>
</div>
</div>
<!-- Fourth row -->
<div class="row section-4">
<div class="row-inner">
<div class="column left-column">
<div class="column-inner">
<h1>Headline 4</h1>
<h2>Subhead 4</h2>
<p>Lorem Ipsum dolar Gamet</p>
</div>
</div>
<div class="column right-column">
<div class="column-inner">
<img src="https://avatars.dicebear.com/api/adventurer/team-member-4.jpg" alt="Image 4">
</div>
</div>
</div>
</div>
</div>
</body>
</html>
As suggested in one of the comments to your question, it's more effective to use IntersectionObserver rather than the 'scroll' event listener.
If I'm correct, this snippet should help you achieve what you're looking for:
window.addEventListener("load", (event) => {
let options = {
rootMargin: "0px",
threshold: 0.8
};
const intersectionHandler = function(entries, observer) {
entries.forEach((entry) => {
if (entry.isIntersecting && entry.intersectionRatio > 0.8) {
entry.target.classList.remove("fadeOut");
entry.target.classList.add("fadeIn");
} else {
entry.target.classList.remove("fadeIn");
entry.target.classList.add("fadeOut");
}
});
};
let observer = new IntersectionObserver(intersectionHandler, options);
document.querySelectorAll(".section").forEach((row, index) => {
observer.observe(row);
});
});
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0 0 0 0;
font-family: sans-serif;
font-family: "Manrope", helvetiva, sans-serif;
transition: background-color 1s;
color: #fff;
background: linear-gradient(0deg, #100521 0%, #50357c 50%, #878290 100%);
}
header {
position: fixed;
top: 0;
width: 100%;
background: #fff;
z-index: 999;
}
header h1 {
font-family: "Lack";
font-size: 4em;
text-align: center;
padding: 10px;
color: #100521;
}
.row-container .row:first-child {
opacity: 1;
}
.row {
opacity: 0;
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
.row-inner {
display: grid;
grid-template-columns: 1fr 1fr;
position: sticky;
top: 0;
/* border: 1px solid yellow; */
padding: 0;
height: 100vh;
}
.column {
display: flex;
flex-direction: column;
justify-content: center;
align-content: center;
flex: 50%;
padding: 10px;
height: 100vh;
box-sizing: border-box;
}
.left-column {
background-color: transparent;
}
.right-column {
background-color: transparent;
}
h1 {
font-family: "Unbounded", helvetica, sans-serif;
}
h1 {
margin: 0;
font-size: 36px;
}
h2 {
margin: 0;
font-size: 24px;
}
p {
margin: 10px 0;
font-size: 18px;
line-height: 1.5;
}
img {
width: 100%;
height: auto;
}
.fadeIn {
animation-name: fadeIn;
animation-duration: 1s;
animation-fill-mode: forwards;
}
.fadeOut {
animation-name: fadeOut;
animation-duration: .5s;
animation-fill-mode: forwards;
}
#keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
#keyframes fadeOut {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
<!-- First row -->
<div class="row-container">
<div class="row section">
<div class="row-inner">
<div class="column left-column">
<div class="column-inner">
<h1>Headline 1</h1>
<h2>Subhead 1</h2>
<p>Lorem Ipsum dolar Gamet</p>
</div>
</div>
<div class="column right-column">
<div class="column-inner">
<img src="https://avatars.dicebear.com/api/adventurer/team-member-1.jpg" alt="Image 1">
</div>
</div>
</div>
</div>
<!-- Second row -->
<div class="row section">
<div class="row-inner">
<div class="column left-column">
<div class="column-inner">
<h1>Headline 2</h1>
<h2>Subhead 2</h2>
<p>Lorem Ipsum dolar Gamet</p>
</div>
</div>
<div class="column right-column">
<div class="column-inner">
<img src="https://avatars.dicebear.com/api/adventurer/team-member-2.jpg" alt="Image 2">
</div>
</div>
</div>
</div>
<!-- Third row -->
<div class="row section">
<div class="row-inner">
<div class="column left-column">
<div class="column-inner">
<h1>Headline 3</h1>
<h2>Subhead 3</h2>
<p>Lorem Ipsum dolar Gamet</p>
</div>
</div>
<div class="column right-column">
<div class="column-inner">
<img src="https://avatars.dicebear.com/api/adventurer/team-member-3.jpg" alt="Image 3">
</div>
</div>
</div>
</div>
<!-- Fourth row -->
<div class="row section">
<div class="row-inner">
<div class="column left-column">
<div class="column-inner">
<h1>Headline 4</h1>
<h2>Subhead 4</h2>
<p>Lorem Ipsum dolar Gamet</p>
</div>
</div>
<div class="column right-column">
<div class="column-inner">
<img src="https://avatars.dicebear.com/api/adventurer/team-member-4.jpg" alt="Image 4">
</div>
</div>
</div>
</div>
</div>
You'll notice I've made some minor modifications to your HTML and CSS, but the magic happens in the Javascript.
I've also used the window "load" event listener to create the observer options, object and callback function when the page is loaded.
You can check the IntersectionObserver API docs for more details on how it works and how to tweak the options or the callback to suit your needs.
Let me know if you have any questions for modifying my code. (I've also had to change your css a bit - the content of the row element was higher than the row element itself what led to some weird issues.)
I have rewritten most of it and it should now work for large row heights. The math for it is just a quadratic formula (Opacity of offset from center of the screen) with it's roots at the height of the row and a maximum at 0 offset with a value of 1.
/** #format */
const calculateOpacity = () => {
const ROW_CONTAINER = document.querySelector(".row-container");
const rows = ROW_CONTAINER.children;
const rowContainerBBox = ROW_CONTAINER.getBoundingClientRect();
console.log("calc")
for (const row of rows) {
const rowBBox = row.getBoundingClientRect();
const top = rowBBox.top - rowContainerBBox.top;
const bottom = rowBBox.bottom - rowContainerBBox.bottom;
if (rowBBox.height > rowContainerBBox.height) {
const opacityOfOffset = (offset) => {
const a = -1 / Math.pow(rowBBox.height, 2);
return Math.max(0, a * Math.pow(offset, 2) + 1);
};
row.style.opacity = opacityOfOffset(top + bottom);
continue;
}
const unvisibleHeightTop = -Math.min(0, top);
const unvisibleHeightBottom = Math.max(0, bottom);
const visibleHeight = rowBBox.height - unvisibleHeightTop - unvisibleHeightBottom;
row.style.opacity = visibleHeight / rowBBox.height;
}
};
document.querySelector(".row-container").addEventListener("scroll", calculateOpacity);
/** #format */
body {
height: 100%;
margin: 0;
padding: 0;
font-family: sans-serif;
font-family: "Manrope", helvetiva, sans-serif;
color: #fff;
}
header {
height: max(70px, 10vh);
position: fixed;
background-color: black;
width: 100%;
display: flex;
align-items: center;
}
header h1 {
margin: 0;
}
main {
padding-top: max(70px, 10vh);
height: 100%;
box-sizing: border-box;
}
.row-container {
background: linear-gradient(0deg, #100521 0%, #50357c 50%, #878290 100%);
height: calc(100vh - max(70px, 10vh));
overflow-y: auto;
}
.row {
height: 150vh;
display: flex;
align-items: center;
padding: 10vh 0;
}
.row .row-inner {
width: 100%;
height: 100%;
display: grid;
grid-template-columns: 1fr 1fr;
}
.row .row-inner .col {
margin-inline: 10px;
display: flex;
align-items: center;
height: 100%;
}
.row img {
width: 100%;
height: auto;
}
.row h2 {
margin: 0;
font-family: "Unbounded", helvetica, sans-serif;
font-size: 36px;
}
.row h3 {
margin: 0;
font-size: 24px;
}
.row p {
margin: 10px 0;
font-size: 18px;
line-height: 1.5;
}
<!-- #format -->
<html>
<head>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Manrope&family=Unbounded&family=Lack&display=swap" rel="stylesheet" />
</head>
<body>
<header>
<h1>My App</h1>
</header>
<main>
<div class="row-container">
<div class="row row-1">
<div class="row-inner">
<div class="col left">
<div class="col-inner">
<h2>Headline 1</h2>
<h3>Subhead 1</h3>
<p>Lorem ipsum dolor sit</p>
</div>
</div>
<div class="col right">
<img src="https://avatars.dicebear.com/api/adventurer/team-member-1.jpg" alt="Image 1" />
</div>
</div>
</div>
<div class="row row-1">
<div class="row-inner">
<div class="col left">
<div class="col-inner">
<h2>Headline 1</h2>
<h3>Subhead 1</h3>
<p>Lorem ipsum dolor sit</p>
</div>
</div>
<div class="col right">
<img src="https://avatars.dicebear.com/api/adventurer/team-member-1.jpg" alt="Image 1" />
</div>
</div>
</div>
<div class="row row-1">
<div class="row-inner">
<div class="col left">
<div class="col-inner">
<h2>Headline 1</h2>
<h3>Subhead 1</h3>
<p>Lorem ipsum dolor sit</p>
</div>
</div>
<div class="col right">
<img src="https://avatars.dicebear.com/api/adventurer/team-member-1.jpg" alt="Image 1" />
</div>
</div>
</div>
</div>
</main>
</body>
</html>
You didn't really ask for a web component, I just got carried away. This is a fun challenge! Anyway, here's a version that is NOT a web component. I also realized that I missed all the css that you provided. So I added that as well. I'm not sure if tweaking the css a little is verbotten ... but I did it. I also added a single [section] tag, which wraps everything but the app header, to make it more bullet proof ... Please let me know if I need to use your code exactly as you posted it.
I had a lot of trouble finding settings that provide a dramatic enough effect on both large screens and phones without some ugly hacks, so I opted for a compromise. You can optimize for either large or small by changing the rootMargin option for the IntersectionObserver. Smaller values make the fade effect more dramatic, but values that are too small cause the content to look washed out on smaller devices. As I'm posting this, rootMargin is set to "-25px". Apparantly you can use negative values, but as far as I can tell, you can only use "px" as the measurement. Other measurements don't seem to work.
Sooooo...
I used IntersectionObserver
const observer = new IntersectionObserver(fadeInOut, options);
One of the options IntersectionObserver takes is something called "threshold". I don't totally understand it, but MDN suggested that if you want to poll the viewport to determine element visibility, you should set the value of "threshold" as an array with many numbers ranging from 0 to 1. The example provided a function that generates 20 of them, so that's what I used.
function buildThresholds() {
const steps = 20;
const thresholds = [];
for (let i = 0; i <= steps; i++) {
let ratio = i/steps;
thresholds.push(ratio);
}
thresholds.push(0);
return thresholds;
}
Then I grabbed the collection of elements to watch using querySelectorAll(".row-container").
Then I looped through the elements and added each one to the observer's watch list.
observer.observe(elem);
Then, in the callback you provide when you create a new instance of IntersectionObserver, I set each element's opacity to the the intersectionRatio, which is a property provided by the instance of the IntersectionObserverEntry which is passed to the callback.
function fadeInOut(items, observer) {
items.forEach( (item) => {
item.target.style.opacity = item.intersectionRatio;
});
}
function buildThresholds() {
const steps = 20;
const thresholds = [];
for (let i = 0; i <= steps; i++) {
let ratio = i/steps;
thresholds.push(ratio);
}
thresholds.push(0);
return thresholds;
}
function fadeInOut(items, observer) {
items.forEach( (item) => {
item.target.style.opacity = item.intersectionRatio;
});
}
function scrollFade() {
const elems = document.querySelectorAll('.row-container');
const options = {
root: null,
rootMargin: "-25px",
threshold: buildThresholds()
};
const observer = new IntersectionObserver(fadeInOut, options);
for (let elem of elems) {
observer.observe(elem);
}
}
document.addEventListener('DOMContentLoaded', scrollFade);
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0 0 0 0;
font-family: sans-serif;
font-family: 'Manrope', helvetiva, sans-serif;
transition: background-color 1s;
color:#fff;
background: linear-gradient(0deg, #100521 0%, #50357c 50%, #878290 100%);
}
header {
position: fixed;
top: 0;
width: 100%;
background: #fff;
z-index: 999;
}
header h1 {
font-family: 'Lack';
font-size: 4em;
text-align: center;
padding: 10px;
color: #100521;
}
section {
margin-top: 6em;
}
h1 {
font-family: 'Unbounded', helvetica, sans-serif;
}
h1 {
margin: 0;
font-size: 36px;
}
h2 {
margin: 0;
font-size: 24px;
}
p {
margin: 10px 0;
font-size: 18px;
line-height: 1.5;
}
img {
width: 100%;
height: auto;
}
.row-container {
background-color: rgb(16, 5, 33);
min-height: 50vh;
}
.row-inner {
align-content: center;
align-items: center;
display: flex;
flex-direction: row;
flex-wrap: wrap-reverse;
padding: 10px;
gap: 10px;
}
.left-column {
flex: 1 1 70vw;
}
.right-column {
flex: 1 1 100px;
text-align: center;
}
<header>
<h1>My App</h1>
</header>
<section>
<div class="row-container">
<div class="row section">
<div class="row-inner">
<div class="column left-column">
<div class="column-inner">
<h1>Headline One</h1>
<h2>Subhead One</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
</div>
<div class="column right-column">
<div class="column-inner">
<img slot="avatar" src="https://avatars.dicebear.com/api/adventurer/team-member-1.jpg" alt="Image 1">
</div>
</div>
</div>
</div>
</div>
<div class="row-container">
<div class="row section">
<div class="row-inner">
<div class="column left-column">
<div class="column-inner">
<h1>Headline Two</h1>
<h2>Subhead Two</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
</div>
<div class="column right-column">
<div class="column-inner">
<img slot="avatar" src="https://avatars.dicebear.com/api/adventurer/team-member-2.jpg" alt="Image 1">
</div>
</div>
</div>
</div>
</div>
<div class="row-container">
<div class="row section">
<div class="row-inner">
<div class="column left-column">
<div class="column-inner">
<h1>Headline Three</h1>
<h2>Subhead Three</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
</div>
<div class="column right-column">
<div class="column-inner">
<img slot="avatar" src="https://avatars.dicebear.com/api/adventurer/team-member-3.jpg" alt="Image 1">
</div>
</div>
</div>
</div>
</div>
<div class="row-container">
<div class="row section">
<div class="row-inner">
<div class="column left-column">
<div class="column-inner">
<h1>Headline Four</h1>
<h2>Subhead Four</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
</div>
<div class="column right-column">
<div class="column-inner">
<img slot="avatar" src="https://avatars.dicebear.com/api/adventurer/team-member-4.jpg" alt="Image 1">
</div>
</div>
</div>
</div>
</div>
<div class="row-container">
<div class="row section">
<div class="row-inner">
<div class="column left-column">
<div class="column-inner">
<h1>Headline Five</h1>
<h2>Subhead Five</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
</div>
<div class="column right-column">
<div class="column-inner">
<img slot="avatar" src="https://avatars.dicebear.com/api/adventurer/team-member-5.jpg" alt="Image 1">
</div>
</div>
</div>
</div>
</div>
<div class="row-container">
<div class="row section">
<div class="row-inner">
<div class="column left-column">
<div class="column-inner">
<h1>Headline Six</h1>
<h2>Subhead Six</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
</div>
<div class="column right-column">
<div class="column-inner">
<img slot="avatar" src="https://avatars.dicebear.com/api/adventurer/team-member-6.jpg" alt="Image 1">
</div>
</div>
</div>
</div>
</div>
</section>
Will this work? I used IntersectionObserver and set the opacity of the sections based on the intersection ratio of each section with the viewport. I pretty much took the idea straight from MDN.
MDN Intersection Observer API
class ScrollFade extends HTMLElement {
template;
shadow;
static observer = false;
constructor() {
super();
this.shadow = this.attachShadow({mode: 'open'});
if (!ScrollFade.observer) {
const buildThresholds = function() {
const steps = 20;
const thresholds = [];
for (let i = 0; i <= steps; i++) {
let ratio = i/steps;
thresholds.push(ratio);
}
thresholds.push(0);
return thresholds;
}
const options = {
root: null,
rootMargin: "0px",
threshold: buildThresholds()
};
ScrollFade.observer = new IntersectionObserver(ScrollFade.fadeInOut, options);
}
}
connectedCallback() {
const tmpl = document.querySelector('#scroll-fade-template').cloneNode(true);
const template = tmpl.content;
ScrollFade.observer.observe(this.shadow.host);
this.shadow.append(template);
}
disconnectedCallback() {
ScrollFade.observer.unobserve(this.shadow.host);
}
static fadeInOut(items, observer) {
items.forEach( (item) => {
item.target.style.opacity = item.intersectionRatio;
});
}
}
document.addEventListener('DOMContentLoaded', customElements.define('scroll-fade', ScrollFade));
body { margin: 0 }
img { width: 100%; }
<scroll-fade>
<h1 slot="headline">Headline One</h1>
<h2 slot="subhead">Subhead One</h2>
<img slot="avatar" src="https://avatars.dicebear.com/api/adventurer/team-member-1.jpg" alt="Image 1">
<div>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip </p>
</div>
</scroll-fade>
<scroll-fade>
<h1 slot="headline">Headline Two</h1>
<h2 slot="subhead">Subhead Two</h2>
<img slot="avatar" src="https://avatars.dicebear.com/api/adventurer/team-member-2.jpg" alt="Image 2">
<div>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip </p>
</div>
</scroll-fade>
<scroll-fade>
<h1 slot="headline">Headline Three</h1>
<h2 slot="subhead">Subhead Three</h2>
<img slot="avatar" src="https://avatars.dicebear.com/api/adventurer/team-member-3.jpg" alt="Image 1">
<div>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip </p>
</div>
</scroll-fade>
<template id="scroll-fade-template">
<style>
.row-container {
background-color: seagreen;
height: 100vh;
overflow: auto;
}
.row-inner {
align-content: center;
display: flex;
flex-direction: row;
flex-wrap: wrap-reverse;
padding: 10px;
gap: 10px;
}
.left-column {
flex: 1 1 70vw;
}
.right-column {
flex: 1 1 100px;
text-align: center;
}
</style>
<div class="row-container">
<div class="row section">
<div class="row-inner">
<div class="column left-column">
<div class="column-inner">
<slot name="headline">Need Headline</slot>
<slot name="subhead">Need Subhead</slot>
<slot>Need Content</slot>
</div>
</div>
<div class="column right-column">
<div class="column-inner">
<slot name="avatar">Need Avatar</slot>
</div>
</div>
</div>
</div>
</div>
</template>
I want to be able to have effect on my website like google maps. The top bar the side bars, I don't want them to change at all, whether using normal scroolwheel or ctrl + scrollwheel.
However, the central part I want to be able to zoom in/out like pretty much google maps
, just by using scrollwheel. How do I achieve this?
It's for the angular app that I'm building. Also is using canvas better for this zoom effects?
Using jQuery you can install a $('#container').on('wheel', zoomContainer) handler that uses a transform with scale to resize the container. The native JavaScript equivalent handler is: document.querySelector('#container').onwheel = zoomContainer;
Here is a proof of concept. I have not figured out though how to crop the container on zoom.
let scale = 1;
function zoomContainer(event) {
event.preventDefault();
if(event.originalEvent.deltaY !== 0) {
if(event.originalEvent.deltaY < 0) {
// wheeled up
scale += 0.1;
} else {
// wheeled down
if(scale > 0.2) {
scale -= 0.1;
}
}
$('#wrapper').css({transform: 'scale(' + scale+')'});
}
}
$('#container').on('wheel', zoomContainer);
#page {
width: 500px;
}
#header, #footer, #leftSide, #rightSide {
border: 1px gray solid;
padding: 3px 10px;
}
#leftSide {
float: left;
height: 200px;
}
#rightSide {
float: right;
height: 200px;
}
#wrapper {
height: 200px;
overflow: hidden;
}
#container {
width: 300px;
height: 200px;
padding: 3px 10px;
overflow: auto;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="page">
<div id="header">Header</div>
<div>
<div id="leftSide" style="float: left;">Left Sidebar</div>
<div id="rightSide" style="float: right;">Right Sidebar</div>
<div id="wrapper">
<div id="container">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.<br /><br />
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.<br /><br />
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.<br /><br />
</div>
</div>
<div style="clear: both;"></div>
</div>
<div id="footer">Footer</div>
</div>
Similarly you could implement panning using a transform with translate.
I'm trying to create a drop down menu that exposes the content when it's hovered over. I didn't know how to do it myself so I referenced W3school's collapsible tutorial's code.
In the tutorial the collapsible opens when clicked on, and I was able to make it open when hovered over, but it's automatically closes when I try and hover over the content within. I need it to stay open when I hover over the internal content. I don't really know any js though so could someone help me?
var coll = document.getElementsByClassName("collapsible");
var i;
for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("mouseover", function() {
this.classList.toggle("active");
var content = this.nextElementSibling;
if (content.style.maxHeight){
content.style.maxHeight = null;
} else {
content.style.maxHeight = content.scrollHeight + "px";
}
});
}
You need to handle the mouseenter and mouseleave event, but please do a container for both of the elements so that you can refer those events to it (as it includes both elements)
var coll = document.getElementsByClassName("both");
var i;
for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("mouseenter", function() {
var collapser = this.querySelector(".collapsible");
collapser.classList.add("active")
var content = collapser.nextElementSibling;
content.style.maxHeight = content.scrollHeight + "px";
});
coll[i].addEventListener("mouseleave", function() {
var collapser = this.querySelector(".collapsible");
collapser.classList.remove("active")
var content = collapser.nextElementSibling;
content.style.maxHeight = null;
});
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
.collapsible {
background-color: #777;
color: white;
cursor: pointer;
padding: 18px;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
}
.active,
.collapsible:hover {
background-color: #555;
}
.collapsible:after {
content: '\002B';
color: white;
font-weight: bold;
float: right;
margin-left: 5px;
}
.active:after {
content: "\2212";
}
.content {
padding: 0 18px;
max-height: 0;
overflow: hidden;
transition: max-height 0.2s ease-out;
background-color: #f1f1f1;
}
</style>
</head>
<body>
<h2>Animated Collapsibles</h2>
<p>A Collapsible:</p>
<div class="both">
<button class="collapsible">Open Collapsible</button>
<div class="content">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</div>
</div>
<div class="both">
<button class="collapsible">Open Collapsible</button>
<div class="content">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</div>
</div>
</body>
I want to create a navigation menu that must be scroll horizontally. But this navigation should be display when the user scroll down the window in the browser (Mobile+Desktop).
I have tried out this code so that the navigation menu display after scroll bottom but its item does not scroll horizontally. When removing position: fixed then it's item scroll horizontally.
So I want both functionalities scrollable navigation horizontal+display navigation menu bar when the user scrolls down the window.
window.onscroll = function() {
scrollFunction()
};
function scrollFunction() {
if (document.body.scrollTop > 150 || document.documentElement.scrollTop > 150) {
document.getElementById("navigation").style.top = "0";
} else {
document.getElementById("navigation").style.top = "-150px";
}
}
#navigation {
top: -50px;
position: fixed;
}
.nav-header {
background: #002347;
color: #ffffff;
}
.nav-header a {
color: #ffffff;
}
.nav-header nav {
width: 100%;
}
.itemlist {
padding: 6px 12px;
}
.itemlist:not(:last-child) {
border-right: 1px solid #ece4e4;
}
.itemlist {
font-size: 17px;
font-weight: bold;
}
.itemlist,
.center {
cursor: pointer;
vertical-align: middle;
display: inline-block;
}
.scrolltab {
overflow-x: auto;
white-space: nowrap;
-webkit-overflow-scrolling: touch;
-ms-overflow-style: -ms-autohiding-scrollbar;
}
.scrolltab::-webkit-scrollbar {
display: none;
}
<div id="navigation" class="nav-header">
<nav class="center scrolltab">
<span class="itemlist"> About us </span>
<span class="itemlist">Web Solution</span>
<span class="itemlist">Web Application</span>
<span class="itemlist">Scripting Language</span>
<span class="itemlist">Andriod Application</span>
<span class="itemlist">Contact</span>
</nav>
</div>
<div style="padding:15px 15px 2500px;font-size:30px">
<p>Lorem ipsum dolor dummy text sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
To make the navigation bar scroll horizontally you need to give the containing element a width. i.e. add
#navigation {
width:100%;
}
also, you have
.scrolltab::-webkit-scrollbar {
display: none;
}
I am not sure whether you needed this for some reason, but on Microsoft Edge and Chrome at least it causes the scroll bar not to appear. Removing it renders the menu scrollable.
Here is what im working on... A maximizable and minimizable pop-up that is called by an hyperlink.
<html>
<style type="text/css">
.pop_out {
background: #333;
border-radius: 5px 5px 0 0;
box-shadow: 0px 0px 10px #000;
}
.minimized {
display: inline-block;
margin-right: 10px;
bottom: 0;
width: 250px;
height: 60px;
overflow: hidden;
}
.maximized {
top: 0;
position: fixed;
display: block;
width: auto;
height: auto;
/* Whatever styling you want when maximized, as long as you add the same styling to minimized class to change it back */
}
.close_pop {
cursor: pointer;
color: #fff;
}
.close_pop:hover {
color: red;
}
.expand_collapse {
margin-right: 10px;
cursor: pointer;
color: #fff;
height: 3px;
}
.expand_collapse:hover {
color: #ccc;
}
a {
position: fixed;
top: 150;
}
</style>
<script type="text/javascript">
var max = true;
function expand_collapse(elem) {
var top_div = elem.parentNode.parentNode.parentNode;
if (max === false) {
elem.innerHTML = "▼";
top_div.classList.toggle("minimized", false);
top_div.classList.toggle("maximized", true);
max = true;
} else if (top_div.classList.contains("maximized")) {
elem.innerHTML = "▲";
top_div.classList.toggle("minimized", true);
top_div.classList.toggle("maximized", false);
max = false
}
}
function close_pop(elem) {
var top_div = elem.parentNode.parentNode.parentNode;
top_div.style.display = 'none';
if (top_div.classList.contains("maximized")) {
max = false;
}
}
</script>
CLICK HERE<!--Right Here -->
<div style="position:fixed;bottom:0px;">
<div class="pop_out maximized">
<div style="padding:2px;position:relative;"> <span style="margin-left:10px;">Tab 1</span>
<span style="position:absolute;right:15px;">
<span class="expand_collapse" onclick="expand_collapse(this);">▼</span>
<span class="close_pop" onclick="close_pop(this);">×</span></span>
</div>
<div style="background:white;font-size:15px;padding:2px;">The standard Lorem Ipsum passage, used since the 1500s "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
laborum.</div>
</div>
</div>
But the pop up opens with the page and i want the pop up to only be opened when the hyperlink commented is clicked and not when the page is loaded. Any help will be much appreciated. I have tried different methods on the hyperlink but to no avail.
Please check the link below
$(document).ready(function() {
$('.show-popup').on('click', function() {
$('.popup').fadeIn();
});
$('.close_pop').on('click', function() {
$('.popup').fadeOut();
});
});
.popup {
display: none;
}
.pop_out {
background: #333;
border-radius: 5px 5px 0 0;
box-shadow: 0px 0px 10px #000;
}
.minimized {
display: inline-block;
margin-right: 10px;
bottom: 0;
width: 250px;
height: 60px;
overflow: hidden;
}
.maximized {
top: 0;
position: fixed;
display: block;
width: auto;
height: auto;
/* Whatever styling you want when maximized, as long as you add the same styling to minimized class to change it back */
}
.close_pop {
cursor: pointer;
color: #fff;
}
.close_pop:hover {
color: red;
}
.expand_collapse {
margin-right: 10px;
cursor: pointer;
color: #fff;
height: 3px;
}
.expand_collapse:hover {
color: #ccc;
}
a {
position: fixed;
top: 150;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.0/jquery.min.js"></script>
<a class="show-popup" href="#">CLICK HERE</a>
<!--Right Here -->
<div class="popup" style="position:fixed;bottom:0px;">
<div class="pop_out maximized">
<div style="padding:2px;position:relative;"> <span style="margin-left:10px;">Tab 1</span>
<span style="position:absolute;right:15px;">
<span class="expand_collapse" onclick="expand_collapse(this);">▼</span>
<span class="close_pop">×</span></span>
</div>
<div style="background:white;font-size:15px;padding:2px;">The standard Lorem Ipsum passage, used since the 1500s "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
laborum.
</div>
</div>
</div>
The pop-up element isn't being hidden on initial page load.
Add style to the pop-up element in CSS.
display:none
Give the pop-up element an ID attribute for easier Javascript targeting
id="pop1"
Add attribute to the hyperlink
onclick="open_pop('#pop1')"
Add javascript function
function open_pop(elem)
{
elem.style.display = 'block';
}
Or to synchronise more effectively with your code, give the pop-up one of your custom classes to modify the display style.
I would also consider using ID's for Javascript selection as opposed to crawling through parents which could break the code if children elements or added or removed from the tree.
in simple code,
html
<div id="welcomeDiv" style="display:none;" class="answer_list" > WELCOME</div>
<input type="button" name="answer" value="Show Div" onclick="showDiv()" />
javascript
function showDiv() {
document.getElementById('welcomeDiv').style.display = "block";
}
I could not share my jsfiddle link. so this helps you i guess.
OR
Just make the div style = "display:none" on mouse click toggle the display.