Click here to Skip to main content
15,125,547 members
Articles / Web Development / HTML
Article
Posted 3 Oct 2005

Stats

77.9K views
712 downloads
32 bookmarked

A Web-based Rich Content Editor that works on Opera

Rate me:
Please Sign up or sign in to vote.
4.40/5 (7 votes)
18 Oct 2005CPOL9 min read
The Editor used DOM to alter node properties. This enables edition of rich content in any DOM-supporting browser.

Sample Image - Editor/Editor.gif

Introduction

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

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 to do it 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 implementations. You will have two choices then: Either you will spend ten times more effort and create a code spaghetti 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 achieve 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 opportunities 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 Code Project is manifesting 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 philosophy, 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 Opera: my second favorite browser. My first is Mozilla; I've never considered Internet Explorer at all, shame on me. :)

Editor uses several js classes to function:

  • 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 separating 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.

JavaScript
_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:

HTML
<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 EditorArea layer includes editable content, whereas EditorControls layer includes GUI controls.

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

JavaScript
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 element 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/). Those interested may follow the changes there. Here is a list of things to do that come to mind at first glance. Though I would like to remind you that I am open to any suggestions, positive&negative criticisms, 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 anchor and dropping it to another anchor will move the node before the second one, shift-dragging will copy it, etc)
  • Allow selection and modification of multiple nodes (i.e., making two paragraphs 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 operations
  • 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 the 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 release.

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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

volkan.ozcelik
Web Developer
Turkey Turkey
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.

Comments and Discussions

 
QuestionHow to show text from table to FCKeditor textarea Pin
Nitin C.N17-Nov-07 4:23
MemberNitin C.N17-Nov-07 4:23 
Questionhow to show text from table to FCKeditor textarea+java script Pin
Nitin C.N17-Nov-07 4:11
MemberNitin C.N17-Nov-07 4:11 
GeneralI have an Open Source JS Only Rich Editor Project Pin
T1Oracle1-Aug-07 15:12
MemberT1Oracle1-Aug-07 15:12 
GeneralRe: I have an Open Source JS Only Rich Editor Project Pin
volkan.ozcelik2-Aug-07 18:30
Membervolkan.ozcelik2-Aug-07 18:30 
Generalhi Pin
k_v_arumugam16-Jan-07 22:02
Memberk_v_arumugam16-Jan-07 22:02 
Newsopera now supports wysiwyg mode Pin
volkan.ozcelik17-Apr-06 21:07
Membervolkan.ozcelik17-Apr-06 21:07 
Questiontime is precious isn't it? Pin
volkan.ozcelik9-Mar-06 21:27
Membervolkan.ozcelik9-Mar-06 21:27 
Generalxcx Pin
dsfsgfrhgkj7-Jan-06 3:29
Memberdsfsgfrhgkj7-Jan-06 3:29 
GeneralInteresting approach Pin
Ashley van Gerven6-Oct-05 14:49
MemberAshley van Gerven6-Oct-05 14:49 
GeneralRe: Interesting approach Pin
volkan.ozcelik6-Oct-05 21:45
Membervolkan.ozcelik6-Oct-05 21:45 
GeneralRe: Interesting approach Pin
Ashley van Gerven6-Oct-05 23:20
MemberAshley van Gerven6-Oct-05 23:20 
GeneralRe: Interesting approach Pin
volkan.ozcelik7-Oct-05 1:34
Membervolkan.ozcelik7-Oct-05 1:34 
GeneralRe: Interesting approach Pin
Anonymous18-Oct-05 3:00
MemberAnonymous18-Oct-05 3:00 
GeneralRe: Interesting approach Pin
volkan.ozcelik18-Oct-05 3:20
Membervolkan.ozcelik18-Oct-05 3:20 
GeneralRe: Interesting approach Pin
Anonymous18-Oct-05 21:09
MemberAnonymous18-Oct-05 21:09 
QuestionSuggestions Pin
volkan.ozcelik5-Oct-05 12:23
Membervolkan.ozcelik5-Oct-05 12:23 
GeneralThank you Pin
volkan.ozcelik4-Oct-05 21:55
Membervolkan.ozcelik4-Oct-05 21:55 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.