React.js: Stop render from scrolling to top of page - javascript

Every time you perform a render in React.js, the UI scrolls to the top of the page.
JSFiddle: http://jsfiddle.net/bengrunfeld/dcfy5xrd/
Any nifty or reactive way to stop that?
E.g. If a User scrolls down the page, then pushes a button which causes a Render, the UI would stay in the same scroll location as before.
// Forces a render which scrolls to top of page
this.setState({data: data});
UPDATE: Why does the UI scroll to the top for some renders, but not others?

Ok if anyone read this , in my case the problem wasn't any of above. You must try first suggestions on above any way. I did everything including preventDefault() but didn't help me. The problem was ; using styled-components. Because, styled-components give a random classname every render. So it resets scroll. I gave the class name from css and solved my problem.

#floydophone answered this one on Twitter.
https://twitter.com/floydophone/status/608129119025561600
#bengrunfeld #reactjs you forgot preventDefault() on your link handlers

Wanted to add here for those who are not using anchor tags, preventDefault will not save you. As bizarre as it is, for me it was related to rendering my child components inside a div with
display:table > display:tablecell
For some reason the child component's scroll position is lost in this situation when there is any re-render. Problem vanished when I switched to flexbox (display:flex).

If render trigger scrolling to the top it usually means some UI component is changing its dimension because of the state, this can be fixed by adding a minimum width/height

This could be happening because of the change in redux state, which will show the changes everywhere in the code where it is getting used. Try making the change using the local state of that component on action of which, the page is getting scrolled.

2022 Hacky Solution:
I couldn't find a way to completely prevent the scrolling on the re-render. preventDefault didn't help, and I didn't have access to the specific element structure, but I did have the className of the scrolling element.
useEffect(() => {
const el = document.getElementsByClassName('my-classname')?.[0];
const newTimeoutRef = setTimeout(() => el?.scrollTo(0, scrollTop), 50);
return () => clearTimeout(newTimeoutRef);
}, [scrollTop]);
Then you set the scrollTop (as a component state) inside the event that was previously causing the scroll to top.
The hack here is that setTimeout, which basically gives the DOM time to scroll to the top before calling your el.scrollTo, it flashes a bit, but it was the best solution that worked for me.

Related

Scrolling to top inside an accordion

In my page I have 2 scrolls.
One is apparent in the page ( They are not loaded with ajax or something.)
The other one is inside an accordion.
I want these scroll to be always at the top. They have same class names.
With the snipets I can achieve my first goal, scrolling to top in apparent scroll
$('.m-messenger__messages').scrollTop($('.m-messenger__messages')[0].scrollHeight);
As well as with this one
var messageBody = document.querySelector('.m-messenger__messages');
messageBody.scrollTop = messageBody.scrollHeight - messageBody.clientHeight;
However, the scroll inside the accordion menu is not affected by this change.
If I open the accordion and run this snippets it scrolls to top.
So that either
I need to find a way to run this snippet not only to apperent but also all scrolls in the page or
when I click the accordion this javascript code needs to be executed.
I would like to solve this problem with the first solution.
I tried this and I couldn't succeed as well. If I put alert() rather than scrolltop inside this function, I got the alertbox.
$(".m-accordion__item").click(function() {
$('.m-messenger__messages').scrollTop($('.m-messenger__messages')[0].scrollHeight);
});
How can I achieve this goal?
This solved my problem. BTW this is bootstrap4. So that it may apply to any of bootstrap 4 templates.
$('.collapse').on('shown.bs.collapse', function(e) {
$('.m-messenger__messages').scrollTop($('.m-messenger__messages')[0].scrollHeight);
});

breaking out of browser.actions().mouseMove in protractor

I have a need for using the actions.mouseMove() fuction to hover over a reactive dropdown element. After doing so and clicking the elemen, that reactive dropdown persists so I now need to find someway to move the mouse away from the hover box. I do this by including a return browser.actions().mouseDown().perform() after I click my link. Here is my full function
this.changeCurrentClient = function() {
var profile = $('[id="hdr-profile-wrapper"]');
var changeClient = element(by.buttonText('Change Client'));
browser.actions().mouseMove(profile).perform();
changeClient.click();
return browser.actions().mouseDown().perform();
};
This works fine, however the problem is that when I run this on the grid, it sometimes does not properly do the moveDown() function to break out of the hover menu. I assume this is because it runs a bit slower on the grid and the DOM takes a bit longer to load. Is there a better way to break out of this rather than refreshing or doing a hard wait?
I have tried implicitly waiting for another element on the page outside of the hover menu before doing the mouseDown(), however it seems like that first action.perform() pretty much isolates you to that hover menu, so no other elements outside of the hover menu can be found. Is a refresh really the only way to do this?

