直接的答案是没有方法可以实际获得这些数字.但是,您可以应用多种不同的工作来估算(我使用估计,因为我不认为它们可以100%准确)这些值.
要回答第一个问题,如何获得元素中的行数.假设元素使用一致的行高,您可以通过将元素高度除以行高来找到行数.如果您在元素中获得具有边距,填充或区分线高的元素,那么这当然会变得更加复杂.如果这还不够问题,行高属性不一定必须是数值,即它可以是正常的或%等等.关于行高属性有很多问题以及如何在这里得到它的数字表示,所以我不会过多地详细说明.为了我的回答,我特意在元素上指定了一个共同的行高.
问题中更成问题的部分是在元素中找到插入位置.同样,没有一种方法能够给你正确的答案.相反,您可以采用一种估算插入符号行位置的方法是在当前插入符号位置(选择)处设置一个范围,在那里插入一个虚拟节点,相对于容器偏移量获取节点偏移量,计算基于的行数它的顶部偏移,然后删除虚拟元素.
当您无法隐藏()或将其留空时,会出现虚拟元素的其他问题.
这似乎是一种非常糟糕的做法,而且确实如此.当试图用箭头导航时,它肯定无法正常工作.那么为什么不使用getClientRects()进行选择呢?当它只是没有任何空间的文本时,它可以正常工作,但是当你抛出一些< 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($('').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();
}
});
如上所述,创建虚拟元素有时会使插入符号遍布整个地方,所以我再次使用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($('').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($('').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();
}
});