webabcd - 专注于asp.net, html5, silverlight

ASP.NET
从现在开始 一切都不晚
posts - 287, comments - 7866, trackbacks - 594, articles - 0
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理
原文地址:http://aspalliance.com/1156_Rich_Text_Editor__Part_II
[原文源码下载]


[翻译]开发一个自己的HTML在线编辑器(二)


原文发布日期:2007.02.27
作者:Haissam Abdul Malak
翻译:webabcd


文章内容
介绍
javascrit文件
后置代码
结论


介绍
在我们一起研究了如何开发一个HTML在线编辑器后(第一部分在这里 http://aspalliance.com/1092_Rich_Text_Editor_Part_I )(译者注:中文在这里),现在来给它增加一些特性,扩展它的功能,使它更实用。本文我们将了解这些新的特性是如何实现的,所有的新增功能都是用javascript来写的。

从之前的文章你看到了开发一个你自己的HTML在线编辑器是多么简单。当然,我们的第一个版本只包括一些标准功能,现在,我们要给它增加一些新的功能,我敢说,它将包括更多的特性并且更加实用。

下面列出这些新增的特性
1、删除线:可以给用户输入的文本增加删除线
2、减少/增加缩进:可以减少或增加文本的缩紧
3、插入图片:可以新开一个窗口让用户选择需要上传的图片,然后直接插入到编辑器里
4、复制,剪切和粘贴:通过剪切板实现复制,剪切和粘贴的功能
5、打印:在文本模式的时候可以调出打印对话框进行打印
6、项目符号和编号:可以在文本的开头增加项目符号和编号
7、插入线:可以在文本中插入一条线

在我们的用户控件里增加一些HTML代码
这部分我们将在工具栏处增加一些新的HTML元素。我们主要是放置了一些图片,为了实现上面提及过的行为并且处理这些事件以实现它们的功能。

列表1
<IMG class=StrikeOut id=Strikethrough
 
onmouseover="ChangeImg('Strikethrough','strikethrough.over.gif')"
 title
="Strike Through" onclick="Formats('StrikeThrough','<%= this.HamEditorChildID %>' )"
 onmouseout
="ReturnImg('Strikethrough','strikethrough.gif',imgStatusUnderLine)"
 src
="Images/strikethrough.gif" />

上面的列表增加了一个img的HTML控件,它的onmouseover事件将调用一段javascript函数(我之前的文章已经对此解释过了),以改变图片的选中状态,onclick事件将调用另一段javascript函数以得到选中的文本,onmouseover将返回图片最初的状态。本次新增的功能除了插入图片外都将使用于此相同的概念。

请下载源码以查看所有的HTML标签是如何创建的

图1



javascript文件
这一部分我们将在已经创建好的javascript文件内增加一些新的功能,从而去处理这些新特性的事件。所有的这些功能都被定义到了这个javascript文件内,然后被编辑器引用

列表2
function Formats(style,editorId)
{
    
// 存储Iframe的id
    var finalDivId = editorId + '_content';
                  
    
// 设置焦点
    document.frames[finalDivId].focus();
                  
    
// 应用新的命令
    document.frames[finalDivId].document.execCommand(style);
                  
    
// 设置焦点
    document.frames[finalDivId].focus();
}

本文所列的几乎所有的新增的事件处理都会使用上面的功能。它有两个参数:第一个参数是需要应用的命令,例如复制、粘贴之类的没;第二个参数是编辑器的id。我们使用一个相同的功能(execCommand)来应用这些变化。

列表3
function SetBorders(id)
{
    
// 设置onmouseover事件时表情图片的边框样式
    var imgBorder = document.getElementById(id);
    imgBorder.style.borderStyle 
= "solid";
    imgBorder.style.borderWidth 
= "thin";
    imgBorder.style.borderColor 
= "#688B9A";
}

在插入表情的时候将会调用上面的函数。当鼠标经过了一个表情文件,我们将会调用这个函数设置表情图片的边框。用这种方法,可以方便用户观察到鼠标是经过了哪一个表情图片。注意我们增加了一个“x”图片,用于当用户不需要选择任何表情图片的时候关闭div层。

图2


列表4
function ClearBorders(id)
{
    
// 清除onmouseover事件时表情图片的边框样式
    var imgBorder = document.getElementById(id);
    imgBorder.style.borderStyle 
= "solid";
    imgBorder.style.borderWidth 
= "thin";
    imgBorder.style.borderColor 
= "white";
}

当鼠标离开一个表情图片的时候清除它的边框样式则调用这个函数

列表5
function SetImage(editorId,path,e)
{
    
// 获得单击位置
    var height = e.clientY + parseInt('5');
    
// 获得被单击图片的高度
    var offsetHeight = parseInt(e.offsetY);
    height 
= height - offsetHeight;
                      
    
// 获得单击位置(宽)
    var width = e.clientX
    
// 获得被单击图片的宽度
    var offsetWidth = parseInt(e.offsetX);
    width 
= width - offsetWidth;
                      
    
// 存储iframe的id
    var finalDivId = editorId + '_content';
    path 
= unescape(path);
                            
    
// 从用户的电脑里插入一个图片 
    window.open('UploadImages.aspx?path=+ path +
     '
&f=+ finalDivId,null,'width=500px,height=50px,titlebar=no,menubar=no,statusbar=no,toolbar=no,top='
     
+ height + 'left=+ width );                                   
}

我们创建了一个新的被称作UploadImages.aspx的webform,它来实现插入图片的特性。当用户单击了插入图片的图标后,这个form被打开,它允许用户从他/她的电脑内选择一个图片上传到服务器从而实现把图片插入到编辑器内的功能。这个webform还包括了两个文本框,用于让用户指定他/她上传图片后图片所显示的宽度和高度。当这个webform打开的时候显示如下

图3


用户已经可以浏览他/她的电脑选择一个图片并上传到服务器了,我们是在后置代码中处理文件上传的。我提供了一个选项,可以让管理员设置图片上传到服务器后所存放的目录。我将在后置代码的部分中详细解释这些。我们规定高度属性的最小值为100px,宽度属性的最小值为150px,并且这也是默认值

列表6
function insertsImage(imageurl,editorId,height,width)
{
    opener.document.frames[editorId].focus();
            
    
if(imageurl != "" | editorId!= "")
    
{
        imageurl 
= unescape(imageurl);
        
var imageurl = imageurl + '"'  + "width=" + width + "px" + " " + "height="+ height + "px";

        opener.document.frames[editorId].document.execCommand('InsertImage',false,imageurl);
    }
    opener.document.frames[editorId].focus();
}

当用户点击了插入图片后将调用这段函数。在这个单击事件中,我们上传了文件并且通过javascript把这个图片插入到编辑器中,显示的宽度和高度就是我们所指定的

图4



后置代码
这部分我们将了解如何在后置代码中实现上传图片的功能,并且将知道管理员如何设置图片上传的路径。

列表7
public string FilePath
{
    
get
    
{
        
return _filePath;
    }

    
set
    
{
        _filePath 
= Server.HtmlEncode(Request.ApplicationPath + value);
    }

}

上面的这个string类型的属性用于让管理员设置图片上传的默认路径。接下来的列表将告诉你如何在你的webform中指定这个路径。

列表8
string imagePath = "/UserImages";
((hamHtmlEditor)
this.FindControl("HamHtmlEditor1")).FilePath = imagePath;

列表9
public string Location
{
    
get
    
{
        
return _location;
    }

    
set
    
{
        _location 
= value;
    }

}

为了使图片上传到web服务器后可以直接插入到编辑器里,我们需要使用上面这个属性,它用于获得编辑器的ID

列表10
if (heightValidator.IsValid && widthValidator.IsValid)
{
    
string elementToInsert = Request.QueryString["f"];
    
if(UploadImage.PostedFile != null && UploadImage.PostedFile.ContentLength > 0)
    
{
        
try
        
{
            
string fileName = System.IO.Path.GetFileName(UploadImage.PostedFile.FileName);
            
string fileLocation = Request.QueryString["path"+ "/" + fileName; 
            
this.ImagePath = Server.HtmlEncode(fileLocation);
            
this.Location = elementToInsert;
            Button1.Enabled 
= false;
            UploadImage.PostedFile.SaveAs(Server.MapPath(fileLocation));
        }

        
catch(Exception ex)
        
{
            Response.Write(
"<script language=javascript>alert('"+ex.Message.ToString().Replace(@"\",@"\\")+"');</script>");
        }

        
finally
        
{
            Button1.Enabled 
= true;
        }

    }

}

上面这段代码是写在插入图片按钮的点击事件里的。它的作用是上传图片到指定的路径,try catch块用于处理异常事件。

注意:有一个要非常注意的地方就是ASPNET(注:windows 2003 下是NETWORK SERVICE)用户是否有你的上传目录的写入权限。

列表11
public bool ShowHeader
{
    
get
    
{
        
return _Header;
    }

    
set
    
{
        _Header 
= value;
    }

}

这是一个Boolean类型的属性。如果设置它为false则工具栏不会显示。

图5


当你把ShowHeader属性设置为false的时候编辑器显示如上。工具栏和其他的图标都将被隐藏。

图6


这就是增加了一个新的特性之后的编辑器的最终版本


结论
这个编辑器的版本只在IE中进行了测试。下一个版本中我将让它在更多的浏览器中工作。如果你有增强这个编辑器功能的建议或者提出其中的bug,我将非常欢迎。我也会把它封装成一个自定义控件,使你能更简单的使用它。希望你能从本文中获得一些有用的信息。

[原文源码下载]
 
祝编程愉快!

Feedback

#1楼  回复 引用   

2007-03-05 11:37 by ivw[未注册用户]
哗,COOL!!
支持一下。

#2楼[楼主]  回复 引用 查看   

2007-03-05 12:45 by webabcd      
@ivw
如果想要个轻量级的话或者自己做的话看看这个确实不错

#3楼  回复 引用   

2007-03-05 13:05 by ivw[未注册用户]
呵呵,
有个SQL的问题想请教你。
例如一个表有A,B,C,D几个字段,我想按A,B分组,分组后能不能把C,D的内容也显示出来啊?
select a,b from tab group by a,b
这样可以按a,b分组,但不能显示c,d字段
如果select a,b,c,d from tab group by a,b,c,d
这样就是按4个字段分组了。

#4楼[楼主]  回复 引用 查看   

2007-03-05 18:00 by webabcd      
@ivw
因为不能保证每组a,b所对应的c,d唯一
所以不能

#5楼  回复 引用 查看   

2007-03-05 19:33 by reonlyrun      
这个东东相比其它成熟的HTML在线编辑器有何独到之处?效率高?功能强?开源?还是。。。。。。

#6楼[楼主]  回复 引用 查看   

2007-03-05 20:03 by webabcd      
@reonlyrun
独到之处就是有一步一步的讲解

#7楼  回复 引用   

2007-03-05 20:36 by ivw[未注册用户]
可以不用理会c,d是否唯一,只要是跟a,b是同一行就可以了

#8楼[楼主]  回复 引用 查看   

2007-03-05 22:30 by webabcd      
@ivw
用a,b分组的话,c,d又不能确定为一与之对应,就不知道要检索那个c,d了,所以这样是不行的

#9楼  回复 引用   

2007-03-05 22:33 by ivw[未注册用户]
如果是这样就算了。我想到个办法,有点麻烦,就是用循环。

#10楼[楼主]  回复 引用 查看   

2007-03-05 22:42 by webabcd      
@ivw
你可以仔细想一想,其实你的要求是不可能实现的

能说一下你的实际项目的需求吗?

#11楼  回复 引用   

2007-03-05 22:54 by ivw[未注册用户]
其实是一个表里头有些数据是重复的。想把这表里的数据插入到另一个表。重复的记录只拿其中一条。就是这样

#12楼[楼主]  回复 引用 查看   

2007-03-06 08:14 by webabcd      
@ivw
select distinct * from table

#13楼  回复 引用   

2007-03-06 08:44 by ivw[未注册用户]
这就不能实现只要a,b的分组了,你这样就是全部字段分组了。

#14楼[楼主]  回复 引用 查看   

2007-03-06 08:53 by webabcd      
@ivw
你的需求不是“一个表里头有些数据是重复的。想把这表里的数据插入到另一个表。重复的记录只拿其中一条”吗?为什么要分组?

#15楼  回复 引用   

2007-03-06 12:48 by xuyao[未注册用户]
太酷了!!!感谢楼主分享这么好的文章。

#16楼  回复 引用   

2007-03-06 14:06 by ivw[未注册用户]
这样的,后面可以随便拿,但必须是跟a,b,这两列是同一行数据

#17楼[楼主]  回复 引用 查看   

2007-03-06 23:05 by webabcd      
@xuyao
:)

#18楼[楼主]  回复 引用 查看   

2007-03-06 23:08 by webabcd      
@ivw
比如a,b,c,d四个列的数据如下
1 2 3 4
1 2 5 6

a,b分组了就是
1 2

那么c,d是3 4呢还是5 6呢
所以这个需求是有问题的

#19楼  回复 引用   

2007-03-07 01:08 by ivw[未注册用户]
谢谢兄弟了,我用了个麻烦的方法做了。

#20楼[楼主]  回复 引用 查看   

2007-03-07 08:15 by webabcd      
@ivw
呵呵,解决了就好

#21楼  回复 引用   

2007-03-07 15:21 by 风使者[未注册用户]
谢谢了!好用的话我将改进这个编辑器......

#22楼[楼主]  回复 引用 查看   

2007-03-07 15:38 by webabcd      
@风使者
我觉得这个还有好多改进的地方,比如目前不能在FF下使用,不能直接编辑html代码,没封装成自定义控件等,作者正在改进中

如果这些功能都完善了,确实是一个比较好的轻量级的rich text editor解决方案

#23楼  回复 引用   

2007-03-20 15:35 by 啊啊[未注册用户]
期待,在次更新版~~~~~~

这样在鼠标移动的情况下直接变化图片,效率怎么样

#24楼[楼主]  回复 引用 查看   

2007-03-20 18:16 by webabcd      
@啊啊
不会有什么效率问题的,肯定比那些成熟的rich text editor效率高的,而且很小巧

#25楼  回复 引用 查看   

2007-03-20 19:03 by reonlyrun      
@啊啊
不会有什么效率问题的,肯定比那些成熟的rich text editor效率高的,而且很小巧

何以有此说法?为什么会比现在成熟的效率高?

#26楼[楼主]  回复 引用 查看   

2007-03-20 20:32 by webabcd      
@reonlyrun
像FreeTextBox,WebHtmlEditor,CuteEditor,FCKeditor都是功能超强,自然会生成大量的js,相对这个小东西肯定是占带宽,耗资源的

#27楼  回复 引用   

2007-03-22 16:46 by onetwofree[未注册用户]
可识别word图片,自动上传本机图片文件的编辑器webnoteeditor
http://www.shaoys.com/webnoteeditor_sample

#28楼[楼主]  回复 引用 查看   

2007-03-22 17:06 by webabcd      
@onetwofree
广告?

#29楼  回复 引用   

2007-03-28 15:02 by @donnil[未注册用户]
如果我要把数据(HTML代码)从数据库读取出来然后赋给这个服务器控件,如何操作呢?谢谢!

#30楼[楼主]  回复 引用 查看   

2007-03-28 18:00 by webabcd      
@@donnil
代码中有这句,给这个属性赋值就好了
public string ContentInnerHtml
{
get
{
return _html = ContentHtml.Value;
}
set
{
_html = value;
}
}

#31楼  回复 引用   

2007-03-29 11:13 by @donnil[未注册用户]
webabcd

非常感谢您了,看完以后觉得受益良多,以后我肯定会常来你的博客,还有想问您一下您前面提到的“翻译团队”,是一个网站吗,如果是的话,网址多少啊?谢谢!

#32楼  回复 引用   

2007-03-29 13:44 by @donnil[未注册用户]
刚刚试了一下,把一短Html代码赋值给编辑器控件,然后然它显示出来:但是
控件的属性:
public string ContentInnerHtml
{
get
{
return _html = ContentHtml.Value;
}
set
{
_html = value;
}
}
我在WebForm2.aspx.cs页面Page_load里面写上了下面两句话,逐语句进行调试的时候虽然在_htm=value中value显示的值是"<b><i>dddddoooppp</i></b>";但是
在this.HamHtmlEditor1.ContentInnerHtml = me5;中this.HamHtmlEditor1.ContentInnerHtml却显示为""(没有值,为空),
page_load中写的两句代码:
protected void Page_Load(object sender, System.EventArgs e)
{
string me5 = "<b><i>dddddoooppp</i></b>";
this.HamHtmlEditor1.ContentInnerHtml = me5;
}

接着我把属性改了一下,如下:
public string ContentInnerHtml
{
get
{
return _html;
}
set
{
_html = value;
}
}
这样的话this.HamHtmlEditor1.ContentInnerHtml显示为"<b><i>dddddoooppp</i></b>",但是编辑器控件文本区域中还是一片空白,不能像TextBox.Text="aaa"一样,赋值后控件就显示aaa了,还有我把属性改成这样return _html; 好像就取不到控件的值了。我不知道是我用的不对,还是控件方面的事。

#33楼[楼主]  回复 引用 查看   

2007-03-29 17:30 by webabcd      
@@donnil
抱歉,我看到了他有一个ContentInnerHtml属性,并且有set访问器,就以为可以给那个html编辑器赋值,看到你的问题后,试了一下这个,根本没用,看了他的源码后,发现这就是个摆设,又看了他的原文的评论后作者给出这个解决办法
在用户控件的Page_Load事件里增加如下代码
if(_text != string.Empty)
{
frameDiv.InnerText = _text;
}
然后设置ContentText属性

不过经我测试后发现会有问题

还是等待作者的下一个版本吧



翻译团队就是博客园里的一个团队http://www.cnblogs.com/team/BluePrint.html
不过最近好像只有我比较勤(每个礼拜至少一篇翻译)


#34楼  回复 引用   

2007-03-30 10:25 by @donnil[未注册用户]
webabcd
好的,我也试过了,确实是会有一些问题的,那就等待作者下一个版本了,期待你第一时间内把它翻译过来!还有谢谢你告诉我翻译团队的网址 ^_^!

#35楼[楼主]  回复 引用 查看   

2007-03-30 12:36 by webabcd      
@@donnil
:)
会的

#36楼  回复 引用   

2007-04-05 17:19 by onetwofree[未注册用户]
是广告
最新的官方网站是www.webnoteeditor.com

专门用于发布图文混排的文章类信息(如新闻、论文、标准、企业简介、OA等信息)的编辑器。编辑器所使用的是HTML4.0标准Web格式语言。当前,网络上类似的编辑器有好几款,WebNoteEditor针对最日常的应用,提供以下具有特色的功能:

1)能自动把编辑框中引用的本地机图片和从Word文档粘贴过来的图片自动上传到服务器(需要客户端安装“自动上传组件”),显示真实反应当前上传进度的进度条。并可以设置对图片进行水印操作。

2)能把编辑框中引用的站点域外的图片自动保存到服务器。(能智能虚拟突破被引用服务器文件过滤器的限制。)

3)根据网上文章互相摘取的现象,专门增加了“规则性粘贴的功能”。用于解决普通粘贴其他网站页面的时候,不能识别表格、层对象中的背景图片问题。

4)当修改保存文章的时候,将即时自动删除文章中多余的图片或Flash文件。

5)支持显示Word文件中拷贝过来的流程图。最大程度保持Word文档原样。

6)支持大文件上传功能。(实测能上传50M的文件至远程服务器)

7)自动适配图片的实际显示大小,从而减少服务器空间的负担。

#37楼[楼主]  回复 引用 查看   

2007-04-05 17:46 by webabcd      
@onetwofree
多一个选择总是好的

#38楼  回复 引用   

2007-04-07 16:36 by onetwofree[未注册用户]
谢谢楼上的兄弟的支持!

记得我在csdn论坛也发了一个帖子。(http://community.csdn.net/Expert/topic/5413/5413010.xml?temp=.1916315

有人说“现在有免费开源干什么不用呢”

里面其中又有人说说过:“不免费就要想办法超过免费的!”。

确实是这样,如果免费开源的已经满足自己的应用需要,那么完全没必要去买收费的。收费的必须在功能上要超过免费提供的版本才行,可以快速满足大家的需求才能对得起大家。我在这里只是发个信息,如果需要的人,可以知道有什么解决方案。请大家不要鄙视我善意的广告。

最后,支持版主的共享精神。

#39楼[楼主]  回复 引用 查看   

2007-04-07 19:53 by webabcd      
@onetwofree
不错
首先要知道可以选择的有什么,再根据需求决定去用什么

当然还是最支持开源

#40楼  回复 引用   

2007-04-27 16:44 by abc[未注册用户]
Gooooooood!!!!

#41楼[楼主]  回复 引用 查看   

2007-04-27 17:44 by webabcd      
@abc
:)
有每一句代码的详细说明确实很好
不过要实际应用确实还是有很多不足