$(window).scrollTop executes before expected?

I want the page to scroll to the top if its on a certain page of my app.
It needs to do this after the page has been shown.
I'm using the code:
$('.current-page, #'+desiredPage).toggleClass('current-page')
if(desiredPage === 'page-search-results'){
$(window).scrollTop(scrollPosition)
}else{
$(window).scrollTop(0)
}
however, the page scrolls to the top a split second before the class is actually toggled (the class has the css in it to show the page). Why is this? and how to make sure it only happens after?
This is only noticeable on mobile.
as per the comments/ answer i tried using:
$('.current-page, #'+desiredPage).toggleClass('current-page').promise().done(function(){
if(desiredPage === 'page-search-results'){
$(window).scrollTop(scrollPosition)
}else{
$(window).scrollTop(0)
}
});
but it still happens?
If you add a return false; at the end of the function (i.e. after the if/else clauses) it might solve the problem?
I've been wrestling with a similar problem, and found the hint at the bottom of this article:
http://chris-spittles.co.uk/jquery-smooth-page-scrolling/
It says there,
Returning false simply prevents the browser carrying out its default behaviour
For me, and in the example given on the page that means that the anchor does not jump down to the page before the animation, although, since your not using anchors, but toggling classes, I'm not quite sure what the "default behaviour" is in this case.
Hope it helps.

How can I check if a user has grabbed a scrollbar on a website?

Basically I want to see if a user has a hold of a scrollbar on my site. is there a Jquery function I can use to return a Boolean based on weather the user has a hold of the scrollbar or not?
Edit: adding my site so you can see the problem. When you try to scroll the scrollbar up the timed event to set the scrollbar to the bottom kicks in and sends it to the bottom. I need it to not do that while focus is on the chatbox/chatbox scrollbar, but it's not working: http://exvs.us/
If you really need to accomplish this, one way would be to hide the default scrollbar and create your own in javascript. You can then handle click events on your scrollbar. However, this won't work well for users that have javascript disabled by default.
Use scroll method of the Jquery object :
more information there => http://api.jquery.com/scroll/
sample :
$(window).scroll( function () {
console.log('pretty easy no ?');
});

How to prevent page's scroll position reset after DOM manipulation?

I've got two JS functions, one that is adding options to a select box
function addOption(selectId, text, value) {
var selectbox = document.getElementById(selectId);
var optNew = document.createElement('option');
optNew.text = text;
optNew.value = value;
try {
selectbox.add(optNew, null); //page position resets after this
}
catch(ex) {
selectbox.add(optNew);
}
}
and another that is doing a document.getElementById(formId).appendChild(newHiddenInput) in a similarly simple function.
They both work, elements are added as expected. However, upon calling either of them, the page resets its scroll position to the top of the page in both IE6 and FF. There is no postback, this is purely clientside manipulation. I've set breakpoints in Firebug, and it occurs immediately after the element.appendChild or select.add gets executed. I know I can use JS to manually set a scroll position, but I didn't think it was necessary when the page isn't being re-rendered.
I'm no expert with JS or the DOM, so I may very well be missing something, but I've looked here and ran through their examples with the Try it Here options and I can't replicate the problem, indicating the codebase I'm working with is the culprit.
Any ideas why the scroll position is being reset? jQuery is available to me as well, if it provides a better alternative.
If the functions are being called from a link you might have an internal anchor in your link:
http://www.website.com/page.html#
This is causing said behavior. The default behavior is that if an anchor does not exist, the page scroll position jumps to the top (scrollTop = 0).
If this happens on every function call regardless of the source, then this can be crossed off the list.
What is activating the event?
If it's an anchor then on the click event you need to "return false;" after the call to your jQuery/Ajax/jScript code.
If it's a button you may need to do the same.
I had this issue yesterday and this was the resolution.
So My link

Categories