背景
開発中のシステムで、データベースにHTMLが入っているカラムがあり、それをAPIで取ってきて、Reactで表示するという構成の画面があります。
その画面にはHTML内の文字列を検索できる機能があり、「検索ワードをハイライトして表示できないか?」という相談を受けたので、実装しました。
(Google検索だと、ハイライトではなく太字になっていますが、やりたいことはこんな感じです)
ただ文字列をハイライトするのはかんたんですが、HTMLとしてレンダリングしつつというところで少し悩んだので記事にしました。
できたもの
だいたいこのような感じです。
HTMLに対して、スペース区切りで文字を入力したら、入力された文字とマッチする箇所をハイライトします。
codesandboxに上げました。
ポイント
文字をハイライトさせるのには、react-highlight-wordsというパッケージが便利だったので使っています。
処理の流れは、
- (HTMLのstring)HTMLをstateから受け取る
- (React elementでstringをラップ)ハイライトしてくれるコンポーネントに突っ込む
- (HTMLのstring)
react-dom/server
でサーバで一旦レンダリングする - (HTMLのstring)正規表現でHTMLタグをアンエスケープする
- (React Element)dangerouslySetInnerHTMLというHTMLのstringをReactでHTML要素として表示できるプロパティに入れる
という感じです。
もしかしたらもっといいやり方があるかもですね。
HTMLタグのアンエスケープはこれでできます。
const unescapeHtml = string => {
const patterns = {
"<": "<",
">": ">",
"&": "&",
""": '"',
"'": "'",
"`": "`"
};
const escapedString = string.replace(
/&(lt|gt|amp|quot|#x27|#x60);/g,
match => patterns[match]
);
return escapedString;
};
注意
HTML内部のリンクとマッチするワードが入力されたら、HTMLがこわれます。
必要ならエスケープすることになります。
また、もととなるデータが、ユーザーが自由に入れることができるものである場合は、XSS対策などセキュリティ対策が必要です。