2009-07-14 4 views

ответ

21

Существует еще одна проблема.

Решение Nico Burns работает, если div contenteditable не содержит других многострочных элементов.

Например, если div содержит другие divs, и эти другие divs содержат другие вещи внутри, могут возникнуть некоторые проблемы.

Для того, чтобы решить их, я организовал следующее решение, которое является улучшение Nico «s один:

//Namespace management idea from http://enterprisejquery.com/2010/10/how-good-c-habits-can-encourage-bad-javascript-habits-part-1/ 
(function(cursorManager) { 

    //From: http://www.w3.org/TR/html-markup/syntax.html#syntax-elements 
    var voidNodeTags = ['AREA', 'BASE', 'BR', 'COL', 'EMBED', 'HR', 'IMG', 'INPUT', 'KEYGEN', 'LINK', 'MENUITEM', 'META', 'PARAM', 'SOURCE', 'TRACK', 'WBR', 'BASEFONT', 'BGSOUND', 'FRAME', 'ISINDEX']; 

    //From: https://stackoverflow.com/questions/237104/array-containsobj-in-javascript 
    Array.prototype.contains = function(obj) { 
     var i = this.length; 
     while (i--) { 
      if (this[i] === obj) { 
       return true; 
      } 
     } 
     return false; 
    } 

    //Basic idea from: https://stackoverflow.com/questions/19790442/test-if-an-element-can-contain-text 
    function canContainText(node) { 
     if(node.nodeType == 1) { //is an element node 
      return !voidNodeTags.contains(node.nodeName); 
     } else { //is not an element node 
      return false; 
     } 
    }; 

    function getLastChildElement(el){ 
     var lc = el.lastChild; 
     while(lc && lc.nodeType != 1) { 
      if(lc.previousSibling) 
       lc = lc.previousSibling; 
      else 
       break; 
     } 
     return lc; 
    } 

    //Based on Nico Burns's answer 
    cursorManager.setEndOfContenteditable = function(contentEditableElement) 
    { 

     while(getLastChildElement(contentEditableElement) && 
       canContainText(getLastChildElement(contentEditableElement))) { 
      contentEditableElement = getLastChildElement(contentEditableElement); 
     } 

     var range,selection; 
     if(document.createRange)//Firefox, Chrome, Opera, Safari, IE 9+ 
     {  
      range = document.createRange();//Create a range (a range is a like the selection but invisible) 
      range.selectNodeContents(contentEditableElement);//Select the entire contents of the element with the range 
      range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start 
      selection = window.getSelection();//get the selection object (allows you to change selection) 
      selection.removeAllRanges();//remove any selections already made 
      selection.addRange(range);//make the range you have just created the visible selection 
     } 
     else if(document.selection)//IE 8 and lower 
     { 
      range = document.body.createTextRange();//Create a range (a range is a like the selection but invisible) 
      range.moveToElementText(contentEditableElement);//Select the entire contents of the element with the range 
      range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start 
      range.select();//Select the range (make it the visible selection 
     } 
    } 

}(window.cursorManager = window.cursorManager || {})); 

Использование:

var editableDiv = document.getElementById("my_contentEditableDiv"); 
cursorManager.setEndOfContenteditable(editableDiv); 

В Таким образом, курсор уверенно расположен в конце последнего элемента, в конце концов вложен.

EDIT # 1: Чтобы быть более общим, инструкция while должна учитывать также все другие теги, которые не могут содержать текст. Эти элементы называются недействительными элементами, а в this question есть несколько способов проверки того, является ли элемент недействительным. Таким образом, если предположить, что существует функция называется canContainText, которая возвращает true, если аргумент не является недействительным элементом, следующая строка кода:

contentEditableElement.lastChild.tagName.toLowerCase() != 'br' 

следует заменить:

canContainText(getLastChildElement(contentEditableElement)) 

EDIT # 2: приведенный выше код полностью обновлен, при этом каждый из описанных и описанных изменений

+0

Интересно, я бы ожидал, что браузер позаботится об этом случае автоматически (не то, что я удивлен, что это не так, браузеры никогда, похоже, не делают интуитивно понятных вещей с доступным контентом). У вас есть пример HTML, где ваше решение работает, но у меня нет? –

+0

В моем коде была еще одна ошибка. Я починил это. Теперь вы можете проверить, работает ли мой код на этой странице (http://html5demos.com/contenteditable), а у вас нет –

+0

Я получаю сообщение об ошибке с помощью вашей функции, консоль говорит 'Uncaught TypeError: Не удается прочитать свойство 'nodeType' of null ', и это происходит от вызываемой функции getLastChildElement. Вы знаете, что может вызвать эту проблему? – Derek

183

Решение Geowa4 будет работать в текстовом поле, но не для контент-контента.

Это решение предназначено для перемещения каретки до конца элемента, соответствующего контенту. Он должен работать во всех браузерах, которые поддерживают контент.

function setEndOfContenteditable(contentEditableElement) 
{ 
    var range,selection; 
    if(document.createRange)//Firefox, Chrome, Opera, Safari, IE 9+ 
    { 
     range = document.createRange();//Create a range (a range is a like the selection but invisible) 
     range.selectNodeContents(contentEditableElement);//Select the entire contents of the element with the range 
     range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start 
     selection = window.getSelection();//get the selection object (allows you to change selection) 
     selection.removeAllRanges();//remove any selections already made 
     selection.addRange(range);//make the range you have just created the visible selection 
    } 
    else if(document.selection)//IE 8 and lower 
    { 
     range = document.body.createTextRange();//Create a range (a range is a like the selection but invisible) 
     range.moveToElementText(contentEditableElement);//Select the entire contents of the element with the range 
     range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start 
     range.select();//Select the range (make it the visible selection 
    } 
} 

Он может быть использован кодом, аналогичным:

elem = document.getElementById('txt1');//This is the element that you want to move the caret to the end of 
setEndOfContenteditable(elem); 
+0

У вас есть способ сделать эту работу хромом? – Jason

+1

Решение geowa4 будет работать для textarea's в chrome, оно не будет работать для контент-контента в любом браузере. мои работы для контент-элементов, но не для текстовых областей. –

+4

Это правильный ответ на этот вопрос, отлично, спасибо Нико. – Rob

1

Можно установить курсор в конец в диапазоне:

setCaretToEnd(target/*: HTMLDivElement*/) { 
    const range = document.createRange(); 
    const sel = window.getSelection(); 
    range.selectNodeContents(target); 
    range.collapse(false); 
    sel.removeAllRanges(); 
    sel.addRange(range); 
    target.focus(); 
    range.detach(); // optimization 

    // set scroll to the end if multiline 
    target.scrollTop = target.scrollHeight; 
} 
+0

Используя вышеприведенный код, он делает трюк - но я хочу иметь возможность перемещать курсор в любом месте разрешаемого содержимого div и продолжать вводить с этой точки - например, пользователь распознал опечатку для примера ... Как мне изменить свой код выше? – Zabs

+1

@ Zabs это довольно просто: не вызывать 'setCaretToEnd()' каждый раз - вызывать его только тогда, когда вам это нужно: например. после Copy-Paste или после ограничения длины сообщения. – Andrew