jQuery autoScroll to the nearest section - javascript

If the user is place in the top half of current section it automatically scroll top of that section.
Then if the user is in the bottom half of the current section it automatically scroll to the top next section.
function autoScroll(aid){
var aTag = $("#"+ aid);
body.animate({scrollTop: aTag.offset().top},1500);
}
$(window).scroll(function() {
var windowScroll = $(window).scrollTop();
if(windowScroll < ($("#Section2").offset().top/2) && !(windowScroll > ($("#Section2").offset().top/2))){
section_id = 'Section1';
}
$(document).off('scroll');
console.log(section_id);
autoScroll(section_id);
});
http://jsfiddle.net/x6xzh69v/2/

I created a working example in CODEPEN.
$(document).ready(function() {
var origHeight = [];
var curScroll = 0;
var cumSumHeight = 0;
var animHeight = 0;
var i = 0;
var timeoutVar;
$(".section").each(function(index) {
origHeight.push($(this).height());
});
$(window).scroll(function() {
curScroll = $("body").scrollTop();
cumSumHeight = 0;
while ((cumSumHeight + origHeight[i]) < curScroll) {
cumSumHeight += origHeight[i];
i++;
}
if (i == 0) {
if (curScroll < (origHeight[i] / 2)) {
animHeight = 0;
} else {
animHeight = origHeight[i];
}
} else {
if ((curScroll - cumSumHeight) < (origHeight[i] / 2)) {
animHeight = cumSumHeight;
} else {
animHeight = origHeight[i] + cumSumHeight;
}
}
clearTimeout(timeoutVar);
timeoutVar = setTimeout(function() {
$("body").stop(true,true).animate({
scrollTop: animHeight
}, 200);
}, 300);
});
});
.section {
width: 100%;
position: relative;
height: 300px;
}
#Section1 {
background: red;
}
#Section2 {
background: blue;
}
#Section3 {
background: orange;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section id="Section1" class="section"></section>
<section id="Section2" class="section"></section>
<section id="Section3" class="section"></section>

Related

How to wait for the function to end before start the function again

