JavaScript 'contenteditable' -- 获取/设置光标位置

16

我已经阅读了一些关于定位光标的帖子,但没有一个能回答我的特定问题。

  1. 我有两个div(div1div2
  2. div1 = 不可编辑的div
  3. div2 = 可编辑的div
  4. 两个div包含完全相同的内容
  5. 当用户点击div1时,它会被隐藏,然后div2出现在完全相同的位置,用户可以进行编辑

问题:我希望光标在div2中出现在与div1完全相同的位置

所以,我需要一种方法来读取用户单击div1时的位置,并且在div2出现时将光标/插入点放置在相同的位置,因此需要一组getCaretLocation(in_div_id)setCaretLocation(in_div_id)函数。

有什么方法可以做到吗?

谢谢 -


嗨,你找到解决方案了吗?我也遇到了同样的问题,让我感到疯狂。 - Grant Zhu
5个回答

8

简短回答:你不能。

详细回答:你将面临的问题是,你可以获得div1上点击事件的(x,y)坐标,但任何实现光标位置的操作都需要你知道内容中光标的位置(即光标之前的字符数)。

要将(x,y)坐标转换为字符位置,你实际上需要知道光标之前有多少个字符(即当前行和上方剩余的字数,如果文本是从左到右)。

如果你使用固定宽度的字体,你可以简化问题:将(x,y)坐标映射到字符网格上的(line, column)坐标。

然而,你仍然面临不知道文本如何换行的问题。例如:

------------------
|Lorem ipsum     |
|dolor sit amet  |
|consectetur     |
|adipiscing elit |
------------------

如果用户在“dolor”中点击“d”,则您知道该字符是第2行的第1个,但是如果不知道换行算法,则无法知道它是“Lorem ipsum dolor sit…”中的第13个字符。而且并不能保证这样的换行算法在浏览器和平台上是相同的。
现在,我想知道为什么您要在第一次使用两个不同的同步的
?难道不是更容易只使用一个div并在用户单击(或悬停)时将其内容设置为可编辑吗?

使用2个div是因为我要让用户可以拖动div,这会干扰到选择文本(例如,将其加粗)。 - OneNerd
想着也许我可以获取坐标,并在div2上使用相同的坐标触发click()事件 - 将尝试并查看它的工作原理。 - OneNerd
@OneNerd:我会倾向于使用一个手柄或类似的东西来拖动,但如果不知道你的用户界面的其余部分是什么,我就不知道这是否对你所做的事情有意义。 - T.J. Crowder
是的 - 在坐标处触发点击无效。我投票支持你的答案,因为它很详细并且有帮助。谢谢。 - OneNerd
这个问题与我的一个问题非常相关(https://dev59.com/-nA75IYBdhLWcg3w9-Ox)。如果能够使用原始点击的坐标成功“模拟”单击contenteditable div,那么这可能会更容易。我尝试过jquery.simulate插件,但似乎根本不起作用,也许是因为它与jQuery 1.6不兼容(该插件最后更新于2009年)。一旦我能够使其正常工作或找到合适的解决方案,我将在此发布。 - NeilC

1
你可以在光标处插入一个小的 span 元素,获取其位置,然后将其删除。如果需要跨浏览器的范围和选择库,请参见 rangy

+1 for rangy。自己处理范围是一场灾难。 - Ruan Mendes

1

你可以在第一个 div 上设置临时内容可编辑,以捕获光标位置。

$('div1').hover(function()
{ $(this).attr('contenteditable','true');
},function()
{ $(this).removeAttr('contenteditable');
}).mouseup(function()
{   var t = $(this);
    // get caret position and remove content editable
    var caret = t.getCaret();
    t.removeAttr('contenteditable');
    // do your div switch stuff
    ...
    // and apply saved caret position
    $('div2').setCaret(caret);
});

现在只需要获取/设置插入符号的方法 :)

编辑 > 这是我的自己的方法,(演示

        getSelection:function($e)
        {   if(undefined === window.getSelection) return false;
            var range = window.getSelection().getRangeAt(0);

            function getTreeOffset($root,$node)
            {   if($node.parents($root).length === 0) return false; // is node child of root ?
                var tree = [], treesize = 0;
                while(1)
                {   if($node.is($root)) break;
                    var index, $parent = $node.parent();
                    index = $parent.contents().index($node);
                    if(index !== -1) { tree[treesize++] = index; } $node = $parent;
                };  return tree.reverse();
            }

            var start = getTreeOffset($e,$(range.startContainer));
            var end   = getTreeOffset($e,$(range.endContainer));

            if(start & end === false) return false;

            return {start:start,end:end,startOffset:range.startOffset,endOffset:range.endOffset};
        }, setSelection:function($e,s,win)
        {   $e.focus(); if(s === false) return; var sel = win.getSelection(); sel.removeAllRanges();

            function getNode($e,s)
            {   var node = $e;
                for( var n=0;n<s.length;n++ )
                {   var index = s[n]; if(index < 0) break;
                    node = node.contents(':eq('+index+')');
                }   return node.get(0);
            }

            var start = getNode($e,s.start), end = getNode($e,s.end), range = win.document.createRange();
            range.setStart(start,s.startOffset); range.setEnd(end,s.endOffset); sel.addRange(range);
        }

0

听起来你想要进行内联编辑... 你看过 jeditable 插件了吗?


0
当您点击一个元素时,会创建一个长度为零的Selection对象(从element.getSelection()获取它,其中element是所讨论的div)。该对象的focusOffset将让您知道您点击了div中的第74个字符,例如(这就是Adrien在另一个回答中说不可能的事情)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接