Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to refresh scroll for a particular link when using morph/preserve scroll otherwise #1350

Open
littleforest opened this issue Dec 10, 2024 · 6 comments

Comments

@littleforest
Copy link

We are using Turbo refresh with morph and scroll preserve with turbo action "replace" on an index view page that allows filtering and search. It works really well, except that we have some pagination links at the bottom of the page that we would like to drop the scroll preserve, so that the scroll moves up to the top of the search results instead of staying at the bottom of the page. Is this possible? Google's AI helpfully(?) told me:

Yes, with Turbo (a JavaScript framework for fast web interactions), you can configure individual links to either "reset" or "preserve" the scroll position when navigating, meaning some links can jump to the top of the page while others maintain the current scroll position upon clicking, using the data-turbo-refresh-scroll attribute with values "reset" or "preserve" respectively.
Key points about using data-turbo-refresh-scroll:
How to implement: Add the data-turbo-refresh-scroll attribute directly to the anchor tag () with either "preserve" to maintain the scroll position or "reset" (default) to jump to the top on navigation.

I tried removing the scroll preserve directive from the meta tag, and adding data-turbo-refresh-scroll='preserve' to the form, and data-turbo-refresh-scroll='reset' to the pagination links, but this did not work and I could not find any solution otherwise.

I ended up adding some custom JavaScript with a data-turbo-refresh-scroll attribute in a similar vein to #37 (comment), but it is a little convoluted since we are trying to scroll to an anchor. It would be great if we could just add an attribute to the link to drop the scroll preserve.

@brunoprietog
Copy link
Collaborator

Can't you control this with the meta tag from the server side depending on the URL?

@littleforest
Copy link
Author

littleforest commented Dec 13, 2024

Do you mean modifying the meta tag from the server side when someone clicks a pagination link? We have this set up roughly using turbo-rails gem (using Stimulus to submit the form automatically when filters are selected):

<% content_for :head do %>
  <%= turbo_refreshes_with method: :morph, scroll: :preserve %>
<% end %>

<%= form_with(url: some_path, method: :get, data: {controller: "form", turbo_action: "replace"}) do |f| %>
  ... search box ...
  ... filter checkboxes ...
  
  <div id="record-count">
     <% @pagy.count %>
     <% .. sort_by form field .. %>
  </div>
  <% render "results"%>
<% end %>
<%== pagy_nav(@pagy, anchor_string: "data-turbo-action='replace'", fragment: "#record-count") %>

This works really well in that scroll is preserved when the user selects different filters. However, when they click a pagination link, we'd like to remove the scroll preservation. If we modify the meta tag when a pagination link is clicked, then we would have the opposite problem that then the filters wouldn't have scroll preserve. Or am I misunderstanding your suggestion?

If we don't use data-turbo-action='replace' on the pagy links, then our accordion javascript that is part of how we display the search results breaks on subsequent pages.

@brunoprietog
Copy link
Collaborator

You can change the value of the meta tag dynamically when you want the scroll to be preserved. For example, you could add a parameter to the filters form that indicates that the request is coming from that form, and if so, you make the scroll preserved using the meta tag.

@littleforest
Copy link
Author

Ah yeah, I see, that works, and is probably nicer than my convoluted JS override. Though it would be nice to have a solution that doesn't involve passing a parameter through the URL.

@brunoprietog
Copy link
Collaborator

We don't plan to increase the scope of the API yet, but I understand the use case, so I'll leave it open for now!

@littleforest
Copy link
Author

littleforest commented Dec 17, 2024

Great, thank you! I ended up using the turbo:morph event in my Stimulus controller to avoid passing a parameter through the URL. Thanks for pointing out that I could dynamically change the meta tag.

View:

<% content_for :head do %>
  <%= turbo_refreshes_with method: :morph, scroll: :preserve %>
<% end %>

<%= form_with(url: some_path, method: :get, data: {controller: "form", turbo_action: "replace"}) do |f| %>
  ... search box ...
  ... filter checkboxes ...
  
  <div id="record-count">
     <% @pagy.count %>
     <% .. sort_by form field .. %>
  </div>
  <% render "results"%>
  <%== pagy_nav(@pagy, anchor_string: "data-turbo-action='replace' data-form-target='resetScroll'", fragment: "#record-count") %>
<% end %>

Stimulus controller:

import { Controller } from '@hotwired/stimulus';

export default class extends Controller {
  static targets = ['resetScroll'];

  connect() {
    let resetScroll = false;
    this.element.addEventListener('turbo:before-fetch-request', (event) => {
      if (this.resetScrollTargets.includes(event.target)) {
        resetScroll = true;
      } 
    });

    document.addEventListener('turbo:morph', () => {
      if (resetScroll) {
        const metaTurboRefreshScroll = document.querySelector('meta[name="turbo-refresh-scroll"]');
        metaTurboRefreshScroll.content = 'reset';
        resetScroll = false;
      }
    });
  }

  submit() {
    this.element.requestSubmit();
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants