javascript – 使用箭头键遍历contenteditable段落

前端之家收集整理的这篇文章主要介绍了javascript – 使用箭头键遍历contenteditable段落前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我试图使用箭头键在可疑段落之间进行遍历.我不能在所有段落中放置一个包含div,因为它可能被其他不可编辑的元素划分.

我需要能够确定第一行的字符长度,以便当光标在行上时按下向上箭头键,然后它将跳到上一段 – 希望保持光标相对于行的位置.

我可以用以下方法获取游标索引:

  1. function cursorIndex() {
  2. return window.getSelection().getRangeAt(0).startOffset;
  3. }

并将其设置为:在此处找到 – Javascript Contenteditable – set Cursor / Caret to index

  1. var setSelectionRange = function(element,start,end) {
  2. var rng = document.createRange(),sel = getSelection(),n,o = 0,tw = document.createTreeWalker(element,NodeFilter.SHOW_TEXT,null,null);
  3. while (n = tw.nextNode()) {
  4. o += n.nodeValue.length;
  5. if (o > start) {
  6. rng.setStart(n,n.nodeValue.length + start - o);
  7. start = Infinity;
  8. }
  9. if (o >= end) {
  10. rng.setEnd(n,n.nodeValue.length + end - o);
  11. break;
  12. }
  13. }
  14. sel.removeAllRanges();
  15. sel.addRange(rng);
  16. };
  17.  
  18. var setCaret = function(element,index) {
  19. setSelectionRange(element,index,index);
  20. };

假设光标位于第三段的顶行并按下向上箭头,我希望它跳转到第二段的底行

http://jsfiddle.net/Pd52U/2/

解决方法

看起来没有简单的方法可以做到这一点,我有以下工作示例.有一些处理,所以它有点慢,当在段落之间上下移动时,它可以由奇数字符输出.

请告知我可以进行的任何改进.

http://jsfiddle.net/zQUhV/47/

我所做的是通过每个工作拆分段落,逐个将它们插入一个新元素,检查高度变化 – 当它改变时,添加了一个新行.

函数返回包含行文本,起始索引和结束索引的行对象数组:

  1. (function($) {
  2. $.fn.lines = function(){
  3. words = this.text().split(" "); //split text into each word
  4. lines = [];
  5.  
  6. hiddenElement = this.clone(); //copies font settings and width
  7. hiddenElement.empty();//clear text
  8. hiddenElement.css("visibility","hidden");
  9.  
  10. jQuery('body').append(hiddenElement); // height doesn't exist until inserted into document
  11.  
  12. hiddenElement.text('i'); //add character to get height
  13. height = hiddenElement.height();
  14. hiddenElement.empty();
  15.  
  16. startIndex = -1; // quick fix for now - offset by one to get the line indexes working
  17. jQuery.each(words,function() {
  18. lineText = hiddenElement.text(); // get text before new word appended
  19. hiddenElement.text(lineText + " " + this);
  20. if(hiddenElement.height() > height) { // if new line
  21. lines.push({text: lineText,startIndex: startIndex,endIndex: (lineText.length + startIndex)}); // push lineText not hiddenElement.text() other wise each line will have 1 word too many
  22. startIndex = startIndex + lineText.length +1;
  23. hiddenElement.text(this); //first word of the next line
  24. }
  25. });
  26. lines.push({text: hiddenElement.text(),endIndex: (hiddenElement.text().length + startIndex)}); // push last line
  27. hiddenElement.remove();
  28. lines[0].startIndex = 0; //quick fix for now - adjust first line index
  29. return lines;
  30. }
  31. })(jQuery);

现在,您可以使用它来测量直到光标点的字符数,并在遍历段落时应用该字符以保持光标相对于行的开头的位置.然而,当将’i’的宽度考虑为’m’的宽度时,这会产生非常不准确的结果.

