简体   繁体   English

JavaScript'contenteditable' - 获取/设置插入位置

[英]JavaScript 'contenteditable' — Getting/Setting Caret Position

I have read a few posts on positioning the caret, but none seem to answer my particular issue. 我已经阅读了几篇关于插入符号的帖子,但似乎都没有回答我的特定问题。

  1. I have 2 divs ( div1 and div2 ) 我有2个div( div1div2
  2. div1 = noneditable div div1 =不可编辑的div
  3. div2 = contenteditable div div2 = contenteditable div
  4. both divs contain exact same contents 两个div包含完全相同的内容
  5. when user clicks on div1 , it gets hidden, and div2 appears in exact location and user can edit 当用户点击div1时 ,它会被隐藏, div2出现在确切的位置,用户可以编辑

The problem : I want the caret to appear in exact location on div2 as div1 问题 :我希望插入符号作为div1出现在div2上的确切位置

So, I need some way to READ the location where the user clicks on div1, and then when div2 appears place the cursor/caret in that same location, so a getCaretLocation(in_div_id) and setCaretLocation(in_div_id) set of functions. 所以,我需要一些方法来读取用户点击div1的位置,然后当div2出现时将光标/插入符号放在同一位置,这样就是一组getCaretLocation(in_div_id)setCaretLocation(in_div_id)函数。

Any way to do that? 有办法吗?

Thanks - 谢谢 -

Short answer : You can't 简答 :你做不到

Long answer : The problem you'll face is that you'll be able to get (x,y) coordinates for the click event on div1, but any implementation of the caret position while require you knowing the position of the caret in the content (which is the number of characters preceding the caret). 答案很长 :你将面临的问题是你将能够获得div1上click事件的(x,y)坐标,但是任何实现的插入位置都要求你知道插入符号在内容中的位置(这是插入符号前面的字符数)。

To convert the (x,y) coordinates to a character position you actually need to know how many characters were before (ie. left on the current line and above, if the text is ltr). 要将(x,y)坐标转换为字符位置,您实际上需要知道之前有多少个字符(即,如果文本是ltr,则保留在当前行和上面的字符处)。

If you use a fixed width font, you can simplify the problem : mapping an (x,y) coordinate to a (line, column) coordinate on a character grid. 如果使用固定宽度字体,则可以简化问题:将(x,y)坐标映射到字符网格上的(线,列)坐标。

However, you still face the problem of not knowing how the text is wrapped. 但是,您仍然面临不知道文本如何被包装的问题。 For example : 例如 :

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

If the user clicks on the d in dolor , you know that the character is the 1st on the 2nd line, but without knowing the wrapping algorithm there is no way you'll know that it is the 13th character in "Lorem ipsum dolor sit…". 如果用户点击了dol中d ,你知道该字符是第二行的第一个,但是在不知道包装算法的情况下,你无法知道它是“Lorem ipsum dolor sit中的第13个字符...... ”。 And there is no guarantee that such a wrapping algorithm is identical across browsers and platform. 并且无法保证这种包装算法在浏览器和平台之间是相同的。

Now, what I'm wondering is why would you use 2 different synced div in the first place ? 现在,我想知道为什么你会首先使用2个不同的同步div Wouldn't it be easier to use only one div and set its content to editable when the user clicks (or hovers) ? 当用户点击(或悬停)时,仅使用一个div并将其内容设置为可编辑是不是更容易?

you can, basically you need set temporary content editable on your first div to catch caret pos 你可以,基本上你需要在你的第一个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);
});

now just need get/set caret method :) 现在只需要获取/设置插入符号方法:)

edit > here is my own, ( live demo ) 编辑>这是我自己的,( 现场演示

        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);
        }

You could insert a tiny span-element at the caret, get its position, and remove it. 您可以在插入符号处插入一个小的span-element,获取其位置并将其删除。 For a cross-browser range and selection library, see rangy . 对于跨浏览器范围和选择库,请参阅rangy

When you click on an element, a Selection object with zero length is created (get it from element.getSelection() , where element is the div in question). 单击元素时,将创建一个长度为零的Selection对象(从element.getSelection()获取,其中element是有问题的div)。 The focusOffset of that object will let you know that you clicked on, for example, the 74th character in that div (this is the thing that Adrien said was impossible in a different answer). 该对象的focusOffset将告诉您单击该div中的第74个字符(这是Adrien所说的在不同答案中不可能的事情)。

读取文本中的插入位置,然后在编辑窗口中设置插入位置。

听起来你正在尝试进行内联编辑...你看过jeditable插件了吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM