タグ挿入エディタ作成

最近、仕事が忙しくて、なかなか自分の作業時間が取れない。
いま、タグ挿入エディタみたいなものを作っている。
既存のJSを参考にだけど。


テキストエリアで文字を選択して、それに対してタグを囲むという動作だ。
たとえばこんな感じ。

<html>
<head>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
    jQuery.noConflict();
</script>
</head>
<body>
<div class="tageditor-viewer">
<textarea name="description" id="inputtext" class="tageditor-html">てすとてすと
てすと&amp;#xE63E;</textarea>
</div>
<script type="text/javascript">
jQuery('.tageditor-viewer textarea').tageditor('page');
</script>
</body>
</html>
jQuery.fn.tageditor = function(toolbartype) {

    jQuery(this).each( function(){
        var textarea = jQuery(this);
        init(textarea);
    });

    function init(textarea) {
        jQuery(textarea).parent('div.tageditor-viewer').after(getToolbar(textarea));
    }

    var selectsel=null; //IE
    var hash;

    function getToolbar(textarea) {
(略)


で、実際のタグ挿入部分はこんな感じ

    /**
     * insertTag
     */
    function insertTag(textarea, starttag, endtag, sel) {
        element = textarea.get(0);
        if (document.selection) {
            //IE support
            textarea.focus();
            if (!sel) sel = document.selection.createRange();
            if (sel.text.length > 0) {
                sel.text = starttag + sel.text + endtag;
            } else {
                inertText(textarea, starttag, endtag);
            }
            textarea.focus();
        } else if (element.selectionStart || element.selectionStart == '0') {
            //Firefox support
            var startPos = element.selectionStart;
            var endPos = element.selectionEnd;

            if (startPos != endPos) {
                var cursorPos = endPos;
                var scrollTop = element.scrollTop;
                element.value = element.value.substring(0, startPos)
                              + starttag
                              + element.value.substring(startPos, endPos)
                              + endtag
                              + element.value.substring(endPos, element.value.length);
                element.focus();
                element.selectionStart = cursorPos;
                element.selectionEnd = cursorPos;
                element.scrollTop = scrollTop;
                cursorPos += starttag.length + endtag.length;
            } else {
                inertText(textarea, starttag, endtag);
            }

        }
    }


これで、問題が出たのが、タグ挿入時に、いったんテキストボックスなどにカーソルを移してしまうと、textareaの選択状態が保持されなくなってしまう。
たとえば、aタグのリンクタグ挿入時に、リンク先URLをユーザに入力させる場合などだ。

<div class='tageditor-toolbar-link'>
    <a href='javascript:void(0);' id='tageditor-toolbar-link-" + hash + "'>リンク作成</a>
</div>
<div id='tageditor-toolbar-link-"+hash+"-popup' style='display:none; position:absolute; border: solid #000000 1px; background-color:#FFFFFF; padding:10px;'>
<div align='right'><a href='javascript:void(0);' class='popup-close'>[×]</a></div>
リンク先を入力してください<br />
<input type='text' value='' name='' size='50' maxlength='1024' class='tageditor-toolbar-link' /><br />
<input type='button' value='リンク作成' name='' class='tageditor-toolbar-createlink' />
</div>

で、JS側で、

jQuery("#tageditor-toolbar-link-" + hash, tb).click(function(){
    var name = this.id+'-popup';
    if(jQuery('#'+name).css('display')=='none'){
        if (document.selection) selectsel = document.selection.createRange(); //IE
        jQuery('#'+name).show();
    }else{
        jQuery('#'+name).hide();
    }
    return false;
    });
    jQuery("#tageditor-toolbar-link-" + hash + "-popup a.popup-close", tb).click(function(){
    jQuery(this).parent('div').parent('div').hide();
    return false;
});
jQuery("#tageditor-toolbar-link-" + hash + "-popup input.tageditor-toolbar-createlink", tb).click(function(){
    var link = jQuery(this).parent('div').children('input.tageditor-toolbar-link').val();
    insertTag(textarea, '<a href="' + link + '">', "</a>", selectsel);
    jQuery(this).parent('div').hide();
    return false;
});

この場合に、「//IE」とコメントを書いているところがミソ。
textareaから外れる前に、createRangeを使って現在の選択状態を保持しておくと、他のテキストボックスにフォーカスが移ってもその後で選択状態に対して処理が出来る。


うまくいかない場合もあるかもしれないし、まだまだ検証中なので中途半端な情報ですが。とりあえずです。