我的问题有两个部分,但它们是相关的。
首先 - 我有带有一些文本的 Contenteditable DIV,我需要获取此 DIV 的总行数(行)。是否可以 ?
其次 - 我需要获得插入符号行位置,如果它在第 1、2、3 等行上......
有人可以帮忙吗?
我的问题有两个部分,但它们是相关的。
首先 - 我有带有一些文本的 Contenteditable DIV,我需要获取此 DIV 的总行数(行)。是否可以 ?
其次 - 我需要获得插入符号行位置,如果它在第 1、2、3 等行上......
有人可以帮忙吗?
直接的答案是没有任何方法可以真正得到这些数字。但是,您可以应用许多不同的解决方法来估算这些值(我使用estimate,因为我认为它们不能 100% 准确)这些值。
要回答您的第一个问题,如何获取元素中的行数。假设元素使用一致line-height
,你可以通过将元素除以 来找到行height
数line-height
。margin
如果您在元素中使用s、padding
s 或区分s 的元素,这当然会变得更加复杂line-height
。如果这还不够,那么line-height
属性不一定必须是数值,即它可以是normal
或 % 等。关于line-height
属性以及如何在这里获得它的数字表示有很多问题关于 SO,所以我不会对此进行太多详细介绍。为了回答我的其余部分,我确实专门line-height
为元素分配了一个公共点。
您的问题中更有问题的部分是在元素中找到插入符号的位置。同样,没有一种方法可以只为您返回正确的答案。相反,您可以用来估计插入符号行位置的一种方法是range
在当前插入符号位置 ( selection
) 处创建一个,在那里插入一个虚拟节点,获取offset
相对于容器的节点offset
,根据它计算行数top offset
,然后删除虚拟元素。
当您不能使用虚拟元素hide()
或将其留空时,会出现其他问题。
这似乎是一种非常糟糕的做法,而且确实如此。尝试使用箭头导航时,它当然不能完美地工作。那么为什么不只getClientRects()
使用selection
? 当它只是没有任何空格等的文本时,它可以正常工作,但是当你<br /><br />
在那里扔几个时,它不会再返回你那里的行位置。此外,当您位于一行的第一个或最后一个字符时,有时会给出不正确的行行,因为它会根据可用空间量向上或向下抛出元素。
如果您正在寻找一种万无一失的方法来查找插入符号的位置和行数,那么没有. 如果粗略估计就足够了,那么我的解决方案就是一个好的开始(它当然可以使用更多的工作,并且对 IE 的支持在这种情况下应该容易得多,因为TextRange
对象实际上以像素为单位提供偏移量)。
我对此的看法:
var lineHeight = parseInt($('#editable').css('line-height'));
//var ce = $('#editable')[0].getClientRects();
var ce = $('#editable').position();
console.log("Lines: "+$('#editable').height()/lineHeight);
$('#editable').bind('click keyup keydown',function(){
if(window.getSelection){
range = window.getSelection().getRangeAt(0);
range.insertNode($('<canvas />').attr('id','tempCaretFinder')[0]);
var p = $('canvas#tempCaretFinder').position();
$('canvas#tempCaretFinder').remove();
console.log("Caret line: "+(Math.ceil((p.top-ce.top)/lineHeight)+1));
}else if(document.selection) {
// the IE way, which should be relatively easier. as TextRange objects return offsets directly.
range = document.selection.createRange();
}
});
示例:http: //jsfiddle.net/niklasvh/mKQUH/
编辑:尝试 2 http://jsfiddle.net/niklasvh/mKQUH/129/
如前所述,创建虚拟元素有时会使插入符号到处乱跳,所以我再次尝试使用 getClientRects() 获取范围。为了使它们更可行,我使选择扩展了一个字符,然后创建范围,然后检查 Rect 位置,然后将插入符号移回一个字符。
为什么?
因为在一行的第一个字符上的插入符号,它的顶部参数与前一行相同,所以通过对其应用额外的字符,它可以检查它是否真的是一个新行。getClientRects() 的另一个问题是它不适用于空换行符。为了适应这一点,我在这些情况下使用了之前创建的虚拟元素作为后备。
最终结果:
var lineHeight = parseInt($('#editable').css('line-height'));
//var ce = $('#editable')[0].getClientRects();
var ce = $('#editable').offset();
console.log("Lines: "+$('#editable').height()/lineHeight);
$('#editable').bind('click keyup keydown',function(e){
//alert($(window).scrollTop());
if(window.getSelection){
var save = window.getSelection();
var s = window.getSelection();
s.modify('extend','forward','character');
// s.modify('extend','forward','line');
range = s.getRangeAt(0);
var p = range.getClientRects();
var top;
//console.log(p);
if (typeof p[1] != "undefined"){
top = p[1].top+$(window).scrollTop();
}else if (typeof p[0] != "undefined"){
top = p[0].top+$(window).scrollTop();
}
else{
// sigh... let's make a real work around then
range.insertNode($('<canvas />').attr('id','tempCaretFinder')[0]);
var p = $('canvas#tempCaretFinder').offset();
$('canvas#tempCaretFinder').remove();
top = p.top;
}
// console.log(top-ce.top);
console.log("Caret line: "+(Math.ceil((top-ce.top)/lineHeight)));
save.modify('move','backward','character');
/*
range = s.getRangeAt(0);
range.insertNode($('<canvas />').attr('id','tempCaretFinder')[0]);
var p = $('canvas#tempCaretFinder').position();
$('canvas#tempCaretFinder').remove();
console.log("Caret line: "+(Math.ceil((p.top-ce.top)/lineHeight)+1));
console.log(e.which);
switch(e.which){
case 40:
s.modify("move", "forward","line");
break;
case 38:
s.modify("move", "backward","lineboundary");
break;
}
*/
}else if(document.selection) {
// the IE way, which should be relatively easier. as TextRange objects return offsets directly.
range = document.selection.createRange();
}
});