Skip to content

fix(ui): prevent hash links from jumping to page top#963

Merged
imaegoo merged 1 commit into
twikoojs:mainfrom
wyf027:fix/prevent-hash-navigation-scroll
May 28, 2026
Merged

fix(ui): prevent hash links from jumping to page top#963
imaegoo merged 1 commit into
twikoojs:mainfrom
wyf027:fix/prevent-hash-navigation-scroll

Conversation

@wyf027

@wyf027 wyf027 commented May 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #832

  • Admin logout link now uses @click.prevent to avoid href="#" navigation
  • @user reply links scroll to the target comment in-page instead of using hash URLs that reset scroll position in swup/SPA themes

Test plan

  • Like/reply/expand comments on a swup-powered site — page should stay at current scroll position
  • Open admin panel, click logout — should not jump to page top
  • Click @user in a reply — should scroll to referenced comment

Summary by Sourcery

通过在应用内部对哈希链接(hash links)进行处理,使用页面内滚动行为和安全的点击事件处理程序,避免因点击应用内哈希链接导致整页跳转。

Bug 修复:

  • 确保管理员登出链接在点击时不再触发页面跳转到顶部。
  • 使 @user 回复链接平滑滚动到被引用的评论位置,而不是通过会重置滚动位置的哈希 URL 进行导航。
Original summary in English

Summary by Sourcery

Prevent in-app hash links from causing full-page jumps by handling them with in-page scroll behavior and safe click handlers.

Bug Fixes:

  • Ensure admin logout link no longer triggers navigation to page top when clicked.
  • Make @user reply links scroll smoothly to the referenced comment instead of navigating via hash URLs that reset scroll position.

Use click handlers with preventDefault for admin logout and @user
reply links so SPA/swup themes do not scroll to the top on comment
actions.

Fixes #832

Co-authored-by: Cursor <cursoragent@cursor.com>
@sourcery-ai

sourcery-ai Bot commented May 25, 2026

Copy link
Copy Markdown
Contributor
审阅者指南(在小型 PR 上折叠)

审阅者指南

更新评论回复和管理员登出链接,避免基于 hash 的导航把页面跳转到顶部,改为通过 Vue 的点击事件处理程序和页面内平滑滚动来处理相关行为。

评论回复链接触发的页面内滚动时序图

sequenceDiagram
  actor User
  participant TkComment
  participant Document
  participant TargetComment

  User->>TkComment: click tk-ruser link (@click.prevent)
  TkComment->>TkComment: scrollToPid(pid)
  TkComment->>Document: getElementById(pid)
  Document-->>TkComment: element
  TkComment->>TargetComment: scrollIntoView(behavior, block)
Loading

文件级变更

Change Details Files
通过页面内平滑滚动而不是重置滚动位置的 hash href 来处理 @user 回复导航。
  • 将回复链接 href 模板替换为固定的 "#",并绑定一个阻止默认导航行为的 Vue 点击事件处理程序。
  • 引入 scrollToPid(pid) 方法,通过 id 查找目标元素,并调用 scrollIntoView 实现平滑、居中的滚动。
  • 将新的 scrollToPid 方法接入回复链接的点击处理程序,以保持导航在当前 SPA 滚动上下文内完成。
src/client/view/components/TkComment.vue
防止管理员登出链接触发默认的 hash 导航导致页面跳转到顶部。
  • 在登出链接的点击事件绑定中添加 .prevent Vue 事件修饰符。
  • 确保登出仍然会调用 onLogout,同时抑制默认的锚点导航行为。
src/client/view/components/TkAdmin.vue

与关联 issue 的对照评估

Issue Objective Addressed Explanation
#832 通过避免基于 hash 的导航,防止评论相关交互(尤其是 @user 回复链接)在 swup/SPA 主题中导致页面跳回顶部。
#832 通过阻止 href="#" 导航,防止管理面板的登出操作导致页面跳回顶部。

使用技巧与指令

