Как получить количество строк в области ContentEditable и текущее положение строки каретки? - PullRequest
6 голосов
/ 03 апреля 2011

мой вопрос состоит из двух частей, но они связаны между собой.

Во-первых, у меня есть Contenteditable DIV с некоторым текстом, и мне нужно получить общее количество строк (строк) этого DIV. Возможно ли это?

Во-вторых, мне нужно получить позицию строки каретки, если она находится в строке № 1, 2, 3 и т. Д.

Кто-нибудь может помочь?

1 Ответ

8 голосов
/ 04 июня 2011

Прямой ответ заключается в том, что не существует метода, который на самом деле получает эти цифры.Однако существует ряд различных обходных путей, которые вы можете применить к оценке (я использую оценку , потому что я не думаю, что они могут быть сделаны на 100% точными) этих значений.

Чтобы ответить на ваш первый вопрос, как узнать количество строк в элементе.Предполагая, что элемент использует согласованное line-height, вы можете найти количество строк, разделив элемент height на line-height.Это, конечно, значительно усложняется, если вы получаете элементы с margin s, padding s или дифференцированием line-height s внутри элемента.Если проблемы недостаточно, свойство 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();  
    }

});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...