如何使范围偏移量在多行内容可编辑的div中使用HTML元素



我的代码在插入符号定位、内容可编辑div和其中的HTML标记方面遇到了一些问题。

我正在努力实现的目标

我想要一个内容可编辑的div,它允许通过键入某种快捷方式插入换行符和多个HTML标记-在我的情况下是左双括号"{{"。

到目前为止我所取得的成就

div允许使用单个HTML标记,并且只能在单行文本中使用。

问题

1) 当我用回车键换行时,{{不再触发标签显示。我认为你必须以某种方式制作脚本,在创建范围时考虑换行(节点?)。

2) 如果已经有一个HTML标记可见,则无法插入另一个。相反,您会在浏览器的控制台中得到以下错误。

Uncaught DOMException: Failed to execute 'setStart' on 'Range': The offset 56 is larger than the node's length (33).

我注意到范围偏移量为0(或从HTML标记的末尾开始),这可能是这里问题的罪魁祸首。

以下是我迄今为止的代码。。。

所有的东西都是在按键或鼠标点击时触发的。

var tw_template_trigger = '{{';
var tw_template_tag = '<span class="tw-template-tag" contenteditable="false"><a href="#" class="tw-template-tag-remove"><i class="tw-icon tw-icon-close"></i></a>Pick a tag</span>';
$('.tw-post-template-content').on( 'keyup mouseup', function() {
// Basically check if someone typed {{ 
// if yes, attempt to delete those two characters
// then paste tag HTML in that position
if( checkIfTagIsTriggered( this ) && deleteTagTrigger( this ) ) {
pasteTagAtCaret();
}   
});

function pasteTagAtCaret(selectPastedContent) {
// Then add the tag
var sel, range;
if (window.getSelection) {
// IE9 and non-IE
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0);
range.deleteContents();
// Range.createContextualFragment() would be useful here but is
// only relatively recently standardized and is not supported in
// some browsers (IE9, for one)
var el = document.createElement("div");
el.innerHTML = tw_template_tag;
var frag = document.createDocumentFragment(), node, lastNode;
while ( (node = el.firstChild) ) {
lastNode = frag.appendChild(node);
}
var firstNode = frag.firstChild;
range.insertNode(frag);
// Preserve the selection
if (lastNode) {
range = range.cloneRange();
range.setStartAfter(lastNode);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}
}
} else if ( (sel = document.selection) && sel.type != "Control") {
// IE < 9
var originalRange = sel.createRange();
originalRange.collapse(true);
sel.createRange().pasteHTML( tw_template_tag );
}
}
function checkIfTagIsTriggered(containerEl) {
var precedingChar = "", sel, range, precedingRange;
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount > 0) {
range = sel.getRangeAt(0).cloneRange();
range.collapse(true);
range.setStart(containerEl, 0);
precedingChar = range.toString().slice(-2);
}
} else if ( (sel = document.selection) && sel.type != "Control") {
range = sel.createRange();
precedingRange = range.duplicate();
precedingRange.moveToElementText(containerEl);
precedingRange.setEndPoint("EndToStart", range);
precedingChar = precedingRange.text.slice(-2);
}
if( tw_template_trigger == precedingChar )
return true;
return false;
}
function deleteTagTrigger(containerEl) {
var preceding = "",
sel,
range,
precedingRange;
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount > 0) {
range = sel.getRangeAt(0).cloneRange();
range.collapse(true);
range.setStart(containerEl, 0);
preceding = range.toString();
}
} else if ((sel = document.selection) && sel.type != "Control") {
range = sel.createRange();
precedingRange = range.duplicate();
precedingRange.moveToElementText(containerEl);
precedingRange.setEndPoint("EndToStart", range);
preceding = precedingRange.text;
}
// First Remove {{
var words = range.toString().trim().split(' '),
lastWord = words[words.length - 1];
if (lastWord && lastWord == tw_template_trigger ) {
/* Find word start and end */
var wordStart = range.toString().lastIndexOf(lastWord);
var wordEnd = wordStart + lastWord.length;
range.setStart(containerEl.firstChild, wordStart);
range.setEnd(containerEl.firstChild, wordEnd);
range.deleteContents();
range.insertNode(document.createTextNode(' '));
// delete That specific word and replace if with resultValue
return true;
}
return false;
}

我注意到这两行导致了第二期中的浏览器错误

range.setStart(containerEl.firstChild, wordStart);
range.setEnd(containerEl.firstChild, wordEnd);

从理论上讲,我知道问题是什么。我相信这两个问题都可以通过让创建范围的脚本使用父节点而不是子节点来解决,也可以通过换行符所在的文本节点来循环。然而,在这一点上,我不知道如何实现它。

你能告诉我正确的方向吗?

编辑

事实上,我已经上传了一个迄今为止的进展演示,以使其更加清晰。

演示

我自己解决了这个问题,并将所有函数合并为一个函数。整洁的以下是最终代码。在进一步考虑后,我删除了按回车键的功能。

希望它能帮助

var tw_template_trigger = '{{';
var tw_template_tag = '<span class="tw-template-tag" contenteditable="false">Pick a tag</span>';
$(".tw-post-template-content").keypress(function(e){ return e.which != 13; });
$('.tw-post-template-content').on( 'keyup mouseup', function() {
triggerTag( this ); 
});
function triggerTag(containerEl) {
var sel,
range,
text;
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount > 0) {
range = sel.getRangeAt(0).cloneRange(); // clone current range into another variable for manipulation#
range.collapse(true);
range.setStart(containerEl, 0);
text = range.toString();
}
}
if( text && text.slice(-2) == tw_template_trigger ) {
range.setStart( range.endContainer, range.endOffset - tw_template_trigger.length);
range.setEnd( range.endContainer, range.endOffset );
range.deleteContents();
range.insertNode(document.createTextNode(' '));
//
var el = document.createElement("div");
el.innerHTML = tw_template_tag;
var frag = document.createDocumentFragment(), node, lastNode;
while ( (node = el.firstChild) ) {
lastNode = frag.appendChild(node);
}
var firstNode = frag.firstChild;
range.insertNode(frag);
// Preserve the selection
if (lastNode) {
range = range.cloneRange();
range.setStartAfter(lastNode);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}
return true;
}
return false;
}

最新更新