如何使用Turbolinks实现页面滚动位置的持久化?

11
有时候,希望在页面访问之间保留滚动位置。Turbolinks 在加载数据后会重置滚动位置。如何禁用特定元素的此功能?
4个回答

8

我的ES6解决方案:

const turbolinksPersistScroll = () => {
  const persistScrollDataAttribute = 'turbolinks-persist-scroll'
  let scrollPosition = null
  let enabled = false

  document.addEventListener('turbolinks:before-visit', (event) => {
    if (enabled)
      scrollPosition = window.scrollY
    else
      scrollPosition = null
    enabled = false
  })

  document.addEventListener('turbolinks:load', (event) => {
    const elements = document.querySelectorAll(`[data-${persistScrollDataAttribute}="true"]`)
    for (let i = 0; i < elements.length; i++) {
      elements[i].addEventListener('click', () => {
        enabled = true
      })
    }

    if (scrollPosition)
      window.scrollTo(0, scrollPosition)
  })
}

turbolinksPersistScroll()

在您想要保留滚动条位置的链接上添加data-turbolinks-persist-scroll=true

<a href="..." data-turbolinks-persist-scroll=true>链接</a>

这对我有效,也适用于link_to remote: true


2
使用以下 JavaScript 持久化滚动。我已创建一个选择器,匹配所有具有类 turbolinks-disable-scroll 的元素。在加载之前,脚本保存滚动位置,在加载后加载保存的位置。
// persist scrolls
// pirated from https://github.com/turbolinks/turbolinks-classic/issues/205
var elementsWithPersistentScrolls, persistentScrollsPositions;

elementsWithPersistentScrolls = ['.turbolinks-disable-scroll'];

persistentScrollsPositions = {};

$(document).on('turbolinks:before-visit', function() {
    var i, len, results, selector;
    persistentScrollsPositions = {};
    results = [];
    for (i = 0, len = elementsWithPersistentScrolls.length; i < len; i++) {
        selector = elementsWithPersistentScrolls[i];
        results.push(persistentScrollsPositions[selector] = $(selector).scrollTop());
    }
    return results;
});

$(document).on('turbolinks:load', function() {
    var results, scrollTop, selector;
    results = [];
    for (selector in persistentScrollsPositions) {
        scrollTop = persistentScrollsPositions[selector];
        results.push($(selector).scrollTop(scrollTop));
    }
    return results;
});

对于使用Turbolinks 5的任何人,请将事件'page:load'替换为'turbolinks:load'。非常好用,谢谢 :) - Paul Leader
1
这个功能按预期工作,但我唯一的问题是有时在加载新页面时会“跳过”禁用元素,它从顶部开始,然后半秒钟后跳到先前的滚动位置。 - Dan L

1
我注意到有时滚动条会先向上再向下。这个版本可以防止这种行为:
const persistScrollDataAttribute = 'turbolinks-persist-scroll';
let scrollPosition = null;

const turbolinksPersistScroll = () => {
  if (scrollPosition) {
    window.scrollTo(0, scrollPosition);
    scrollPosition = null;
  }


  const elements = document.querySelectorAll(`[data-${persistScrollDataAttribute}="true"]`)
  for (let i = 0; i < elements.length; i++) {
    elements[i].addEventListener('click', () => {
      document.addEventListener("turbolinks:before-render", () => {
        scrollPosition = window.scrollY;
      }, {once: true})
    })
  }
}

document.addEventListener('turbolinks:load', turbolinksPersistScroll);
document.addEventListener('turbolinks:render', turbolinksPersistScroll);

1

看起来有两种方法可以解决这个问题。

  1. 保留标记的元素(@vedant1811的答案)
  2. 对标记的链接保留正文滚动

第二种方法是我一直在寻找但无法找到的方法,因此我将在此提供我的答案。

这里的解决方案与第一种方法非常相似,但可能更简单。思路是在单击元素时获取正文的当前滚动位置,然后在加载页面后滚动到该位置:

Javascript

Turbolinks.scroll = {}

$(document).on('click', '[data-turbolinks-scroll=false]', function(e){
  Turbolinks.scroll['top'] = $('body').scrollTop();
})

$(document).on('page:load', function() {
  if (Turbolinks.scroll['top']) {
    $('body').scrollTop(Turbolinks.scroll['top']);
  }
  Turbolinks.scroll = {};
});

标记语言

<a href='/' data-turbolinks-scroll='false'>Scroll preserving link</a>

我在 Turbolinks 对象上使用了一个 scroll 属性来存储我的滚动位置,当点击一个 [data-turbolinks-scroll=false] 链接时,然后在我滚动页面后清除此属性。
重要的是要清除该属性 (Turbolinks.scroll = {}),否则,对非标记锚链接的后续单击将继续将您滚动到相同的位置。
注意:根据特定的 html 和 body 的样式,您可能需要同时使用两者的滚动偏移量。一个可能实现这一点的例子是:
Turbolinks.scroll = {};

$(document).on('click', '[data-turbolinks-scroll=false]', function (e) {
    Turbolinks.scroll['top'] = {
        html: $("html").scrollTop(),
        body: $("body").scrollTop()
    }
});

$(document).on('turbolinks:load', function() {
    if (Turbolinks.scroll['top']) {
        $('html').scrollTop(Turbolinks.scroll['top']['html']);
        $('body').scrollTop(Turbolinks.scroll['top']['body']);
    }
    Turbolinks.scroll = {};
});

如果有多个可滚动区域,这将无法正常工作。 - Vedant Agarwala
没错。这个解决方案只是为了保留页面主体的滚动。 - gabeodess

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接