表題の件、令和なのにスマートな書き方がないなんて!!!という話です。

やりたいこと

  • JavaScriptでとあるタグに文字列を出力したい。
  • 改行は改行して表示したい
    • 改行コードが含まれていたら<br>タグに置換
  • 他のタグは当然サニタイジングする

対応までの流れ

var str = 'xxxxx' + "\n" + 'yyyyy';

まず単純に文字列を出力しました。 jQuery使ってたのでtext()でいいやと。

$('#target').text(str);
// xxxxx yyyyy

改行コードは改行表示したいので<br>タグに置換してあげました。 この場合、text()サニタイズされるのでブラウザ上は下記のように表示されてしまいます。

$('#target').text(str.replace(/\n/g, '<br>'));
// xxxxx<br>yyyyy

ということで'text()'ではなく'html()'にします。

$('#target').html(str.replace(/\n/g, '<br>'));
// xxxxx
// yyyyy

すると見事に改行してくれました。でも当然これで完成ではありません。
文字列の中にタグが含まれていたら、タグとして判定されてしまいます。

var str = 'xxxxx' + "\n" + 'y<b>yyy</b>y';
$('#target').html(str.replace(/\n/g, '<br>'));
// xxxxx
// yyyyy // 真ん中3つのyが太字になる

この場合、サニタイジングして下記のように出力されなければいけません。

// xxxxx
// y<b>yyy</b>y

さてどうしたものか。

ググったら意外と簡単な方法がなかった

意外にもこれ!という方法がなく。。。 jsってネイティブコードとかでサニタイズするような関数ないんですね。普通ないのか。PHPが優しさに包まれてるだけ?

ということで最終的に下記コードにしました。

$('#target').html($('<dummy>').text(str).html().replace(/\n/g, '<br>'));

おまけ

余談ですが、特定の文字だけ無害化してる人の中で下記一文があったんですね。

最初はjQueryのtext()でいいんじゃないかと思ったが、「innerText(textContent)/innerHTMLを使ったHTMLエスケープは充分でないので今すぐやめろ、お前たちはもう終わりだ - TODESKING」の記事によると、それだと”がエスケープされるとは限らず不完全であるらしい。

一応text()使っちゃってるので調べましたが、この記事だとサニタイズした文字を属性値の中に入れて出力しようとしてました。

'<a href="/path/to/some_content/' + escape(user_input) + '">CLICK HERE THIS IS SAFE I PROMISE</a>'

そりゃーその場合はダブルコーテーションもサニタイズする必要あるよねってことで、今回のように要素に出力する場合は問題なし。

このあたりしっかり理解するためのサイトとか作ったら需要あるかな??

Updated: