我目前正在开发一个项目,以启动并运行富文本编辑器,允许在按下令牌键后在光标所在位置出现自动完成建议,例如“@”。我已经通过jQuery在没有问题的情况下在textarea内部工作了,但它在RTE iFrame中不会很好用。
自动完成示例我通过AJAX扫描服务器文件以检查为自动完成'标记'键入的文本。
Mozilla的编辑:www-archive.mozilla.org/editor/midasdemo /
自动填写来自:www.amirharel.com/2011/03/07/implementing-autocomplete-jquery-plugin-for-textarea /
<html>
<head>
<style type="text/css">
.imagebutton {height: 22; width: 23; border: solid 2px #C0C0C0; background-color: #C0C0C0}
.image {position: relative; left: 1; top: 1; height:20; width:21; border:none;}
.toolbar {height: 30; background-color: #C0C0C0;}
</style>
<script>
var command = "";
function InitToolbarButtons() {
var kids = document.getElementsByTagName('DIV');
for (var i=0; i < kids.length; i++) {
if (kids[i].className == "imagebutton") {
kids[i].onmouseover = tbmouseover;
kids[i].onmouseout = tbmouseout;
kids[i].onmousedown = tbmousedown;
kids[i].onmouseup = tbmouseup;
kids[i].onclick = tbclick;
}
}
}
function tbmousedown(e)
{
var evt = e ? e : window.event;
this.firstChild.style.left = 2;
this.firstChild.style.top = 2;
this.style.border="inset 2px";
if (evt.returnValue) {
evt.returnValue = false;
} else if (evt.preventDefault) {
evt.preventDefault( );
} else {
return false;
}
}
function tbmouseup()
{
this.firstChild.style.left = 1;
this.firstChild.style.top = 1;
this.style.border="outset 2px";
}
function tbmouseout()
{
this.style.border="solid 2px #C0C0C0";
}
function tbmouseover()
{
this.style.border="outset 2px";
}
function insertNodeAtSelection(win, insertNode)
{
// get current selection
var sel = win.getSelection();
// get the first range of the selection
// (there's almost always only one range)
var range = sel.getRangeAt(0);
// deselect everything
sel.removeAllRanges();
// remove content of current selection from document
range.deleteContents();
// get location of current selection
var container = range.startContainer;
var pos = range.startOffset;
// make a new range for the new selection
range=document.createRange();
if (container.nodeType==3 && insertNode.nodeType==3) {
// if we insert text in a textnode, do optimized insertion
container.insertData(pos, insertNode.nodeValue);
// put cursor after inserted text
range.setEnd(container, pos+insertNode.length);
range.setStart(container, pos+insertNode.length);
} else {
var afterNode;
if (container.nodeType==3) {
// when inserting into a textnode
// we create 2 new textnodes
// and put the insertNode in between
var textNode = container;
container = textNode.parentNode;
var text = textNode.nodeValue;
// text before the split
var textBefore = text.substr(0,pos);
// text after the split
var textAfter = text.substr(pos);
var beforeNode = document.createTextNode(textBefore);
afterNode = document.createTextNode(textAfter);
// insert the 3 new nodes before the old one
container.insertBefore(afterNode, textNode);
container.insertBefore(insertNode, afterNode);
container.insertBefore(beforeNode, insertNode);
// remove the old node
container.removeChild(textNode);
} else {
// else simply insert the node
afterNode = container.childNodes[pos];
container.insertBefore(insertNode, afterNode);
}
range.setEnd(afterNode, 0);
range.setStart(afterNode, 0);
}
sel.addRange(range);
};
function getOffsetTop(elm) {
var mOffsetTop = elm.offsetTop;
var mOffsetParent = elm.offsetParent;
while(mOffsetParent){
mOffsetTop += mOffsetParent.offsetTop;
mOffsetParent = mOffsetParent.offsetParent;
}
return mOffsetTop;
}
function getOffsetLeft(elm) {
var mOffsetLeft = elm.offsetLeft;
var mOffsetParent = elm.offsetParent;
while(mOffsetParent){
mOffsetLeft += mOffsetParent.offsetLeft;
mOffsetParent = mOffsetParent.offsetParent;
}
return mOffsetLeft;
}
function tbclick()
{
if ((this.id == "forecolor") || (this.id == "hilitecolor")) {
parent.command = this.id;
buttonElement = document.getElementById(this.id);
document.getElementById("colorpalette").style.left = getOffsetLeft(buttonElement);
document.getElementById("colorpalette").style.top = getOffsetTop(buttonElement) + buttonElement.offsetHeight;
document.getElementById("colorpalette").style.visibility="visible";
} else if (this.id == "createlink") {
var szURL = prompt("Enter a URL:", "http://");
if ((szURL != null) && (szURL != "")) {
document.getElementById('edit').contentWindow.document.execCommand("CreateLink",false,szURL);
}
} else if (this.id == "createimage") {
imagePath = prompt('Enter Image URL:', 'http://');
if ((imagePath != null) && (imagePath != "")) {
document.getElementById('edit').contentWindow.document.execCommand('InsertImage', false, imagePath);
}
} else if (this.id == "createtable") {
e = document.getElementById("edit");
rowstext = prompt("enter rows");
colstext = prompt("enter cols");
rows = parseInt(rowstext);
cols = parseInt(colstext);
if ((rows > 0) && (cols > 0)) {
table = e.contentWindow.document.createElement("table");
table.setAttribute("border", "1");
table.setAttribute("cellpadding", "2");
table.setAttribute("cellspacing", "2");
tbody = e.contentWindow.document.createElement("tbody");
for (var i=0; i < rows; i++) {
tr =e.contentWindow.document.createElement("tr");
for (var j=0; j < cols; j++) {
td =e.contentWindow.document.createElement("td");
br =e.contentWindow.document.createElement("br");
td.appendChild(br);
tr.appendChild(td);
}
tbody.appendChild(tr);
}
table.appendChild(tbody);
insertNodeAtSelection(e.contentWindow, table);
}
} else {
document.getElementById('edit').contentWindow.document.execCommand(this.id, false, null);
}
}
function Select(selectname)
{
var cursel = document.getElementById(selectname).selectedIndex;
/* First one is always a label */
if (cursel != 0) {
var selected = document.getElementById(selectname).options[cursel].value;
document.getElementById('edit').contentWindow.document.execCommand(selectname, false, selected);
document.getElementById(selectname).selectedIndex = 0;
}
document.getElementById("edit").contentWindow.focus();
}
function dismisscolorpalette()
{
document.getElementById("colorpalette").style.visibility="hidden";
}
function Start() {
document.getElementById('edit').contentWindow.document.designMode = "on";
try {
document.getElementById('edit').contentWindow.document.execCommand("undo", false, null);
} catch (e) {
alert("This demo is not supported on your browser.");
}
InitToolbarButtons();
if (document.addEventListener) {
document.addEventListener("mousedown", dismisscolorpalette, true);
document.getElementById("edit").contentWindow.document.addEventListener("mousedown", dismisscolorpalette, true);
document.addEventListener("keypress", dismisscolorpalette, true);
document.getElementById("edit").contentWindow.document.addEventListener("keypress", dismisscolorpalette, true);
} else if (document.attachEvent) {
document.attachEvent("mousedown", dismisscolorpalette, true);
document.getElementById("edit").contentWindow.document.attachEvent("mousedown", dismisscolorpalette, true);
document.attachEvent("keypress", dismisscolorpalette, true);
document.getElementById("edit").contentWindow.document.attachEvent("keypress", dismisscolorpalette, true);
}
}
</script>
<style type="text/css" style="display:none">ul.auto-list{display:none;position:absolute;top:0px;left:0px;border:1px solid green;background-color:#A3DF99;padding:0;margin:0;list-style:none;}ul.auto-list>li:hover,ul.auto-list>li[data-selected=true]{background-color:#236574;}ul.auto-list>li{border:1px solid gray;cursor:default;padding:2px;}mark{font-weight:bold;}</style>
<style type="text/css">#ta{width:300px;height :100px;font-size:11px;font-family:"Helvetica Neue",Arial,sans-serif;white-space:pre;}</style>
<script src="js/jquery-1.5.js" type="text/javascript"></script>
<script src="js/autocomplete.js" type="text/javascript"></script>
<script type="text/javascript">
var tags = [];
function initEditorTextarea(){
$.ajax("autotags.txt",{
success: function(data, textStatus, jqXHR){
tags = data.replace(/\r/g, "" ).split("\n");
$("#editor textarea").autocomplete({
wordCount:1,
on: {
query: function(text,cb){
var words = [];
for( var i=0; i<tags.length; i++ ){
if( tags[i].toLowerCase().indexOf(text.toLowerCase()) == 0 ) words.push(tags[i]);
if( words.length > 5 ) break;
}
cb(words);
}
}
});
}
});
}
$(document).ready(function(){
Start();
initEditorTextarea();
});
</script>
</head>
<body>
<div id="editor">
<textarea style="width: 750px; height: 150px; margin-bottom: 10px;"></textarea>
</div>
<table bgcolor="#C0C0C0" id="toolbar">
<tr>
<td>
<select id="formatblock" onchange="Select(this.id);">
<option value="<p>">Normal</option>
<option value="<p>">Paragraph</option>
<option value="<h1>">Heading 1 <H1></option>
<option value="<h2>">Heading 2 <H2></option>
<option value="<h3>">Heading 3 <H3></option>
<option value="<h4>">Heading 4 <H4></option>
<option value="<h5>">Heading 5 <H5></option>
<option value="<h6>">Heading 6 <H6></option>
<option value="<address>">Address <ADDR></option>
<option value="<pre>">Formatted <PRE></option>
</select>
</td>
<td>
<select id="fontname" onchange="Select(this.id);">
<option value="Font">Font</option>
<option value="Arial">Arial</option>
<option value="Courier">Courier</option>
<option value="Times New Roman">Times New Roman</option>
</select>
</td>
<td>
<select unselectable="on" id="fontsize" onchange="Select(this.id);">
<option value="Size">Size</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
</select>
</td>
<td>
<div class="imagebutton" id="bold"><img class="image" src="images/bold.gif" alt="Bold" title="Bold"></div>
</td>
<td>
<div class="imagebutton" id="italic"><img class="image" src="images/italic.gif" alt="Italic" title="Italic"></div>
</td>
<td>
<div class="imagebutton" id="underline"><img class="image" src="images/underline.gif" alt="Underline" title="Underline"></div>
</td>
<td>
</td>
<td>
<div style="left: 10;" class="imagebutton" id="forecolor"><img class="image" src="images/forecolor.gif" alt="Text Color" title="Text Color"></div>
</td>
<td>
<div style="left: 40;" class="imagebutton" id="hilitecolor"><img class="image" src="images/backcolor.gif" alt="Background Color" title="Background Color"></div>
</td>
<td>
<div style="left: 10;" class="imagebutton" id="justifyleft"><img class="image" src="images/justifyleft.gif" alt="Align Left" title="Align Left"></div>
</td>
<td>
<div style="left: 40;" class="imagebutton" id="justifycenter"><img class="image" src="images/justifycenter.gif" alt="Center" title="Center"></div>
</td>
<td>
<div style="left: 70;" class="imagebutton" id="justifyright"><img class="image" src="images/justifyright.gif" alt="Align Right" title="Align Right"></div>
</td>
<td>
<div style="left: 10;" class="imagebutton" id="insertorderedlist"><img class="image" src="images/orderedlist.gif" alt="Ordered List" title="Ordered List"></div>
</td>
<td>
<div style="left: 40;" class="imagebutton" id="insertunorderedlist"><img class="image" src="images/unorderedlist.gif" alt="Unordered List" title="Unordered List"></div>
</td>
<td>
<div style="left: 10;" class="imagebutton" id="outdent"><img class="image" src="images/outdent.gif" alt="Outdent" title="Outdent"></div>
</td>
<td>
<div style="left: 40;" class="imagebutton" id="indent"><img class="image" src="images/indent.gif" alt="Indent" title="Indent"></div>
<td>
<div style="left: 10;" class="imagebutton" id="createlink"><img class="image" src="images/link.gif" alt="Insert Link" title="Insert Link"></div>
</td>
<td>
<div style="left: 10;" class="imagebutton" id="createimage"><img class="image" src="images/image.gif" alt="Insert Image" title="Insert Image"></div>
</td>
<td>
<div style="left: 10;" class="imagebutton" id="createtable"><img class="image" src="images/table.gif" alt="Insert Table" title="Insert Table"></div>
</td>
</tr>
</table>
<br>
<iframe id="edit" width="100%" height="400px"></iframe>
<iframe width="250" height="170" id="colorpalette" src="images/colors.html" style="visibility:hidden; position: absolute;"></iframe>
<script>
function viewsource(source)
{
var html;
if (source) {
html = document.createTextNode(document.getElementById('edit').contentWindow.document.body.innerHTML);
document.getElementById('edit').contentWindow.document.body.innerHTML = "";
html = document.getElementById('edit').contentWindow.document.importNode(html,false);
document.getElementById('edit').contentWindow.document.body.appendChild(html);
document.getElementById("toolbar").style.visibility="hidden";
} else {
html = document.getElementById('edit').contentWindow.document.body.ownerDocument.createRange();
html.selectNodeContents(document.getElementById('edit').contentWindow.document.body);
document.getElementById('edit').contentWindow.document.body.innerHTML = html.toString();
document.getElementById("toolbar").style.visibility="visible";
}
}
function usecss(source)
{
document.getElementById('edit').contentWindow.document.execCommand("useCSS", false, !(source));
}
function readonly(source)
{
document.getElementById('edit').contentWindow.document.execCommand("readonly", false, !(source));
}
</script>
<input type="checkbox" onclick="viewsource(this.checked)">
View HTML Source</input>
</body>
</html>
我花了很多时间研究基于浏览器的解决方案和基于JS的选项,但它们都没有工作。我能找到的最好的例子是Confluence RTE环境:https://confluence.atlassian.com/display/CONF33/Using+Autocomplete+in+the+Rich+Text+Editor
我不需要任何超级想象力,只需能够运行所见即所得的基本富文本编辑器,并能够在特定键输入上自动完成。我该如何处理这个问题呢?
编辑:因为我是新来的,所以不得不更改代码上方的链接,这样它们就不会自动链接。