相反,最好找到直到光标点的直线宽度:

  1. function distanceToCaret(textElement,caretIndex){
  2.  
  3. line = findLineViaCaret(textElement,caretIndex);
  4. if(line.startIndex == 0) {
  5. // +1 needed for substring to be correct but only first line?
  6. relativeIndex = caretIndex - line.startIndex +1;
  7. } else {
  8. relativeIndex = caretIndex - line.startIndex;
  9. }
  10. textToCaret = line.text.substring(0,relativeIndex);
  11.  
  12. hiddenElement = textElement.clone(); //copies font settings and width
  13. hiddenElement.empty();//clear text
  14. hiddenElement.css("visibility","hidden");
  15. hiddenElement.css("width","auto"); //so width can be measured
  16. hiddenElement.css("display","inline-block"); //so width can be measured
  17.  
  18. jQuery('body').append(hiddenElement); // doesn't exist until inserted into document
  19.  
  20. hiddenElement.text(textToCaret); //add to get width
  21. width = hiddenElement.width();
  22. hiddenElement.remove();
  23.  
  24. return width;
  25. }
  26. function findLineViaCaret(textElement,caretIndex){
  27. jQuery.each(textElement.lines(),function() {
  28. if(this.startIndex <= caretIndex && this.endIndex >= caretIndex) {
  29. r = this;
  30. return false; // exits loop
  31. }
  32. });
  33. return r;
  34. }

然后将目标线分割成字符,并通过逐个添加字符找到最接近上面宽度的点,直到达到该点:

  1. function getCaretViaWidth(textElement,lineNo,width) {
  2. line = textElement.lines()[lineNo-1];
  3.  
  4. lineCharacters = line.text.replace(/^\s+|\s+$/g,'').split("");
  5.  
  6. hiddenElement = textElement.clone(); //copies font settings and width
  7. hiddenElement.empty();//clear text
  8. hiddenElement.css("visibility","inline-block"); //so width can be measured
  9.  
  10. jQuery('body').append(hiddenElement); // doesn't exist until inserted into document
  11.  
  12. if(width == 0) { //if width is 0 index is at start
  13. caretIndex = line.startIndex;
  14. } else {// else loop through each character until width is reached
  15. hiddenElement.empty();
  16. jQuery.each(lineCharacters,function() {
  17. text = hiddenElement.text();
  18. prevWidth = hiddenElement.width();
  19. hiddenElement.text(text + this);
  20. elWidth = hiddenElement.width();
  21. caretIndex = hiddenElement.text().length + line.startIndex;
  22. if(hiddenElement.width() > width) {
  23. // check whether character after width or before width is closest
  24. if(Math.abs(width - prevWidth) < Math.abs(width - elWidth)) {
  25. caretIndex = caretIndex -1; // move index back one if prevIoUs is closes
  26. }
  27. return false;
  28. }
  29. });
  30. }
  31. hiddenElement.remove();
  32. return caretIndex;
  33. }

使用以下keydown函数足以在令人满意的段落之间准确遍历:

  1. $(document).on('keydown','p[contenteditable="true"]',function(e) {
  2. //if cursor on first line & up arrow key
  3. if(e.which == 38 && (cursorIndex() < $(this).lines()[0].text.length)) {
  4. e.preventDefault();
  5. if ($(this).prev().is('p')) {
  6. prev = $(this).prev('p');
  7. getDistanceToCaret = distanceToCaret($(this),cursorIndex());
  8. lineNumber = prev.lines().length;
  9. caretPosition = getCaretViaWidth(prev,lineNumber,getDistanceToCaret);
  10. prev.focus();
  11. setCaret(prev.get(0),caretPosition);
  12. }
  13. // if cursor on last line & down arrow
  14. } else if(e.which == 40 && cursorIndex() >= $(this).lastLine().startIndex && cursorIndex() <= ($(this).lastLine().startIndex + $(this).lastLine().text.length)) {
  15. e.preventDefault();
  16. if ($(this).next().is('p')) {
  17. next = $(this).next('p');
  18. getDistanceToCaret = distanceToCaret($(this),cursorIndex());
  19. caretPosition = getCaretViaWidth(next,1,getDistanceToCaret);
  20. next.focus();
  21. setCaret(next.get(0),caretPosition);
  22. }
  23. //if start of paragraph and left arrow
  24. } else if(e.which == 37 && cursorIndex() == 0) {
  25. e.preventDefault();
  26. if ($(this).prev().is('p')) {
  27. prev = $(this).prev('p');
  28. prev.focus();
  29. setCaret(prev.get(0),prev.text().length);
  30. }
  31. // if end of paragraph and right arrow
  32. } else if(e.which == 39 && cursorIndex() == $(this).text().length) {
  33. e.preventDefault();
  34. if ($(this).next().is('p')) {
  35. $(this).next('p').focus();
  36. }
  37. };

猜你在找的JavaScript相关文章