Why isn't my scrolling position working in react? - javascript

I'm trying to make a sticky nav with an active state while scrolling. So when you're scrolling over each section, the nav has an active state. Kind of like what is seen here:
https://codepen.io/rishabhp/pen/aNXVbQ
The problem I'm having is some of my numbers aren't correct. Here's the code:
handleScroll = () => {
let sections = document.querySelectorAll('.deal-details__container'),
nav = document.querySelectorAll('.overview-nav'),
navHeight = nav[0].clientHeight;
let totalScroll = document.body.getBoundingClientRect().top;
window.addEventListener('scroll', () => {
sections.forEach(section => {
let topOffset = section.getBoundingClientRect().top;
let top = topOffset - navHeight,
bottom = top + section.clientHeight;
if (totalScroll >= top && totalScroll <= bottom) {
this.setState({ activeSection: true });
} else {
this.setState({ activeSection: false });
}
});
});
console.log(totalScroll);
};
For instance my totalScroll inside of the forEach only amounts to -20 or something similar.
I feel like I'm missing something simple. Any thoughts?

Related

Adding class to element after scroll

So I'm working on showing/hiding a nav element based on scrolling behavior. Once the user scrolls and scrolls past the nav element, I add a class to make it sticky but keep it out of view. Then once the user stops scrolling I add another class to the transition the element into view. Once the user scrolls again that class needs to be removed again and the nav disappears again.
This is the JS
let mobile_toolbar = document.querySelector(".mobile-toolbar");
let mobile_toolbar_top = (mobile_toolbar.offsetTop) + 50;
let scrollpos = window.scrollY;
let timer = null;
window.addEventListener(
"scroll",
function () {
scrollpos = window.scrollY;
console.log(timer)
if (timer !== null) {
if (scrollpos > mobile_toolbar_top) {
mobile_toolbar.classList.add("mobile-toolbar__hidden");
mobile_toolbar.classList.remove("mobile-toolbar--fixed");
clearTimeout(timer);
} else {
mobile_toolbar.classList.remove("mobile-toolbar__hidden");
mobile_toolbar.classList.remove("mobile-toolbar--fixed");
clearTimeout(timer);
}
}
if (scrollpos > mobile_toolbar_top) {
timer = setTimeout(function () {
mobile_toolbar.classList.add("mobile-toolbar--fixed");
}, 400);
}
},
false
);
As you can see I'm setting a timer to detect when the user stops scrolling and also check the scroll position to determine whether the add the fixed class or not. However, this isn't quite working as I'd like as the nav once slides down as soon as I scroll past itself and then disappears again as the timer is already not null at this point. Can anyone tell me what's wrong with my cod or if there's a better way to detect when the user has stopped scrolling? Vanilla JS only please as I'm trying not to use jQuery
you can refer a below code (it's tell you when user stop scrolling)
<html>
<body onscroll="bodyScroll();">
<script language="javascript">
var scrollTimer = -1;
function bodyScroll() {
document.body.style.backgroundColor = "white";
if (scrollTimer != -1)
clearTimeout(scrollTimer);
scrollTimer = window.setTimeout("scrollFinished()", 500);
}
function scrollFinished() {
document.body.style.backgroundColor = "red";
}
</script>
<div style="height:2000px;">
Scroll the page down. The page will turn red when the scrolling has finished.
</div>
</body>
</html>
Check out this example:
https://codepen.io/len0xx/pen/JjadOgR
const toolbar = document.querySelector('.mobile-toolbar')
const mobileToolbarTop = (toolbar.offsetTop) + 50
let previousScroll = 0
let previousTimeout = 0
window.addEventListener('scroll', () => {
const currentScroll = window.scrollY
if (currentScroll > mobileToolbarTop) {
if (currentScroll > previousScroll) {
toolbar.classList.add('mobile-toolbar__hidden')
toolbar.classList.remove('mobile-toolbar--fixed')
}
else {
toolbar.classList.remove('mobile-toolbar__hidden')
toolbar.classList.add('mobile-toolbar--fixed')
}
}
if (previousTimeout) {
clearTimeout(previousTimeout)
}
previousTimeout = setTimeout(() => {
const newScroll = window.scrollY
if (newScroll <= currentScroll) {
toolbar.classList.remove('mobile-toolbar__hidden')
toolbar.classList.add('mobile-toolbar--fixed')
}
}, 300)
previousScroll = currentScroll
})

Make navbar link active on wheel event rather than scroll?

I want to make the links in my navbar have an active class when you scroll into the corresponding section.
The code below was working just fine until I implemented a smooth scroll/parallax library which removes the scroll event.
I tried making the code work using the wheel event, tried seeing if there was anything similar to scrollY for wheel events but I couldn't find anything.
Any ideas on how I could implement this feature? It can be different from the implementation I had before
EDIT: Here's a codepen. If you uncomment the locomotive portion, the code no longer works. How can I make it work?
const sections = document.querySelectorAll("section");
const navLinks = document.querySelectorAll("nav a");
window.addEventListener("scroll", function () {
let navbar = document.querySelector("nav");
let current = "";
sections.forEach(function (section) {
const sectionTop = section.offsetTop;
const sectionHeight = section.clientHeight;
if (scrollY >= sectionTop - sectionHeight / 3) {
current = `#${section.getAttribute("id")}`;
}
navLinks.forEach(function (each) {
// add/remove active class
each.classList.remove("nav-active");
if (each.getAttribute("href") == current) {
each.classList.add("nav-active");
}
});
});
});
Try using an IntersectionObserver instead:
const navLinks = document.querySelectorAll("nav a");
const updateNav = (entries, observer) => {
const matchingIds = entries.filter(e => e.isIntersecting).map(e => `#${e.target.id}`);
if (matchingIds.length !== 0) {
const current = matchingIds[0];
navLinks.forEach(function(link) {
link.classList.remove("nav-active");
if (link.getAttribute("href") == current) {
link.classList.add("nav-active");
}
});
}
};
const options = {
root: null,
rootMargin: "0px",
threshold: 0.66
}
const observer = new IntersectionObserver(updateNav, options);
document.querySelectorAll("*[data-scroll-section]").forEach(el => observer.observe(el));
Intersection Observer API
Demo

JS Smooth Scroll Vertical & Horizontal Function

I want to create a multisite in a one-page, where everytime a link is clicked, it automatically scrolls to that element in the page (all div elements).
The function works but it still jumps to the given element.
Here's the code I've got so far and the elements I use to call the function:
<li class="topli">
<a id="toplink" onclick="Scroll('#home')" href="javascript:void(0);">HOME</a>
</li>
<script>
function Scroll(element) {
var ID = element.split('#').join('');
var target = document.getElementById(ID);
var offset = target.getBoundingClientRect();
console.log("X:",offset.x,"Y:",offset.y);
if (window.scrollY != offset.y) {
window.scroll(window.scrollY, offset.y);
}
if (window.scrollX != offset.x) {
window.scroll(window.scrollX, offset.x);
}
}
</script>
If needed I'll add a more detailed code to a JSFiddle.
Create jQuery helper for this
(function($) {
$.fn.goTo = function() {
$('html, body').animate({
scrollTop: $(this).offset().top + 'px'
}, 'fast');
return this;
}
})(jQuery);
And use like
$('#el').goTo();
Try this for scrolling vertically (where 100 is the rate of scroll):
const goTo = (targetEl) => {
const elm = document.querySelector(targetEl);
const offset = elm.getBoundingClientRect().bottom;
if (offset > window.innerHeight) {
window.scroll(0, window.scrollY + 100);
setTimeout(() => {
goTo(targetEl);
}, 16.666);
} else {
return;
}
};
Call it like so:
goTo('#scroll-target');
or on click:
window.addEventListener('DOMContentLoaded', () => {
document.querySelector('.long-div').addEventListener('click', () => {
goTo('#scroll-target');
});
});
Vertical smooth scroll, easy and native way:
let element = document.querySelector('#element');
// Vertical Scroll
this.element.scrollTo({
left: element.offsetLeft,
behavior: 'smooth'
})
// Horizontal Scroll
element.scrollIntoView({block: "end", behavior: "smooth"});
docs:
https://developer.mozilla.org/pt-BR/docs/Web/API/Element/scrollIntoView
https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollTo

Sticky nav bar with native JavaScript only

How would I make a sticky nav bar without using jQuery?
I want to do something like:
if (scrollheight == x)
navbar.addclass "sticky"
I have recently worked on sticky sidebar nav using EmberJS.
Anyway at the end I decided to extract the solution into vanilla javascript based pen as it seems sticky navigation is pretty common feature.
You can see it here: http://codepen.io/moubi/pen/ALpmwy
Github project: https://github.com/moubi/sticky-nav
Here is the js code I am using for the functionality:
// https://github.com/moubi/sticky-nav
const NAV_OFFSET = 30;
class StickyNav {
constructor() {
this.el = null;
this.anchor = null;
this.removedScrollClass = '';
this.events();
}
didRender() {
this.el = document.getElementsByTagName('nav')[0];
this.anchor = document.getElementsByClassName('nav-anchor')[0];
this.removedScrollClass = this.el.className;
this.onResize();
}
onResize() {
var { paddingLeft, paddingRight } = window.getComputedStyle(this.el.parentNode),
parentWidth = this.el.parentNode.offsetWidth - parseInt(paddingLeft) - parseInt(paddingRight);
this.el.style.width = `${parentWidth}px`;
}
onScroll() {
var scroll = Math.max(document.documentElement.scrollTop, document.body.scrollTop),
topOffset = this.anchor.offsetTop - NAV_OFFSET;
if (this.el.className.indexOf('scroll') != -1) {
if (scroll <= topOffset) {
this.el.className = this.removedScrollClass;
}
} else if (scroll >= topOffset) {
this.el.className += ' scroll';
}
}
events() {
window.addEventListener('load', () => { this.didRender(); });
window.addEventListener('scroll', () => { this.onScroll(); });
window.addEventListener('resize', () => { this.onResize(); });
}
}
new StickyNav();
This solution should work for top nav bar as well with some minor adjustments.
In the code I am actually adding scroll class, but in your case it should be sticky - at the end "sticky" sounds more reasonable.

jQuery - sticky element is overlapping the footer, how to avoid it?

I have a sticky sidebar working on my project, but when you go to the bottom of the page, the sticky sidebar is overlapping my footer.
What I want is that when the sticky element reach the footer, then stop just right there so the user can see the entire footer.
here is a demonstration of what I have so far.
or a jsfiddle in case it is easier for you
this is the code:
var stickySidebar = $('.sticky');
if (stickySidebar.length > 0) {
var stickyHeight = stickySidebar.height(),
sidebarTop = stickySidebar.offset().top;
}
// on scroll move the sidebar
$(window).scroll(function () {
if (stickySidebar.length > 0) {
var scrollTop = $(window).scrollTop() + 70;
if (sidebarTop < scrollTop) {
stickySidebar.stop(true, false).animate({top: scrollTop - sidebarTop});
// stop the sticky sidebar at the footer to avoid overlapping
var sidebarBottom = stickySidebar.offset().top + stickyHeight,
stickyStop = $('.main-content').offset().top + $('.main-content').height();
if (stickyStop < sidebarBottom) {
var stopPosition = $('.main-content').height() - stickyHeight;
stickySidebar.stop(true, true).animate({top: stopPosition});
}
}
else {
stickySidebar.stop().animate({top: 0});
}
}
});
$(window).resize(function () {
if (stickySidebar.length > 0) {
stickyHeight = stickySidebar.height();
}
});
This is maybe not perfect, but I think it gives you the right idea, how to solve this problem. You just have to check, if the bottom of the sidebar is below the top position of the footer. Than stop the animation.
http://jsfiddle.net/hdj99b21/1/
[...]
var stickyTopPos = stickySidebar.offset().top;
var stickyBottomPos = stickyHeight + stickyTopPos;
var footerTopPos = $('footer').offset().top;
if(stickyBottomPos >= footerTopPos) {
var stopPosition = footerTopPos - stickyHeight;
stickySidebar.stop(true, true).css({top: stopPosition});
}
[...]

Categories

Resources