与 Sourcery 交互

  • 触发新审查: 在 pull request 中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的审查评论。
  • 从审查评论生成 GitHub issue: 在某条审查评论下回复,请求 Sourcery 从该评论创建一个 issue。你也可以回复 @sourcery-ai issue 来从该评论创建 issue。
  • 生成 pull request 标题: 在 pull request 标题的任意位置写上 @sourcery-ai,即可随时生成标题。你也可以在 pull request 中评论 @sourcery-ai title,随时(重新)生成标题。
  • 生成 pull request 摘要: 在 pull request 正文任意位置写上 @sourcery-ai summary,即可在指定位置随时生成 PR 摘要。你也可以在 pull request 中评论 @sourcery-ai summary,随时(重新)生成摘要。
  • 生成审阅者指南: 在 pull request 中评论 @sourcery-ai guide,即可随时(重新)生成审阅者指南。
  • 一次性解决所有 Sourcery 评论: 在 pull request 中评论 @sourcery-ai resolve,即可解决所有 Sourcery 评论。如果你已经处理完所有评论且不希望再看到它们,这会很有用。
  • 一次性忽略所有 Sourcery 审查: 在 pull request 中评论 @sourcery-ai dismiss,即可忽略所有已有的 Sourcery 审查。特别适用于你想从头开始新的审查时——别忘了再评论 @sourcery-ai review 来触发新审查!

自定义你的使用体验

访问你的 控制面板 以:

  • 启用或禁用审查功能,例如 Sourcery 自动生成的 pull request 摘要、审阅者指南等。
  • 更改审查所使用的语言。
  • 添加、删除或编辑自定义审查指令。
  • 调整其他审查设置。

获取帮助

Original review guide in English
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Updates comment reply and admin logout links to prevent hash-based navigation from jumping the page to the top, instead handling behavior via Vue click handlers and smooth in-page scrolling.

Sequence diagram for in-page scroll on comment reply link click

sequenceDiagram
  actor User
  participant TkComment
  participant Document
  participant TargetComment

  User->>TkComment: click tk-ruser link (@click.prevent)
  TkComment->>TkComment: scrollToPid(pid)
  TkComment->>Document: getElementById(pid)
  Document-->>TkComment: element
  TkComment->>TargetComment: scrollIntoView(behavior, block)
Loading

File-Level Changes

Change Details Files
Handle @user reply navigation via in-page smooth scrolling instead of hash hrefs that reset scroll position.
  • Replace reply link href template with a fixed "#" and bind a Vue click handler that prevents default navigation.
  • Introduce a scrollToPid(pid) method that finds the target element by id and calls scrollIntoView with smooth, centered scrolling.
  • Wire the new scrollToPid method into the reply link click handler to keep navigation within the current SPA scroll context.
src/client/view/components/TkComment.vue
Prevent admin logout link from triggering default hash navigation that jumps to the top of the page.
  • Add the .prevent Vue event modifier to the logout link click binding.
  • Ensure logout still invokes onLogout while suppressing the default anchor navigation behavior.
src/client/view/components/TkAdmin.vue

Assessment against linked issues

Issue Objective Addressed Explanation
#832 Prevent comment-related interactions (especially @user reply links) from causing the page to jump back to the top in swup/SPA themes by avoiding hash-based navigation.
#832 Prevent the admin panel logout action from causing the page to jump back to the top by stopping href="#" navigation.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 1 个问题,并留下了一些整体性的反馈:

  • 对于你现在通过阻止默认行为处理的 href="#" 链接,建议使用更语义化、更易访问的元素(例如 <button>,或带有 role="button"<a>),这样键盘操作和屏幕阅读器的行为就能与其实际功能更加一致。
  • scrollToPid 中,你可能需要对 pid 进行防御性地转换为字符串,或者在调用 getElementById 之前确保它是一个合法的 DOM id,以避免在传入非字符串值或包含特殊字符时出现意外。
提供给 AI Agents 的提示
Please address the comments from this code review:

## Overall Comments
- Consider using a more semantic/accessible element (e.g., a `<button>` or `<a>` with `role="button"`) for the `href="#"` links you’re now preventing, so that keyboard and screen-reader behavior is more consistent with their actual function.
- In `scrollToPid`, you may want to defensively cast `pid` to a string or ensure it’s a valid DOM id before calling `getElementById`, to avoid surprises if non-string values or special characters slip through.

## Individual Comments

### Comment 1
<location path="src/client/view/components/TkAdmin.vue" line_range="46" />
<code_context>
           <div class="tk-panel-title">
             <div>{{ t('ADMIN_TITLE') }}</div>
-            <a class="tk-panel-logout" href="#" @click="onLogout">{{ t('ADMIN_LOGOUT') }}</a>
+            <a class="tk-panel-logout" href="#" @click.prevent="onLogout">{{ t('ADMIN_LOGOUT') }}</a>
           </div>
           <div class="tk-tabs">
