如何获取ContentEditable区域中的行数和当前插入符行位置?
我的问题有两个部分,但它们是相关的。
首先 - 我有 Contenteditable DIV 和一些文本,我需要获取该 DIV 的总行数。是否可以 ?
其次 - 我需要获取插入符行位置,如果它位于第 1、2、3 行等...
任何人都可以帮忙吗?
my question has two parts, but they are related.
Firstly - I have Contenteditable DIV with some text and I need to get total number of rows (lines) of this DIV. Is it possible ?
Secondly - I need to get caret row position, if it is on row number 1, 2, 3, etc....
Can anybody help ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
直接的答案是没有任何方法可以真正获得这些数字。但是,有许多不同的解决方法可以应用于估计(我使用估计,因为我认为它们不能 100% 准确)价值观。
要回答您的第一个问题,如何获取元素中的行数。假设元素使用一致的
line-height
,则可以通过将元素height
除以line-height
得出行数。当然,如果元素内有margin
、padding
或区分line-height
的元素,这当然会变得更加复杂。如果这还不够严重,则line-height
属性不一定必须是数值,即它可以是normal
或 % 等。关于 line-height 属性以及如何在这里获取它的数字表示有很多问题,因此我不会对此进行过多的详细介绍。为了回答的其余部分,我专门在元素上分配了一个通用的line-height
。您的问题中更有问题的部分是找到元素内的插入符号位置。同样,没有一种方法可以只返回正确的答案。相反,您可以用来估计插入符号行位置的一种方法是在当前插入符号位置(
选择
)创建一个范围
,插入一个虚拟节点,获取节点相对于容器offset
的offset
,根据其top offset
计算行数,然后删除虚拟元素。当您无法
hide()
它或将其留空时,虚拟元素会出现其他问题。这似乎是一种非常糟糕的做法,事实确实如此。当尝试使用箭头导航时,它肯定不能完美地工作。那么为什么不直接使用
getClientRects()
进行选择
呢?当它只是没有任何空格等的文本时它工作得很好,但是当你立即在其中抛出一些
时,它不会返回给你线的位置已经在那里了。此外,当您位于一行的第一个或最后一个字符时,有时会给出不正确的行,因为它会根据可用空间量向上或向下抛出元素。gt;
>
如果您正在寻找一种万无一失的方法来查找插入符位置和行数,那么没有。如果粗略估计就足够了,那么我的解决方案是一个好的开始(它当然需要更多的工作,并且支持 IE,在这种情况下应该容易得多,因为
TextRange
对象实际上给出了以像素为单位的偏移量)。我对此的看法:
示例: http://jsfiddle.net/niklasvh/mKQUH/
编辑:尝试2
http://jsfiddle.net/niklasvh/mKQUH/129/
如前所述,创建虚拟元素有时会使插入符号跳得到处都是,所以我再次尝试使用 getClientRects() 来获取范围。为了使它们更加可行,我将选择范围自身扩展了一个字符,然后创建范围,然后检查矩形位置,然后将插入符号向后移动一个字符。
为什么?
由于一行第一个字符上的插入符号的顶部参数与前一行处于同一级别,因此通过对其应用额外的字符,它可以检查它是否实际上是一个新行。 getClientRects() 的另一个问题是它在空换行符上不起作用。为了适应这一点,我使用之前创建的虚拟元素作为这些情况下的后备。
最终结果:
The direct answer is that there is no method that actually gets you those numbers. However, there are a number of different work arounds which you can apply to estimate (I use estimate, because I don't think they can be made 100% accurate) those values.
To answer your first question, how to get the number of lines in the element. Assuming the element uses a consistent
line-height
, you can find the number of lines by dividing the elementheight
with theline-height
. This of course gets significantly more complicated if you got elements withmargin
s,padding
s, or differentiatingline-height
s within the element. If that isn't enough of a problem, theline-height
property doesn't necessarily have to be a numerical value, i.e. it can benormal
or a % etc. There's quite a few questions regarding theline-height
property and how to get a numerical representation of it here on SO so I won't go too much into detail on that. For the sake of rest of my answer, I did specifically assign a commonline-height
across the element.The far more problematic part of your question is finding the caret position within the element. Again, there isn't a method which will just return you the right answer. Instead, one approach you can take to estimate the caret row position is to make a
range
at the current caret position (selection
), insert a dummy node there, get the nodesoffset
relative to the containersoffset
, calculate the number of lines based on thetop offset
of it, and then remove the dummy element.Additional problems with the dummy element arise when you can't
hide()
it or leave it empty.It seems like a very bad way of doing it, and it is. It certainly doesn't work flawlessly when trying to navigate with arrows. So why not just use
getClientRects()
for theselection
? It works fine when it is just text without any space or such, but immidiately when you throw a few<br /><br />
in there, it won't return you the line position there anymore. Additionally, when you are at the first or last character of a line, it would give sometimes incorrect line rows, as it throws the element either up or down depending on the amount of space available.If you are looking for a fool-proof way of finding the caret position and number of lines, there isn't one. If rough estimates are enough, than my solution is a good start (it certainly could use some more work, and support for IE which should be lot easier in this case as the
TextRange
object actually gives offsets in pixels).My take on this:
Example: http://jsfiddle.net/niklasvh/mKQUH/
EDIT: Try 2
http://jsfiddle.net/niklasvh/mKQUH/129/
As mentioned, creating dummy elements made the caret jump all over the place sometimes, so I had a go again with the getClientRects() for ranges. To make them more feasible, I made the selection extend itself one character, then create the range, then check the Rect position, and then move the caret back one character.
Why?
Because the caret on the first character of a line, would have its top parameter at the same level as the previous line, so with applying an extra character to it, it can check whether it is actually a new line. Another problem with the getClientRects() was that it didn't work on empty line breaks. To accomodate this, I used the previous creating of a dummy element as a fallback in those situations.
End result: