toggle a burger menu background based on which section is overlapping - javascript

I am trying to change the burger menu on my website everytime it sits on top of a section with a light background for visibility.
I tried to use intersection observer, and the solution seems working, however it triggers too early on scroll, before actually the section intersects with the menu.
I am new to intersection observer, I tried to find something useful online but with no much luck.
const body = document.querySelector('body')
const sections = body.querySelectorAll('.change-menu')
const options = {
root: null,
threshold: 0,
rootMargin:'-40px 0px -80% 0px'
}
const observer = new IntersectionObserver((entries,observer) => {
entries.forEach(entry => {
if(entry.isIntersecting) {
document.querySelectorAll('.menu-item').forEach(bar => {
body.classList.add('black')
})
} else {
document.querySelectorAll('.menu-item').forEach(bar => {
body.classList.remove('black')
})
}
})
}, options)
sections.forEach(section => {
observer.observe(section)
})
Here is a jsfiddle of an example of my problem:
https://jsfiddle.net/bc7w8zt1/
Anyone can shed some light on it please?

I think the JSFiddle is buggy, at least, I cannot get any response from it when I change the IO settings. On a real site, having a fixed top margin and variable bottom margin is viewport-height dependent: the top margin must be higher up in the viewport than the bottom margin, otherwise the CB doesn't fire. I think this is part of the issue in JSFiddle - the viewport is very short. For instance, with rootMargin:'-50px 0px -90% 0px' the viewport (window.innerHeight) must be at least 500px (actually ~490px worked) so that the div you are observing triggers the viewport's bottom margin at least 50px (10% of innerHeight) down from the top BEFORE it hits the top margin a fixed 50px from the top. If the bottom is inside (that is, above) the top it won't fire. For any viewport, though again I couldn't get anything from JSFiddle, this seemed to work: rootMargin:'0px 0px -100% 0px'. This will not trigger the IO callback until the grey div's top reaches the top of the viewport, and as you keep scrolling the grey div will be intersecting until it is off-screen. The hamburger menu class changes accordingly.

Related

ReactJS screen follows element

In my app, when viewing a restaurant page, comments are hidden by default. The user can expand them as desired. I want the screen to slide down smoothly behind the bottom edge of the comments window when the comment box is maximized.
When opening the page:
When expanding comments:
That's what I need:
When rolled back, the screen moves smoothly with the bottom edge of the window. I'd like to achieve the same effect when unwrapping.
I implemented folding and expanding in this way:
useEffect(() => {
if (isOpen) {
innerWindowRef.current.setAttribute('style', 'height:' + totalCommentHeight.current + 'px')
outWindowRef.current.setAttribute('style', 'padding: 5px')
} else {
innerWindowRef.current.setAttribute('style', 'height: 0; padding: 0')
setTimeout(() => outWindowRef.current.setAttribute('style', 'padding: 5px 5px 0'), 900)
}
}, [isOpen])
I tried to use the scrollTo() function, but it did not suit me because it only works with elements that have already been drawn, and not with those that are being drawn (expanded) at the moment. Therefore, I was able to implement this option only with a delay, which did not suit me.
The same goes for the scrollIntoView() function.
It is necessary that the screen always scrolls to the bottom of the page at the same speed as the animation of the expanding window. How can I implement this?
This should solves your problem:
window.scrollBy({
top: 100, // this is the amount to be added to the current scroll. it can be negative or positiove number (in px)
left: 0,
behavior: 'smooth'
});
You need to give the height of the expanded comment block to top as value

Navbar which hides when you scroll down and appears again when you scroll up