</code_context>
<issue_to_address>
**suggestion:** Consider using a button or role attribute for the logout control for better semantics.

Since this element now only triggers an action with `@click.prevent` and no longer represents navigation, it should be exposed as a button for accessibility. Either switch to a `<button>` styled like a link, or keep the `<a>` but add `role="button"` (and any needed ARIA/state) so assistive technologies interpret it as an action control.

```suggestion
            <button
              type="button"
              class="tk-panel-logout"
              @click="onLogout"
            >
              {{ t('ADMIN_LOGOUT') }}
            </button>
```
</issue_to_address>

Sourcery 对开源项目免费——如果你觉得这些评审有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进后续评审。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • Consider using a more semantic/accessible element (e.g., a <button> or <a> with role="button") for the href="#" links you’re now preventing, so that keyboard and screen-reader behavior is more consistent with their actual function.
  • In scrollToPid, you may want to defensively cast pid to a string or ensure it’s a valid DOM id before calling getElementById, to avoid surprises if non-string values or special characters slip through.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider using a more semantic/accessible element (e.g., a `<button>` or `<a>` with `role="button"`) for the `href="#"` links you’re now preventing, so that keyboard and screen-reader behavior is more consistent with their actual function.
- In `scrollToPid`, you may want to defensively cast `pid` to a string or ensure it’s a valid DOM id before calling `getElementById`, to avoid surprises if non-string values or special characters slip through.

## Individual Comments

### Comment 1
<location path="src/client/view/components/TkAdmin.vue" line_range="46" />
<code_context>
           <div class="tk-panel-title">
             <div>{{ t('ADMIN_TITLE') }}</div>
-            <a class="tk-panel-logout" href="#" @click="onLogout">{{ t('ADMIN_LOGOUT') }}</a>
+            <a class="tk-panel-logout" href="#" @click.prevent="onLogout">{{ t('ADMIN_LOGOUT') }}</a>
           </div>
           <div class="tk-tabs">
</code_context>
<issue_to_address>
**suggestion:** Consider using a button or role attribute for the logout control for better semantics.

Since this element now only triggers an action with `@click.prevent` and no longer represents navigation, it should be exposed as a button for accessibility. Either switch to a `<button>` styled like a link, or keep the `<a>` but add `role="button"` (and any needed ARIA/state) so assistive technologies interpret it as an action control.

```suggestion
            <button
              type="button"
              class="tk-panel-logout"
              @click="onLogout"
            >
              {{ t('ADMIN_LOGOUT') }}
            </button>
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

<div class="tk-panel-title">
<div>{{ t('ADMIN_TITLE') }}</div>
<a class="tk-panel-logout" href="#" @click="onLogout">{{ t('ADMIN_LOGOUT') }}</a>
<a class="tk-panel-logout" href="#" @click.prevent="onLogout">{{ t('ADMIN_LOGOUT') }}</a>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: 建议为注销控件使用按钮或添加 role 属性,以获得更好的语义。

由于这个元素现在只是在 @click.prevent 中触发一个操作,并不再表示导航行为,从可访问性角度看,它应该以按钮的形式暴露出来。你可以将其改为外观样式像链接的 <button>,或者保留 <a>,但添加 role="button"(以及必要的 ARIA/状态),这样辅助技术就会将其解释为一个“操作控件”。

Suggested change
<a class="tk-panel-logout" href="#" @click.prevent="onLogout">{{ t('ADMIN_LOGOUT') }}</a>
<button
type="button"
class="tk-panel-logout"
@click="onLogout"
>
{{ t('ADMIN_LOGOUT') }}
</button>
Original comment in English

suggestion: Consider using a button or role attribute for the logout control for better semantics.

Since this element now only triggers an action with @click.prevent and no longer represents navigation, it should be exposed as a button for accessibility. Either switch to a <button> styled like a link, or keep the <a> but add role="button" (and any needed ARIA/state) so assistive technologies interpret it as an action control.

Suggested change
<a class="tk-panel-logout" href="#" @click.prevent="onLogout">{{ t('ADMIN_LOGOUT') }}</a>
<button
type="button"
class="tk-panel-logout"
@click="onLogout"
>
{{ t('ADMIN_LOGOUT') }}
</button>

@imaegoo imaegoo merged commit 2031798 into twikoojs:main May 28, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

评论相关操作会回到文章顶部

2 participants