HTML input要素のmaxlengthをサロゲートペア文字(絵文字)に対応してみる話
input要素のmaxlength属性はサロゲート文字を2文字とカウントするため、maxlength=100とすると(全て絵文字だった場合)実は50文字しか入らなくなります。
通常の日本語のみの入力だけであれば問題ありませんが、SNSサービスなどをつくると必然的に絵文字や特殊文字にも対応するケースがあるかと思います。
サロゲートペア文字について
通常、UTF-8では16ビット(2バイト)固定長で1文字を表すのですが、それでは65,536個の文字の表現しかないため、UTF-16の32ビット(16*2)などで表現された文字になります。
javascriptのString.lengthなども同様に2以上として出力されるため注意が必要です。
そのためhtmlのinput[maxlength]はサロゲート文字を2文字カウントしてしまい、最大文字数にずれが生じてしまいます。
Javascriptで対応してみる
htmlの標準機能では対応できないようなのでJavascriptで行う場合、文字数のカウントを正確に取る必要があります。
'😄'.length // NG: 2 […'😄'].length // OK: 1
下記サイトでカウント数を正確に取る方法が紹介されていますので参考にしてみてください。
参考
https://qiita.com/megurock/items/7f3133a79a1dcb193c1d
https://qiita.com/YusukeHirao/items/2f0fb8d5bbb981101be0
javascriptで行う場合、フォームが入力された段階で文字数をチェックし、maxlength以上であれば文字の切り捨てを行うと似た挙動になります。
ただし、通常のmaxlengthの動作だと末尾から切り捨てるのではなく、挿入される文字列が入力できないのでその挙動の違いは生まれることになります。
また、単純に切り捨て文字を置き換えるだけだとカーソルの位置がテキストの末尾になるため切り捨て前のカーソル位置を保持し、文字切り捨て後に戻してあげる動作をおこなってあげます。
完成コード
jQueryを使用していますので別途読み込んでください。
html中のinput,textareaのmaxlengthがあるフォーム要素を自動的に検知し、文字数制御するコードになります。
// 文字数カウント(サロゲートペア対応) function stringToArray (str) { return str.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[^\uD800-\uDFFF]/g) || []; } $('input,textarea').filter('[maxlength]').each(function(){ $(this).data('maxLength', this.maxLength).removeAttr('maxlength'); }).on('input', function(){ var $this = $(this); var maxLength = $this.data('maxLength'); var strs = stringToArray($this.val()); if(strs.length > maxLength){ var text = strs.splice(0, maxLength).join(""); var val = Math.min(this.selectionStart, text.length); $this.val(text); this.setSelectionRange(val, val); } });