Muszę przenieść daszek na koniec contenteditable
węzła, jak w widżecie notatek Gmaila.
Czytam wątki na StackOverflow, ale te rozwiązania są oparte na wykorzystaniu danych wejściowych i nie działają z contenteditable
elementami.
Odpowiedzi:
Jest też inny problem.
Rozwiązanie Nico Burnsa działa, jeśli contenteditable
div nie zawiera innych elementów wielowierszowych.
Na przykład, jeśli element DIV zawiera inne elementy DIV, a te inne elementy DIV zawierają inne elementy, mogą wystąpić pewne problemy.
Aby je rozwiązać, zaaranżowałem następujące rozwiązanie, czyli ulepszenie rozwiązania Nico :
//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: /programming/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: /programming/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 || {}));
Stosowanie:
var editableDiv = document.getElementById("my_contentEditableDiv");
cursorManager.setEndOfContenteditable(editableDiv);
W ten sposób kursor jest z pewnością umieszczony na końcu ostatniego elementu, ostatecznie zagnieżdżony.
EDYCJA NR 1 : Aby była bardziej ogólna, instrukcja while powinna uwzględniać również wszystkie inne znaczniki, które nie mogą zawierać tekstu. Te elementy są nazywane void elements , aw tym pytaniu istnieje kilka metod sprawdzania, czy element jest pusty. Tak więc, zakładając, że istnieje funkcja o nazwie, canContainText
która zwraca, true
jeśli argument nie jest pustym elementem, następujący wiersz kodu:
contentEditableElement.lastChild.tagName.toLowerCase() != 'br'
należy zastąpić:
canContainText(getLastChildElement(contentEditableElement))
EDYCJA # 2 : Powyższy kod jest w pełni zaktualizowany, ze wszystkimi zmianami opisanymi i omówionymi
Uncaught TypeError: Cannot read property 'nodeType' of null
Podczas korzystania z funkcji pojawia się błąd, mówi konsola, a to z wywoływanej funkcji getLastChildElement. Czy wiesz, co może być przyczyną tego problemu?
Rozwiązanie Geowa4 będzie działać dla obszaru tekstowego, ale nie dla elementu, który można edytować.
To rozwiązanie służy do przenoszenia karetki na koniec elementu, który można edytować. Powinien działać we wszystkich przeglądarkach obsługujących edytowalne treści.
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
}
}
Może być używany przez kod podobny do:
elem = document.getElementById('txt1');//This is the element that you want to move the caret to the end of
setEndOfContenteditable(elem);
selectNodeContents
Część Nico był dając mi błędy zarówno w Chrome i FF (nie testowałem innych przeglądarek), dopóki nie okazało się, że widocznie potrzebna, aby dodać .get(0)
do elementu że karmiła funkcję. Wydaje mi się, że ma to związek z używaniem jQuery zamiast czystego JS? Dowiedziałem się tego od @jwarzech w pytaniu 4233265 . Dziękuje za wszystko!
.get(0)
pobiera element dom, który jQuery przechowuje wewnętrznie. Możesz również dołączyć [0]
, co jest równoważne .get(0)
w tym kontekście.
Jeśli nie przejmujesz się starszymi przeglądarkami, ta mi załatwiła sprawę.
// [optional] make sure focus is on the element
yourContentEditableElement.focus();
// select all the content in the element
document.execCommand('selectAll', false, null);
// collapse selection to the end
document.getSelection().collapseToEnd();
document.execCommand
jest teraz przestarzały developer.mozilla.org/en-US/docs/Web/API/Document/execCommand .
Możliwe jest ustawienie kursora na koniec zakresu:
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;
}
setCaretToEnd()
każdym razem - wywołuj go tylko wtedy, gdy tego potrzebujesz: np. Po kopiowaniu-wklejaniu lub po ograniczeniu długości wiadomości.
Miałem podobny problem, próbując udostępnić element do edycji. Było to możliwe w Chrome i FireFox, ale w FireFox karetka albo poszła na początek wejścia, albo przesunęła się o jedną spację po końcu wejścia. Myślę, że to bardzo zagmatwane dla użytkownika końcowego, próbującego edytować zawartość.
Nie znalazłem rozwiązania próbując kilku rzeczy. Jedyną rzeczą, która zadziałała w moim przypadku, było „obejście problemu” poprzez umieszczenie zwykłego starego pola tekstowego WEWNĄTRZ pliku. Teraz działa. Wygląda na to, że „edytowalna treść” to wciąż najnowocześniejsza technologia, która może, ale nie musi, działać tak, jak chcesz, w zależności od kontekstu.
Przesunięcie kursora na koniec edytowalnego zakresu w odpowiedzi na zdarzenie fokusa:
moveCursorToEnd(el){
if(el.innerText && document.createRange)
{
window.setTimeout(() =>
{
let selection = document.getSelection();
let range = document.createRange();
range.setStart(el.childNodes[0],el.innerText.length);
range.collapse(true);
selection.removeAllRanges();
selection.addRange(range);
}
,1);
}
}
I wywołując to w module obsługi zdarzeń (zareaguj tutaj):
onFocus={(e) => this.moveCursorToEnd(e.target)}}
Problem z contenteditable
<div>
i <span>
zostanie rozwiązany, gdy zaczniesz pisać na początku. Jednym z obejść tego problemu może być wywołanie zdarzenia focus na elemencie div i tej funkcji, wyczyszczenie i uzupełnienie tego, co już było w elemencie div. W ten sposób problem został rozwiązany i na koniec możesz umieścić kursor na końcu, używając zakresu i wyboru. Pracował dla mnie.
moveCursorToEnd(e : any) {
let placeholderText = e.target.innerText;
e.target.innerText = '';
e.target.innerText = placeholderText;
if(e.target.innerText && document.createRange)
{
let range = document.createRange();
let selection = window.getSelection();
range.selectNodeContents(e.target);
range.setStart(e.target.firstChild,e.target.innerText.length);
range.setEnd(e.target.firstChild,e.target.innerText.length);
selection.removeAllRanges();
selection.addRange(range);
}
}
W kodzie HTML:
<div contentEditable="true" (focus)="moveCursorToEnd($event)"></div>