5

非常に軽量なリッチテキストエリアを作成するために、テキストエリアのキャレットを模倣しようとしています。codemirror やその他の大規模なライブラリのようなものは使用したくありません。それらの機能を使用しないからです。

<pre>テキストのハイライト効果をシミュレートできるように、背景が透明なテキストエリアの後ろに配置しています。ただし、フォントの色も変更できるようにしたい (常に同じとは限らない)。color: transparentテキストエリアの後ろの要素にしか表示されないので、好きなようにテキストのスタイルを設定できるテキストエリアを試してみました<pre>が、キャレットは消えます。

完璧ではありませんが、かなりうまく機能するようになりました。主な問題は、キーを押したままその文字をスパムすると、キャレットが常に 1 文字遅れて表示されることです。それだけでなく、かなりリソースが重いようです..

コードに改善が必要な点が他にある場合は、それについてもコメントしてください。

ここにコードのフィドルがあります:http://jsfiddle.net/2t5pu/25/

何らかの理由で jsfiddle にアクセスしたくない場合は、コード全体を次に示します。

CSS:

textarea, #fake_area {
    position: absolute;
    margin: 0;
    padding: 0;
    height: 400px;
    width: 600px;
    font-size: 16px;
    font: 16px "Courier New", Courier, monospace;
    white-space: pre;
    top: 0;
    left: 0;
    resize: none;
    outline: 0;
    border: 1px solid orange;
    overflow: hidden;
    word-break: break-word;
    padding: 5px;
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    -ms-box-sizing: border-box;
    box-sizing: border-box;
}
#fake_area {
    /* hide */
    opacity: 0;
}
#caret {
    width: 1px;
    height: 18px;
    position: absolute;
    background: #f00;
    z-index: 100;
}

HTML:

<div id="fake_area"><span></span></div>
<div id="caret"></div>
<textarea id="textarea">test</textarea>

ジャバスクリプト:

var fake_area = document.getElementById("fake_area").firstChild;
var fake_caret = document.getElementById("caret");
var real_area = document.getElementById("textarea");

$("#textarea").on("input keydown keyup propertychange click", function () {
    // Fill the clone with textarea content from start to the position of the caret. 
    // The replace /\n$/ is necessary to get position when cursor is at the beginning of empty new line.
doStuff();    
});

var timeout;
function doStuff() {
    if(timeout) clearTimeout(timeout);
    timeout=setTimeout(function() {
        fake_area.innerHTML = real_area.value.substring(0, getCaretPosition(real_area)).replace(/\n$/, '\n\u0001');
    setCaretXY(fake_area, real_area, fake_caret, getPos("textarea"));
    }, 10);
}


    function getCaretPosition(el) {
        if (el.selectionStart) return el.selectionStart;
        else if (document.selection) {
            //el.focus();
            var r = document.selection.createRange();
            if (r == null) return 0;

            var re = el.createTextRange(), rc = re.duplicate();
            re.moveToBookmark(r.getBookmark());
            rc.setEndPoint('EndToStart', re);

            return rc.text.length;
        }
        return 0;
    }

    function setCaretXY(elem, real_element, caret, offset) {
        var rects = elem.getClientRects();
        var lastRect = rects[rects.length - 1];

        var x = lastRect.left + lastRect.width - offset[0] + document.body.scrollLeft,
            y = lastRect.top - real_element.scrollTop - offset[1] + document.body.scrollTop;

        caret.style.cssText = "top: " + y + "px; left: " + x + "px";
        //console.log(x, y, offset);
    }

    function getPos(e) {
        e = document.getElementById(e);
        var x = 0;
        var y = 0;
        while (e.offsetParent !== null){
            x += e.offsetLeft;
            y += e.offsetTop;
            e = e.offsetParent;
        }
        return [x, y];
    }

前もって感謝します!

4

3 に答える 3

2

遅延は、キーアップが doStuff をトリガーするのが少し遅すぎるためだと思いますが、キーダウンは少し早すぎます。

jQuery イベント フックアップの代わりにこれを試してみてください (通常はポーリングよりもイベントを好みますが、この場合はより良い感じになるかもしれません)...

setInterval(function () { doStuff(); }, 10); // 100 checks per second

function doStuff() {
    var newHTML = real_area.value.substring(0, getCaretPosition(real_area)).replace(/\n$/, '\n\u0001');
    if (fake_area.innerHTML != newHTML) {
        fake_area.innerHTML = newHTML;
        setCaretXY(fake_area, real_area, fake_caret,                         getPos("textarea"));
    }
}

...またはフィドルについてはこちら: http://jsfiddle.net/2t5pu/27/

于 2013-07-23T15:42:04.780 に答える