在线的HTML内容编辑器为用户提供文本的样式控制,例如文字的颜色、字体大小等。虽然现在网上多数的编辑器(如:FCKEditor)功能相当强大的,但是在使用中需要很多的配置,因此代码往往比较“臃肿”。本文的目的就是介绍如何开发一个HTML编辑器,以方便扩展自己想要的特殊功能。
实现HTML编辑器的第1步就是在网页中放置一个可编辑的iframe用来输入文本,使iframe可编辑方法相当简单,只需要将iframe的designMode设置为on即可,具体步骤如下:
- var editor = document.getElementById("IFRAME的ID");
- var editorDoc = editor.contentWindow.document;
- var editorWindow = editor.contentWindow;
- editorDoc.designMode = "on";
- editorDoc.open();
- editorDoc.write("<html><head></head><body style="margin:0px; padding: 0px;" mce_style="margin:0px; padding: 0px;"></body></html>");
- editorDoc.close();
设置选中文本样式的方法最简单的方式就是使用document.execCommand,但是execCommand功能比较局限,有时不能满足需求,例如:execCommand设置字体大小只能是1-7,不能使用像素大小,而且如果你在点击工具栏按钮到调用execCommand的过程中点击了其他的DIV,iframe的选中内容会消失,这时调用execCommand是无效的。因此本文介绍另一种方法,基本思路如下:
(1) 获取选中的HTML;
(2) 修改HTML的样式;
(3) 用修改后的HTML替换选中的HTML。
在不同的浏览器中获取选中的HTML的方法是不同的,在IE中可以使用
- document.selection.createRange()
在Firefox,Chrome中则使用
- window.getSelection().getRangeAt(0);
在不同的浏览器中替换选中的HTML的方法是不同的,在IE中可以使用range.pasteHTML;在Firefox,Chrome中则使用range.deleteContents 和 range.insertNode 来实现
为了方便实现2.1和2.2提到的两个操作,将封装一个操作选中HTML的类SelectionRange,该类有两个方法,GetSelectedHtml和Replace,分别用于获取HTML和替换HTML。同时还实现了一个函数GetSelectionRange用于获取当前选中文本对应的SelectionRange对象,相关代码如下:
- var browser = {};
- var ua = navigator.userAgent.toLowerCase();
- browser.msie = (/msie ([/d.]+)/).test(ua);
- browser.firefox = (/firefox//([/d.]+)/).test(ua);
- browser.chrome = (/chrome//([/d.]+)/).test(ua);
- function GetInnerHTML(nodes)
- {
- var builder = [];
- for (var i = 0; i < nodes.length; i++)
- {
- if (nodes[i].nodeValue != undefined)
- {
- builder.push(nodes[i].innerHTML);
- }
- else
- {
- if (nodes[i].textContent) builder.push(nodes[i].textContent.replace(//</ig, function() { return "<"; }));
- else if (nodes[i].nodeValue) builder.push(nodes[i].nodeValue.replace(//</ig, function() { return "<"; }));
- }
- }
- return builder.join("");
- }
- function SelectionRange(doc, range)
- {
- this.GetSelectedHtml = function()
- {
- if (range == null) return "";
- if (browser.msie)
- {
- if (range.htmlText != undefined) return range.htmlText;
- else return "";
- }
- else if (browser.firefox || browser.chrome)
- {
- return GetInnerHTML(range.cloneContents().childNodes);
- }
- else
- {
- return "";
- }
- }
- this.Replace = function(html)
- {
- if (range != null)
- {
- if (browser.msie)
- {
- if (range.pasteHTML != undefined)
- {
- range.select();
- range.pasteHTML(html);
- return true;
- }
- }
- else if (browser.firefox || browser.chrome)
- {
- if (range.deleteContents != undefined && range.insertNode != undefined)
- {
- var temp = doc.createElement("DIV");
- temp.innerHTML = html;
- var elems = [];
- for (var i = 0; i < temp.childNodes.length; i++)
- {
- elems.push(temp.childNodes[i]);
- }
- range.deleteContents();
- for (var i in elems)
- {
- temp.removeChild(elems[i]);
- range.insertNode(elems[i]);
- }
- return true;
- }
- }
- }
- return false;
- }
- }
- function GetSelectionRange(win)
- {
- var range = null;
- if (browser.msie)
- {
- range = win.document.selection.createRange();
- if (range.parentElement().document != win.document)
- {
- range = null;
- }
- }
- else if (browser.firefox || browser.chrome)
- {
- var sel = win.getSelection();
- if (sel.rangeCount > 0) range = sel.getRangeAt(0); else range = null;
- }
- return new SelectionRange(win.document, range);
- }
修改选中的HTML的样式方法并不复杂,只需要将HTML转成DOM对象,然后递归的设置每一个节点对应的样式的值即可,具体代码如下:
- function SetNodeStyle(doc, node, name, value)
- {
- if (node.innerHTML == undefined)
- {
- return node;
- }
- else
- {
- node.style[name] = value;
- for (var i = 0; i < node.childNodes.length; i++)
- {
- var cn = node.childNodes[i];
- if (node.innerHTML != undefined)
- {
- SetNodeStyle(doc, cn, name, value);
- }
- }
- return node;
- }
- }
- function SetStyle(doc, html, name, value)
- {
- var dom = doc.createElement("DIV");
- dom.innerHTML = html;
- for (var i = 0; i < dom.childNodes.length; i++)
- {
- var node = dom.childNodes[i];
- if (node.innerHTML == undefined)
- {
- var span = doc.createElement("SPAN");
- span.style[name] = value;
- if (node.nodeValue != undefined) span.innerHTML = node.nodeValue.replace(//</ig, function() { return "<"; });
- else if (node.textContent != undefined) span.innetHTML = node.textContent.replace(//</ig, function() { return "<"; });
- dom.replaceChild(span, node);
- }
- else
- {
- SetNodeStyle(doc, node, name, value);
- }
- }
- return dom.innerHTML;
- }
使用以上的代码,就可以相当方便的实现一个HTML编辑器,例如,以下代码实现将选中文本的字体大小设置为32px:
- var range = GetSelectionRange(editorWindow);
- var html = SetStyle(editorDoc, range.GetSelectedHtml(), "fontSize", "32px");
- range.Replace(html);
同理,你可以实现设置行距,缩进,插入图片等功能。
本文给出的代码兼容IE,Firefox和Chrome,如果有其它问题可以通过EMail或者WebIM与我联系。
联系客服