How to make infinite Scrolling? - javascript

I am creating a Slider with horizontal Scrolling effect But I stuck at a point how Can I make the slider scroll infinitely Like in my code you can see After Item 6 it stops Scrolling and I have to scroll backward but I want it like after Item 6, Item 1 will come again Something like this https://durimel.io/nel Here you can see the scrolling is infinite?
So can anyone help in this?
let container = document.querySelector(".container")
let container1 = document.querySelector(".container1")
window.onscroll = ()=>{
container.style.left = `${-window.scrollY}px`
container1.style.right = `${-window.scrollY}px`
}
let currentpos = container.getBoundingClientRect().left
let currentpos1 = container1.getBoundingClientRect().left
let callDisort = () =>{
let newPos = container.getBoundingClientRect().left;
let newPos1 = container1.getBoundingClientRect().left;
let diff = newPos - currentpos;
let speed = diff * 0.50
container.style.transform = `skewX(${speed}deg)`
currentpos = newPos
container1.style.transform = `skewX(${speed}deg)`
currentpos = newPos
requestAnimationFrame(callDisort)
}
console.log(currentpos)
callDisort()
*{
margin:0;
padding:0;
box-sizing:border-box;
font-family: arial;
}
html,body{
height:3000px;
overflow-X:hidden;
}
.container{
position:fixed;
display:flex;
justify-content: space-between;
top:30vh;
width: 3000px;
transition:transform 0.15s;
will-change:transform;
border:2px solid green;
}
.container1{
position:fixed;
display:flex;
justify-content: space-evenly;
top:45vh;
width: 3000px;
transition:transform 0.15s;
will-change:transform;
border:2px solid green;
}
.box{
position:relative;
}
.box h2{
font-size:4em;
}
<!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>Document</title>
</head>
<body>
<div class="container">
<div class="box one">
<h2>Item 1</h2>
</div>
<div class="box two">
<h2>Item 2</h2>
</div>
<div class="box three">
<h2>Item 3</h2>
</div>
<div class="box four">
<h2>Item 4</h2>
</div>
<div class="box five">
<h2>Item 5</h2>
</div>
<div class="box six">
<h2>Item 6</h2>
</div>
</div>
<div class="container1">
<div class="box one">
<h2>Item 1</h2>
</div>
<div class="box two">
<h2>Item 2</h2>
</div>
<div class="box three">
<h2>Item 3</h2>
</div>
<div class="box four">
<h2>Item 4</h2>
</div>
<div class="box five">
<h2>Item 5</h2>
</div>
<div class="box six">
<h2>Item 6</h2>
</div>
</div>
</body>
</html>

