可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I\'m finding tons of good, crossbrowser anwers on how to SET the cursor or caret position in a contentEditable DIV, but none on how to GET or find its position...
What I want to do is know the position of the caret within this div, on keyup.
So, when the user is typing text, I can at any point know its cursor\'s position within the div.
EDIT: I\'m looking for the INDEX within the div contents (text), not the cursor coordinates.
<div id=\"contentBox\" contentEditable=\"true\"></div>
$(\'#contentbox\').keyup(function() {
// ... ?
});
回答1:
The following code assumes:
- There is always a single text node within the editable
<div>
and no other nodes
- The editable div does not have the CSS
white-space
property set to pre
Code:
function getCaretPosition(editableDiv) {
var caretPos = 0,
sel, range;
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount) {
range = sel.getRangeAt(0);
if (range.commonAncestorContainer.parentNode == editableDiv) {
caretPos = range.endOffset;
}
}
} else if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
if (range.parentElement() == editableDiv) {
var tempEl = document.createElement(\"span\");
editableDiv.insertBefore(tempEl, editableDiv.firstChild);
var tempRange = range.duplicate();
tempRange.moveToElementText(tempEl);
tempRange.setEndPoint(\"EndToEnd\", range);
caretPos = tempRange.text.length;
}
}
return caretPos;
}
#caretposition {
font-weight: bold;
}
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js\"></script>
<div id=\"contentbox\" contenteditable=\"true\">Click me and move cursor with keys or mouse</div>
<div id=\"caretposition\">0</div>
<script>
var update = function() {
$(\'#caretposition\').html(getCaretPosition(this));
};
$(\'#contentbox\').on(\"mousedown mouseup keydown keyup\", update);
</script>
回答2:
Try this:
Caret.js
Get caret postion and offset from text field
https://github.com/ichord/Caret.js
demo:
http://ichord.github.com/Caret.js
回答3:
$(\"#editable\").on(\'keydown keyup mousedown mouseup\',function(e){
if($(window.getSelection().anchorNode).is($(this))){
$(\'#position\').html(\'0\')
}else{
$(\'#position\').html(window.getSelection().anchorOffset);
}
});
body{
padding:40px;
}
#editable{
height:50px;
width:400px;
border:1px solid #000;
}
#editable p{
margin:0;
padding:0;
}
<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js\"></script>
<div contenteditable=\"true\" id=\"editable\">move the cursor to see position</div>
<div>
position : <span id=\"position\"></span>
</div>
回答4:
//global savedrange variable to store text range in
var savedrange = null;
function getSelection()
{
var savedRange;
if(window.getSelection && window.getSelection().rangeCount > 0) //FF,Chrome,Opera,Safari,IE9+
{
savedRange = window.getSelection().getRangeAt(0).cloneRange();
}
else if(document.selection)//IE 8 and lower
{
savedRange = document.selection.createRange();
}
return savedRange;
}
$(\'#contentbox\').keyup(function() {
var currentRange = getSelection();
if(window.getSelection)
{
//do stuff with standards based object
}
else if(document.selection)
{
//do stuff with microsoft object (ie8 and lower)
}
});
Note: the range object its self can be stored in a variable, and can be re-selected at any time unless the contents of the contenteditable div change.
Reference for IE 8 and lower:
http://msdn.microsoft.com/en-us/library/ms535872(VS.85).aspx
Reference for standards (all other) browsers:
https://developer.mozilla.org/en/DOM/range (its the mozilla docs, but code works in chrome, safari, opera and ie9 too)
回答5:
This one works for me:
function getCaretCharOffsetInDiv(element) {
var caretOffset = 0;
if (typeof window.getSelection != \"undefined\") {
var range = window.getSelection().getRangeAt(0);
var preCaretRange = range.cloneRange();
preCaretRange.selectNodeContents(element);
preCaretRange.setEnd(range.endContainer, range.endOffset);
caretOffset = preCaretRange.toString().length;
}
else if (typeof document.selection != \"undefined\" && document.selection.type != \"Control\")
{
var textRange = document.selection.createRange();
var preCaretTextRange = document.body.createTextRange();
preCaretTextRange.moveToElementText(element);
preCaretTextRange.setEndPoint(\"EndToEnd\", textRange);
caretOffset = preCaretTextRange.text.length;
}
return caretOffset;
}
the calling line depends on event type, for key event use this:
getCaretCharOffsetInDiv(e.target) + ($(window.getSelection().getRangeAt(0).startContainer.parentNode).index());
for mouse event use this:
getCaretCharOffsetInDiv(e.target.parentElement) + ($(e.target).index())
on these two cases I take care for break lines by adding the target index
回答6:
function getCaretPosition() {
var x = 0;
var y = 0;
var sel = window.getSelection();
if(sel.rangeCount) {
var range = sel.getRangeAt(0).cloneRange();
if(range.getClientRects()) {
range.collapse(true);
var rect = range.getClientRects()[0];
if(rect) {
y = rect.top;
x = rect.left;
}
}
}
return {
x: x,
y: y
};
}
回答7:
A few wrinkles that I don\'t see being addressed in other answers:
- the element can contain multiple levels of child nodes (e.g. child nodes that have child nodes that have child nodes...)
- a selection can consist of different start and end positions (e.g. multiple chars are selected)
- the node containing a Caret start/end may not be either the element or its direct children
Here\'s a way to get start and end positions as offsets to the element\'s textContent value:
// node_walk: walk the element tree, stop when func(node) returns false
function node_walk(node, func) {
var result = func(node);
for(node = node.firstChild; result !== false && node; node = node.nextSibling)
result = node_walk(node, func);
return result;
};
// getCaretPosition: return [start, end] as offsets to elem.textContent that
// correspond to the selected portion of text
// (if start == end, caret is at given position and no text is selected)
function getCaretPosition(elem) {
var sel = window.getSelection();
var cum_length = [0, 0];
if(sel.anchorNode == elem)
cum_length = [sel.anchorOffset, sel.extentOffset];
else {
var nodes_to_find = [sel.anchorNode, sel.extentNode];
if(!elem.contains(sel.anchorNode) || !elem.contains(sel.extentNode))
return undefined;
else {
//var anchor_cum_length = 0;
//var extent_cum_length = 0;
var found = [0,0];
var i;
node_walk(elem, function(node) {
for(i = 0; i < 2; i++) {
if(node == nodes_to_find[i]) {
found[i] = true;
if(found[i == 0 ? 1 : 0])
return false; // all done
}
}
if(node.textContent && !node.firstChild) {
for(i = 0; i < 2; i++) {
if(!found[i])
cum_length[i] += node.textContent.length;
}
}
});
cum_length[0] += sel.anchorOffset;
cum_length[1] += sel.extentOffset;
}
}
if(cum_length[0] <= cum_length[1])
return cum_length;
return [cum_length[1], cum_length[0]];
}