ど素人から毛を生やす。<延>

maxlength属性みたいにinputでバイト数制限する

Web > javascript 2019年10月15日(最終更新:1月前)

どもです。

inputやtextareaでmaxlength属性を設定すれば、最大字数を設定できます。
これを超える入力やペーストは超過分だけカットされるので、非常に使い勝手が良いです。

しかし、バックヤード系だと結構要求されるのが、「字数」でなく「バイト数」の入力制限です。

一度は全部自力で作ってみたのですが、どうにも動作がもっさりしてしまったので、先人の知恵に頼ることにしました。
PisukeCode - Web開発まとめ[jQueryでtextareaの入力をバイト数で制限する方法&コード例]

動作も早く、大変素晴らしいです。
しかしこちらのケースでは、超過入力があるとその全てを無効化してしまいます。

今回欲しいのは、maxlength属性ライクな超過分だけカットする機能です。

というわけで、改造してみましょう。
超過分カットに変更するにあたり、課題となるのはこの辺でしょうか。
超過分をカットした文字列を作成する
フォーカスの位置に文字列を挿入する
選択中の文字列があった場合、削除してから処理する

//全角2バイトチェック(UTF-8的計算が必要な場合は、ここを編集)
$.getByteLengthChar = function(chr){
	var result = 0;
	if((chr >= 0x00 && chr < 0x81) || (chr === 0xf8f0) || (chr >= 0xff61 && chr < 0xffa0) || (chr >= 0xf8f1 && chr < 0xf8f4)){
		//半角文字の場合は1を加算
		result = 1;
	}else{
		//それ以外の文字の場合は2を加算
		result = 2;
	}
	return result;
};

//字数をカウント
$.getByteLength = function(value){
	var result = 0;
	var size = i<value.length;
	for(var i=0; size; i++){
		var chr = value.charCodeAt(i);
		result += $.getByteLengthChar(chr)
	}
	return result;
};

//指定バイトで文字列をカット
$.getByteCut = function(base, lengthVal){
	var le = 0;
	var result = '';
	var size = i<base.length;
	for(var i=0; i<size; i++){ 
		var chr = base.charCodeAt(i);
		le += $.getByteLengthChar(chr)
		if(lengthVal>=le){
			result += base.charAt(i);
		}else{
			break;
		}
	}
	return result;
};

//フォーカス位置に挿入
$.insertForcusPosition = function(selector, str){
	var sentence = selector.value;
	var len = sentence.length;
	var pos = selector.selectionStart;
	var before = sentence.substr(0, pos);
	var after = sentence.substr(pos, len);
	selector.value = before + str + after;
	var newPos = before.length + str.length;
	selector.setSelectionRange(newPos, newPos);
}

//IME入力中の値を取っておく
var maxbytelength_beforeValue = null;
var maxbytelength_beforePos = 0;
$('input[maxbytelength], textarea[maxbytelength]').on('compositionstart', function(e){
	deleteSelection(this);
	maxbytelength_beforeValue = this.value;
	maxbytelength_beforePos = this.selectionStart;
});

//選択状態のとき、その内容を消去してから後続の処理を行う
function deleteSelection(selector){
	var pos_start = selector.selectionStart;
	var pos_end = selector.selectionEnd;
	if(pos_end>0 && pos_end-pos_start>0){
		var val = selector.value;
		var range = val.slice(pos_start, pos_end);
		var beforeNode = val.slice(0, pos_start);
		var afterNode = val.slice(pos_end);
		selector.value = beforeNode + afterNode;
		selector.setSelectionRange(pos_start, pos_start);
	}
}

//本体
$('input[maxbytelength], textarea[maxbytelength]').on('keypress compositionend paste drop', function(e){
	deleteSelection(this);
	var MAX_BLEN = $(this).attr('maxbytelength');
	var value = $(this).val();
	var valueByteLen = $.getByteLength(value);
	if(valueByteLen > MAX_BLEN){
		switch(e.type){
		case 'compositionend': /// IMEでの入力が確定したとき
			var imeData = e.originalEvent.data;
			var scope = MAX_BLEN - $.getByteLength(maxbytelength_beforeValue);
			var sentence = maxbytelength_beforeValue;
			var len = sentence.length;
			var before = sentence.substr(0, maxbytelength_beforePos);
			var after = sentence.substr(maxbytelength_beforePos, len);
			var tmp = $.getByteCut(imeData, scope);
			var pos = before.length + tmp.length;
			this.value = before + tmp + after;
			this.setSelectionRange(pos, pos);
			break;
		}
	}
	switch(e.type){
	case 'keypress': /// 半角文字が入力されたとき
		if($.getByteLength(e.key)==1 && valueByteLen+1 > MAX_BLEN)
			e.preventDefault(); e.stopPropagation();
			break;
	case 'paste': /// 貼り付けが反映される前
		var pasted = (window.clipboardData && window.clipboardData.getData) 
		? window.clipboardData.getData('Text') 
		: e.originalEvent.clipboardData.getData('text/plain');
		var futureByteLen = valueByteLen + $.getByteLength(pasted);
		if(futureByteLen > MAX_BLEN){
			var scope = MAX_BLEN - valueByteLen;
			$.insertForcusPosition(this, $.getByteCut(pasted, scope));
			e.preventDefault();
			e.stopPropagation();
		}
		break;
	case 'drop': /// ドロップが反映される前
		var dropped =  e.originalEvent.dataTransfer.getData("text/plain");
		var futureByteLen = valueByteLen + $.getByteLength(dropped);
		if(futureByteLen > MAX_BLEN){
			var scope = MAX_BLEN - valueByteLen;
			$.insertForcusPosition(this, $.getByteCut(dropped, scope));
			e.preventDefault();
			e.stopPropagation();
		}
		break;
	}
});

Chrome、FF、IE11で動作確認済み。
使い方は「maxbytelength」属性をinputまたはtextareaに設置し、切り取りたいバイト数を設定するだけ。

ペースト時のフォーカスの位置など、細かな調整をすると追記が山のよう_(:3」∠)_
これでも完璧とは言い難いですが、おおよそmaxlength属性に近づけられたかな、と。

この記事は役に立ちましたか?
  • _(:3」∠)_ 面白かった (0)
  • (・∀・) 参考になった (0)
  • (`・ω・´) 役に立った (0)