The method the example (https://durimel.io/nel) uses differs and is not really infinite. It is limited to the max values the css properties left and transform: translate3d() support. But its enough for a normal use.
It changes the position of each box as soon as it is out of view depending on the direction and moving it behind the "last" or before the "first" using transform: translate3d() and left: ....
Overall here is version of the method i mentioned. I recommend testing it in on jsfiddle or run the snippet in "Full Page"-View because of the mouse-wheel-scrolling behavior from unscrollable iframe-childs and a scrollable parents can't be prevented.
Update:
Added a simple speed detection routine to allow faster/slower scrolling.
Also fixed the selector for the observer at the end of the js part
const eventHandler = (e) => {
document.querySelectorAll(".boxes-container").forEach(container => {
const cur = +container.dataset.cur || 0;
container.dataset.before = container.dataset.cur;
container.dataset.scrollspeed = (+container.dataset.scrollspeed || 0) +1;
setTimeout(() => {
container.dataset.scrollspeed = +container.dataset.scrollspeed -1;
}, 33 * +container.dataset.scrollspeed);
let moveByPixels = Math.round(e.deltaY / (6 - Math.min(+container.dataset.scrollspeed,5)));
if (container.dataset.direction == "invert") {
moveByPixels *= -1;
}
container.style.left = `${cur + -moveByPixels}px`;
container.dataset.cur = cur + -moveByPixels;
});
};
window.addEventListener("wheel", eventHandler);
window.addEventListener("mousewheel", eventHandler);
const observer = new IntersectionObserver((entries, opts) => {
entries.forEach(entry => {
entry.target.classList.toggle('visible', entry.isIntersecting);
});
document.querySelectorAll(".boxes-container").forEach(container => {
const before = (+container.dataset.before || 0),
current = (+container.dataset.cur || 0),
diff = before - current,
boxes = [...container.querySelectorAll(".box")],
visible = [...container.querySelectorAll(".box.visible")],
first = boxes.indexOf(visible[0]),
last = boxes.indexOf(visible[visible.length - 1]),
adjust = (by) => {
container.dataset.cur = +container.dataset.cur + by;
container.dataset.before = +container.dataset.before + by;
container.style.left = +container.dataset.cur + 'px';
};
if (diff >= 0) {
if (first > 0) { // move the first to the end
const box = boxes[0];
box.parentNode.append(box);
adjust(box.clientWidth);
}
} else {
if (last == 0 || first == 0) { // move the to first
const box = boxes[boxes.length - 1];
box.parentNode.prepend(box);
adjust(-box.clientWidth);
}
}
})
}, { // trigger on any percent value
threshold: new Array(101).fill(0).map((n, i) => +(i / 100).toFixed(2))
});
document.querySelectorAll(".boxes-container .box").forEach(el => observer.observe(el));
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: Sans-Serif;
}
.boxes-container {
position: fixed;
display: flex;
flex-wrap: nowrap;
flex-direction: row;
white-space: nowrap;
min-width: -min-content;
}
.v30 {
top: 30vh;
}
.v60 {
top: 60vh;
}
.box {
position: relative;
margin: 0 !important;
padding: 0 50px;
}
.box h2 {
font-size: 5rem;
}
<div class="boxes-container">
<div class="box">
<h2>0</h2>
</div>
<div class="box">
<h2>1</h2>
</div>
<div class="box">
<h2>2</h2>
</div>
<div class="box">
<h2>3</h2>
</div>
<div class="box">
<h2>4</h2>
</div>
<div class="box">
<h2>5</h2>
</div>
<div class="box">
<h2>6</h2>
</div>
</div>
<div class="boxes-container v30" data-direction="invert">
<div class="box">
<h2>A</h2>
</div>
<div class="box">
<h2>B</h2>
</div>
<div class="box">
<h2>C</h2>
</div>
<div class="box">
<h2>D</h2>
</div>
<div class="box">
<h2>E</h2>
</div>
<div class="box">
<h2>F</h2>
</div>
<div class="box">
<h2>G</h2>
</div>
</div>
<div class="boxes-container v60">
<div class="box">
<h2>0</h2>
</div>
<div class="box">
<h2>1</h2>
</div>
<div class="box">
<h2>2</h2>
</div>
<div class="box">
<h2>3</h2>
</div>
<div class="box">
<h2>4</h2>
</div>
<div class="box">
<h2>5</h2>
</div>
<div class="box">
<h2>6</h2>
</div>
</div>

A few comments on this solution.
If the container is set to display:flex; justify-content:space-around; then the space between the items will change as you scroll (the more items, the closer packed they are). Changing to justify-content:flex-start; with a fixed width for each .box delivers the best result.
Adding a debounce fn greatly improved (and simplified) the job. At least, the console logs are underwhelming(!).
If you scroll the scroll wheel very fast, you might hit the end of the carousel before it re-populates. To make that easier to see, change the delay from 50 to 500 (milliseconds).
The debounce is saying, "only plunk more boxes into the container when 50ms has elapsed since the last scroll event. You might prefer a throttle function instead, where it will run at most one re-population every 50ms (or as you set the debounceDelay value).
The HTML,Body height needs to be set to a very large number - in this demo it is now set to 30,000. The .container width should be auto, and it (the .container width) will increase every time new items are plonked into the container.
Most important: the $ and $$ are not jQuery. They are pure vanilla javaScript shorthand for document.querySelector() and document.querySelectorAll()
The demo is best viewed full page (link at top right of demo window).
const $ = document.querySelector.bind(document);
const $$ = document.querySelectorAll.bind(document);
let kontainer = $(".container");
const boxes = $$('.box');
const debounceDelay = 50; //change to 100 for better performance
const updateCarousel = debounce(function(e){
console.log(scrollY +' // '+ kontainer.getBoundingClientRect().right)
const currKontainerWidth = kontainer.getBoundingClientRect().right;
if ( currKontainerWidth - scrollY < 300 ){
for (let i=0, j=boxes.length; j > i; i++){
kontainer.appendChild(boxes[i].cloneNode(true));
}
}
}, debounceDelay);
window.addEventListener('scroll', updateCarousel, false);
window.onscroll = () => {
kontainer.style.left = `${-window.scrollY}px`;
}
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
*{
margin:0;
padding:0;
box-sizing:border-box;
font-family: arial;
}
html,body{
height:30000px;
overflow-X:hidden;
}
.container{
position:fixed;
display:flex;
justify-content: flex-start;
top:30vh;
width: auto;
transition:transform 0.15s;
will-change:transform;
border:2px solid green;
}
.box{
position:relative;
min-width:250px;
}
.box h2{
font-size:4em;
}
<!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>Document</title>
</head>
<body>
<div class="container">
<div class="box one">
<h2>Item 1</h2>
</div>
<div class="box two">
<h2>Item 2</h2>
</div>
<div class="box three">
<h2>Item 3</h2>
</div>
<div class="box four">
<h2>Item 4</h2>
</div>
<div class="box five">
<h2>Item 5</h2>
</div>
<div class="box six">
<h2>Item 6</h2>
</div>
</div>
</body>
</html>
Best viewed "full page" (top right link after clicking Run Code Snippet)

