A Web-based Rich Content Editor that works on Opera

本文介绍了一款基于DOM操作的富文本编辑器,旨在实现跨浏览器兼容性,包括Opera在内的多种浏览器均可使用。该编辑器支持丰富的文本格式化选项,并提供了一个简单的GUI界面。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A Web-based Rich Content Editor that works on Opera

By volkan.ozcelik

Sample Image - Editor/Editor.gif

Introduction

There are plenty of web-based rich text editors around. To list some:

#http://xstandard.com/ - XStandard's WYSIWYG Editor which creates strict XHTML markup.

#http://www.fckeditor.net/ - FCKEditor which works in IE and Mozilla-based browsers.

#http://www.kevinroth.com/rte/demo.htm - Kevin's Cross-browser rich text editor.

#CodeProject's Article Editor.

Before I begin, I'd like to state that I do not want to be understood incorrectly. All the above rich content editors do an excellent job: They convert the boring textarea to a fully editable item. And their development team put great effort to improve/enhance/support them. I fully respect their work. This article is by no means a complementary method. I just wanted to look at things from a different aspect. That's it.

There are many many other rich content editors, which offer different features. All these editors utilize ContentEditable mode of the browsers to create the so called "what you see is what you get" effect.

However, enabling edition of rich text content is not a standard. So each vendor has their own implementation.

Moreover, the support for ContentEditable mode is limited: Only mozilla-based browsers and Internet Explorer currently support it. So say when a third browser comes out and says "hey you guys out there! I have my own way of implementing ContentEditable mode as well!" there is nobody to enforce them do in a certain way. Because there isn't any declared standard for rich content creation.

What happens then? We will triple our effort to fork our code to support both of those browsers. Say all of a sudden ten browsers pop out with ten different API implemantation. You will have two chocies then: Either you will spend ten times more effort and create a code spagethi to handle all possible cases or you will ignore some (or most) of the minority browsers.

But this should not be the case:

Actually there is a standard way of editing HTML documents which World Wide Web Consortium recommends:

a la Document Object Model!

If we somehow achive rich content editability via manipulating DOM, we can add rich content support to any DOM-supporting browser; not only Mozilla and IE.

I know that we cannot support every single browser on earth. But, imho, at least equal opportunuties should be given to all the browsers that fully support DOM.

But, is this really necessary?

The necessity of such a product is another issue. As mentioned above, the vast majority of the web use IE and Mozilla-family browsers which implement content-editability to an excellent extent.

Another point of discussion may be the suitability and necessity of wysiwyg on a web page. Or to state in other words, will it be better to use textareas along with BBCode and leave the template-management and wysiwyg stuff to application packages like Macromedia Contribute.

According to my web stats Opera users constitute less than 1% of my traffic. (8-10 percent Mozilla, which is good :) ; around 90 percent IE and 1-2 percent all the rest).

So people will be happy with the WYSIWYG editors that work in mozilla-based browsers and IE (which make up about 96-99% of my incoming web traffic)

Will I be happy? Certainly not. But that's me and just me :)

Anyway one goal of the code project is manifesing crazy-looking thoughts, isn't it?

May be an idea will pull it from a techy-minded perspective to a more reasonable ground where average web user may utilize.

I hear you saying "enough philosopy, let's see the work done". So here we go:

Implementation

The best way to test an article editor is to write an article with it; so that I may get an idea of which features I may add, what can be done to ease use of the editor, kill bugs etc. So I'm writing this article from the Editor (sorry I couldn't find a more imaginative name for it :) )

Believe me or not, I'm writing this article from within Opeara: my second favorite browser. My first is Mozilla; I've never considered Internet Exploder at all, shame on me :)

Editor uses several js classes to function:

Collapse
DraggableLayer:
The class is used to add drag-drop support and DOM integration.

ToolTip:

This class is used to pop tiny tooltip on hovering over the node anchors
(the images with a white plus on them).

DOMManager:

The object is used to seek parent-child relations as well as remove
empty text nodes which cause problem in Opera and Mozilla.

WindowObject:

This class is used to get the inner dimensions and scroll ofsets of the
browser window.

TextFormatter:

The object is used to remove extra markup that is used
for editing purposes only. Moreover, it makes the tags lowercase,
so the markup will be more or less standards-compatible.

Editor:

Editor is the model for the article editing application GUI (the view).

Using the Editor, you may do a lot of formatting to your article, add images, add links and still create valid XHTML-strict code. I tried to make the GUI simple, but I am not a usability expert. I'm open to any ideas/sketches/mock-ups etc that may enhance the usability further.

The Editor Object

For those who are new to the MVC (Model View Controller) paradigm: The Editor object is a Model for the editor. By seperating the Model from the view ; we can change the view as much as we like, change the layout, change the style etc. without the need of changing the js code behind.

Here is a highly truncated prototype of the Editor.

Collapse
_this=_Editor.prototype;

