我正在使用一种确定的跨浏览器解决方案,当contentEditable ='on'<div>重新获得焦点时,将光标/插入位置设置为最后一个已知位置。内容可编辑div的默认功能似乎是每次单击时都将插入号/光标移动到div中文本的开头。
我相信当他们离开div的焦点时,我必须将当前光标位置存储在一个变量中,然后当他们再次将焦点放在内部时,将其重置,但是我无法将它们放在一起或找到一个工作的对象代码示例。
如果有人有任何想法,工作代码段或示例,我将很高兴见到他们。
我实际上还没有任何代码,但这是我所拥有的:
<script type="text/javascript">
// jQuery
$(document).ready(function() {
$('#area').focus(function() { .. } // focus I would imagine I need.
}
</script>
<div id="area" contentEditable="true"></div>
PS。我已经尝试了此资源,但它似乎不适用于<div>。也许仅适用于textarea(如何将光标移动到contenteditable实体的末尾)
这与基于标准的浏览器兼容,但在IE中可能会失败。我提供它作为起点。IE不支持DOM Range。
var editable = document.getElementById('editable'),
selection, range;
// Populates selection and range variables
var captureSelection = function(e) {
// Don't capture selection outside editable region
var isOrContainsAnchor = false,
isOrContainsFocus = false,
sel = window.getSelection(),
parentAnchor = sel.anchorNode,
parentFocus = sel.focusNode;
while(parentAnchor && parentAnchor != document.documentElement) {
if(parentAnchor == editable) {
isOrContainsAnchor = true;
}
parentAnchor = parentAnchor.parentNode;
}
while(parentFocus && parentFocus != document.documentElement) {
if(parentFocus == editable) {
isOrContainsFocus = true;
}
parentFocus = parentFocus.parentNode;
}
if(!isOrContainsAnchor || !isOrContainsFocus) {
return;
}
selection = window.getSelection();
// Get range (standards)
if(selection.getRangeAt !== undefined) {
range = selection.getRangeAt(0);
// Get range (Safari 2)
} else if(
document.createRange &&
selection.anchorNode &&
selection.anchorOffset &&
selection.focusNode &&
selection.focusOffset
) {
range = document.createRange();
range.setStart(selection.anchorNode, selection.anchorOffset);
range.setEnd(selection.focusNode, selection.focusOffset);
} else {
// Failure here, not handled by the rest of the script.
// Probably IE or some older browser
}
};
// Recalculate selection while typing
editable.onkeyup = captureSelection;
// Recalculate selection after clicking/drag-selecting
editable.onmousedown = function(e) {
editable.className = editable.className + ' selecting';
};
document.onmouseup = function(e) {
if(editable.className.match(/\sselecting(\s|$)/)) {
editable.className = editable.className.replace(/ selecting(\s|$)/, '');
captureSelection();
}
};
editable.onblur = function(e) {
var cursorStart = document.createElement('span'),
collapsed = !!range.collapsed;
cursorStart.id = 'cursorStart';
cursorStart.appendChild(document.createTextNode('—'));
// Insert beginning cursor marker
range.insertNode(cursorStart);
// Insert end cursor marker if any text is selected
if(!collapsed) {
var cursorEnd = document.createElement('span');
cursorEnd.id = 'cursorEnd';
range.collapse();
range.insertNode(cursorEnd);
}
};
// Add callbacks to afterFocus to be called after cursor is replaced
// if you like, this would be useful for styling buttons and so on
var afterFocus = [];
editable.onfocus = function(e) {
// Slight delay will avoid the initial selection
// (at start or of contents depending on browser) being mistaken
setTimeout(function() {
var cursorStart = document.getElementById('cursorStart'),
cursorEnd = document.getElementById('cursorEnd');
// Don't do anything if user is creating a new selection
if(editable.className.match(/\sselecting(\s|$)/)) {
if(cursorStart) {
cursorStart.parentNode.removeChild(cursorStart);
}
if(cursorEnd) {
cursorEnd.parentNode.removeChild(cursorEnd);
}
} else if(cursorStart) {
captureSelection();
var range = document.createRange();
if(cursorEnd) {
range.setStartAfter(cursorStart);
range.setEndBefore(cursorEnd);
// Delete cursor markers
cursorStart.parentNode.removeChild(cursorStart);
cursorEnd.parentNode.removeChild(cursorEnd);
// Select range
selection.removeAllRanges();
selection.addRange(range);
} else {
range.selectNode(cursorStart);
// Select range
selection.removeAllRanges();
selection.addRange(range);
// Delete cursor marker
document.execCommand('delete', false, null);
}
}
// Call callbacks here
for(var i = 0; i < afterFocus.length; i++) {
afterFocus[i]();
}
afterFocus = [];
// Register selection again
captureSelection();
}, 10);
};
该解决方案可在所有主要浏览器中使用:
saveSelection()
附加到div的onmouseup
和onkeyup
事件,并将选择保存到变量savedRange
。
restoreSelection()
附加到onfocus
div事件并重新选择保存在中的选择savedRange
。
除非您希望在用户单击div时也恢复选择(除非您通常希望光标移到您单击的位置但包含代码以确保完整性),否则这完美地工作了,除非您希望在用户单击div时恢复选择。
为了实现这一点,通过事件可以取消onclick
和onmousedown
事件,该函数cancelEvent()
是一个跨浏览器函数,用于取消事件。该cancelEvent()
函数还会运行该restoreSelection()
函数,因为取消click事件后,div不会获得焦点,因此除非运行该函数,否则根本不会选择任何内容。
该变量isInFocus
存储它是否处于焦点状态,并更改为“ false”onblur
和“ true” onfocus
。这样,仅当div不在焦点上时,才能取消单击事件(否则,您将根本无法更改选择)。
如果您希望在单击时将div聚焦时更改选择,而不是恢复选择onclick
(并且仅当使用document.getElementById("area").focus();
或类似方式以编程方式将焦点聚焦到元素上时,只需删除onclick
andonmousedown
事件。eventonblur
和onDivBlur()
andcancelEvent()
函数在这种情况下也可以安全移除。
如果您想快速测试它,那么如果将其直接放到html页面的主体中,该代码应该可以工作:
<div id="area" style="width:300px;height:300px;" onblur="onDivBlur();" onmousedown="return cancelEvent(event);" onclick="return cancelEvent(event);" contentEditable="true" onmouseup="saveSelection();" onkeyup="saveSelection();" onfocus="restoreSelection();"></div>
<script type="text/javascript">
var savedRange,isInFocus;
function saveSelection()
{
if(window.getSelection)//non IE Browsers
{
savedRange = window.getSelection().getRangeAt(0);
}
else if(document.selection)//IE
{
savedRange = document.selection.createRange();
}
}
function restoreSelection()
{
isInFocus = true;
document.getElementById("area").focus();
if (savedRange != null) {
if (window.getSelection)//non IE and there is already a selection
{
var s = window.getSelection();
if (s.rangeCount > 0)
s.removeAllRanges();
s.addRange(savedRange);
}
else if (document.createRange)//non IE and no selection
{
window.getSelection().addRange(savedRange);
}
else if (document.selection)//IE
{
savedRange.select();
}
}
}
//this part onwards is only needed if you want to restore selection onclick
var isInFocus = false;
function onDivBlur()
{
isInFocus = false;
}
function cancelEvent(e)
{
if (isInFocus == false && savedRange != null) {
if (e && e.preventDefault) {
//alert("FF");
e.stopPropagation(); // DOM style (return false doesn't always work in FF)
e.preventDefault();
}
else {
window.event.cancelBubble = true;//IE stopPropagation
}
restoreSelection();
return false; // false = IE style
}
}
</script>
更新资料
我编写了一个跨浏览器范围和选择库,称为Rangy,其中包含了我在下面发布的代码的改进版本。您可以将选择保存和还原模块用于该特定问题,但是如果您不对项目中的选择做其他任何事情并且不需要大量的内容,尽管我很想使用@Nico Burns的答案,图书馆。
先前的答案
您可以使用IERange(http://code.google.com/p/ierange/)将IE的TextRange转换为DOM Range之类的东西,并将其与诸如免于眼睑的起点之类的东西结合使用。就我个人而言,我只会使用IERange的算法进行Range <-> TextRange转换,而不是使用全部算法。IE的选择对象没有focusNode和anchorNode属性,但是您应该能够只使用从选择中获得的Range / TextRange。
我可能会组合在一起执行此操作,如果执行此操作,则会在此发布。
编辑:
我创建了一个演示脚本的示例。到目前为止,它可以在我尝试过的所有内容中起作用,除了Opera 9中的一个错误外,我还没有时间去研究它。它可以使用的浏览器是IE 5.5、6和7,Chrome 2,Firefox 2、3和3.5以及Safari 4,它们都在Windows上。
http://www.timdown.co.uk/code/selections/
请注意,可以在浏览器中向后进行选择,以便焦点节点位于选择的开始,并且单击向右或向左光标键会将插入号移动到相对于选择的开始的位置。我认为在还原选择时不可能复制此内容,因此焦点节点始终位于选择的末尾。
我将在某个时候将其完整地写出来。
我有一个相关的情况,我特别需要将光标位置设置为一个内容可编辑div的END。我不想使用像Rangy这样的功能完善的库,而且许多解决方案都过于繁重。
最后,我想到了这个简单的jQuery函数,将克拉位置设置为contenteditable div的末尾:
$.fn.focusEnd = function() {
$(this).focus();
var tmp = $('<span />').appendTo($(this)),
node = tmp.get(0),
range = null,
sel = null;
if (document.selection) {
range = document.body.createTextRange();
range.moveToElementText(node);
range.select();
} else if (window.getSelection) {
range = document.createRange();
range.selectNode(node);
sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
tmp.remove();
return this;
}
理论很简单:在可编辑内容的末尾附加一个跨度,选择它,然后删除该跨度-在div的末尾留下一个光标。您可以调整此解决方案以在需要的任何位置插入跨度,从而将光标置于特定位置。
用法很简单:
$('#editable').focusEnd();
而已!
我接受了Nico Burns的回答,并使用jQuery实现了它:
- 通用:对于每个
div contentEditable="true"
- 更短
您需要jQuery 1.6或更高版本:
savedRanges = new Object();
$('div[contenteditable="true"]').focus(function(){
var s = window.getSelection();
var t = $('div[contenteditable="true"]').index(this);
if (typeof(savedRanges[t]) === "undefined"){
savedRanges[t]= new Range();
} else if(s.rangeCount > 0) {
s.removeAllRanges();
s.addRange(savedRanges[t]);
}
}).bind("mouseup keyup",function(){
var t = $('div[contenteditable="true"]').index(this);
savedRanges[t] = window.getSelection().getRangeAt(0);
}).on("mousedown click",function(e){
if(!$(this).is(":focus")){
e.stopPropagation();
e.preventDefault();
$(this).focus();
}
});
玩转之后,我已经修改了上面的“双眼失明”答案,并使其成为了jQuery插件,因此您可以执行以下操作之一:
var html = "The quick brown fox";
$div.html(html);
// Select at the text "quick":
$div.setContentEditableSelection(4, 5);
// Select at the beginning of the contenteditable div:
$div.setContentEditableSelection(0);
// Select at the end of the contenteditable div:
$div.setContentEditableSelection(html.length);
请原谅冗长的代码发布,但它可能对某人有所帮助:
$.fn.setContentEditableSelection = function(position, length) {
if (typeof(length) == "undefined") {
length = 0;
}
return this.each(function() {
var $this = $(this);
var editable = this;
var selection;
var range;
var html = $this.html();
html = html.substring(0, position) +
'<a id="cursorStart"></a>' +
html.substring(position, position + length) +
'<a id="cursorEnd"></a>' +
html.substring(position + length, html.length);
console.log(html);
$this.html(html);
// Populates selection and range variables
var captureSelection = function(e) {
// Don't capture selection outside editable region
var isOrContainsAnchor = false,
isOrContainsFocus = false,
sel = window.getSelection(),
parentAnchor = sel.anchorNode,
parentFocus = sel.focusNode;
while (parentAnchor && parentAnchor != document.documentElement) {
if (parentAnchor == editable) {
isOrContainsAnchor = true;
}
parentAnchor = parentAnchor.parentNode;
}
while (parentFocus && parentFocus != document.documentElement) {
if (parentFocus == editable) {
isOrContainsFocus = true;
}
parentFocus = parentFocus.parentNode;
}
if (!isOrContainsAnchor || !isOrContainsFocus) {
return;
}
selection = window.getSelection();
// Get range (standards)
if (selection.getRangeAt !== undefined) {
range = selection.getRangeAt(0);
// Get range (Safari 2)
} else if (
document.createRange &&
selection.anchorNode &&
selection.anchorOffset &&
selection.focusNode &&
selection.focusOffset
) {
range = document.createRange();
range.setStart(selection.anchorNode, selection.anchorOffset);
range.setEnd(selection.focusNode, selection.focusOffset);
} else {
// Failure here, not handled by the rest of the script.
// Probably IE or some older browser
}
};
// Slight delay will avoid the initial selection
// (at start or of contents depending on browser) being mistaken
setTimeout(function() {
var cursorStart = document.getElementById('cursorStart');
var cursorEnd = document.getElementById('cursorEnd');
// Don't do anything if user is creating a new selection
if (editable.className.match(/\sselecting(\s|$)/)) {
if (cursorStart) {
cursorStart.parentNode.removeChild(cursorStart);
}
if (cursorEnd) {
cursorEnd.parentNode.removeChild(cursorEnd);
}
} else if (cursorStart) {
captureSelection();
range = document.createRange();
if (cursorEnd) {
range.setStartAfter(cursorStart);
range.setEndBefore(cursorEnd);
// Delete cursor markers
cursorStart.parentNode.removeChild(cursorStart);
cursorEnd.parentNode.removeChild(cursorEnd);
// Select range
selection.removeAllRanges();
selection.addRange(range);
} else {
range.selectNode(cursorStart);
// Select range
selection.removeAllRanges();
selection.addRange(range);
// Delete cursor marker
document.execCommand('delete', false, null);
}
}
// Register selection again
captureSelection();
}, 10);
});
};
您可以利用现代浏览器支持的selectNodeContents。
var el = document.getElementById('idOfYoursContentEditable');
var selection = window.getSelection();
var range = document.createRange();
selection.removeAllRanges();
range.selectNodeContents(el);
range.collapse(false);
selection.addRange(range);
el.focus();
在Firefox中,您可能在子节点(o_div.childNodes[0]
) 中包含div的文本
var range = document.createRange();
range.setStart(o_div.childNodes[0],last_caret_pos);
range.setEnd(o_div.childNodes[0],last_caret_pos);
range.collapse(false);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
文章标签:contenteditable , cursor-position , html , javascript , jquery
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!
评论已关闭!