[4.x]: Live preview doesn't retain scroll position when using scroll-behavior: smooth
What happened?
Description
By default, the live preview retains/restores the current scroll position when a field it's refreshed. However, this doesn't work if you use scroll-behavior: smooth on the HTML element:
html {
@media (prefers-reduced-motion: no-preference) {
scroll-behavior: smooth;
}
}
This happens without using iframe resizer (useIframeResizer is set to false). Using iframe resizer fixes this in some cases, but unfortunately breaks the preview in other ways.
Steps to reproduce
- Include the CSS above in a template.
- Open the live preview for any entry and scroll down.
- Edit any field so that the live preview refreshed. The live preview will reset to the start of the page.
Expected behavior
Scroll position should be retained/restored even when using scroll-behavior: smooth. If that is unfeasible, the live preview could inject some CSS to overwrite the scroll-behavior property for the html element, that should be an acceptable workaround.
Craft CMS version
4.5.7
PHP version
8.2
Operating system and version
No response
Database type and version
No response
Image driver and version
No response
Installed plugins and versions
No response
Not a huge deal but yeah, that feature no longer works for me as well & hasnt over the course of the last few updates in craft cms…
YOU MENTOINED A POSSIBLE SOLUTION: "the live preview could inject some CSS to overwrite the scroll-behavior property for the html element"
Maybe @brandonkelly can integrate a fix like that in future craft cms versions/updates to get the feature working again 👍🏻
Just tested this feature with the following:
Browser Version: Firefox v121.0.1
Craft CMS version: Craft Pro 4.6.1
PHP version: 8.2.8
Have you tried enabling the useIframeResizer config setting?
Have you tried enabling the useIframeResizer config setting?
@brandonkelly Yes, I've tried that. This fixes the issue, but also breaks the layout for some sites, so unfortunately it doesn't work for us.
@mmikkel mentioned on Discord that the live preview is capable of retaining the scroll position without using iframe resizer? If I remove the scroll-behaviour from our CSS it's working without using the iframe resizer. So probably it's something going wrong in whatever function is used to set the offset after the live preview refreshes?
Yeah we fetch the scrollTop and scrollLeft positions from the iframe right before replacing it with a new one.
https://github.com/craftcms/cms/blob/d86bd90e58db325bf24dc3d20bd6f426f097020f/src/web/assets/cp/src/js/Preview.js#L641-L649
I’m guessing that with smooth scrolling, those values aren’t getting reported correctly or something.
With useIframeResizer, the entire iframe is being scrolled within a container div on Craft’s end with overflow: auto, so it’s a lot more reliable.
I may be missing something but…
• I added CRAFT_USE_IFRAME_RESIZER="1" to my .env file.
• Have added iframeResizer.contentWindow.min.js to my JS files & see its loading successfully.
• Checked my CSS for any scroll-behaviour / scroll-behavior, and dont see them there anywhere.
Still, the "scroll to where you are editing" craft feature is still not working for me.
thanks for your help!
Browser Version: Firefox 121.0.1 Craft CMS Version: Craft CMS PRO 4.6.1 PHP Version: 8.2.8
Still, the "scroll to where you are editing" craft feature is still not working for me.
There is no attempt to automatically scroll to where you’re editing. That would be a killer feature, but it would require some additional work in your templates, to give Craft some insight on where it should be scrolling to.
The only thing Craft attempts to do currently is retain your current scroll position, across preview refreshes.
Craft used to do it. If i had a plain text field whose handle edited content at the bottom of my document, typing into that plain text field would jump the viewport down to that area of the screen. Dont you remember that feature?
I’m certain that we’ve never had that as a core feature. Maybe you had a plugin that did it?
As you know, browser features can change drastically from version to version… The only thing I can think of is that there was something in the older versions of Firefox that made pages work like that. That would explain it ya know
Browsers would have no way of knowing they should scroll to some arbitrary point in an iframe on load.
I really appreciate you helping out with this Brandon! I bet there was javascript that was making the CMS do that at the time and didnt even realize that - it was a few years ago when I was first learning Craft CMS… When I get some extra time, Ill fire up my old machine and that clients project to see how in the heck I was getting things to work that way. Thanks again for your help with this man - Craft 5 looks awesome by the way - cant wait to get crackin' on that! 😁
-Johnnie
@brandonkelly Sorry for the late reply!
Yeah we fetch the
scrollTopandscrollLeftpositions from the iframe right before replacing it with a new one.https://github.com/craftcms/cms/blob/d86bd90e58db325bf24dc3d20bd6f426f097020f/src/web/assets/cp/src/js/Preview.js#L641-L649
I’m guessing that with smooth scrolling, those values aren’t getting reported correctly or something.
With
useIframeResizer, the entire iframe is being scrolled within a container div on Craft’s end withoverflow: auto, so it’s a lot more reliable.
I've had a look – the values are being reported correctly. A bit difficult to debug since I don't have a dev setup for Craft's CP, so I can only debug the compiled JS. Was able to find that piece of code in the compiled cp.js and put some debugging statements.
The values for scrollTop and scrollLeft are definitely correct right before this:
$($iframe[0].contentWindow.document).scrollTop(
this.scrollTop
);
I think it might be a bug in jQuery. I tried replacing this with plain JavaScript:
$iframe[0].contentWindow.scrollTo(this.scrollLeft, this.scrollTop)
This works correctly even with smooth scroll enabled. There's a slight issue where the viewport shifts by ~50px (maybe due to the settings bar at the top of the preview window?). But way better than right now.
Could you give this fix a try? This issue is inconveniencing a couple of clients. Smooth-scroll is a default in many websites now, and as mentioned, iframe resizer breaks the layout in other ways :/
That looks to do the trick @MoritzLost! I've opened #14785 with this change.
Craft 4.8.9 and 5.0.4 are out with that fix! Thanks for the help.
Thanks @brandonkelly and @brianjhanson!
In Craft 4.8.9, the scroll position is now correctly restored. However, it's still a bit awkward with smooth scrolling, since every refresh of the live preview will trigger the scroll animation from the top of the page. I've worked around this by disabling smooth scroll in the live preview. Leaving this here in case anyone else has this issue.
<html {{ attr({
class: [
craft.app.request.getQueryParam('x-craft-live-preview') ? 'is-live-preview',
],
}) }}>
html {
@media (prefers-reduced-motion: no-preference) {
scroll-behavior: smooth;
}
&.is-live-preview {
scroll-behavior: auto;
}
}