如何在Hotwire Turbo Frame导航后滚动到锚点?
问题
我希望Turbo Frame内的链接具备常规功能(点击链接时框架默认加载指定URL内容并替换自身),同时添加滚动到指定锚点的功能。
我尝试了两种代码写法都未成功:
第一种写法:
<turbo-frame id="turbo_frame_id" src="http://localhost:3000/articles/2023/edit"> <div id="anchorLink"> Content ... long content ... <%= link_to("Load frame and go to anchor", edit_article_path(article, :anchor => 'anchorLink')) %> </div> </turbo-frame>
第二种调整锚点位置的写法:
<div id="anchorLink"> <turbo-frame id="turbo_frame_id" src="http://localhost:3000/articles/2023/edit"> Content ... <%= link_to("Load frame and go to anchor", edit_article_path(article, :anchor => 'anchorLink')) %> </turbo-frame> </div>
两种写法的结果一致:点击链接后框架成功加载目标URL的内容,但页面并未滚动到anchorLink锚点。我查阅了GitHub上相关锚点问题,大多和导航相关,而非页面滚动。
请问:如何实现框架加载内容后/加载时滚动到锚点?是否需要自定义JS/Stimulus?或者Turbo Frame本身有未被我发现的相关功能?
解决方案
Turbo Frame本身没有自动滚动到锚点的内置功能,但可以通过监听Turbo的事件或者Stimulus控制器轻松实现,以下是两种可行方案:
方法1:利用Turbo内置事件监听
Turbo Frame在内容加载完成后会触发turbo:frame-load事件,我们可以监听这个事件来执行滚动操作:
document.addEventListener('turbo:frame-load', (event) => { // 提取URL中的锚点部分 const anchor = window.location.hash.slice(1); if (anchor) { const targetElement = document.getElementById(anchor); if (targetElement) { // 平滑滚动到目标锚点,可调整behavior和block参数控制滚动效果 targetElement.scrollIntoView({ behavior: 'smooth', block: 'start' }); } } });
如果只想针对特定ID的Turbo Frame生效,可增加ID判断:
document.addEventListener('turbo:frame-load', (event) => { if (event.target.id === 'turbo_frame_id') { const anchor = window.location.hash.slice(1); if (anchor) { const targetElement = document.getElementById(anchor); targetElement?.scrollIntoView({ behavior: 'smooth' }); } } });
方法2:使用Stimulus控制器(更优雅的模块化方式)
创建一个Stimulus控制器frame_scroll_controller.js:
import { Controller } from "@hotwired/stimulus" export default class extends Controller { connect() { // 绑定Turbo Frame加载完成事件 this.element.addEventListener('turbo:frame-load', this.handleLoad.bind(this)); } handleLoad() { const anchor = window.location.hash.slice(1); if (!anchor) return; const targetElement = document.getElementById(anchor); if (targetElement) { targetElement.scrollIntoView({ behavior: 'smooth', block: 'center' }); } } }
然后在Turbo Frame上绑定该控制器:
<turbo-frame id="turbo_frame_id" src="http://localhost:3000/articles/2023/edit" data-controller="frame-scroll"> <!-- 页面内容 --> <div id="anchorLink">...</div> <%= link_to("Load frame and go to anchor", edit_article_path(article, anchor: 'anchorLink')) %> </turbo-frame>
关键注意事项
- 确保锚点元素的
id在整个页面中唯一,避免元素冲突 - 必须在
turbo:frame-load事件触发后执行滚动,因为Turbo Frame加载完成后内部元素会被重新渲染,提前执行找不到目标元素 - Rails中传递锚点参数时,使用
anchor: 'anchorLink'的写法即可生成带#anchorLink的正确URL
内容的提问来源于stack exchange,提问作者Backo
相关产品推荐
相关产品推荐