How to wait for my first function to end before I can click "Click me" button again. I have been searching for a way to let my function end first before I can let the new function run again.
document.getElementById("myBtn").addEventListener("click", myFunction);
setTimeout(function() {
myFunction();
}, 3000);
var id = null;
function myFunction() {
var elem = document.getElementById("myAnimation");
var pos = 0;
clearInterval(id);
id = setInterval(frame, 10);
function frame() {
if (pos == 350) {
clearInterval(id);
} else {
pos++;
elem.style.top = pos + 'px';
elem.style.left = pos + 'px';
}
}
}
#myContainer {
width: 400px;
height: 400px;
position: relative;
background: yellow;
}
#myAnimation {
width: 50px;
height: 50px;
position: absolute;
background-color: red;
}
<h2>JavaScript addEventListener()</h2>
<button id="myBtn">Click Me</button>
<div id="myContainer">
<div id="myAnimation"></div>
If we disable and enable the button in the correct place, it will work quite nicely
I also cleaned up the code a bit to make it more contained.
window.addEventListener('DOMContentLoaded', () => {
const myBtn = document.getElementById("myBtn");
const elem = document.getElementById("myAnimation");
let id = null;
let pos = 0;
const frame = () => {
if (pos == 350) {
clearInterval(id);
myBtn.disabled = false;
} else {
pos++;
elem.style.top = pos + 'px';
elem.style.left = pos + 'px';
}
}
const myFunction = () => {
pos = 0;
clearInterval(id);
id = setInterval(frame, 10);
myBtn.disabled = true;
}
id = setTimeout(myFunction, 3000);
myBtn.addEventListener("click", myFunction);
});
#myContainer {
width: 400px;
height: 400px;
position: relative;
background: yellow;
}
#myAnimation {
width: 50px;
height: 50px;
position: absolute;
background-color: red;
}
<h2>JavaScript addEventListener()</h2>
<button id="myBtn">Click Me</button>
<div id="myContainer">
<div id="myAnimation"></div>
</div>
As a first implementation you could disable the button before you start the animation, and you only enable it after you are done
var myBtn = document.getElementById("myBtn")
myBtn.addEventListener("click", myFunction);
setTimeout(function() {
myFunction();
}, 3000);
var id = null;
function myFunction() {
var elem = document.getElementById("myAnimation");
var pos = 0;
clearInterval(id);
id = setInterval(frame, 10);
// disable the button
myBtn.disabled = true;
function frame() {
if (pos == 350) {
clearInterval(id);
// enable the button again
myBtn.disabled = false;
} else {
pos++;
elem.style.top = pos + 'px';
elem.style.left = pos + 'px';
}
}
}
#myContainer {
width: 400px;
height: 400px;
position: relative;
background: yellow;
}
#myAnimation {
width: 50px;
height: 50px;
position: absolute;
background-color: red;
}
<h2>JavaScript addEventListener()</h2>
<button id="myBtn">Click Me</button>
<div id="myContainer">
<div id="myAnimation"></div>
You should disable your button during your function.
edited: If you don't want to disable maybe with a lock.
document.getElementById("myBtn").addEventListener("click", myFunction);
setTimeout(function() {
myFunction();
}, 3000);
var id = null;
var lock = false;
function myFunction() {
if(!lock){
lock = true;
var elem = document.getElementById("myAnimation");
var pos = 0;
clearInterval(id);
id = setInterval(frame, 10);
function frame() {
if (pos == 350) {
clearInterval(id);
lock = false;
} else {
pos++;
elem.style.top = pos + 'px';
elem.style.left = pos + 'px';
}
}
}
}
#myContainer {
width: 400px;
height: 400px;
position: relative;
background: yellow;
}
#myAnimation {
width: 50px;
height: 50px;
position: absolute;
background-color: red;
}
<h2>JavaScript addEventListener()</h2>
<button id="myBtn">Click Me</button>
<div id="myContainer">
<div id="myAnimation"></div>
edited: Try making your animation with CSS it's a better practice.
let sum = 0;
const x = 10;
async function firstFunction() {
for (i = 0; i < x; i++) {
sum = sum + i;
}
return sum;
}
async function secondFunction() {
await firstFunction();
console.log('After firstFunction executed sum is', sum);
}
secondFunction();

Scrolling Text to Change Fixed Background Image on Scroll