#42楼  回复 引用   

2007-05-04 19:45 by FCLZ(风城浪子)[未注册用户]
最近在用Ajax做个Blog,正好用得上,楼主真的费心了,希望以后能继续看到你的好文章,再次谢谢,收藏了~~~

#43楼[楼主]  回复 引用 查看   

2007-05-04 22:46 by webabcd      
@FCLZ(风城浪子)
:)
呵,不谢,翻译而已

#44楼  回复 引用   

2007-05-30 16:59 by 小张[未注册用户]
看了半天,还不知道如何把编辑内容写入数据库了,
asp.net 应该如何写呢?谢谢楼主

#45楼[楼主]  回复 引用 查看   

2007-05-31 07:50 by webabcd      
@小张
有一个属性
“ContentInnerHtml”,它可以获得详细的HTML代码

#46楼  回复 引用   

2007-07-23 15:30 by hhe[未注册用户]
怎么好像撤销和恢复功能使用不了呢

#47楼[楼主]  回复 引用 查看   

2007-07-23 17:51 by webabcd      
@hhe
看了一下,果然

再看一下
作者用的是execCommand('Undo')和execCommand('Redo')来实现撤销和恢复

查一下MSDN
http://msdn.microsoft.com/workshop/author/dhtml/reference/commandids.asp
知道了答案,因为不支持Undo和Redo

#48楼  回复 引用   

2007-09-29 13:43 by kevinlzf[未注册用户]
目前只能在IE上测试成功,有其他浏览器则不能成功插入图片到可视界面上.

#49楼[楼主]  回复 引用 查看   

2007-09-30 08:01 by webabcd      
@kevinlzf
嗯,现在是这样的
原文作者说过,下一步就是使其同时支持IE和FF
如果原文作者发布了新文章,我会尽快把它翻译过来的

#50楼  回复 引用 查看   

2009-11-24 16:26 by 刘领福      

#51楼[楼主]  回复 引用 查看   

2009-11-25 08:29 by webabcd      
@刘领福
:)
翻译的文章,不过最近很长一段时间都没翻译过了,懒了

#52楼  回复 引用 查看   

2009-11-25 16:13 by 刘领福      
我做东西现在能用楼主的组件就用楼主的组件很好用!

#53楼[楼主]  回复 引用 查看   

2009-11-26 08:18 by webabcd      
@刘领福
:)
过奖了
发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 663102 44GU04VQssY=