Related

HTML/CSS Drag-and-Drop Div Snaps to Incorrect Position Relative to Mouse

Problem
I have a series of cells in a table that store several individual cards. These cards can be rearranged within a cell or moved between cells by dragging and dropping. However, I am finding that when I pick up my card, it does not track directly from where I clicked it. In other words, if I clicked on the upper-right-most corner, it immediately shifts away from my mouse in an unexpected manner. Even stranger is the fact that it works fine if I have my cards stored outside of a table tag. What does the table do that causes my mouse-shift calculation to be incorrect?
JSFiddle
Here is a link to jsfiddle: https://jsfiddle.net/0atymsb9/1/
Keep an eye on where you click, and where the div is positioned relative to your mouse. EDIT: It is while you are dragging the card that the visual defect is occurring, but once you place it it's okay.
(The first square is a within a (doesn't work right), and the second is simply a div (works well.))
I am following this tutorial: https://htmldom.dev/drag-and-drop-element-in-a-list/
Code
JS
// Setup
function setupCards() {
const cards = document.getElementsByClassName("card");
const cells = document.getElementsByClassName("cell");
for (const card of cards) {
card.classList.add("draggable");
}
for (const cell of cells) {
[].slice.call(cell.querySelectorAll('.draggable')).forEach((item) => {
item.addEventListener('mousedown', mouseDownHandler);
});
}
}
// Dragging Logic
let placeholder;
let draggingEle;
let isDraggingStarted = false;
let mouse = { x: 0, y: 0 };
function swap(nodeA, nodeB) {
const parentA = nodeA.parentNode;
const siblingA = nodeA.nextSibling === nodeB ? nodeA : nodeA.nextSibling;
// Move `nodeA` to before the `nodeB`
nodeB.parentNode.insertBefore(nodeA, nodeB);
// Move `nodeB` to before the sibling of `nodeA`
parentA.insertBefore(nodeB, siblingA);
};
function isAbove(nodeA, nodeB) {
const rectA = nodeA.getBoundingClientRect();
const rectB = nodeB.getBoundingClientRect();
return (rectA.top + rectA.height / 2 < rectB.top + rectB.height / 2);
}
// Handlers
const mouseMoveHandler = function (e) {
// Set position for dragging element
const draggingRect = draggingEle.getBoundingClientRect();
if (!isDraggingStarted) {
isDraggingStarted = true;
placeholder = document.createElement('div');
placeholder.classList.add('placeholder');
draggingEle.parentNode.insertBefore(
placeholder,
draggingEle.nextSibling
);
placeholder.style.height = `${draggingRect.height}px`;
}
draggingEle.style.position = 'absolute';
draggingEle.style.top = `${e.pageY - mouse.y}px`;
draggingEle.style.left = `${e.pageX - mouse.x}px`;
const prevEle = draggingEle.previousElementSibling;
const nextEle = placeholder.nextElementSibling;
if (prevEle && isAbove(draggingEle, prevEle)) {
swap(placeholder, draggingEle);
swap(placeholder, prevEle);
return;
}
if (nextEle && isAbove(nextEle, draggingEle)) {
swap(nextEle, placeholder);
swap(nextEle, draggingEle);
}
};
const mouseUpHandler = function () {
placeholder && placeholder.parentNode.removeChild(placeholder);
isDraggingStarted = false;
// Remove the position styles
draggingEle.style.removeProperty('top');
draggingEle.style.removeProperty('left');
draggingEle.style.removeProperty('position');
mouse.x = null;
mouse.y = null;
draggingEle = null;
// Remove the handlers of `mousemove` and `mouseup`
document.removeEventListener('mousemove', mouseMoveHandler);
document.removeEventListener('mouseup', mouseUpHandler);
};
function mouseDownHandler(e) {
draggingEle = e.target;
const rect = draggingEle.getBoundingClientRect();
mouse.x = e.pageX - rect.left;
mouse.y = e.pageY - rect.top;
document.addEventListener('mousemove', mouseMoveHandler);
document.addEventListener('mouseup', mouseUpHandler);
}
// Main
function main() {
setupCards()
}
main()
HTML
<!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>Document</title>
<script src="script.js" defer></script>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<table>
<td>
<div class="cell">
<div class="card">Test 1</div>
<div class="card">Test 2</div>
<div class="card">Test 3</div>
<div class="card">Test 4</div>
<div class="card">Test 5</div>
<div class="card">Test 6</div>
</div>
</td>
</table>
<div class="cell">
<div class="card">Test 1</div>
<div class="card">Test 2</div>
<div class="card">Test 3</div>
<div class="card">Test 4</div>
<div class="card">Test 5</div>
<div class="card">Test 6</div>
</div>
</body>
CSS
td {
width: 8em;
height: 8em;
position: relative;
}
.cell {
overflow-y: auto;
overflow-x: hidden;
background-color: aquamarine;
border: 1px solid black;
width: 100%;
height: 100%;
}
.card {
background-color: darkgreen;
color: white;
width: 4em;
height: 1em;
text-align: center;
}
/* Dragging */
.draggable {
user-select: none;
}
Thank you for your time!
The problem is not exactly with the table tag. I found two ways to solve this problem.
First solution that change the HTML: Simply comment or remove the <td> tag as follows
<body>
<table>
<!-- <td> --> <!--comment or remove this line-->
<div class="cell">
<div class="card">Test 1</div>
<div class="card">Test 2</div>
<div class="card">Test 3</div>
<div class="card">Test 4</div>
<div class="card">Test 5</div>
<div class="card">Test 6</div>
</div>
<!-- </td>--> <!--comment or remove this line-->
</table>
<div class="cell">
<div class="card">Test 1</div>
<div class="card">Test 2</div>
<div class="card">Test 3</div>
<div class="card">Test 4</div>
<div class="card">Test 5</div>
<div class="card">Test 6</div>
</div>
</body>
Second solution that change the CSS:
This solution is better because you do not need to remove anything in the HTML (maybe is what you want), i.e, do not need to remove <td> or <table> tags. In the CSS file change the position: relative to position: center as follows
td {
width: 8em;
height: 8em;
position: center; /*change from relative to center*/
}

On sliding the details of particular position should store in local storage?

I am replicating this webpage https://www.modsy.com/project/furniture and I wrote the code On every slide there will be changing of image and phrase like that there are three phrases and images now I want to store the image and phrase in the local storage what the user has finalized My html code is:
window.addEventListener('load', function() {
var rangeslider = document.getElementById("sliderRange");
var output = document.getElementById("sliderOutput");
var images = document.getElementById("sliderImages");
rangeslider.addEventListener('input', function() {
for (var i = 0; i < output.children.length; i++) {
output.children[i].style.display = 'none';
images.children[i].style.display = 'none';
}
i = Number(this.value) - 1;
output.children[i].style.display = 'block';
images.children[i].style.display = 'block';
});
});
.rangeslider {
width: 50%;
margin: 0 auto;
position: absolute;
}
.myslider {
-webkit-appearance: none;
background: white;
width: 100%;
height: 20px;
opacity: 0.8;
margin-top: 180px;
}
.myslider::-webkit-slider-thumb {
-webkit-appearance: none;
cursor: pointer;
background: #000080;
width: 33%;
height: 20px;
}
.col-4 {
text-align: center;
}
.myslider:hover {
opacity: 1;
}
.image {
position: relative;
width: 400px;
margin: 0 auto;
}
.image>img {
position: absolute;
display: none;
}
.image>img.visible,
.image>img:first-child {
display: block;
}
#sliderOutput>div {
display: none;
}
#sliderOutput>div.visible,
#sliderOutput>div:first-child {
display: block;
}
#p1{
height: 10px;
}
<div class="image mt-3 mb-3" id="sliderImages">
<img src="../static/images/1.jpg" width="400" height="180">
<img src="../static/images/2.jpg" width="400" height="180">
<img src="../static/images/3.jpg" width="400" height="180">
</div><br>
<div class="rangeslider">
<input type="range" min="1" max="3" value="1" class="myslider" id="sliderRange">
<div id="sliderOutput">
<div class="container">
<div class="row mt-4">
<div class="col-4">
<h6 class="display-6">Starting From Scratch</h6>
<p> I'm designing the room </p>
</div>
<div class="col-4">
<h6 class="display-6">Somewhere in Between</h6>
<p>I'm designing around a few pieces I already own</p>
</div>
<div class="col-4">
<h6 class="display-6">Mostly Furnished</h6>
<p>I want to put the finishing touches on my room</p>
</div>
</div>
</div>
<div class="container">
<div class="row mt-4">
<div class="col-4">
<h6 class="display-6">Starting From Scratch</h6>
<p> I'm designing the room </p>
</div>
<div class="col-4">
<h6 class="display-6">Somewhere in Between</h6>
<p>I'm designing around a few pieces I already own</p>
</div>
<div class="col-4">
<h6 class="display-6">Mostly Furnished</h6>
<p>I want to put the finishing touches on my room</p>
</div>
</div>
</div>
<div class="container">
<div class="row mt-4">
<div class="col-4">
<h6 class="display-6">Starting From Scratch</h6>
<p> I'm designing the room </p>
</div>
<div class="col-4">
<h6 class="display-6">Somewhere in Between</h6>
<p>I'm designing around a few pieces I already own</p>
</div>
<div class="col-4">
<h6 class="display-6">Mostly Furnished</h6>
<p>I want to put the finishing touches on my room</p>
</div>
</div>
</div>
</div>
My main requirement if the slider is in the first that phrase and image should be stored in local storage like that if it is in second that details should store.
Are you just trying to remember what the last slide the user viewed is? If so just use the localStorage.setItem() method and utilise the dataset feature to set a flag of each slide.
If I am right in my presumption your on load function would include the following line to default to the first slide:
localStorage.setItem('currentSlide', '1')
Your HTML slides would each have a dataset tag which could be something like:
data-index="1"
Then, in your change slide function you would get the dataset value and update the localStorage parameter:
function changeSlide() {
// ... Whatever code you have...
localStorage.setItem('currentSlide', this.dataset.index);
}
You could also use the sessionStorage instead, if you do not wish for the website to remember the user's last position after they leave the page: https://developer.mozilla.org/en-US/docs/Web/API/Storage
you can save your image using savImage function
html
<img src="../static/images/1.jpg" id="bannerImg" />
js
function saveImage() {
var bannerImage = document.getElementById('bannerImg');
var canvas = document.createElement("canvas");
canvas.width = bannerImage.width;
canvas.height = bannerImage.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(bannerImage, 0, 0);
var dataURL = canvas.toDataURL("image/png");
localStorage.setItem("imgData", dataURL.replace(/^data:image\/(png|jpg);base64,/,"");
}
after your process you can get your image from local storage
html
<img src="" id="tableBanner" />
js
function getImage() {
var dataImage = localStorage.getItem('imgData');
bannerImg = document.getElementById('tableBanner');
bannerImg.src = "data:image/png;base64," + dataImage;
}
you must run these two function for your problem

Change image during scroll when the text becomes visible

I have this script that changes the image(s) when the text reaches the top of the screen. I want it in reverse. I would like to have the image(s) change when the text becomes visible from a certain height (say 50px) from bottom. How do I go about this?
JS Fiddle: https://jsfiddle.net/ramo427e/
HTML:
<div class="main">
<div class="container">
<div class="row">
<div class="col-md-12">
<h1>This is sparta and what comes whatever after. This is sparta and what comes whatever after. This is sparta and what comes whatever after. </h1><h1>This is sparta and what comes whatever after. This is sparta and what comes whatever after. This is sparta and what comes whatever after. </h1>
</div>
<div class="col-md-6">
<section id="one">
<div class="content">
<h1>first heading</h1>
<p>description</p>
</div>
</section>
<section id="two">
<div class="content">
<h1>second heading</h1>
<p>description</p>
</div>
</section>
<section id="three">
<div class="content">
<h1>third heading</h1>
<p>description</p>
</div>
</section>
<section id="four">
<div class="content">
<h1>fourth heading</h1>
<p>description</p>
</div>
</section>
</div>
<div class="col-md-6">
<div class="image"></div>
</div>
</div>
<div class="row">
<div class="col-12">
<h1>This is sparta and what comes whatever after. This is sparta and what comes whatever after. This is sparta and what comes whatever after. </h1>
</div>
</div>
</div>
</div>
CSS:
*{
margin:0;
padding:0;
}
h1{
font-size: 18px;
font-weight: strong;
}
p{
font-size: 12px;
}
.main{
width:100%;
height:auto;
position: relative;
}
.image {
background-image:url('https://i.postimg.cc/FRxNp6yG/hr-ax.png');
background-size:100% 100%;
background-attachment:fixed;
transition: 1000ms;
width: 200px;
height: 200px;
position: fixed;
}
#one, #two, #three, #four {
min-height: 150px;
}
JS:
$(window).on("scroll touchmove", function() {
if ($(document).scrollTop() >= $("#one").position().top && $(document).scrollTop() < $("#two").position().top) {
$('.image').css('background-image', 'url(https://i.postimg.cc/FRxNp6yG/hr-ax.png)')
};
if ($(document).scrollTop() >= $("#two").position().top && $(document).scrollTop() < $("#three").position().top) {
$('.image').css('background-image', 'url(https://i.postimg.cc/wvz9hzm7/hr-bx.png)')
};
if ($(document).scrollTop() >= $("#three").position().top && $(document).scrollTop() < $("#four").position().top) {
$('.image').css('background-image', 'url(https://i.postimg.cc/FRxNp6yG/hr-ax.png)')
};
if ($(document).scrollTop() >= $("#four").position().top) {
$('.image').css('background-image', 'url(https://i.postimg.cc/wvz9hzm7/hr-bx.png)')
};
});
on the scroll, you can get the position of the all element. And calculate whether it is visible or not. Based on the switch case you can change image.
Sample:
window.addEventListener('scroll', function() {
var element = document.querySelector('#main-container');
var position = element.getBoundingClientRect();
// checking whether fully visible
if(position.top >= 0 && position.bottom <= window.innerHeight) {
console.log('Element is fully visible in screen');
}
// checking for partial visibility
if(position.top < window.innerHeight && position.bottom >= 0) {
console.log('Element is partially visible in screen');
}
});
More:
https://usefulangle.com/post/113/javascript-detecting-element-visible-during-scroll
Select the text with jQuery and correct the height relative to that and use that to trigger the event for Example:
if($(document).scrollTop() >= $("#one .content h1").position().top - 50){
$('.image').css('background-image', 'url(https://i.postimg.cc/FRxNp6yG/hr-ax.png)')
}

