如何在contenteditable div中设置光标位置在两个div之间。

6
考虑下面这个可编辑 div 元素。
<div contenteditable="true">
<div>bold text</div><div>bold text</div>
</div>

如果我把光标放在两个div之间并开始输入文本,则文本将变粗,而不是在两个div之间插入新的文本节点。如果你按下home键并在第一个div前面尝试输入一些内容,它将成为第一个div的一部分。
如果我检查从选择返回的范围的startContainer,则会得到其中一个div的内容,而不是我所期望的空文本节点。

$( '#EDITABLE' ).focus();

var selection = window.getSelection();

var range = document.createRange();

var div = $('#div2').get(0);

range.setStartBefore(div);
range.collapse(true);

selection.removeAllRanges();
selection.addRange(range);

// cursor should now be between div1 and div2

range = window.getSelection().getRangeAt(0);

console.log("range object returned is: ", range);

// type something into the content editable div. why is it coming
// up bold since the cursor should be between the divs? it's 
// positioning the cursor at the end of the previous div. I want to
// target the spot between the divs. how?
div.content {
  display: inline-block;
  font-weight: bold;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<div contenteditable="true" id="EDITABLE">
  <div class="content" id="div1">Content for div1</div>
  <div class="content" id="div2">content for div 2</div>
</div>

原始的jsfiddle链接: http://jsfiddle.net/9ZZpX/3/

问题是为什么会发生这种情况?我如何选择div之间的位置,以便在输入时不会出现加粗的问题?(显然我可以添加一个空格来解决问题,但那很丑陋。)

如果您在Facebook的状态更新框中输入@提及并按HOME,则可以看到此功能正常工作。如果您键入文本,则不会突出显示。

我所能想到的唯一方法是拦截keypress并通过编程方式插入文本节点,但这似乎很丑陋。

我搜索了很多,但找不到任何文件记录这个应该如何真正工作。显然,有些事情我不理解,而且文档在这个领域确实缺乏。

(我也想能够检测光标即将进入其中一个这些div并跳过它。如果两个div紧挨着,光标会跳入其中一个div,并且会弄乱工作。)

更多关于我正在尝试做的内容的信息:http://a-software-guy.com/2012/12/the-horrors-of-cursor-positioning-in-contenteditable-divs/

3个回答

8
浏览器在此方面存在不一致性。Firefox可以让您将插入符号定位在比大多数浏览器更多的位置,但是WebKit和IE都对有效的插入符号位置有明确的想法,并且会修改您添加到选择中的范围以符合最近的有效位置。这是有道理的:对于用户来说,在相同的视觉插入符号位置具有不同的文档位置和行为是令人困惑的。然而,这是以开发人员的不灵活为代价的。
没有任何地方记录这一点。当前选择规范没有提到这一点,主要是因为当浏览器实现其选择API时不存在规范,并且当前规范没有统一的行为可供记录。
一个选项是拦截keypress事件,就像您建议的那样,尽管当用户使用编辑或上下文菜单粘贴内容时,这种方法无法帮助。另一个选项是使用鼠标和键盘事件跟踪选择,创建带有零宽度空格字符的元素以放置插入符号,并在必要时将插入符号放置在其中之一。正如您所说,这很丑陋。

@YermoLamers:好文章。很多地方都与我的经验相似。顺便说一下,我使用Unicode BOM(U + FEFF)字符而不是U + 200B,结果更加一致地为零宽度。 - Tim Down
谢谢。希望对某些人有用。分离各种问题和错误只是痛苦的。 BOM?BOM不具有字节顺序意义,在某些情况下可能会引起混淆,您使用U+200B时遇到了什么问题? - Yermo Lamers
谢谢提供的链接。很有意思。我现在会先使用U+200B,并看看会遇到什么问题。作为一个独立开发者,我正在缩小自己的目标范围,只针对IE9及更高版本进行开发,这样可以避免很多麻烦。 - Yermo Lamers
@YermoLamers的(有用的)文章已经失踪了,所以这里提供Archive.org版本的链接:https://web.archive.org/web/20131023142558/http://a-software-guy.com/2013/01/you-cant-select-that-webkit-browser-selections-and-ranges-in-chrome-and-safari/ - andrewf
1
我已将文章移至此处:http://miles-by-motorcycle.com/fv-b-8-665/you-can---t-select-that-----webkit-browser-selections-and-ranges-in-chrome-and-safari - Yermo Lamers
显示剩余2条评论

4

我的回答只是对Tim的回答的补充,他的回答已经很全面了。

据我所知,Facebook并没有使用可编辑内容。状态框由一个简单的文本区域和一个div层组成,在这个层上,他们为昵称渲染蓝色矩形。

即使他们使用了,那也是另一种情况,因为昵称将成为内联元素,幸运的是,对于内联元素的情况更加简单 :)

关于在无法访问的位置定位插入符号 - 在CKEditor中我们也遇到了同样的问题。有许多地方用户无法移动插入符号。我们决定使用一个名为Magic-line的插件来解决这个问题。正如您在演示中看到的,我们完全绕过了选择的问题,我认为这是解决这个问题的最佳方法。它非常易用,在CKEditor 4.0.1中它将通过按键(已经在master上实现)也完全可以访问。


非常有趣 +1。我开始尝试复制Facebook所做的事情,但很快意识到我想要实现的有些不同。顺便提一下,如果您在顶部打开一行,似乎没有办法在Linux下的Chrome中删除顶部行。我按删除键,整个区域都会被突出显示。 - Yermo Lamers

0
另外一件你可以做的事情是使用Mutation Observer来捕获变化,然后在事后修复它们。在我的用例中,我很幸运每个元素都有预定义的文本。当Mutation Observer触发时,我只需将更改的文本移动到适当的元素之前或之后的新文本节点中。这似乎比其他选项容易得多,因为观察器会对所有字符数据的更改触发,并且它还具有所有更改的正确记录,不像keypress事件。

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