I am designing the registration page in Reactjs .and I am doing manual validation. now what I want is after clicking onSubmit, the page should scroll to the top to show all errors in the validation.I tried but failed to achieve this.is there anyone who will help me
This line of code works for me
window.scrollTo(0, 0);
If you task is to scroll to errors rather than scrolling to top, you can try this.
import React { useRef } from 'react';
const Component = () => {
const errorRef = useRef(null);
const onSubmitHandler = () => {
...
errorRef.current.scrollIntoView();
}
return (
...
<div ref={errorRef} className='error-container'>
...
</div>
...
);
}
Note: Still you want to try scroll to top you can try this.
window.scrollTo(0,0);
or
window.scrollTo({
top: 0,
left: 0,
behavior: 'smooth'
});
Related
I have a modal with a long form in my react application. So when I submit the form I am showing the validation messages from the server on top of the form. So the user has to scroll to the top to view the messages. So I want to automatically scroll to the top when the message appears. So I added the below code in the submit handler function. But it is not working.
setAddModalErrorMsg([{ msg: res.data.msg, type: "error" }])
window.scrollTo({
top: 0,
left: 0,
behavior: "smooth"
});
The other answers showed how you can scroll the modal to the top, and that is the generally accepted way of achieving this, though, I want to show you how to scroll the "Message" into view, regardless of whether it's on the top or not.
You would also need to create a ref to where you display your message and use the scrollIntoView functionality to scroll the modal to your validation message.
import React, { useRef } from 'react';
const Modal = () => {
const validationMessageRef = useRef();
const setAddModalErrorMsg = () => {
// scrolls the validation message into view, and the block: 'nearest' ensures it scrolls the modal and not the window
validationMessageRef.current?.scrollIntoView({ block:'nearest' });
}
return (
<div>
<div ref={validationMessageRef}>
// your validation message is displayed here
</div>
// rest of your modal content here
</div>
)
}
to automatically scroll to the top we can use the below code :
constructor(props) {
super(props)
this.myRef = React.createRef() // Create a ref object
}
add the scrollTo function after setAddModalErrorMsg.
setAddModalErrorMsg([{ msg: res.data.msg, type: "error" }])
this.myRef.current.scrollTo(0, 0);
<div ref={this.myRef}></div>
attach the ref property to a top dom element
You're trying to scroll window, but chances are your window is already at the top, it's your modal element that needs to scroll up.
To do this, i'd create a reference to the modal element, then in your function scroll the modal element via the ref, so something along the lines of:
import React, {useRef} from 'react';
const Modal = (props) => {
// use the useRef hook to store a reference to the element
const modalRef = useRef();
const setAddModalErrorMsg = () => {
// check the ref exists (it should always exist, it's declared in the JSX below), and call a regular javascript scrollTo function on it
modalRef.current?.scrollTo({x: 0, y: 0, animated: false});
}
// see here we create a reference to the div that needs scrolled
return (
<div ref={modalRef}>
{ // your modal content }
</div>
)
}
I'm working on a react functional components project, wherein I've to increment scroll position programmatically when a user actually scrolls in a div.
for example, I've a div with id testDiv & a user scrolled down a bit, now the scroll position is 100, so I want to programmatically increment it by 1 and make it 101.
Problem statement: The scroll position keeps on incrementing via onScroll handler, so the scrollbar only stops at the end of the element even if we scroll only once.
Expected behaviour: Scroll position should be incremented only once by the onScroll handler if we scroll once on the UI.
What I tried: (dummy code for the reproduction purpose)
import React, { useCallback } from "react";
import ReactDOM from "react-dom";
const App = (props) => {
const onScroll = useCallback((event) => {
const section = document.querySelector('#testDiv');
// **The problem is here, scrollTop keeps on incrementing even if we scrolled only once**
if (section) section.scrollTop = event.scrollTop + 1;
}, []);
return (
<div id="testDiv" onScroll={onScroll} style={{ height: "500px", overflowY: "scroll" }}>
<div>test</div>
<div className="forceOverflow" style={{height: 500 * 25}}></div>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
What you're looking for is throttling or debouncing the function. It keeps on incrementing because with every bit of scroll, onScroll is being called.
There are many ways to throttle functions in react but I like to use debounce from lodash. If I remember correctly it was about 1.5kb gzipped.
I made you a sandbox. And here is the code:
import React, { useCallback } from "react";
import _debounce from "lodash/debounce";
export const App = (props) => {
let debouncedOnScroll = useCallback(
_debounce(() => {
// YOUR CODE HERE
console.log("CHECK THE CONSOLE TO SEE HOW MANY TIMES IT'S GETTING CALLED");
}, 150), []); // Wait 150ms to see if I'm being called again, if I got called before 150ms, don't run me!
return (
<div
id="testDiv"
onScroll={debouncedOnScroll}
style={{ height: "500px", overflowY: "scroll" }}
>
<div>test</div>
<div className="forceOverflow" style={{ height: 500 * 25 }}></div>
</div>
);
};
By the way, use useRef API instead of document.querySelector. This query selector is getting called with every scroll and it's not the lightest weight on the client computer.
I'm working on a chat app and am using the scroller from bottom to top to load older messages.
When a new message arrives I want to check first if the user is at the bottom of the div, and only then use a scrollToBottom function.
How can I get the current height/position of the user?
https://www.npmjs.com/package/react-infinite-scroller
Thank you,
Omri
Unfortunately it's been a few days without reply. This is my workaround:
I created a boolean called isBottom, and attached onScroll={handleScroll} function to my messages div.
const [isBottom, setIsBottom] = useState(true);
const scrollToBottom = (behavior) => {
messagesEndRef.current.scrollIntoView();
};
const handleScroll = (e) => {
const bottom =
e.target.scrollHeight - e.target.scrollTop === e.target.clientHeight;
if (bottom) {
setIsBottom(true);
} else {
setIsBottom(false);
}
};
The messages div:
<div className="msg_list" onScroll={handleScroll}>
<InfiniteScroll
loadMore={loadMore}
initialLoad={true}
hasMore={hasMoreItems}
loader={<LoadingAnimation key={0} />}
useWindow={false}
isReverse={true}
>
{messages}
<div ref={messagesEndRef} />
</InfiniteScroll>
</div>
And then I added a useEffect to handle changes from my messages array (arriving from props)
useEffect(() => {
if (isBottom) {
scrollToBottom();
} else {
setUnreadMessages((unreadMessages) => unreadMessages + 1);
}
}, [messageList]);
* BTW you also need to wire the scrollTobottom function to your send message box, since if you are the one who sent the message it should scrollToBottom anyway
Am currently using framework7 and I have this problem wherein I need to get a button floating once the user pass scrolling a specific element.
But for some reason am not able to make the scroll event work. Even used a native event listener but still no luck.
Here is my code. In my component:
export default {
methods: {
handleScroll(event) {
alert('should work')
}
},
created() {
window.addEventListener('scroll', this.handleScroll);
},
destroyed() {
window.removeEventListener('scroll', this.handleScroll);
},
mounted() {
window.addEventListener('scroll', this.handleScroll)
this.handleScroll;
var element = document.querySelector(".similar-adventures");
var top = element.offsetTop;
window.scrollTo(0, top);
}
}
And here is my native event listener code:
window.addEventListener(‘scroll’, function(e){
// Get the new Value
newValue = window.pageYOffset;
//Subtract the two and conclude
if(oldValue - newValue < 0){
console.log(“Up”);
} else if(oldValue - newValue > 0){
console.log(“Down”);
}
// Update the old value
oldValue = newValue;
});
I know this is old now but i will answer for future reference, so i think the problem here is that the window is not actually scrolling as framework7 uses pages/views.
In vue the renders to 2 divs like so..
<f7-page>
<div slot="fixed">Fixed element</div>
<p>Page content goes here</p>
</f7-page>
<!-- Renders to: -->
<div class="page">
<div>Fixed element</div>
<div class="page-content">
<p>Page content goes here</p>
</div>
</div>
i found that its the page-content class that you want to put the eventListenter on best way to do this is Dom7 like so...
let page = $$('.page-content')
page.on('scroll', () => {
console.log(page.scrollTop()) // will show page top position
page.scrollTop(0) // will scroll to top
})
//if you have multiple pages
let page = $$('.page-content')
let home = $$(page[0])
let about = $$(page[1])
page.on('scroll', () => {
console.log(home.scrollTop()) //home page top position
console.log(about.scrollTop()) //about page top position
})
//more options
page.scrollTop(position, duration, callback)
page.scrollTo(left, top, duration, callback)
just remember to import $$ from 'Dom7'
This code retrieves all the pages from the f7 component in an array
let pages = document.querySelectorAll('.page-content');
Then to make a page scrollable, select the respective index and do:
pages[0].addEventListener('scroll', function () { console.log('is scrolling...') } );
For the same code but in a more beautiful way as we don't want to specify the page by index:
add an id to your f7-page tag
<f7-page name="whatever" id='myPage'>
then do this code for example in mounted:
let f7page = document.getElementById('myPage');
let scrollableDiv = f7page.querySelector('.page-content');
scrollableDiv.addEventListener('scroll', function () { console.log('is scrolling...') } );
special thanks to BiscuitmanZ's comment for finding the underlying issue
In brief,
I have a infinite scroll list who render for each Item 5 PureComponent.
My idea is to somehow, only render the 5 PureComponent if the Item is visible.
The question is,
How to detect if the Item component is visible for the user or not?
Easiest solution:
add scrollPosition and containerSize to this.state
create ref to container in render()
<div ref={cont => { this.scrollContainer = cont; }} />
in componentDidMount() subscribe to scroll event
this.scrollContainer.addEventListener('scroll', this.handleScroll)
in componentWillUnmount() unsubscribe
this.scrollContainer.removeEventListener('scroll', this.handleScroll)
your handleScroll should look sth like
handleScroll (e) {
const { target: { scrollTop, clientHeight } } = e;
this.setState(state => ({...state, scrollPosition: scrollTop, containerSize: clientHeight}))
}
and then in your render function just check which element should be displayed and render correct ones numOfElementsToRender = state.containerSize / elementSize and firstElementIndex = state.scrollPosition / elementSize - 1
when you have all this just render your list of elements and apply filter base on element's index or however you want to sort them
Ofc you need to handle all edge cases and add bufor for smooth scrolling (20% of height should be fine)
You can use the IntersectionObserver API with a polyfill (it's chrome 61+) . It's a more performant way (in new browsers) to look for intersections, and in other cases, it falls back to piro's answer. They also let you specify a threshold at which the intersection becomes true. Check this out:
https://github.com/researchgate/react-intersection-observer
import React from 'react';
import 'intersection-observer'; // optional polyfill
import Observer from '#researchgate/react-intersection-observer';
class ExampleComponent extends React.Component {
handleIntersection(event) {
console.log(event.isIntersecting); // true if it gets cut off
}
render() {
const options = {
onChange: this.handleIntersection,
root: "#scrolling-container",
rootMargin: "0% 0% -25%"
};
return (
<div id="scrolling-container" style={{ overflow: 'scroll', height: 100 }}>
<Observer {...options}>
<div>
I am the target element
</div>
</Observer>
</div>
);
}
}