🪽
【React Native】WebView でアンカーリンクへ自動スクロールされない問題を解決する
WebView でアンカーリンク遷移できない
React Native モバイルアプリの WebView
で以下のような #fragment
付き URL を開いたとき、ブラウザのようにアンカーリンクまでスクロールされない 時の対処法を紹介したいと思います。
https://example.com/#fragment
ブラウザならリンクを踏むだけでスクロールしてくれるのに…🤔
injectedJavaScript
を使ってアンカーリンクへスクロールさせる
#fragment
)を取り出す
ステップ 1 — フラグメント(まずは渡された URL からハッシュ部分だけを抜き出します。
// 例: https://example.com/#fragment → 'fragment'
const parsedUrl = new URL(url);
const fragment = parsedUrl.hash ? parsedUrl.hash.slice(1) : null;
ステップ 2 — 挿入用スクリプトを組み立てる
抽出した fragment
を使ってアンカーリンクへスクロールする JS 文字列を生成します。
要素が存在しない場合でも失敗しないようオプショナルチェイニング (?.
) で呼び出します。
const buildInjectedScript = (url: string): string => {
const parsedUrl = new URL(url);
const fragment = parsedUrl.hash ? parsedUrl.hash.slice(1) : null;
// 挿入する JavaScript コードを組み立てる
// fragment があれば該当 ID の要素を探し、なければ null を設定
// 100ms後に target?.scrollIntoView を実行
return `
setTimeout(() => {
const target = ${
fragment ? `document.getElementById('${fragment}')` : 'null'
};
target?.scrollIntoView({ behavior: 'smooth' });
}, 100);
`;
};
⏱ なぜ遅延させている?
ページサイズや通信状況によっては DOM が描画される前にスクリプトが走ってしまうことがあるためです。
遅延値は必要に応じて変更してください。
ステップ 3 — WebView に挿入する
React Native WebView の injectedJavaScript
プロパティに、先ほど生成した文字列をそのまま渡します。
URL が変わるたびに再生成してあげればマルチページでも動作します。
import React, { useEffect, useState } from 'react';
import { WebView } from 'react-native-webview';
type Props = { uri: string };
export const AnchorWebView = ({ uri }: Props) => {
const [injectedJs, setInjectedJs] = useState('');
// uri が変更されたら挿入するスクリプトを再生成
useEffect(() => {
setInjectedJs(buildInjectedScript(uri)); // ← ステップ 2 で作成した関数を呼び出し
}, [uri]);
return (
<WebView
source={{ uri }}
injectedJavaScript={injectedJs} // 生成したスクリプトを挿入
javaScriptEnabled // JavaScript を有効にする
/>
);
};
全コード
import React, { useEffect, useState } from 'react';
import { WebView } from 'react-native-webview';
type Props = { uri: string }; // 例: https://example.com/#fragment
/* ===== スクリプトを生成 ====================================== */
const buildInjectedScript = (url: string): string => {
// URLからハッシュ部分(#fragment)を抽出
const parsedUrl = new URL(url);
const fragment = parsedUrl.hash ? parsedUrl.hash.slice(1) : null;
// WebView に挿入する JavaScript コードを生成
return `
setTimeout(() => {
// fragment があれば該当 ID の要素を探し、なければ null
const target = ${
fragment ? `document.getElementById('${fragment}')` : 'null'
};
// 100ms 後にスムーズスクロールで要素表示位置へ移動
target?.scrollIntoView({ behavior: 'smooth' });
}, 100);
`;
};
/* ===== WebView コンポーネント =============================== */
export const AnchorWebView = ({ uri }: Props) => {
const [injectedJs, setInjectedJs] = useState('');
// uri が変更されるたびに挿入するスクリプトを更新
useEffect(() => {
setInjectedJs(buildInjectedScript(uri));
}, [uri]);
return (
<WebView
source={{ uri }} // 表示する URL
injectedJavaScript={injectedJs} // 生成した JavaScript を挿入
javaScriptEnabled // JavaScript の実行を許可
/>
);
};
まとめ
-
URL からハッシュを抽出:
new URL(url).hash
を利用。 -
遅延スクロール用 JS を生成:
setTimeout
とscrollIntoView
を組み合わせる。 -
injectedJavaScript
に渡すだけで OK: WebView 側で実行される。
これでブラウザ同様アンカーリンク遷移を React Native アプリの WebView でも再現できます 🎉
Discussion