2017年3月29日水曜日

テキストエリア内でのタブキー操作

先日「textareaでタブスペースを入力できるようにする」という記事を作成しました。
その後、そのサンプルを使っていたら「選択範囲末尾の改行がうまく制御できていない!」ということに気が付きました。

末尾の改行にも対応するサンプルを作成しました。


改行がうまく制御できないとはどういうことかというと、単純なサンプルは以下。
<html>
<title>textRange</title>
<body>
<textarea id=ta0 style="width:100%;height:50%;">123456
1
</textarea>
<textarea id=ta1 style="width:100%;height:50%;"></textarea>
</body>
<script>
resizeTo(200,300)
onload=function(){
ta0.select()
ta1.value = '['+document.selection.createRange().text+']'
}
</script>
</html>

サンプルの実行画面



「123456」から「1」までの改行は取得できていますが「1」より後ろの改行はtextプロパティには含まれていません。ただし末尾の改行はRangeオブジェクトの対象にはなっているのでtextプロパティを書き換えると、その変更は末尾の改行に対しても影響を及ぼします。

上記を考慮しないでtextプロパティを書き換えると末尾の改行が失われます。
その問題を解決してタブキーによるインデントの増減を可能にしたサンプルが以下です。

以下ソース
<html>
<head>
<title>行番号</title>
<style type="text/css">
.max{width:100%;height:100%}
.bc{border-collapse:collapse}
.of-hidden{overflow:hidden}
</style>
</head>
<body class="of-hidden">
<table class="bc max">
<tr><td><textarea id=ta class=max wrap=off></textarea></td></tr>
<tr height=1><td>行:<input id=inp行> 列:<input id=inp列></td></tr>
</table>
</body>
<script>
resizeTo(400,400)
ta.onkeyup=ta.onkeydown=function(){
var obj=getテキストカーソル位置(this), str=this.value.slice(0,obj.start), arr=str.split('\n')
inp行.value = arr.length
inp列.value = arr[arr.length-1].length + 1
}
ta.onkeydown=function(){
if(event.keyCode!=9){return}
event.cancelBubble = true
var obj=getテキストカーソル位置(this), rng=document.selection.createRange(), str=this.value.slice(obj.start, obj.end)
if(event.shiftKey){
if(str){
rng.text = str.replace(/\n\t|\n {4}/g,'\n').replace(/^\t|^ {4}/g,'')
}else if(obj.end < this.value.length){
var 文字数=Math.min(4,this.value.length-obj.end), reg=/^\t|^ {1,4}/, res
rng.moveEnd('character', 文字数)
if(res=rng.text.match(reg)){}else{ return false }
rng.text = rng.text.replace(reg,'')
rng.moveEnd('character', -(4-res[0].length))
rng.select()
}
}else{
rng.text = '\t' + str.replace(/\n/g,'\n\t')
}
return false
}
getテキストカーソル位置=function(elem){
// 左記URLのページを参照させていただきました。 http://d.hatena.ne.jp/nakazawaken1/20100125/p1
var rngSel=document.selection.createRange(), rngElem=document.body.createTextRange()
rngElem.moveToElementText(elem)
// rngSelより前の範囲を選択する(rngElemのEndをrngSelのStart位置に合わせる)
rngElem.setEndPoint('EndToStart', rngSel)
var f=function(rng){
var str=rng.text, str0=str
// 選択文字数が0でない間(前の範囲)
while(rng.compareEndPoints('StartToEnd', rng) != 0){
// 一文字ずつ範囲終了位置を前にずらしていく
rng.moveEnd('character', -1)
// ずらす前と後の文字列が異なる状態ならbreak
if(str0 != rng.text){break}
// ずらす前と後の値が同じ状態の間は改行を付け足していく。
str += '\r\n'
}
return str
}
var str前=f(rngElem), str選択=f(rngSel), start=str前.length, end=start + str選択.length
return {start:start, end:end}
// 以下の方法では選択範囲の末尾に改行文字がある場合、意図した通りの動作にならない。
/*
rngSel.moveStart('character', -this.value.length)
rngSel.moveEnd ('character', 1)
var reg=/\n/g, res=rSel.text.match(reg), 行, 列
if(res){
行 = res.length
列 = RegExp.rightContext.length - 1
}else{
行 = 0
列 = rSel.text.length - 1
}
inp行.value = 行
inp列.value = 列
*/
}
inp行.onchange = inp列.onchange = function(){ setカーソル位置(ta, inp行.value, inp列.value) }
setカーソル位置=function(elem, 行,列){
var arr=ta.value.split('\r\n'), rng=document.body.createTextRange()
行 = isFinite(行) ? 行 : 1
列 = isFinite(列) ? 列 : 1
rng.moveToElementText(elem)
rng.moveStart('character', arr.slice(0,行).join('\r\n').length - (行-1))
rng.moveStart('character', 列 - arr[行-1].length - 1)
while(rng.compareEndPoints('StartToEnd', rng) != 0){ rng.moveEnd('character',-1) }
rng.select()
}
</script>
</html>

0 件のコメント:

コメントを投稿