SVG 要素のリンク処理を正しくハンドリングする修正
SVG 要素内のアンカータグが href を文字列として扱えない問題を解消し、同一ページへのハッシュリンクやプリフェッチ判定が正しく機能するようになりました。
背景
Turbo のリンク判定ロジックは HTMLAnchorElement.href.startsWith('#') でハッシュリンクを除外していましたが、SVG のアンカー (SVGAElement) は href を SVGAnimatedString として提供し、文字列メソッドが存在しません。そのため、SVG 内のハッシュリンクでクリックやホバーした際に例外が発生し、期待通りに動作しないケースが報告されていました(#1510)。
この欠陥は、SVG リンクが実装対象外であったことに起因し、SPA のナビゲーションやプリフェッチ機能が SVG 内でも一貫して動作しないという UX の不整合を招いていました。
技術的な変更
src/util.js に getLinkHrefString ヘルパーを追加し、HTMLAnchorElement と SVGAElement の両方から属性値を安全に取得できるようにしました。実装は link.getAttribute('href') ?? link.getAttribute('xlink:href') ?? '' というシンプルなフォールバックです。
/**
* Returns consistently the href attribute value as a string for both HTMLAnchorElement and SVGAElement.
* HTMLAnchorElement href property returns an absolute URL if the attribute contains a valid relative URL.
* SVGAElement exposes href as SVGAnimatedString which does not implement String methods.
* getAttribute() will return the proper value of the attribute in both cases.
*/
export function getLinkHrefString(link) {
return link.getAttribute("href") ?? link.getAttribute("xlink:href") ?? ""
}
findLinkFromClickTarget では従来 link.href.startsWith('#') を使用していた箇所を getLinkHrefString(link).startsWith('#') に置き換え、SVG のハッシュリンクでも安全に判定できるようにしました。
- if (link.href.startsWith("#")) return null
+ if (getLinkHrefString(link).startsWith("#")) return null
src/observers/link_prefetch_observer.js でも同様に linkToTheSamePage の比較ロジックを getLinkHrefString(link).startsWith('#') に変更し、プリフェッチ対象判定を統一しました。
- return (link.pathname + link.search === document.location.pathname + document.location.search) || link.href.startsWith("#")
+ return (link.pathname + link.search === document.location.pathname + document.location.search) || getLinkHrefString(link).startsWith("#")
テストケースは SVG 内のハッシュリンク と 同一オリジンの SVG リンク を新たに追加し、クリックやホバー時に期待通りにページ遷移・プリフェッチが行われないことを検証しています。これにより、SVG が関与するシナリオでも既存の挙動が保たれることを保証しました。
設計判断
SVG の href 取得を専用ヘルパー getLinkHrefString に抽象化した点が本修正の中心です。この選択は 「同一インタフェースで属性取得を統一」 という設計方針に沿っており、将来的に他の属性(例: xlink:href)が追加された場合でもヘルパーを拡張すれば既存ロジックを改変せずに対応できます。
また、findLinkFromClickTarget や link_prefetch_observer の変更は、最小限の差分 に抑えることで既存コードの動作保証と後方互換性を維持しています。新しいヘルパー導入以外のロジック変更は行っていないため、既存のテストスイートへの影響は極めて低くなっています。
まとめ
SVG 要素の href が文字列メソッドを持たない問題を getLinkHrefString で統一的に処理し、リンク判定とプリフェッチロジックを修正しました。この変更により、SVG 内のハッシュリンクや同一オリジンリンクでも例外が発生せず、Turbo のナビゲーション体験が一貫化されます。