Scroll to bottom of div automatically on page load - javascript

I have a chat application in react and have used npm package react-scroll-to-bottom until now. But it's not maintained and throwing warnings so i thought it should not be too hard to write this myself.
I'm following various answers on stackoverflow but cant seem to get it right.
It should just scroll to the bottom of the container as the page loads.
Any ideas what i'm doing wrong? Thanks in advance!
EDIT: now i have a div at the end of the tree which has the ref because i thought it would be easier to scroll to that instead of trying to tell a div to scroll inside it.
const chatHistoryRef = React.useRef();
React.useEffect(() => {
window.scrollTo(0, chatHistoryRef.current.offsetTop);
}, [chatHistoryRef]);

Your useEffect does not have any dependencies, so if the ref changes (as in, the div gets rendered) the effect will not be run. Dependencies are set in a second parameter as an array, like so:
useEffect(() => {
// do something with chatHistoryRef.current
}, [chatHistoryRef.current])

Found the solution. As i'm working with polling in my app right now i only get chat messages every two seconds. So my function was in fact scrolling, but there just wasnt anything to scroll away from. :)
This is my solution. Thanks everyone for the help. By adding length to message dependency this is only fired if there are new chat messages
const chatHistoryRef = React.useRef();
React.useEffect(() => {
chatHistoryRef.current.scrollIntoView({
behavior: 'smooth',
block: 'end'
});
}, [chatHistoryRef, messages.length]);

Related

cy.click() failed because it requires a DOM element. The subject received was: > undefined

I have made a custom command for logout which I am calling in my afterEach hook but every time I am getting this error. Below is the code attached:
Cypress.Commands.add('logout', () => {
//cy.get('#react-burger-menu-btn').should('be.visible').click({force:true})
cy.xpath("//div[#class = 'bm-burger-button']").click({ force: true })
cy.get('#react-burger-menu-btn')
cy.get('#logout_sidebar_link').click()})
I am using the Swag Labs dummy website. Attaching the website's link as well for reference:
https://www.saucedemo.com/
Took help from the documentation but unable to solve the issue.
You can try waiting for visibility of all the elements involved.
cy.get('#react-burger-menu-btn').should('be.visible')
cy.get('#logout_sidebar_link').should('be.visible')
.click()
If it does not work, look at the HTML of the menu and try checking other elements for visibility as well.
You should open the devtools and observe which elements are modified after the click (they will flash in the devtools Elements tab).
Cypress.Commands.add('logout', () => {
cy.get('button[id="react-burger-menu-btn"]').click()
cy.get('#logout_sidebar_link').click()
});
Try this

React-flow & dare: reactFlowInstance.fitView() fits the instance in the screen after 2nd button click. (1st only changes the direction of the graph)

I have tried different ways of implementing this beauty, but it doesn't seem to work. My problem is that when I hit the button, I want to change the layout of the graph, which happens and I am glad for it, but I also want my graph to be centered (fit) on the screen. The first button click changes the direction, but doesn't fit the instance. To fit the instance I need to hit the button for a second time. I guess it has to do with asynchronous JS or? This is my code:
const onChangeTreeLayout = useCallback(
(treeLayoutDirection) => {
const layoutedElements = getLayoutedGraphElements(
elements,
treeLayoutDirection,
setTreeLayoutDirection
);
setElements(layoutedElements);
},
[elements]
);
Then how I get the instance and trigger it follows: Note: I can't use useReactFlow() hook as we decided not to migrate to a newer version. But useZoomPanHelper does its work anyway.
const reactFlowInstance = useZoomPanHelper();
<button
onClick={() => {
onChangeTreeLayout('TB');
reactFlowInstance.fitView();
}}
>
Horizontal Layout
</button>
I have also tried putting the function .fitView() inside the onChangeTreeLayout but I get the same behaviour.

Why can't I scroll to the bottom of my div?

