Мені потрібно перемістити каретку до кінця contenteditable
вузла, як у віджеті нотаток Gmail.
Я читав потоки на StackOverflow, але ці рішення засновані на використанні входів, і вони не працюють з contenteditable
елементами.
Відповіді:
Також є ще одна проблема.
Ніко Бернс працює, якщо contenteditable
div не містить інших мультилінітованих елементів.
Наприклад, якщо div містить інші div, а ці інші div містять все інше всередині, можуть виникнути деякі проблеми.
Для їх вирішення я влаштував наступне рішення, це вдосконалення рішення Ніко :
//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 || {}));
Використання:
var editableDiv = document.getElementById("my_contentEditableDiv");
cursorManager.setEndOfContenteditable(editableDiv);
Таким чином, курсор, безсумнівно, розміщується в кінці останнього елемента, врешті-решт вкладеного.
EDIT # 1 : Для того, щоб бути більш загальним, оператор while повинен враховувати також усі інші теги, які не можуть містити текст. Ці елементи називаються порожніми елементами , і в цьому питанні є кілька методів, як перевірити, чи порожній елемент. Отже, якщо припустити, що існує функція, що викликається, canContainText
яка повертає, true
якщо аргумент не є порожнім елементом, наступний рядок коду:
contentEditableElement.lastChild.tagName.toLowerCase() != 'br'
слід замінити на:
canContainText(getLastChildElement(contentEditableElement))
EDIT # 2 : Наведений вище код повністю оновлений, з усіма описаними та обговореними змінами
Uncaught TypeError: Cannot read property 'nodeType' of null
і це відбувається від функції getLastChildElement, яку викликають. Чи знаєте ви, що може спричинити цю проблему?
Рішення 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);
selectNodeContents
Частина Ніко давав помилки мені як в Chrome і FF (не перевіряв інші браузери) , поки я не дізнався, що я , мабуть , потрібно додати .get(0)
до елементу , який я годував функцію. Я думаю, це пов’язано зі тим, що я використовую jQuery замість простого JS? Я дізнався про це від @jwarzech на запитання 4233265 . Дякую всім!
.get(0)
отримує елемент dom, який jQuery зберігає всередині. Ви також можете додати [0]
, що еквівалентно .get(0)
в цьому контексті.
Якщо ви не дбаєте про старіші браузери, цей для мене зробив свою справу.
// [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
зараз застарілий developer.mozilla.org/en-US/docs/Web/API/Document/execCommand .
Можна встановити курсор до кінця через діапазон:
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()
кожного разу - викликайте його лише тоді, коли вам це потрібно: наприклад, після копіювання-вставки або після обмеження довжини повідомлення.
У мене була подібна проблема при спробі зробити елемент редагованим. Це було можливо в Chrome та FireFox, але у FireFox каретка або переходила на початок введення, або вона проходила один пробіл після закінчення введення. Думаю, це дуже бентежить кінцевого користувача, намагаючись відредагувати вміст.
Я не знайшов рішення, щоб спробувати кілька речей. Єдине, що мені вдалося, це "обійти проблему", помістивши звичайний старий введення тексту ВНУТРІ мого. Зараз це працює. Здається, "редагований вміст" все ще є сучасною технологією, яка може працювати, а може і не працювати, як ви хотіли б, щоб вона працювала, залежно від контексту.
Переміщення курсору до кінця редагованого діапазону у відповідь на подію фокусування:
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);
}
}
І викликаючи це в обробнику подій (React тут):
onFocus={(e) => this.moveCursorToEnd(e.target)}}
Проблема з contenteditable
<div>
та <span>
вирішується, коли ви починаєте вводити його спочатку. Одним з обхідних шляхів для цього може бути активація події фокусу на вашому елементі div та на цій функції, очищення та заповнення того, що вже було в елементі div. Таким чином проблема вирішується, і нарешті ви можете помістити курсор в кінець, використовуючи діапазон та виділення. Працював у мене.
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);
}
}
У HTML-коді:
<div contentEditable="true" (focus)="moveCursorToEnd($event)"></div>