On click scroll up or down 100vh pure javascript

I am trying to implement a simple button that on click will scroll the page either up or down 100vh between my sections. I can see plenty of examples doing this with jQuery but I'm looking for a pure javascript solution. I'm not to sure how to achieve it.
Appreciate any advice.
HTML
<section class="section section-1">
<div class="btn"></div>
</section>
<section class="section section-2">
<div class="btn"></div>
</section>
<section class="section section-3">
<div class="btn"></div>
</section>
CSS
.section {
width: 100%;
height: 100vh:
}
This is what I have come up with so far
for (var s = 0; s < btn.length; s++) {
btn[s].addEventListener('click', function(){
window.scrollBy(0,1000);
});
}
There are a few ways to get viewport size in JavaScript. Using one of these ways, you should be able to scroll as you are with the viewport size in place of your 1000.
For instance, if I wanted to scroll exactly the height of one viewport with window.innerHeight:
let pageHeight = window.innerHeight;
window.scrollBy(0, pageHeight);
document.querySelectorAll('.btn').forEach(btn => {
btn.addEventListener('click', function() {
let scrollDistance = document.documentElement.clientHeight;
if (btn.className.split(' ').includes('scroll-up')) {
scrollDistance *= -1;
}
window.scrollBy(0, scrollDistance);
});
});
body {
margin: 0;
}
.section {
width: 100%;
height: 100vh;
}
.section-1 {
background-color: blue;
}
.section-2 {
background-color: red;
}
.section-3 {
background-color: green;
}
<section class="section section-1">
<div class="btn scroll-down">V</div>
<div class="btn scroll-up">^</div>
</section>
<section class="section section-2">
<div class="btn scroll-down">V</div>
<div class="btn scroll-up">^</div>
</section>
<section class="section section-3">
<div class="btn scroll-down">V</div>
<div class="btn scroll-up">^</div>
</section>