function _Editor() {
/*initialize members*/
/**/
}
/*public methods*/
_this.setActiveNodeName=function(strValue){...};
_this.setActiveNode=function(obj){...};
_this.getActiveNode=function(){...};
_this.setCurrentAction=function(intValue){...};
_this.isBlockLevel=function(strName){...}
_this.isInline=function(strName){...}
_this.isActiveNodeBlockLevel=function(){...}
_this.getActiveNodeName=function(){...};
_this.getCurrentAction=function(){...};
_this.init=function(){
this.controlPane={
/*
* Bind GUI elements to the Editor model
* using this associative array.
*/

};

/*
* create span elements that contain
* icons which will initiate the editor when
* clicking on them.
*/


/*register events*/
};

_this.getExtendedNodeDescription=function(strNode){...};
_this._RadH1_click=function(evt){...};
_this._RadH2_click=function(evt){...};
_this._RadH3_click=function(evt){...};
_this._RadH4_click=function(evt){...};
_this._RadH5_click=function(evt){...};
_this._RadH6_click=function(evt){...};
_this._RadP_click=function(evt){...};
_this._RadPre_click=function(evt){...};
_this._RadStrong_click=function(evt){...};
_this._RadEm_click=function(evt){...};
_this._RadNormal_click=function(evt){...};
_this._btnChangeType_click=function(evt){...};
_this.toggleCommitAction=function(){...};
_this._btnEdit_click=function(evt) {...};
_this._blnPreview_click=function(evt){...};
_this._btnAddAfter_click=function(evt) {...};
_this._btnAddBefore_click=function(evt){...};
_this._btnMoveAfter_click=function(evt){...};
_this._btnMoveBefore_click=function(evt){...};
_this._btnCopyHere_click=function(evt){...};
_this._btnCopyBefore_click=function(evt){...};
_this._btnCopyAfter_click=function(evt){...};
_this._btnDelete_click=function(evt){...}
_this._btnCancel_click=function(evt){...}
_this.cancelAction=function(){...};
_this._resetGUI=function(){...};
_this.createProperNode=function(strName,strText){...};
_this.getProperNodeValue=function(objNode){...};
_this.getProperNode=function(objNode){...};
_this._btnCommit_click=function(evt){
switch(Editor.getCurrentAction()){
case Constant.Editor.Action.ADD_AFTER:
Editor.addAfter();
break;
case Constant.Editor.Action.ADD_BEFORE:
Editor.addBefore();
break;
case Constant.Editor.Action.MOVE_AFTER:
Editor.moveAfter();
break;
case Constant.Editor.Action.MOVE_BEFORE:
Editor.moveBefore();
break;
case Constant.Editor.Action.DUPLICATE:
Editor.duplicate();
break;
case Constant.Editor.Action.DELETE_NODE:
Editor.deleteNode();
break;
case Constant.Editor.Action.EDIT_TEXT:
Editor.editText();
break;
case Constant.Editor.Action.CHANGE_TYPE:
Editor.changeType();
break;
default:
break;
}
/*set GUI back to initial state*/
Editor.cancelAction();
};

_this.addAfter=function(){...};
_this.addBefore=function(){...};
_this.moveAfter=function(){...};
_this.moveBefore=function(){...};
_this.duplicate=function(){...};
_this.deleteNode=function(){...};
_this.editText=function(){...};
_this.changeType=function(){...};
_this._appendControlsToFamily=function(nodeToInsert){...};
_this.organizeEditPane=function(){...};
_this.appendControls=function(theNode){...};
_this._edit_click=function(evt){...};

The HTML

The HTML for the Editor page is as follows:

Collapse
<div id="EditorArea" class="""textEditor">""  
<h2>Introduction</h2>
</div>

<div id="EditorControls">
<h3 id="EditorInfo">Informational Heading</h3>
<div style="padding:10px;" id="EditorControlButtons">
<h4>transform</h4>
<div><input type="button" id="btnChangeType" value="change type" /></div>

<h4>alter</h4>
<div>
<input type="button" id="btnEdit" value="edit text" />
<input type="button" id="btnAddBefore" value="add before" />
<input type="button" id="btnAddAfter" value="add after" />
</div>
<h4>move</h4>
<div>
<input type="button" id="btnMoveBefore" value="move before / move up" />
<input type="button" id="btnMoveAfter" value="move after / move down" />
</div>
<h4>copy</h4>
<div>
<input type="button" id="btnCopyHere" value="duplicate element" />
</div>
<h4>remove</h4>
<div><input type="button" id="btnDelete" value="remove element" /></div>
</div>
<div style="padding:10px;">

<ul id="ListTagsBlockLevel">
... radio buttons ...
</ul>

<ul id="ListTagsInline">
.... radio buttons ...
</ul>


<textarea rows="10" cols="30" id="TxtContent"></textarea>
</div>

<div style="padding:10px;text-align:right;">
<input type="button" value="cancel" id="btnCancel" />
<input type="button" value="commit" id="btnCommit" />
</div>
</div>

The EditorArealayer includes editable content, wheras EditorControls layer includes GUI controls.

To initialize the editor we call its init() method on page load.

Collapse
window.onload=function() {
Editor.init();
document.getElementById("BtnArticleSource"
).onclick=BtnArticleSource_click;
};

function BtnArticleSource_click(evt){
alert("This action may take some time and your browser may_
hang for a few seconds./nPlease be patient."
);

var src=new EventObject(evt).getSource();

document.getElementById("ArticleSource").value=
TextFormatter.properHTML(
Editor.controlPane.panel.EditorArea.getObject().innerHTML);
}

BtnArticleSource_click method is triggered when clicking the "retrieve article's html" button. This will fill the textbox at the bottom of the page with the article's HTML.

You may further investigate the code. I tried to write the code and the markup as clean as possible. Feel free to express any queries and comments.

Tips and Usage

tipCreation and modification of the article is done on node basis. If you are working in the edit mode, you will recognize the node anchors (inline edit icon and block level edit icon). inline edit icon is used to add/edit/modify inline elements (strong text, emphasized text, normal text, links and images) whereas block level edit icon is used to add/edit/modify block-level elements (heading level 1-6, paragraphs and preformatted text).

tipClicking on a node anchor pops up available actions for that particular node. The pop-up is draggable DHTML layer (my following article will be on creating a simple drag&drop layer I suppose).

tipDouble-clicking anywhere on the article toggles between edit and preview modes.

tipAs far as my experience is concerned: getting used to the Editor takes some time. Because, you need to look the entire document as a whole instead of bunches of paragraphs and text to copy, paste and drag around. But you become more creative as you get used to it. In fact the "duplicate element" functionality happens to be a good helper at times. In addition bubbling block level elements up and down is fun.

tipActions available for all nodes (inline and block-level):

# change type: Changes the type of the node, for instance you may convert a text elemen to a link or to an image.

# add before: Adds an element before that node.

# add after: Adds an element after that node.

# move before/move up: Moves the node upwards in the node hierarchy.

# move after/move down: Moves the node downwards in the node hierarchy.

# duplicate: Creates an identical node and positions it just after the selected node.

# remove: Removes the selected node entirely. caution! this operation cannot be undone.

# cut node: removes the node and copies the node contents to a temporary variance.

# copy node: copies the node content to a temporary variance; it does not remove the original node.

# paste before: pastes the copied or cut node just before the selected node.

# paste after: pastes the copied or cut node just after the selected node.

tipActions only available for inline nodes only:

# edit: changes the contents of the node (the text and (source, link, width etc if available)).

tipAvailable types for inline nodes:

# strong text: bold text.

# emphasized text: italic text.

# normal text: normal text.

# link:A link.

# image:lovely image.

tipAvailable types for block-level nodes:

# heading level 1: First level heading.

# heading level 2: Second level heading.

# heading level 3: Third level heading.

# heading level 4: Fourth level heading.

# heading level 5: Fifth level heading.

# heading level 6: Sixth level heading.

# paragraph: A paragraph.

# preformatted text: preformatted text (which is generally used for formatting code).

Things that can be Done to Improve the Editor

From now on, I will post new versions to the Editor to sardalya (http://www.sarmal.com/sardalya/). The interested may follow the changes there. Here is a list of things to do that come mind at a first glance. Though I would like to remind you that I am open to any suggestions positive&negative criticisims so that I may enhance the Editor further.

# Ability to insert lists and nested lists.

# Add support for other tags (like <q>, <blockquote>, <hr />)

# Add some descriptive icons to the pop-up interface.

# Add tab-support for preformatted text, which is a great aid when indenting code (Opera will not support it but it will transform gracefully - may be add a button to copy tab character (from a hidden field may be) for Opera and other non-supporting browsers).

# Some more mouse interaction (such as dragging an ancor and dropping it to another anchor will move the node befoore the second one, shift-dragging will copy it etc).

# Allow selection and modification of multiple nodes (i.e making two paragraps bold, converting three headings to paragraphs etc).

# Allow creation and modification of nested elements (such as <strong><em>strong and emphasized text</em></strong>).

# Assign keyboard shortcuts to certain opearations.

# Ability to add predefined smiley images, bullet images, avatars etc.

Note to the Reader

I've included only the necessary parts of s@rdalya API that I used in this project not to deviate from the subject and to keep code small. You may find the entire api at: http://www.sarmal.com/sardalya/.

However, the API on web currently does not include the Editor. Editor will be added to the next stable relase.

Also note that this is a development release and it is not optimized for web. An optimized version along with usage examples and documentation will be available soon.

History

#03-10-2005 - Article created.

#18-10-2005 - version 1.1.0 is released. (former version was 1.0.0). Article updated accordingly.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

volkan.ozcelik


Volkan is a java enterprise architect who left his full-time senior developer position to venture his ideas and dreams. He codes C# as a hobby, trying to combine the .Net concept with his Java and J2EE know-how. He also works as a freelance web application developer/designer.

Volkan is especially interested in database oriented content management systems, web design and development, web standards, usability and accessibility.

He was born on May '79. He has graduated from one of the most reputable universities of his country (i.e. Bogazici University) in 2003 as a Communication Engineer. He also has earned his Master of Business Administration degree from a second university in 2006.
Occupation: Web Developer
Location: Turkey Turkey

From: http://www.codeproject.com/KB/scripting/DOMEditor.aspx
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值