I have explored few approaches to this, but none really seems to work exactly like I would like:
I would like that when scrolling down, navbar is moving up at the speed the user is scrolling down, like that is static at that point.
When it disappears, I would like that the bottom of it is still visible, because this is where I have a progress bar (but maybe progress bar should detach at that point and be on top of the viewport).
When you scroll up, I would like that navbar appears again, again at the speed of scrolling, like it is static, until you see the whole navbar, when it should stick to the top of the viewport.
Here is an example of behavior I would like, but not performance/experience (because behavior is implemented using scroll event, it is not smooth).
I have also attempted to use CSS transform, which would on scroll down add a class to hide the navbar, and scroll up remove the class, animating the navbar hiding/showing, but the issue with that is that animation speed is disconnected with scrolling speed.
I tried CSS sticky position as well, but it looks like I need the opposite of what it provides.
Is there some other way to make this work well?
I've looked at your problem and I think i found a simple approach.
with this simple function you can get the amount of pixels user has scrolled.
window.onscroll = function (e) {
console.log(window.scrollY); // Value of scroll Y in px
};
after user scrolls the desired amount of pixels, make the progress bar fixed top ( or position:fixed;top:0)
Checking the link you provided, it seems to work as expected (you want it to be linked to the scroll event since you want it to move as "static"). If, though, it staggers on some system due to inconsistent scroll events, you could try adding a transition property with a small enough duration. Keep in mind the this should only be enabled while the position property remains the same, otherwise when changing from "absolute" to "fixed" it would mess things up, since the coordinate origin changes.
So you can add another variable let changedPosition = false; and whenever you change the position property you can do
if (position !== "absolute") {
changedPosition = true;
} else {
changedPosition = false;
}
position = "absolute";
or
if (position !== "fixed") {
changedPosition = true;
} else {
changedPosition = false;
}
position = "fixed";
and when you apply the style do
navbar.style = `position: ${position}; top: ${navbarTop}px; transitiona: ${
changedPosition ? "none" : "100ms linear"
}`;
like https://codepen.io/gpetrioli/pen/XWVKxNG?editors=0010
You should play around a bit with the transition properties you provide, i just put some sample values there.

Change element background on scroll, based on current section and performance optimized

The idea is to change the .wrapper background depending on the current section. Unfortunately, there are some limitations. I need to change it based on the current window.scrollTop and section.offsetTop and NOT on which section is in the viewport at the moment. (IntersectionObserver will not work)
It will be something like scrollSpy, but I am not able to loop over all of the sections in every scroll event because of performance reasons. Also, I am using a virtual scroll plugin for smooth scrolling, which makes even more calculations on scroll.
To the code I have till the moment:
My sections look like this:
<section class="intro"
data-bg="#ffffff"
data-color="#1A1919"
>
Section content goes here
</section>
I have an array of objects. In each object, I have the node element and its top.
const sections = [
{
el: section.intro, // Node
top: 1224 // Number
},
....
]
I can take the scrollTop from the plugin on scroll. So the code for now looks like:
const wrapper = document.querySelector('.wrapper');
const changeBg = (scrollTop, sections) => {
sections.map(({ el, top }) => {
const bg = el.getAttribute('data-bg');
const color = el.getAttribute('data-color');
if (top < scrollTop + window.innerHeight) {
console.log(bg);
wrapper.style.backgroundColor = bg;
wrapper.style.color = color;
}
});
};
myCustomPluginForScroll.on('scroll', e => {
const { y } = e.scroll;
changeBg(y, sections);
});
The problems are:
I loop all of the sections on every scroll event (which is multiplied by many from the plugin to make smooth scrolling) and it causes lag on scroll;
As I scroll more downwards, more and more sections become valid for the condition top < y + window.innerHeight which changes the wrapper background even more times per scroll.
I can't use debouncer to make number of calls of changeBg lower, because of the smooth scroll plugin.
To the question:
How can I make call of changeBg only once when new section is added/removed from the condition of top < y + window.innerHeight and take the bg/color attributes only on the last added/removed section.
Attaching screenshot with log of bg with 6 sections and scrolled to the bottom of the site. That way, on every single scroll I have 6 changes of wrapper's background.

Transition logo only when it slides left not when it goes back to original position

I'm building my homepage with a custom theme in wordpress.
I placed my logo in the middle of the page and I need it goes fixed when I scroll to it. I achieved it with this code:
var stickyLogo = jQuery('.logo_centered').offset().top;
jQuery(window).scroll(function() {
if (jQuery(window).scrollTop() > stickyLogo) {
jQuery('.logo_centered').addClass('fixato');
}
else {
jQuery('.logo_centered').removeClass('fixato');
}
});
and it works.
I also added a transition when click on it to hide it and open the slidein menu.
jQuery('.logo_centered').on('click', function(){
jQuery('.logo_centered').toggleClass('hideme')
})
While this is the menu opening
jQuery('.centrato').on('click', function(){
jQuery('.overlay').toggleClass('overlay--active')
})
jQuery('.overlay').on('click', function(){
if(jQuery('.overlay').hasClass('overlay--active')){
jQuery('.overlay').removeClass('overlay--active')
}
})
jQuery('.overlay').on('click', function(){
jQuery('.logo_centered').toggleClass('pushme');
})
jQuery('.overlay').on('click', function(){
if(jQuery('.logo_centered').hasClass('hideme')){
jQuery('.logo_centered').removeClass('hideme')
jQuery('.logo_centered').removeClass('pushme')
}
})
But, as you can see on my demo website:
http://arioldigioielleria.it/test/
the animation is triggered also when it goes fixed and back.
How can I avoid this?
You can disable the transition when scrolling by making your transition specific to horizontal movement:
.logo_centered {
transition:left 0.5s;
}
By the way, this is now possible using only CSS with position:sticky. You might find it more maintainable than JavaScript:
A stickily positioned element is treated as relatively positioned until it crosses a specified threshold, at which point it is treated as fixed until it reaches the boundary of its parent.
-MDN

Disable scrolling but keep scrollbar CSS

I can't find a solution to this, there was a question over here, but the answers are not very usable (at least for me).
I have a JavaScript modal pop-up that disables everything on the background by placing transparent div over the page. It also disables the scrolling by setting the overflow to hidden, and must do so, because the page is scrollable with the mouse wheel otherwise and distracts the user.
The problem is, when hiding and showing the scrollbar the page resizes and the effect is ugly. Also, my page is designed in such a way that if I stop it from resizing that would be ugly either.
What I want is to disable the scrollbar, but keep it visible (the page content is longer than the screen fits). Is this somehow possible in CSS?
Instead of changing the css, which will remove the scrollbar, and as you said change the layout of the page, try calling a jquery function instead.
// call your pop up and inside that function add below
$('body').on('scroll mousewheel touchmove', function(e) {
e.preventDefault();
e.stopPropagation();
return false;
});
then when you close the modal, call the same function but replace on with off
Since scrollbars are not all 17px wide, I solved this with JavaScript. That is, I calculated the exact width of the scrollbar and added an equal amount of margin to the right of the body element. This also works when the scrollbar isn't present due to a high resolution or a lack of content.
function toggleMenu() {
// get width before hiding scrollbar
let oldWidth = document.documentElement.clientWidth;
// toggle CSS class that sets overflow to hidden
document.body.classList.toggle('MenuOpen');
// get new width after hiding scrollbar
let newWidth = document.documentElement.clientWidth;
// set margin-right value equal to width of the scrollbar
let scrollbarWidth = Math.max(0, newWidth - oldWidth);
document.body.style.marginRight = `${scrollbarWidth}px`;
}
...and my CSS looks like:
html {
background-color: #e6e6e6; /* color of fake scrollbar */
}
body.MenuOpen {
overflow: hidden;
}
Once you start showing your popup, give the body a class (like popupOpen). This should be an easy workaround.
.popupOpen {
overflow: hidden;
margin-right: 17px //size of the scrollbar in each browser
}
When you close your popup, simply remove the class from the body.

Categories

Resources