Complex continuous scroll loop

I have a code similar to:
<div id='right-column'>
<div id='results'>
<div id='result1>
<div class='main'></div>
<div class='details'></div>
</div>
<!-- ... -->
<div id='result50>
<div class='main'></div>
<div class='details'></div>
</div>
</div>
</div>
the total number of results depends of the ajax query, I insert all the results dynamically in one go.
div.main is always visible (fixed height) and div.details "unfolds/folds" below div.main when the user clicks on a result div.
the details div height can vary.
If #results scrollHeight is bigger than #right-column height, I would like to create a continuous scroll loop.
In this case, scrolling past #result50 would show #result1, scrolling before #result1 would show #result50.
I can't .append() the first child to the bottom as in some cases a portion of a result can be seen on top and at the bottom of the column.
I can't duplicate a result unless I detect if .details is unfolded/folded.
The fact that the height of a result can change when a user unfolds the .details div, makes it even more complicated...
Here is an example of continuous scroll loop (2 columns):
$(document).ready(function() {
var num_children = $('#up-left').children().length;
var child_height = $('#up-left').height() / num_children;
var half_way = num_children * child_height / 2;
$(window).scrollTop(half_way);
function crisscross() {
$('#up-left').css('bottom', '-' + window.scrollY + 'px');
$('#down-right').css('bottom', '-' + window.scrollY + 'px');
var firstLeft = $('#up-left').children().first();
var lastLeft = $('#up-left').children().last();
var lastRight = $('#down-right').children().last();
var firstRight = $('#down-right').children().first();
if (window.scrollY > half_way ) {
$(window).scrollTop(half_way - child_height);
lastRight.appendTo('#up-left');
firstLeft.prependTo('#down-right');
} else if (window.scrollY < half_way - child_height) {
$(window).scrollTop(half_way);
lastLeft.appendTo('#down-right');
firstRight.prependTo('#up-left');
}
}
$(window).scroll(crisscross);
});
div#content {
width: 100%;
height: 100%;
position: absolute;
top:0;
right:0;
bottom:0;
left:0;
}
#box {
position: relative;
vertical-align:top;
width: 100%;
height: 200px;
margin: 0;
padding: 0;
}
#up-left {
position:absolute;
z-index:4px;
left: 0;
top: 0px;
width: 50%;
margin: 0;
padding: 0;
}
#down-right {
position:fixed;
bottom: 0px;
z-index: 5px;
left: 50%;
width: 50%;
margin: 0;
padding: 0;
}
h1 {margin: 0;padding: 0;color:#fff}
.black {background: black;}
.white {background: grey;}
.green {background: green;}
.brown {background: brown;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<div id="content">
<div id="up-left">
<div id="box" class="brown">
<h1>ONE</h1>
</div>
<div id="box" class="black">
<h1>TWO</h1>
</div>
<div id="box" class="white">
<h1>THREE</h1>
</div>
<div id="box" class="black">
<h1>FOUR</h1>
</div>
<div id="box" class="white">
<h1>FIVE</h1>
</div>
<div id="box" class="black">
<h1>SIX</h1>
</div>
</div><!-- #up-left -->
<div id="down-right">
<div id="box" class="white">
<h1>SIX</h1>
</div>
<div id="box" class="black">
<h1>FIVE</h1>
</div>
<div id="box" class="white">
<h1>FOUR</h1>
</div>
<div id="box" class="black">
<h1>THREE</h1>
</div>
<div id="box" class="white">
<h1>TWO</h1>
</div>
<div id="box" class="green">
<h1>ONE</h1>
</div>
</div><!-- #down-right -->
</div><!-- .content -->
(fiddle: http://jsfiddle.net/franckl/wszg1d6c/)
Any hint/ideas on how I could do it ?
Move items to top or bottom based on scroll direction
You can use jQuery's .append() and .prepend() to move items without cloning them.
You'll use similar techniques to infinite scrolling with lazy loading (AJAX), but in this scenario you want to handle scrolling up as well as down, and instead of loading new content from the server, you're just recycling existing DOM elements in the list.
Below I demonstrate one technique. I store the scroll position in the element's .data cache for easy retrieval when detecting scrolling direction. I chose to detect scrolling direction to avoid making unnecessary variable assignments upfront to improve performance. Otherwise, you'd be getting elements and doing math for a scroll event that isn't going to happen in that direction.
The scroll handler:
$('#right-column').on('scroll', function (e) {
var $this = $(this),
$results = $("#results"),
scrollPosition = $this.scrollTop();
if (scrollPosition > ($this.data('scroll-position') || 0)) {
// Scrolling down
var threshold = $results.height() - $this.height() - $('.result:last-child').height();
if (scrollPosition > threshold) {
var $firstResult = $('.result:first-child');
$results.append($firstResult);
scrollPosition -= $firstResult.height();
$this.scrollTop(scrollPosition);
}
} else {
// Scrolling up
var threshold = $('.result:first-child').height();
if (scrollPosition < threshold) {
var $lastResult = $('.result:last-child');
$results.prepend($lastResult);
scrollPosition += $lastResult.height();
$this.scrollTop(scrollPosition);
}
}
$this.data('scroll-position', scrollPosition)
});
A complete working example:
$('#right-column').on('scroll', function (e) {
var $this = $(this),
$results = $("#results"),
scrollPosition = $this.scrollTop();
if (scrollPosition > ($this.data('scroll-position') || 0)) {
// Scrolling down
var threshold = $results.height() - $this.height() - $('.result:last-child').height();
if (scrollPosition > threshold) {
var $firstResult = $('.result:first-child');
$results.append($firstResult);
scrollPosition -= $firstResult.height();
$this.scrollTop(scrollPosition);
}
} else {
// Scrolling up
var threshold = $('.result:first-child').height();
if (scrollPosition < threshold) {
var $lastResult = $('.result:last-child');
$results.prepend($lastResult);
scrollPosition += $lastResult.height();
$this.scrollTop(scrollPosition);
}
}
$this.data('scroll-position', scrollPosition)
});
$('#results').on('click', '.result', function (e) {
$(this).find('.details').toggle();
});
$('#newNumber').on('input', function (e) {
var results = '';
for (var n = 1; n <= $(this).val(); n++) {
results +=
'<div class="result" id="result' + n + '">' +
' <div class="main">Result ' + n + '</div>' +
' <div class="details">Details for result ' + n + '</div>' +
'</div>';
}
$('#results').html(results);
});
body {
font-family: sans-serif;
}
h1 {
font: bold 2rem/1 Georgia, serif;
}
p {
line-height: 1.5;
margin-bottom: 1em;
}
label {
font-weight: bold;
margin-bottom: 1em;
}
.column {
box-sizing: border-box;
float: left;
width: 50%;
height: 100vh;
padding: 1em;
overflow: auto;
}
#right-column {
background-color: LemonChiffon;
}
.result {
padding: 1em;
cursor: pointer;
}
.result .main {
height: 2em;
font-weight: bold;
line-height: 2;
}
.result .details {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class=
"column" id="left-column">
<p>Existing DOM elements are moved to the top or bottom of the list depending on your scroll direction.</p>
<label>Change the number of results to display
<input id="newNumber" type="number" value="10" />
</div>
<div class=
"column" id="right-column">
<div id="results">
<div id="result1" class="result">
<div class="main">Result 1</div>
<div class="details">Details for result 1</div>
</div>
<div id="result2" class="result">
<div class="main">Result 2</div>
<div class="details">Details for result 2</div>
</div>
<div id="result3" class="result">
<div class="main">Result 3</div>
<div class="details">Details for result 3</div>
</div>
<div id="result4" class="result">
<div class="main">Result 4</div>
<div class="details">Details for result 4</div>
</div>
<div id="result5" class="result">
<div class="main">Result 5</div>
<div class="details">Details for result 5</div>
</div>
<div id="result6" class="result">
<div class="main">Result 6</div>
<div class="details">Details for result 6</div>
</div>
<div id="result7" class="result">
<div class="main">Result 7</div>
<div class="details">Details for result 7</div>
</div>
<div id="result8" class="result">
<div class="main">Result 8</div>
<div class="details">Details for result 8</div>
</div>
<div id="result9" class="result">
<div class="main">Result 9</div>
<div class="details">Details for result 9</div>
</div>
<div id="result10" class="result">
<div class="main">Result 10</div>
<div class="details">Details for result 10</div>
</div>
</div>
</div>
A complete working example on CodePen, if you prefer.

Categories