綺麗に死ぬITエンジニア

jQueryで改行を含む文字列(非HTML)を挿入する方法

2017-03-03

jQueryを使っていて、.text()メソッドを用いる際、改行を含んだ文字列をそのまま入力して、表示上も改行させたい場合がままあります。しかし、実際にはそれは.text()メソッドでは実現できません。

なぜなら、HTML上で改行するには<br>タグが必要であり、タグを利用する以上、HTMLとして文字列を挿入する必要があるためです。

今回は、.text()メソッドを拡張し、改行コードを<br>タグに変換して、なおかつエスケープして表示する自作プラグインを作成したので、それを紹介します。

ユースケース

例えば、<textarea>タグなどでユーザーに改行を含む文字列を入力させた際、表示側でも改行を含む文字列として表示させたい場合があります。

そういった場合に用いるといいでしょう。

PHPではnl2br()関数など用意されていますが、JavaScriptでは独自に実装する必要があります。API利用時など、JavaScriptからレンダリングさせたい場合には、こちらを利用すると便利だと思います。

定義

jQueryを読み込んだ後、以下のJavaScriptを読み込むことで、.textWithLF()メソッドがjQueryオブジェクト上から利用できるようになります。

(function ($) {
    var escapes = {
            '&': '&amp;',
            '<': '&lt;',
            '>': '&gt;',
            '"': '&quot;',
            "'": '&#39;'
        },
        escapeRegexp = /[&<>"']/g,
        hasEscapeRegexp = new RegExp(escapeRegexp.source),
        unescapes = {
            '&amp;': '&',
            '&lt;': '<',
            '&gt;': '>',
            '&quot;': '"',
            '&#39;': "'"
        },
        unescapeRegexp = /&(?:amp|lt|gt|quot|#39);/g,
        hasUnescapeRegexp = new RegExp(unescapeRegexp.source),
        stripRegExp = /<(?:.|\n)*?>/mg,
        hasStripRegexp = new RegExp(stripRegExp.source),
        nl2brRegexp = /([^>\r\n]?)(\r\n|\n\r|\r|\n)/g,
        hasNl2brRegexp = new RegExp(nl2brRegexp.source),
        br2nlRegexp = /<br\s*\/?>/mg,
        hasBr2nlRegexp = new RegExp(br2nlRegexp.source);

    $.fn.textWithLF = function (text) {
        var type = typeof text;

        return (type == 'undefined')
            ? htmlToText(this.html())
            : this.html((type == 'function')
                ? function (index, oldHtml) {
                    var result = text.call(this, index, htmlToText(oldHtml));
                    return (typeof result == 'undefined')
                        ? result
                        : textToHtml(result);
                } : textToHtml(text));
    };

    function textToHtml(text) {
        return nl2br(escape(toString(text)));
    }

    function htmlToText(html) {
        return unescape(strip(br2nl(html)));
    }

    function escape(string) {
        return replace(string, escapeRegexp, hasEscapeRegexp, function (match) {
            return escapes[match];
        });
    }

    function unescape(string) {
        return replace(string, unescapeRegexp, hasUnescapeRegexp, function (match) {
            return unescapes[match];
        });
    }

    function strip(html) {
        return replace(html, stripRegExp, hasStripRegexp, '');
    }

    function nl2br(string) {
        return replace(string, nl2brRegexp, hasNl2brRegexp, '$1<br>');
    }

    function br2nl(string) {
        return replace(string, br2nlRegexp, hasBr2nlRegexp, '\n');
    }

    function replace(string, regexp, hasRegexp, replacement) {
        return (string && hasRegexp.test(string))
            ? string.replace(regexp, replacement)
            : string;
    }

    function toString(value) {
        if (value == null) return '';
        if (typeof value == 'string') return value;
        if (Array.isArray(value)) return value.map(toString) + '';
        var result = value + '';
        return '0' == result && 1 / value == -(1 / 0) ? '-0' : result;
    }
})(jQuery);

以下から、上記のコードが取得できます。

動作

.textWithLF()メソッドは、.text()メソッドと同じように利用できます。

.text()メソッドはAPIドキュメントにもあるとおり、引数なしのときにその要素のテキストを返し、引数に文字列を指定したときにその文字列をセットし、引数に関数を指定したときにその関数の返り値の文字列をセットします。

.textWithLF()メソッドは、その.text()メソッドで改行が扱えるように拡張したメソッドです。利用方法は.text()メソッドと変わりません。

<p id="sample">サンプルテキスト!</p>
var text = $('#sample').textWithLF();
// "サンプルテキスト!"

当然、文字列も取得できますし、

$('#sample').textWithLF('Hello\nWorld!');
<p id="sample">Hello<br>World!</p>

改行付きの文字列をセットできます。

$('#sample').textWithLF();
// "Hello\nWorld!"

改行付きの文字列を取得することも可能です。

$('#sample').textWithLF('<span>Hello\nWorld!</span>');
<p id="sample">&lt;span&gt;Hello<br>World!&lt;/span&gt;</p>

もちろん、HTMLは全てエスケープされるので、ユーザー定義のデータに対してもセキュリティ上問題なく利用できます。

その他、引数に関数を指定したりする場合も、仕様は.text()メソッドと何ら変わりません。

まとめ

今回は、jQueryで改行を含む文字列(非HTML)を挿入する自作プラグインを紹介しました。

jQueryをゴリゴリに使っているようなプロジェクトでは活用できる機会も多いと思います。ぜひ、利用してみてください。