ReactJS screen follows element - javascript

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

Related

toggle a burger menu background based on which section is overlapping

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.

iOS browser scrolling bug

I'm having a vague bug while scrolling in a browser on an iOS device (iPad), it works 90% of the time but 10% it the scrolling is blocked. I then have to wait for a couple of seconds before it works again. This often happens after I've contracted/expanded a filter panel, which is also a scrollable component.
On all other devices, the scrolling is no issue whatsoever. I've looked everywhere on the internet but I couldn't find anyone with a similar problem?
My two scrollable components do have overflow-y: scroll and
-webkit-overflow-scrolling: touch, but in general I highly doubt it's a CSS issue as it works most of the time, just not at random times.
I have tried adding onTouchStart={ () => {} } to my component, but also that doesn't solve it.
This problem is still here in iOs15, and I developed a small fix and put it on GitHub.
Complete code here:
https://github.com/AlessandroCipolletti/fix-ios15-safari-scroll/blob/main/preventScrollBugsIfNeeded.ts
I noticed that the bug happens only when the scrollable content is "all on top" or "all at the end" of its scroll space.
I mean, when there is no scroll available in one of the two directions (top-bottom / left-right makes no difference for the occurrence of the bug).
So the idea is to check if this is the case during a touchstart event, and if so, make a 1px scroll by code.
I don't know why iOS "needs" this to make a proper scroll, but it works fine for me.
// do this inside a onTouchStart event handler:
const target = myScrollableContainer
// if it has vertical scroll
if (target.scrollHeight > target.clientHeight) {
// if scroll is on top
if (target.scrollTop === 0) {
// move content 1px up
target.scrollTop = 1
} else
// if scroll in at the bottom
if (target.scrollTop === (target.scrollHeight - target.clientHeight)) {
// move content 1px down
target.scrollTop = (target.scrollHeight - target.clientHeight) - 1
}
}
// if it has horizontal scroll
if (target.scrollWidth > target.clientWidth) {
// if scroll is at the beginning (left)
if (target.scrollLeft === 0) {
// move content 1px to the left
target.scrollLeft = 1
} else
// if scroll is at the end (right)
if (target.scrollLeft === (target.scrollWidth - target.clientWidth)) {
// move content 1px to the right
target.scrollLeft = (target.scrollWidth - target.clientWidth) - 1
}
}
I've come across this bug before. It seems to arise when setting elements to be the height of the browser viewport (e.g., height: 100vh). Try setting this on your <body> element:
body {
position: relative;
}

Scroll down transformation and logo movement

What would be the best way to achieve the following:
I would like to get a big centered logo, to shrink and move to the top left of the page as you scroll down and become part of the nav bar.
I would also like a nav bar to fade down from the top.(I only know how to do this timed not dependent on scroll)
When the site loads it will look like just a logo with a simple background. When you scroll down the logo will shift to the top left and the nav bar will ease in and the background picture will also move up out of sight.
Is there any way that you can do the transformation dependent on how far you have scrolled. So that if you stop half way your transformation will also stop?
If I understand you correctly, you want to make a transition to the nav bar / header and the logo dependent on how much you have scrolled, while you are scrolling? If so, it is not as hard is it might sound.
First we have to find out, how many pixels you have scrolled on the page and how many pixels to scroll before the transition is complete. Se code example below.
$(window).on("scroll", function() {
var yPos = $(this).scrollTop(),
yPer = (yPos / 120);
if (yPer > 1) {
yPer = 1;
}
});
Here the y position of the page is found yPos and the transition is set to complete when scrolled down 120 pixels. With these informations we can then calculate the percentage of scrolled pixels until reaching 120 pixels; yPer.
0px = 0%, 60px = 50%, 120px = 100%
In the if-statement we make sure, that the percentage cannot exceed 100%.
HTML and CSS part
Before we go any further with the JavaScript part, let’s setup the HTML and CSS.
HTML structure:
<header></header>
<img class="logo" src="http://oi68.tinypic.com/2z5m4pu.jpg" />
In this case the logo is not inside the header, since we are going to hide the header by default, which would also hide all the child-elements including the logo, which we do not want.
Hiding the header/nav bar:
header {
width: 100%;
height: 60px;
background: #FFF;
position: fixed;
top: -60px;
opacity: 0;
}
Since you want the header to fade in from the top while scrolling, top is set to the negative height of the header itself and the opacity to 0.
Centering the logo:
img.logo {
height: 200px;
position: fixed;
top: 50%;
left: 50%;
transform: translate3d(-50%, -50%, 0);
}
In this way we are going to center the logo in the middle of the screen, no matter the size and resolution of the browser window. If the values in top, left and translate3d all where set to 0, the logo would be in the top left corner. That's why this is a good way to do it in our situration.
JavaScript part - continued
Now we're ready the continue with the JavaScript part.
Store heights in variables:
var header = $("header"),
headerHeight = header.height(),
logo = $(".logo"),
logoHeight = logo.height();
$(window).on("scroll", function() {
// Rest of our code
});
To make it a lot easier for us self later on, we are going to automatically find the height of both the header and the logo, and store those in variables. By doing this we don’t have to change anything in the JavaScript code, if we later on in the future wants to change the height of our elements. We only have to change it, in the CSS.
We are going to set those variables before our scroll-function since, they do not change while scrolling as seen above.
The Calculations:
var logoPos = ( -1*(yPer * 50) + 50),
logoSize = ((headerHeight * yPer) - (logoHeight * yPer) + logoHeight),
headerPos = ((yPer * headerHeight) - headerHeight);
This is a very important part of our code. These expressions are the once that calculates how the elements should animate as we scroll.
logoPos: Calculating the new position of the logo as we scroll. We know that the top, left and translate3d always has
a value of "50". | Starts on 50, ends on 0.
logoSize: Calculating the size of the logo as we scroll. | Starts on the height of the logo, ends on the height of the
header.
headerPos: Calculating the position of the header as we scroll. | Starts on the negative height of header, ends on 0.
NOTE: If we didn’t stored the heights of your elements like before, we had to manually change them in calculations if we later wanted to change them.
Add the new styles:
logo.css({
top: logoPos + "%",
left: logoPos + "%",
transform: "translate3d(-" + logoPos + "%,-" + logoPos + "%,0)",
height: logoSize
});
header.css({
top: headerPos,
opacity: yPer
});
Here we using all our calculations to style our elements as we scroll. On the logo at translate3d we have to remember to put a minus before the logoPos. Else the logo will start moving from the bottom right instead of the center.
For the opacity, we don't have to calculate anything. We just use the yPer.
Well that’s basically it. Hope this could help you out.
You can se the full code in context and with comments in this fiddle:
Working Fiddle
If I understand your question correctly then you should use $(window).scrollTop().
For example in your javascript file,
$(document).ready(
function () {
setInterval(function () {
if ($(window).scrollTop() >= 650) {
// Move your logo to where you want
}
else{
//Move the logo back to the original position
}
}, 1000);
});
This code will check if the current top of your scroll is past 650px and will trigger that code. If it's less than 650px they it will trigger the other code to move your picture back. The setInterval is used so it will constantly check every 1 second but you can change that to suit your needs.
Change the 650 to whatever you think fits best.
It kind of hard to figure out your problem without providing any code but I hope this helps.

"Back to top" element which works with jQuery popup

I have a jQuery selectmenu list which opens as a popup because it so long. What I'd like is a "back to top" button at the end of the list. I found this tutorial which looks like it should do the trick, except that it doesn't show the "Back to Top" text because I imagine that it's underneath the popup. Any ideas how to get around this? Or another implementation of a "back to top" solution?
Break it down by parts. The Back to Top will automatically put you on the top of the page. It's no rocket science.
Then you can add some styling via CSS. In this case the tutorial uses the class "back to top". Back to Top. You can make this look like whatever you want, no big deal.
However, I'll recommend you keep this bit of code, since is the one that positions it. position: fixed; bottom: 2em; right: 0px;. As a side note, replace position:fixed with position:relative, so it appears on the popup.
Then, you can add some nice smooth scrolling effects or whatever you want.
jQuery(document).ready(function() {
var offset = 220;
var duration = 500;
jQuery(window).scroll(function() {
if (jQuery(this).scrollTop() > offset) {
jQuery('.back-to-top').fadeIn(duration);
} else {
jQuery('.back-to-top').fadeOut(duration);
}
});
jQuery('.back-to-top').click(function(event) {
event.preventDefault();
jQuery('html, body').animate({scrollTop: 0}, duration);
return false;
})
});
Tell me how it goes. Cheers, Sebastian.

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