I'm looking for some help, I'm trying to recreate the homepage on this site.
https://madebyarticle.com/
I don't want to use Jquery, either Plain JS or Vue.
I've got it to do most what I want it to but have a few issues.
Codepen Example
HTML
<main id="parent" class="Loop js-loop">
<section>
<h1 class="step1">One</h1>
</section>
<section>
<h1 class="step2">For</h1>
</section>
<section>
<h1 class="step3">All</h1>
</section>
<section>
<h1>And</h1>
</section>
<section>
<h1>All</h1>
</section>
<section>
<h1>For</h1>
</section>
<!--
These blocks are the same as the first blocks to get that looping illusion going.
You need to add clones to fill out a full viewport height.
-->
<section class="green is-clone">
<h1>One</h1>
</section>
<section class="red is-clone">
<h1>For</h1>
</section>
</main>
CSS
html,
body {
height: 100%;
overflow: hidden;
}
.Loop {
position: relative;
height: 100%;
overflow: auto;
-webkit-overflow-scrolling: touch;
}
section {
position: relative;
text-align: center;
/* min-height: 300px;
max-height: 700px; */
height: 100vh;
}
::scrollbar {
display: none;
}
body {
font-family: "Avenir Next", Helvetica, sans-serif;
font-weight: normal;
font-size: 100%;
}
h1 {
margin: 0;
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 100%;
font-size: 80px;
letter-spacing: 5px;
/* color: #fff; */
text-transform: uppercase;
}
.mystyle {
background: red;
}
.mystyle1 {
background-image: url(https://images.unsplash.com/photo-1528834379234-2de7f8328fd8?ixlib=rb-1.2.1&auto=format&fit=crop&w=1920&q=10);
}
.mystyle2 {
background-image: url(https://images.unsplash.com/photo-1501854140801-50d01698950b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2600&q=80);
}
JS
var doc = window.document,
context = doc.querySelector(".js-loop"),
clones = context.querySelectorAll(".is-clone"),
disableScroll = false,
scrollHeight = 0,
scrollPos = 0,
clonesHeight = 0,
i = 0;
function getScrollPos() {
return (context.pageYOffset || context.scrollTop) - (context.clientTop || 0);
}
function setScrollPos(pos) {
context.scrollTop = pos;
}
function getClonesHeight() {
clonesHeight = 0;
for (i = 0; i < clones.length; i += 1) {
clonesHeight = clonesHeight + clones[i].offsetHeight;
}
return clonesHeight;
}
function reCalc() {
scrollPos = getScrollPos();
scrollHeight = context.scrollHeight;
clonesHeight = getClonesHeight();
if (scrollPos <= 0) {
setScrollPos(1); // Scroll 1 pixel to allow upwards scrolling
}
}
function scrollUpdate() {
if (!disableScroll) {
scrollPos = getScrollPos();
if (clonesHeight + scrollPos >= scrollHeight) {
// Scroll to the top when you’ve reached the bottom
setScrollPos(1); // Scroll down 1 pixel to allow upwards scrolling
disableScroll = true;
} else if (scrollPos <= 0) {
// Scroll to the bottom when you reach the top
setScrollPos(scrollHeight - clonesHeight);
disableScroll = true;
}
}
if (disableScroll) {
// Disable scroll-jumping for a short time to avoid flickering
window.setTimeout(function() {
disableScroll = false;
}, 40);
}
}
function init() {
reCalc();
context.addEventListener(
"scroll",
function() {
window.requestAnimationFrame(scrollUpdate);
},
false
);
window.addEventListener(
"resize",
function() {
window.requestAnimationFrame(reCalc);
},
false
);
}
if (document.readyState !== "loading") {
init();
} else {
doc.addEventListener("DOMContentLoaded", init, false);
}
// Just for this demo: Center the middle block on page load
window.onload = function() {
setScrollPos(
Math.round(
clones[0].getBoundingClientRect().top +
getScrollPos() -
(context.offsetHeight - clones[0].offsetHeight) / 2
)
);
};
const images = document.querySelectorAll('h1');
observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.intersectionRatio > 0) {
entry.target.classList.add('active');
} else {
entry.target.classList.remove('active');
}
});
});
images.forEach(image => {
observer.observe(image);
});
const header = document.getElementById("parent");
const sectionOne = document.querySelector("h1.step1");
const sectionTwo = document.querySelector("h1.step2");
const sectionThree = document.querySelector("h1.step3");
const sectionOneObserver = new IntersectionObserver(function(
entries,
sectionOneObserver
) {
entries.forEach(entry => {
if (!entry.isIntersecting) {
header.classList.add("mystyle");
} else {
header.classList.remove("mystyle");
}
});
});
sectionOneObserver.observe(sectionOne);
const sectionTwoObserver = new IntersectionObserver(function(
entries,
sectionTwoObserver
) {
entries.forEach(entry => {
if (!entry.isIntersecting) {
header.classList.add("mystyle1");
} else {
header.classList.remove("mystyle1");
}
});
});
sectionTwoObserver.observe(sectionTwo);
const sectionThreeObserver = new IntersectionObserver(function(
entries,
sectionThreeObserver
) {
entries.forEach(entry => {
if (!entry.isIntersecting) {
header.classList.add("mystyle2");
} else {
header.classList.remove("mystyle2");
}
});
});
sectionThreeObserver.observe(sectionThree);
// if (document.querySelectorAll('h1.step1.active')){
// document.getElementById("parent").classList.toggle("mystyle");
// } else {
// document.getElementById("parent").classList.remove("mystyle");
// }
// if (document.classList.contains("h1.step1.active")) {
// document.getElementById("parent").classList.add("mystyle");
// } else {
// document.getElementById("parent").classList.remove("mystyle");
// }
I'm having to duplicate the IntersectionObserver for every image I need, is there a cleaner way todo this?
At some points, there are 2 active states so it doesn't display any image only a background colour.
What would be the best way to fade the image on change like the example website?
Are there any examples or scripts that can do what I'm trying todo?
Thanks

