Подобное тегу автозаполнение и перемещение курсора / курсора в элементах содержания
Я работаю над плагином JQuery, который позволит вам сделать @username
теги стиля, как это делает Facebook в своем поле ввода обновления статуса.
Моя проблема в том, что даже после нескольких часов исследований и экспериментов ДЕЙСТВИТЕЛЬНО трудно просто перемещать каретку. Мне удалось ввести <a>
тег с именем кого-то, но размещение каретки после того, как это кажется ракетостроением, особенно если предполагается, что он работает во всех браузерах.
И я даже не смотрел на замену набранного @username
текст с тегом, а не просто вводить его, как я делаю сейчас... LOL
Здесь, в Stack Overflow, есть куча вопросов о работе с contenteditable, и я думаю, что прочитал их все, но они не совсем правильно охватывают то, что мне нужно. Так что любая дополнительная информация, которую кто-либо может предоставить, была бы великолепна:)
3 ответа
Вы можете использовать мою библиотеку Rangy, которая с некоторым успехом пытается нормализовать диапазон браузера и реализацию выбора. Если вам удалось вставить <a>
как вы говорите, и вы получите его в переменной с именем aElement
Вы можете сделать следующее:
var range = rangy.createRange();
range.setStartAfter(aElement);
range.collapse(true);
var sel = rangy.getSelection();
sel.removeAllRanges();
sel.addRange(range);
Я заинтересовался этим, поэтому я написал отправную точку для полного решения. Следующее использует мою библиотеку Rangy с ее модулем сохранения / восстановления выбора, чтобы сохранить и восстановить выделение и нормализовать кросс-браузерные проблемы. Он окружает весь соответствующий текст (в любом случае @whwh) элементом ссылки и размещает выделение там, где оно было ранее. Это срабатывает после того, как в течение одной секунды не было никаких действий клавиатуры. Это должно быть довольно многоразовым.
function createLink(matchedTextNode) {
var el = document.createElement("a");
el.style.backgroundColor = "yellow";
el.style.padding = "2px";
el.contentEditable = false;
var matchedName = matchedTextNode.data.slice(1); // Remove the leading @
el.href = "http://www.example.com/?name=" + matchedName;
matchedTextNode.data = matchedName;
el.appendChild(matchedTextNode);
return el;
}
function shouldLinkifyContents(el) {
return el.tagName != "A";
}
function surroundInElement(el, regex, surrounderCreateFunc, shouldSurroundFunc) {
var child = el.lastChild;
while (child) {
if (child.nodeType == 1 && shouldSurroundFunc(el)) {
surroundInElement(child, regex, surrounderCreateFunc, shouldSurroundFunc);
} else if (child.nodeType == 3) {
surroundMatchingText(child, regex, surrounderCreateFunc);
}
child = child.previousSibling;
}
}
function surroundMatchingText(textNode, regex, surrounderCreateFunc) {
var parent = textNode.parentNode;
var result, surroundingNode, matchedTextNode, matchLength, matchedText;
while ( textNode && (result = regex.exec(textNode.data)) ) {
matchedTextNode = textNode.splitText(result.index);
matchedText = result[0];
matchLength = matchedText.length;
textNode = (matchedTextNode.length > matchLength) ?
matchedTextNode.splitText(matchLength) : null;
surroundingNode = surrounderCreateFunc(matchedTextNode.cloneNode(true));
parent.insertBefore(surroundingNode, matchedTextNode);
parent.removeChild(matchedTextNode);
}
}
function updateLinks() {
var el = document.getElementById("editable");
var savedSelection = rangy.saveSelection();
surroundInElement(el, /@\w+/, createLink, shouldLinkifyContents);
rangy.restoreSelection(savedSelection);
}
var keyTimer = null, keyDelay = 1000;
function keyUpLinkifyHandler() {
if (keyTimer) {
window.clearTimeout(keyTimer);
}
keyTimer = window.setTimeout(function() {
updateLinks();
keyTimer = null;
}, keyDelay);
}
HTML:
<p contenteditable="true" id="editable" onkeyup="keyUpLinkifyHandler()">
Some editable content for @someone or other
</p>
Поскольку вы говорите, что уже можете вставить тег в каретку, я начну оттуда. Первое, что нужно сделать, это присвоить тегу идентификатор при его вставке. Вы должны иметь что-то вроде этого:
<div contenteditable='true' id='status'>I went shopping with <a href='#' id='atagid'>Jane</a></div>
Вот функция, которая должна поместить курсор сразу после тега.
function setCursorAfterA()
{
var atag = document.getElementById("atagid");
var parentdiv = document.getElementById("status");
var range,selection;
if(window.getSelection) //FF,Chrome,Opera,Safari,IE9+
{
parentdiv.appendChild(document.createTextNode(""));//FF wont allow cursor to be placed directly between <a> tag and the end of the div, so a space is added at the end (this can be trimmed later)
range = document.createRange();//create range object (like an invisible selection)
range.setEndAfter(atag);//set end of range selection to just after the <a> tag
range.setStartAfter(atag);//set start of range selection to just after the <a> tag
selection = window.getSelection();//get selection object (list of current selections/ranges)
selection.removeAllRanges();//remove any current selections (FF can have more than one)
parentdiv.focus();//Focuses contenteditable div (necessary for opera)
selection.addRange(range);//add our range object to the selection list (make our range visible)
}
else if(document.selection)//IE 8 and lower
{
range = document.body.createRange();//create a "Text Range" object (like an invisible selection)
range.moveToElementText(atag);//select the contents of the a tag (i.e. "Jane")
range.collapse(false);//collapse selection to end of range (between "e" and "</a>").
while(range.parentElement() == atag)//while ranges cursor is still inside <a> tag
{
range.move("character",1);//move cursor 1 character to the right
}
range.move("character",-1);//move cursor 1 character to the left
range.select()//move the actual cursor to the position of the ranges cursor
}
/*OPTIONAL:
atag.id = ""; //remove id from a tag
*/
}
РЕДАКТИРОВАТЬ: Протестированный и исправленный скрипт. Он определенно работает в IE6, Chrome 8, Firefox 4 и Opera 11. У вас нет других браузеров для тестирования, но он не использует функции, которые были недавно изменены, поэтому он должен работать во всем, что поддерживает contenteditable.
Эта кнопка удобна для тестирования:<input type='button' onclick='setCursorAfterA()' value='Place Cursor After <a/> tag' >
Нико