so basicaly, i'm doing a chat in vuejs + socket.io
and I want that every time a message is sent, received, or when you come to the site, you arrive directly to the bottom of the page, where the latest message is
I've done a function that fires on the onMounted hook, when I retrieve the data, and on the socket.on('message') :
const scrollToLatest = () => {
let container = document.getElementById("messages")
console.log("scrolltop",container.scrollTop)
console.log("scrollheight",container.scrollHeight)
container.scrollTop = container.scrollHeight;
console.log("scrolltop after", container.scrollTop)
}
Here are what the console.log returns :
(if you can't load the image :)
scrolltop 0
scrollheight 5594
scrolltop after 0
I've tried to use window.scrollTo(0, container.scrollHeight) but it doesn't work either
I hope that someone will be able to help me, I've searched a lot on this issue, but I wasn't able to make it work properly
pass a custom class to your div in your <template> first:
<div :class="`messages`">
</div>
and then get the position of the div as a number:
getPositionOfElement() {
return (document.getElementsByClassName("messages").getBoundingClientRect().top - window.innerHeight);
}
This would be the method for scrolling to the position where your div is located - adapt it to your needs as described in the official documentation:
smoothScroll(vertPos) {
window.scrollTo({
top: vertPos,
left: 0,
behavior: 'smooth'
});
}
combine both methods like this from the place where you need to perform the scrolling - calling this either from another method or directly from the template...
this.smoothScroll(this.getPositionOfElement());
Please let me know if this works for you.

How to start at the bottom of a page and scroll upwards in reactJS

so i'm making a react webApp that has a group-chat feature. Everything is going so well apart from one thing. I wan't the chat to default to the bottom of the screen once entered, then be scrolled upward to view older messages. i've tried the whole useEffect scroll to bottom nonsense and it works alright, except it would be a lot nicer if i could have my chat more like whatsApp. i need a cleaner fix and all i'm finding is half assed solutions, anyone know how whatsApp do it?
Here's what i'm using currently, incase anyone wants a quick fix. i just throw this into useEffect and at the end of my 'getMessages' fetch.
const scrollToBottom = () => {
window.scrollTo({
top: document.documentElement.scrollHeight,
behavior: 'auto'
/* you can also use 'auto' behaviour
in place of 'smooth' */
})
};
I use this
function ScrollToBottom(){
const elementRef = useRef();
useEffect(() => elementRef.current.scrollIntoView());
return <div ref={elementRef} />;
};
Then you just call it at the end of the messages.
<p>some message</p>
<p>another message</p>
<ScrollToBottom />

gatsby setPageSection makes page jump after images finish loading

This current piece of code:
<button onClick={() => setPageSection([p.slug+"/", s.slug])}>
makes my page jump to the start of the section when the images finish loading
In this case I have a grid of pictures that are correctly lazy loaded with an initial base64 lowres pre-render but everytime each one of the pictures finishes loading the page scrolls back to the top of the section.
This only happens when I use the setPageSection.
When I click a direct link to an anchor ("page/#section") of the page instead of using the above method of setting the page section the correct behavior happens (images continue lazy loading in the background and the pages doesn't snap to the top of the section everytime when each one finishes loading).
UPDATE:
The following looks like the culprit:
enter useEffect(() => {
if (typeof prevPageSection === 'undefined') return;
const scrollToSection = () => {
const el = document.getElementById(pageSection[1]);
if (scrollContainer.current && el) {
scrollContainer.current.scrollTo({ top: el.offsetTop });
}
};
if (pageSection[0] !== prevPageSection![0] && pageSection[0] !== pageContext.slug) {
navigate(locale + pageSection[0]);
scrollContainer.current?.addEventListener('load', scrollToSection, true);
}
scrollContainer.current?.removeEventListener('load', scrollToSection);
}, [pageSection, prevPageSection, locale, pageContext.slug]);
If I remove these event listeners the page stops jumping after the images load but the links get broken (they become useless when navigating to a section on a different page, without these event listeners one can only jump to the desired section if already on that page, otherwise the browser will navigate to the top of the page)
You have too many side effects in your useEffect hook. Each time one dependency (pageSection or prevPageSection or locale or pageContext.slug) is being changed, it triggers the hook, creating your unwanted effect:
const el = document.getElementById(pageSection[1]);
if (scrollContainer.current && el) {
scrollContainer.current.scrollTo({ top: el.offsetTop });
}
In addition, don't define a function inside the useEffect because it will cause a new scoped function creation each time the effect is triggered, overloading your code and application.
Moreover, don't point directly the DOM using document.getElementById(). It's a high-cost action for the browser and you can achieve the same effect using the useRef hook, you will have the exact same information using the .current property of the useRef hook, in the same way than you do the scrollContainer.current. Indeed, you are creating and manipulating a virtual DOM with React, to avoid pointing the DOM, while your code attacks the real DOM.
Be aware of the use of global objects such as window or document in Gatsby, they are not available during the SSR (Server-Side Rendering) so they may potentially break your code. To summarize, gatsby build compiles the code in your Node server, where there is no window or document because it's not even created yet. The code may work under gatsby develop because it's directly rendered by the browser.
To fix your issue, I would clean the useEffect, you can create as many effects as needed, and you can name them using this notation to make your code more readable:
useEffect(someNamedFunction(){
// your code
}, []).
To bypass it faster, you can create an auxiliary function that forces the page to scroll to certain element without involving the hooks:
const scrollToElement=(el)=>{
scrollContainer.current.scrollTo({ top: el.offsetTop });
}
<button onClick={(el)=> scrollToElement(el)}>

Categories

Resources