Установить положение курсора на contentEditable
Я после окончательного кросс-браузерного решения для установки позиции курсора / курсора в последнюю известную позицию, когда contentEditable='on'
Я считаю, что мне придется хранить в переменной текущую позицию курсора, когда они покидают фокус 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. Я пробовал этот ресурс, но, похоже, он не работает для
8 ответов:
это совместимо со стандартными браузерами, но, вероятно, не удастся в IE. Я предоставляю его в качестве отправной точки. IE не поддерживает диапазон DOM.
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()
присоединен кonmouseup
иonkeyup
события div и сохраняет выбор в переменнойsavedRange
.
restoreSelection()
присоединен кonfocus
событие div и повторно выбирает выбор, сохраненный вsavedRange
.это работает отлично, если вы не хотите, чтобы выбор был восстановлен, когда пользователь нажимает div aswell (что немного неинтуитивно, как обычно ожидайте, что курсор будет идти туда, где вы нажимаете, но код включен для полноты)
для этого
onclick
иonmousedown
мероприятия отменены с помощью функцииcancelEvent()
который является функцией кросс-браузера, чтобы отменить событие. Элемент
обновление
Я написал кросс-браузерный диапазон и библиотеку выбора под названием Rangy это включает в себя улучшенную версию кода, который я опубликовал ниже. Вы можете использовать выбор сохранить и восстановить модуль для этого конкретного вопроса, хотя я был бы соблазн использовать что-то вроде @ ответ Нико Бернса если вы больше ничего не делаете с выборками в своем проекте и не нуждаетесь в основной части библиотека.
предыдущий ответ
вы можете использовать IERange (http://code.google.com/p/ierange/), чтобы преобразовать текстовый диапазон IE в нечто вроде диапазона DOM и использовать его в сочетании с чем-то вроде начальной точки eyelidlessness. Лично я бы использовал только алгоритмы из IERange, которые выполняют преобразования Range TextRange, а не используют все это. И выбора объекта, т. е. не имеют focusNode и anchorNode свойства, но вы должны быть в состоянии просто использовать диапазон/TextRange, полученный из выбора вместо этого.Я мог бы собрать что-то вместе, чтобы сделать это, опубликую здесь, если и когда я это сделаю.
EDIT:
Я создал демо-версию скрипта, который делает это. Он работает во всем, что я пробовал до сих пор, за исключением ошибки в Opera 9, которую я еще не успел изучить. Браузеры, в которых он работает, - это IE 5.5, 6 и 7, Chrome 2, Firefox 2, 3 и 3.5, а также Safari 4, Все на Windows.
http://www.timdown.co.uk/code/selections/
обратите внимание, что выбор может быть сделан в обратном направлении в браузерах, так что узел фокусировки находится в начале выбора и нажатие правой или левой клавиши курсора переместит курсор в положение относительно начала выбора. Я не думаю, что это можно повторить при восстановлении выделения, поэтому узел фокуса всегда находится в конце выделения.
I напишу это полностью в какой-то момент в ближайшее время.
у меня была связанная ситуация, когда мне специально нужно было установить положение курсора в конец contenteditable div. Я не хотел использовать полноценную библиотеку, такую как 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; }
теория проста: добавьте пролет в конец редактируемого, выберите его, а затем удалите пролет - оставив нас с курсором в конце разд. Вы можете адаптировать это решение, чтобы вставить пролет везде, где вы хотите, таким образом, поставив курсор в определенном месте.
использование просто:
$('#editable').focusEnd();
вот именно!
я взял ответ Нико Бернса и сделал это с помощью jQuery:
- Generic: для каждого
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(); } });
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(); } });
div[contenteditable] { padding: 1em; font-family: Arial; outline: 1px solid rgba(0,0,0,0.5); }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div contentEditable="true"></div> <div contentEditable="true"></div> <div contentEditable="true"></div>
после игры я изменил ответ eyelidlessness выше и сделал его плагином 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 у вас может быть текст div в дочернем узле (
o_div.childNodes[0]
)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);