Javascript scroll listener not working in chrome or firefox

I have created a simple slider that on scroll scrolls down 100vh. It works perfectly in Safari but it doesn't seem to fire at all in both Chrome or Firefox.
Really appreciate it if anyone could point out to me where i may have gone wrong. I'm sure it's something simple but I just can't figure it out.
I have uploaded the files to my test web server so you can see the issue.
test.liamcrane.co.uk
var slider = document.querySelector('.section__wrapper__inner');
var sections = document.querySelectorAll('.section');
var currentTransform = 0;
var activeSection = 0;
function slideDown() {
if (!(activeSection === sections.length - 1)) {
sectionReset();
currentTransform -= 100;
slider.style.transform = "translate3d(0," + currentTransform + "vh, 0)";
activeSection++;
sections[activeSection].classList.add('active');
}
setTimeout(function() {
ready = true;
}, 2000);
}
function slideUp() {
if (!(activeSection === 0)) {
sectionReset();
currentTransform += 100;
slider.style.transform = "translate3d(0," + currentTransform + "vh, 0)";
activeSection--;
sections[activeSection].classList.add('active');
}
setTimeout(function() {
ready = true;
}, 2000);
}
function sectionReset() {
sections[activeSection].classList.remove('active');
}
var ready = true;
document.addEventListener('scroll', function() {
if (ready && window.pageYOffset > 0) {
ready = false;
slideDown();
} else if (ready && window.pageYOffset <= 0) {
ready = false;
slideUp();
}
});
.section__wrapper {
height: 100vh;
overflow: hidden;
}
.section__wrapper__inner {
height: 100%;
transform: translate3d(0, 0, 0);
transition: transform 1s;
}
.section {
position: relative;
height: 100vh;
color: black;
text-align: center;
}
.section span {
line-height: 100vh;
display:block;
}
<div class="section__wrapper">
<div class="section__wrapper__inner">
<section class="section"><span>1</span></section>
<section class="section"><span>2</span></section>
<section class="section"><span>3</span></section>
<section class="section"><span>4</span></section>
<section class="section"><span>5</span></section>
</div>
</div>
I think is that what you want
I have made a little workarround to force scroll ... maybe a little bit ugly but work see fakeScroll() function bellow.
That force the scrollbar to does not reach the beggining and the end. Because in your example above if the scroll bar reach the end ... scroll event can't be triggered (same if reach the begining).
I have also changed the conditions and the timming from setTimeout (ready = true) to 500. You can change it as you want
Sory for my English.
var slider = document.querySelector('.section__wrapper__inner');
var sections = document.querySelectorAll('.section');
var currentTransform = 0;
var activeSection = 0;
var lastOffset = window.pageYOffset;
var actualOffset = lastOffset;
function fakeScroll(){
if(lastOffset > 1){
window.scrollTo(0,lastOffset - 1);
}else{
window.scrollTo(0,1);
}
}
function slideDown() {
if (!(activeSection === sections.length - 1)) {
sectionReset();
currentTransform -= 100;
slider.style.transform = "translate3d(0," + currentTransform + "vh, 0)";
activeSection++;
sections[activeSection].classList.add('active');
}
fakeScroll();
setTimeout(function() {
ready = true;
}, 500);
}
function slideUp() {
if (!(activeSection === 0)) {
sectionReset();
currentTransform += 100;
slider.style.transform = "translate3d(0," + currentTransform + "vh, 0)";
activeSection--;
sections[activeSection].classList.add('active');
}
fakeScroll();
setTimeout(function() {
ready = true;
}, 500);
}
function sectionReset() {
sections[activeSection].classList.remove('active');
}
var ready = true;
window.addEventListener('scroll', function() {
actualOffset = window.pageYOffset;
if (actualOffset > lastOffset) {
if(ready){
ready = false;
slideDown();
}else{
fakeScroll();
}
} else if (window.pageYOffset <= lastOffset) {
if(ready){
ready = false;
slideUp();
}else{
fakeScroll();
}
}
lastOffset = window.pageYOffset;
});
.section__wrapper {
height: 100vh;
position:relative;
overflow: hidden;
}
.section__wrapper__inner {
height: 100%;
position:relative;
transform: translate3d(0, 0, 0);
transition: transform 1s;
}
.section {
position: relative;
height: 100vh;
color: black;
text-align: center;
}
.section span {
line-height: 100vh;
display:block;
}
<div class="section__wrapper">
<div class="section__wrapper__inner">
<section class="section"><span>1</span></section>
<section class="section"><span>2</span></section>
<section class="section"><span>3</span></section>
<section class="section"><span>4</span></section>
<section class="section"><span>5</span></section>
</div>
</div>
window.addEventListener('scroll', function() {
if (ready && window.pageYOffset > 0) {
ready = false;
slideDown();
} else if (ready && window.pageYOffset <= 0) {
ready = false;
slideUp();
}
});

Add class when element reaches top of viewport

I am trying to add a class to the header when an element reaches the top of the viewport but I cannot seem to find out why it is not working. I have no errors and I have checked to see that jquery is fetching the offsets and it is. Any help would be great. I would also like to know how to extend this code to any number of section's rather than stating just 6.
JS FIDDLE
$(document).ready(function () {
var project1 = $('section:nth-child(1)').offset();
var project2 = $('section:nth-child(2)').offset();
var project3 = $('section:nth-child(3)').offset();
var project4 = $('section:nth-child(4)').offset();
var project5 = $('section:nth-child(5)').offset();
var project6 = $('section:nth-child(6)').offset();
var $window = $(window);
$window.scroll(function () {
if ($window.scrollTop() >= project1) {
$("header").removeClass().addClass("project1");
}
if ($window.scrollTop() >= project2) {
$("header").removeClass().addClass("project2");
}
if ($window.scrollTop() >= project3) {
$("header").removeClass().addClass("project3");
}
if ($window.scrollTop() >= project4) {
$("header").removeClass().addClass("project4");
}
if ($window.scrollTop() >= project5) {
$("header").removeClass().addClass("project5");
}
if ($window.scrollTop() >= project6) {
$("header").removeClass().addClass("project6");
}
});
});
The method .offset(), returns an object containing the properties top and left:
{top: 1808, left: 8}
Therefore you need to access the top property in your conditional statements.
Change
if ($window.scrollTop() >= project1) { ... }
to:
if ($window.scrollTop() >= project1.top) { ... }
Updated Example
As a side note, $('section:nth-child(1)').offset() will be undefined because the section element isn't the first element (the <header> is). Use :nth-of-type rather than :nth-child. Since you're using jQuery, eq() would work too.
$(document).ready(function() {
var project1 = $('section:nth-of-type(1)').offset();
var project2 = $('section:nth-of-type(2)').offset();
var project3 = $('section:nth-of-type(3)').offset();
var project4 = $('section:nth-of-type(4)').offset();
var project5 = $('section:nth-of-type(5)').offset();
var project6 = $('section:nth-of-type(6)').offset();
var $window = $(window);
$window.scroll(function() {
if ( $window.scrollTop() >= project1.top) {
$("header").removeClass().addClass("project1");
}
if ( $window.scrollTop() >= project2.top ) {
$("header").removeClass().addClass("project2");
}
if ( $window.scrollTop() >= project3.top ) {
$("header").removeClass().addClass("project3");
}
if ( $window.scrollTop() >= project4.top ) {
$("header").removeClass().addClass("project4");
}
if ( $window.scrollTop() >= project5.top ) {
$("header").removeClass().addClass("project5");
}
if ( $window.scrollTop() >= project6.top ) {
$("header").removeClass().addClass("project6");
}
});
});
header {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 100px;
background: #000;
}
header.project1 {
background: red;
}
header.project2 {
background: orange;
}
header.project3 {
background: blue;
}
header.project4 {
background: green;
}
header.project5 {
background: red;
}
header.project6 {
background: blue;
}
section {
height: 900px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<header></header>
<section>Section 1</section>
<section>Section 2</section>
<section>Section 3</section>
<section>Section 4</section>
<section>Section 5</section>
<section>Section 6</section>

Add transition to dynamic DOM element

I am trying to add transition to div element. Transition is applied successfully to first added div element. When I add more divs by clicking on .container then these new divs do not have this animation. How can I add transitions to dynamic divs?
Below is my code
var transition = 1;
var x = 0;
var left = 0;
draw('white');
var $rect = $('.rect');
var $container = $('.container');
var arr = ['red', 'green', 'black'];
var count = 0;
$('body').on('click', function () {
draw(arr[count]);
count++;
});
function init () {
setInterval(onEachStep, 1000/60);
}
function onEachStep () {
x += transition;
left = left + transition;
$rect.css('left', left + 'px');
if (x > $container.outerWidth() - $rect.outerWidth()) {
transition = -10;
}
if (x < 0) {
transition = 1;
}
}
function draw (color) {
var rect = $('<div />', {
'class': 'rect'
});
rect.css('background', color);
rect.appendTo('.container');
}
init();
.container {
background: lightblue;
height: 300px;
}
.rect {
width: 50px;
height: 20px;
position: absolute;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div class="container"></div>
You need to store left, x and transition for each rectangle. Having the same left, x and transition for all of them would mean they all will be at the same position on any point in time.
draw('white');
var $container = $('.container');
var arr = ['red', 'green', 'black'];
var count = 0;
$('body').on('click', function () {
draw(arr[count%arr.length]);
count++;
});
function init () {
setInterval(onEachStep, 1000/60);
}
function onEachStep () {
$rect = $('.rect');
for(var i = 0; i < $rect.length; i++) {
var thisrect = $rect[i];
thisrect.x += thisrect.transition;
thisrect.left += thisrect.transition;
$(thisrect).css('left', thisrect.left + 'px');
if (thisrect.x > $container.outerWidth() - $(thisrect).outerWidth()) {
thisrect.transition = -10;
}
if (thisrect.x < 0) {
thisrect.transition = 1;
}
}
}
function draw (color) {
var rect = $('<div />', {
'class': 'rect'
});
rect.css('background', color);
rect[0].x = 0;
rect[0].left = 0;
rect[0].transition = 1;
rect.appendTo('.container');
}
init();
.container {
background: lightblue;
height: 300px;
}
.rect {
width: 50px;
height: 20px;
position: absolute;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div class="container"></div>
You have to update the $rect variable for that. find the code below
var transition = 1;
var x = 0;
var left = 0;
draw('white');
var $rect = $('.rect');
var $container = $('.container');
var arr = ['red', 'green', 'black'];
var count = 0;
$('body').on('click', function () {
draw(arr[count]);
count++;
$rect = $('.rect');
});
function init () {
setInterval(onEachStep, 1000/60);
}
function onEachStep () {
x += transition;
left = left + transition;
$rect.css('left', left + 'px');
if (x > $container.outerWidth() - $rect.outerWidth()) {
transition = -10;
}
if (x < 0) {
transition = 1;
}
}
function draw (color) {
var rect = $('<div />', {
'class': 'rect'
});
rect.css('background', color);
rect.appendTo('.container');
}
init();
.container {
background: lightblue;
height: 300px;
}
.rect {
width: 50px;
height: 20px;
position: absolute;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div class="container"></div>

Categories

Resources