Tôi cần di chuyển dấu mũ đến cuối contenteditable
nút như trên tiện ích ghi chú Gmail.
Tôi đọc các chủ đề trên StackOverflow, nhưng các giải pháp đó dựa trên việc sử dụng đầu vào và chúng không hoạt động với contenteditable
các phần tử.
Câu trả lời:
Ngoài ra còn có một vấn đề khác.
Giải pháp của Nico Burns hoạt động nếu contenteditable
div không chứa các phần tử nhiều dòng khác.
Ví dụ: nếu một div chứa các div khác và những div khác này chứa những thứ khác bên trong, có thể xảy ra một số vấn đề.
Để giải quyết chúng, tôi đã sắp xếp giải pháp sau, đó là một cải tiến của 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: /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 || {}));
Sử dụng:
var editableDiv = document.getElementById("my_contentEditableDiv");
cursorManager.setEndOfContenteditable(editableDiv);
Bằng cách này, con trỏ chắc chắn được định vị ở cuối phần tử cuối cùng, cuối cùng được lồng vào nhau.
CHỈNH SỬA # 1 : Để chung chung hơn, câu lệnh while cũng nên xem xét tất cả các thẻ khác không thể chứa văn bản. Các phần tử này được đặt tên là phần tử void , và trong câu hỏi này có một số phương pháp về cách kiểm tra xem một phần tử có trống hay không. Vì vậy, giả sử tồn tại một hàm được gọi là canContainText
trả về true
nếu đối số không phải là phần tử void, thì dòng mã sau:
contentEditableElement.lastChild.tagName.toLowerCase() != 'br'
nên được thay thế bằng:
canContainText(getLastChildElement(contentEditableElement))
CHỈNH SỬA # 2 : Đoạn mã trên được cập nhật đầy đủ, với mọi thay đổi được mô tả và thảo luận
Uncaught TypeError: Cannot read property 'nodeType' of null
và đây là do hàm getLastChildElement đang được gọi. Bạn có biết điều gì có thể gây ra vấn đề này không?
Giải pháp của Geowa4 sẽ hoạt động cho một vùng văn bản, nhưng không hoạt động cho một phần tử có thể nội dung.
Giải pháp này là để di chuyển dấu mũ đến cuối phần tử có thể nội dung. Nó sẽ hoạt động trong tất cả các trình duyệt hỗ trợ contentedable.
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
}
}
Nó có thể được sử dụng bằng mã tương tự như:
elem = document.getElementById('txt1');//This is the element that you want to move the caret to the end of
setEndOfContenteditable(elem);
selectNodeContents
phần của Nico đã cho tôi lỗi trong cả Chrome và FF (không kiểm tra các trình duyệt khác) cho đến khi tôi phát hiện ra rằng tôi dường như cần thêm .get(0)
vào phần tử mà tôi đang cung cấp chức năng. Tôi đoán điều này phải làm với tôi bằng cách sử dụng jQuery thay vì JS trần? Tôi đã học được điều này từ @jwarzech ở câu hỏi 4233265 . Cảm ơn tất cả!
.get(0)
truy xuất phần tử dom mà jQuery lưu trữ bên trong. Bạn cũng có thể nối thêm [0]
, tương đương với .get(0)
trong ngữ cảnh này.
Nếu bạn không quan tâm đến các trình duyệt cũ hơn, thì cái này đã giúp tôi.
// [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
hiện đã lỗi thời developer.mozilla.org/en-US/docs/Web/API/Document/execCommand .
Có thể đặt con trỏ đến cuối trong phạm vi:
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()
mỗi lần - chỉ gọi khi bạn cần: ví dụ sau khi Copy-Paste hoặc sau khi giới hạn độ dài thư.
Tôi đã gặp sự cố tương tự khi cố gắng làm cho một phần tử có thể chỉnh sửa được. Điều này có thể xảy ra trong Chrome và FireFox nhưng trong FireFox, dấu mũ ở đầu phần nhập hoặc dấu cách nằm sau phần cuối của phần nhập. Tôi nghĩ rất khó hiểu đối với người dùng cuối, đang cố gắng chỉnh sửa nội dung.
Tôi không tìm thấy giải pháp nào khi thử một số thứ. Chỉ có điều hiệu quả với tôi là "giải quyết vấn đề" bằng cách đưa một kiểu nhập văn bản cũ đơn giản vào BÊN TRONG của tôi. Bây giờ nó hoạt động. Có vẻ như "nội dung có thể chỉnh sửa" vẫn còn là công nghệ tiên tiến, có thể có hoặc không hoạt động như bạn muốn, tùy thuộc vào ngữ cảnh.
Di chuyển con trỏ đến cuối khoảng có thể chỉnh sửa để phản hồi sự kiện tiêu điểm:
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);
}
}
Và gọi nó trong trình xử lý sự kiện (Phản ứng tại đây):
onFocus={(e) => this.moveCursorToEnd(e.target)}}
Sự cố với contenteditable
<div>
và <span>
được giải quyết khi bạn bắt đầu nhập vào nó ban đầu. Một giải pháp cho điều này có thể là kích hoạt sự kiện tiêu điểm trên phần tử div của bạn và trên chức năng đó, xóa và điền lại những gì đã có trong phần tử div. Bằng cách này, vấn đề được giải quyết và cuối cùng bạn có thể đặt con trỏ ở cuối bằng cách sử dụng phạm vi và lựa chọn. Đã làm cho tôi.
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);
}
}
Trong mã HTML:
<div contentEditable="true" (focus)="moveCursorToEnd($event)"></div>