10

@usernameFacebookがステータス更新入力ボックスで行うように、スタイルタグを実行できるようにするjQueryプラグインに取り組んでいます。

私の問題は、何時間もの調査と実験を行った後でも、キャレットを単純に移動するのが本当に難しいように見えることです。私は<a>誰かの名前でタグを挿入することに成功しましたが、キャレットの後にキャレットを置くのはロケット科学のように思えます。特に、すべてのブラウザで動作するはずです.

@usernameそして、私が今行っているように単に挿入するのではなく、入力したテキストをタグに置き換えることさえまだ検討していません...笑

ここ Stack Overflow での contenteditable の操作についての質問が山ほどあります。私はそれらすべてを読んだと思いますが、必要なものを適切にカバーしているとは言えません。したがって、誰でも提供できる情報はこれ以上ありません:)

4

3 に答える 3

5

私の Rangy ライブラリを使用できます。これは、ブラウザの範囲と選択の実装を正規化することにある程度成功しています。あなたが言うように を挿入することができ<a>、それを という変数に入れaElementたら、次のことができます:

var range = rangy.createRange();
range.setStartAfter(aElement);
range.collapse(true);
var sel = rangy.getSelection();
sel.removeAllRanges();
sel.addRange(range);
于 2010-10-20T21:54:38.607 に答える
3

I got interested in this, so I've written the starting point for a full solution. The following uses my Rangy library with its selection save/restore module to save and restore the selection and normalize cross browser issues. It surrounds all matching text (@whatever in this case) with a link element and positions the selection where it had been previously. This is triggered after there has been no keyboard activity for one second. It should be quite reusable.

function createLink(matchedTextNode) {
    var el = document.createElement("a");
    el.style.backgroundColor = "yellow";
    el.style.padding = "2px";
    el.contentEditable = false;
    var matchedName = matchedTextNode.data.slice(1); // Remove the leading @
    el.href = "http://www.example.com/?name=" + matchedName;
    matchedTextNode.data = matchedName;
    el.appendChild(matchedTextNode);
    return el;
}

function shouldLinkifyContents(el) {
    return el.tagName != "A";
}

function surroundInElement(el, regex, surrounderCreateFunc, shouldSurroundFunc) {
    var child = el.lastChild;
    while (child) {
        if (child.nodeType == 1 && shouldSurroundFunc(el)) {
            surroundInElement(child, regex, surrounderCreateFunc, shouldSurroundFunc);
        } else if (child.nodeType == 3) {
            surroundMatchingText(child, regex, surrounderCreateFunc);
        }
        child = child.previousSibling;
    }
}

function surroundMatchingText(textNode, regex, surrounderCreateFunc) {
    var parent = textNode.parentNode;
    var result, surroundingNode, matchedTextNode, matchLength, matchedText;
    while ( textNode && (result = regex.exec(textNode.data)) ) {
        matchedTextNode = textNode.splitText(result.index);
        matchedText = result[0];
        matchLength = matchedText.length;
        textNode = (matchedTextNode.length > matchLength) ?
            matchedTextNode.splitText(matchLength) : null;
        surroundingNode = surrounderCreateFunc(matchedTextNode.cloneNode(true));
        parent.insertBefore(surroundingNode, matchedTextNode);
        parent.removeChild(matchedTextNode);
    }
}

function updateLinks() {
    var el = document.getElementById("editable");
    var savedSelection = rangy.saveSelection();
    surroundInElement(el, /@\w+/, createLink, shouldLinkifyContents);
    rangy.restoreSelection(savedSelection);
}

var keyTimer = null, keyDelay = 1000;

function keyUpLinkifyHandler() {
    if (keyTimer) {
        window.clearTimeout(keyTimer);
    }
    keyTimer = window.setTimeout(function() {
        updateLinks();
        keyTimer = null;
    }, keyDelay);
}

HTML:

<p contenteditable="true" id="editable" onkeyup="keyUpLinkifyHandler()">
    Some editable content for @someone or other
</p>
于 2010-10-26T18:19:16.403 に答える
1

すでにキャレットにタグを挿入できるとおっしゃっていますが、そこから始めます。最初に行うことは、タグを挿入するときにタグに ID を与えることです。次に、次のようなものが必要です。

<div contenteditable='true' id='status'>I went shopping with <a href='#' id='atagid'>Jane</a></div>

タグの直後にカーソルを配置する関数を次に示します。

function setCursorAfterA()
{
    var atag = document.getElementById("atagid");
    var parentdiv = document.getElementById("status");
    var range,selection;
    if(window.getSelection) //FF,Chrome,Opera,Safari,IE9+
    {
        parentdiv.appendChild(document.createTextNode(""));//FF wont allow cursor to be placed directly between <a> tag and the end of the div, so a space is added at the end (this can be trimmed later)
        range = document.createRange();//create range object (like an invisible selection)
        range.setEndAfter(atag);//set end of range selection to just after the <a> tag
        range.setStartAfter(atag);//set start of range selection to just after the <a> tag
        selection = window.getSelection();//get selection object (list of current selections/ranges)
        selection.removeAllRanges();//remove any current selections (FF can have more than one)
        parentdiv.focus();//Focuses contenteditable div (necessary for opera)
        selection.addRange(range);//add our range object to the selection list (make our range visible)
    }
    else if(document.selection)//IE 8 and lower
    { 
        range = document.body.createRange();//create a "Text Range" object (like an invisible selection)
        range.moveToElementText(atag);//select the contents of the a tag (i.e. "Jane")
        range.collapse(false);//collapse selection to end of range (between "e" and "</a>").
        while(range.parentElement() == atag)//while ranges cursor is still inside <a> tag
        {
             range.move("character",1);//move cursor 1 character to the right
        }
        range.move("character",-1);//move cursor 1 character to the left
        range.select()//move the actual cursor to the position of the ranges cursor
    }
    /*OPTIONAL: 
    atag.id = ""; //remove id from a tag
    */
}

編集: テストおよび修正されたスクリプト。IE6、chrome 8、firefox 4、opera 11 で確実に動作します。他のブラウザをテストする必要はありませんが、最近変更された機能を使用していないため、contenteditable をサポートするものであればすべて動作するはずです。

このボタンはテストに便利です: <input type='button' onclick='setCursorAfterA()' value='Place Cursor After &lt;a/&gt; tag' >

ニコ

于 2010-10-19T20:42:57.593 に答える