如何使用 turbolinks 持久化卷轴?

How to persist scrolls with turbolinks?

有时需要在页面访问之间保持滚动位置。

Turbolinks 在加载数据后重置滚动位置。

如何为特定元素禁用它?

使用以下 javascript 来持久化卷轴。我创建了一个选择器,它匹配具有 class 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;
});

似乎有两种方法可以解决这个问题。

  1. 保留标记的元素(@vedant1811 的回答)
  2. 为已标记的 links
  3. 保留正文滚动

第二种方法是我一直在寻找但在任何地方都找不到的方法,所以我会在这里提供我的答案。

此处的解决方案与第一种方法非常相似,但可能更简单一些。思路是在点击元素时抓取body的当前滚动位置,然后在页面加载后滚动到那个位置:

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] link 时的滚动位置,然后在滚动页面后清除这个属性。

清除属性 (Turbolinks.scroll = {}) 很重要,否则,随后单击 non-flagged 锚点 links 将继续将您滚动到同一位置。

注意: 根据具体情况 styling of html and 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 = {};
});

我在 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>Link</a>

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

我注意到有时滚动条会向上滚动,然后才向下滚动。此版本可防止此类行为:

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);