FCKeditor, AJAX and edit-in-place, (Part III) - The Final Frontier
In my previous two posts I showed you how I’ve been attempting to implement FCKeditor with AJAX to get that all elusive edit-in-place functionality going. Now I’m going to reveal the final code.
The Content Problem
I use a topic map with xhtml content in the xml. When I generate content I automatically add titles and alt attributes using an xsl stylesheet. The problem is that, obviously, the content has extra bits inside it that I don’t want for editing. So, my solution requires me to fetch the raw content - without title or alt attributes, etc
If you don’t tweak your content out of a database for display, then you could just use the innerHtml method to grab the existing content from your page.
The Solution
First, download the file
You can download the javascript I use for my FCKeditor solution from here: fn_ajaxstuffjs.jpg
Make sure that you change the file extension (using a jpg file extension was the only way to get it here onto WordPress).
Second, stick it into your html file
<head>
<title>Matthew’s page</head>
<script src=”fn_ajaxstuff.js” type=”text/javascript”></script>
</head>
Third, create your activation button
In an earlier version, I stuck the function to activate the editor on the <div>. I’ve got a few other features to stick onto my page, so I figured I would add an edit button. Of course, just move it back to the <div> if you want to use a Flickr-like click-edit feature.
Here’s what I’ve done:
<div id=”toolbar”>
<a href=”#” onkeypress=”javascript:edit(’matthew-hodgson’);”
onclick=”javascript:edit(’matthew-hodgson’);” class=”edit”
title=”edit this page”>
<span>Edit content</span>
</a>
<noscript>
This page is AJAX-enhanced for editing. You need to hava javascript enabled,
and an AJAX-compatible browser, in order to use some of the features on this
page.
</noscript>
</div>
Cheat stuff
I put some variables into the page and use javascript to make queries of that content to use for the database/xml lookups.
<div id=”endnotes”>
<p><span>itemID:</span><span id=”itemID”>matthew-hodgson</span></p>
<p><span>type:</span><span id=”type”>player</span></p>
</div>
It’s generated by the xsl and is handy to do debugging by just looking at the page. It means, though, I have a handy way of using some variables without the need for a form.
The widget
When the POST request is made to your widget all you need to do is to return the value of the content you’re saving. This content will then replace the existing content in the page.
If the widget gets stuck and there’s an error you need to work out some way of letting your AJAX routine know and what to do as a result.
Where does the content go?
In this example, I use two <div> to handle the content.
<div id=”story”>
<div id=”story-content”><p>Here is some content.</p><p>I love content.</p><p>Content is great</p></div>
</div>
The <div id=”story”> element simply gives me a reference point for the content and AJAX action.
It’s after last child of this <div> that the textarea gets created. If you want to see it actually being created then simply turn off the layer hides and style it with some nice big green lines (that’s what I did :)).
After I change the content and submit the AJAX form, I’ll receive a notice that the widget has processed the content. It will be in the form <div id=”story-content”>some stuff</div>. I then replace all of the insides of <div id=”story”> with this new content.
A note about Safari
Don’t forget that FCKeditor doesn’t work with Safari. If people are going to use your editor feature let them know. Maybe you could even provide them with an alternative? I’ve not done that here, but it’s a thought I might take onboard in a later version.
FCKeditor and Prototype
When I was first trying to get FCkeditor to work my searching lead me to Prototype. There’s a lot of questions about how to get Prototype to work with FCKeditor or TinyMCE but no action. I had a go at making it work and had to just give up after several weeks. I’m no Javascript or DOM guru. I can stumble around at best. So, let me just say, forget Prototype and FCKeditor.
But, there is hope!
FCkeditor and Prototype can work side-by-side so that you can have edit-in-place for headings or other parts that will really only require a text field, and use [instances of] FCKeditor for the WYSIWYG stuff.
In my implementation of FCKeditor I’ve got Prototype working on the same page for the heading (the name element that corresponds to the content in the database) and it works fine. Just keep in mind the overhead you’re going to have with all these javascripty bits.
Here’s the code in it’s full glory!
OK. Enough chatter. Let’s go with the code.
I’ve tried to make comments here and there to explain what’s going on. Make use of the comments area below the blog if you need help or have any questions or just want to say ‘hi’ or ‘thanks’.
If you’d like to see an example of this in action, just visit topicmaps.matthewhodgson.com
var itemID
function edit(someID) {
var agt = navigator.userAgent.toLowerCase();
if(agt.indexOf(”safari”) != -1) {
alert(’FCKedior does not work with Safari\n\nFor more information see http://www.fckeditor.net/safari’);
} else {
//alert(’I notice you\’re not using Safari. Good! We can make FCKeditor available to you!’);
itemID = someID;
makeRequest(’widget.php’, ‘mode=api&templateID=content&itemID=’+itemID, ‘loadContent’);
}
}
function setEditorValue(instanceName, text ) {
document.ajaxform.FCKeditor1.value = text;
}
function showContentValue() {
return FCKeditorAPI.GetInstance(’FCKeditor1′).GetXHTML(true);
}
function hideContentLayer(someID) {
var someLayer = document.getElementById(someID);
someLayer.style.display = ‘none’;
}
function showContentLayer(someID) {
var someLayer = document.getElementById(someID);
someLayer.style.display = ‘block’;
}
function replaceContentInLayer(id, content) {
var someLayer = document.getElementById(id);
someLayer.innerHTML = content;
}
function alertContents(http_request) {
if (http_request.readyState == 4) {
// everything is good, the response is received
if (http_request.status == 200) {
//alert(’test1:’+http_request.responseText);
return http_request.responseText;
} else if(http_request.status == 500) {
alert(’500 Internal Server Error\nThe server encountered an unexpected condition which prevented it from ‘+
‘fulfilling the request. ‘);
} else if(http_request.status == 503) {
alert(’The server is currently unable to handle the request due to a temporary overloading or maintenance of the server. ‘+
‘The implication is that this is a temporary condition which will be alleviated after some delay. If known, the length of’+
‘ the delay MAY be indicated in a Retry-After header. If no Retry-After is given, the client SHOULD handle the ‘+
‘ response as it would for a 500 response.’);
} else if(http_request.status == 504) {
alert(’The server, while acting as a gateway or proxy, did not receive a timely response from the upstream server ‘+
’specified by the URI (e.g. HTTP, FTP, LDAP) or some other auxiliary server (e.g. DNS) it needed to access in ‘+
‘attempting to complete the request. ‘);
} else {
//you can find more errors messages at:
//http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
alert(’There was a problem with the request. Error code=’+ http_request.status +’.');
}
} else {
// still not ready
//alert(’still not ready… waiting to receive msg from widget’);
}
}
function makeRequest(url, parameters, passType) {
var http_request = false;
if (window.XMLHttpRequest) { // Mozilla, Safari, …
http_request = new XMLHttpRequest();
if (http_request.overrideMimeType) {
http_request.overrideMimeType(’text/xml’);
// See note below about this line
}
} else if (window.ActiveXObject) { // IE
try {
http_request = new ActiveXObject(”MSXML4.XMLHTTP”);
} catch (e) {
try {
http_request = new ActiveXObject(”Microsoft.XMLHTTP”);
} catch (e) {}
}
}
if (!http_request) {
alert(’Giving up
Cannot create an XMLHTTP instance’);
return false;
}
http_request.onreadystatechange = function() {
var strText = alertContents(http_request);
if (strText == undefined) {
//do something … anything you like …
//someMsg = document.createTextNode(’.');
//document.getElementById(’story’).appendChild(someMsg);
} else {
if(passType == ‘loadContent’) {
//dont want anyone doing any special actions now, do we!
hideContentLayer(’toolbar’);
//need a reference point for these new layers?
var targetLayer = document.getElementById(’story’);
//create a msg layer
var msgLayer = document.createElement(’div’);
msgLayer.setAttribute(’id’, ‘msgLayer’);
//stick the msg layer before the first child of the story layer
//this is above where the story-content layer exists
targetLayer.insertBefore(msgLayer, targetLayer.firstChild);
//create some text for our msg layer
//create a textarea layer
var textarea = document.createElement(’textarea’);
textarea.setAttribute(’id’, ‘FCKeditor1′);
textarea.setAttribute(’name’, ‘FCKeditor1′);
//create a submit button
var someSubmitButton = document.createElement(’button’);
someSubmitButton.setAttribute(’id’, ’submitAjaxForm’);
someSubmitButton.setAttribute(’name’, ’submitAjaxForm’);
someSubmitButton.setAttribute(’value’, “Submit”);
//someSubmitButton.addEventListener(’click’, submitFCKform, false);
if(typeof someSubmitButton.addEventListener != ‘undefined’) {
someSubmitButton.addEventListener(’click’, submitFCKform, false);
}
else if(typeof someSubmitButton.addEventListener != ‘undefined’) {
someSubmitButton.addEventListener(’click’, submitFCKform, false);
}
else if(typeof someSubmitButton.attachEvent != ‘undefined’) {
someSubmitButton.attachEvent(’onclick’, submitFCKform);
}
//create a cancel button
var someCancelButton = document.createElement(’button’);
someCancelButton.setAttribute(’id’, ‘cancelAjaxForm’);
someCancelButton.setAttribute(’name’, ‘cancelAjaxForm’);
someCancelButton.setAttribute(’value’, “Cancel”);
if(typeof someCancelButton.addEventListener != ‘undefined’) {
someCancelButton.addEventListener(’click’, cancelFCKform, false);
}
else if(typeof someCancelButton.addEventListener != ‘undefined’) {
someCancelButton.addEventListener(’click’, cancelFCKform, false);
}
else if(typeof someCancelButton.attachEvent != ‘undefined’) {
someCancelButton.attachEvent(’onclick’, cancelFCKform);
}
//where we gonna put the textarea and the buttons?
targetLayer.appendChild(textarea);
//targetLayer.parentNode.appendChild(someSubmitButton);
//targetLayer.parentNode.appendChild(someCancelButton);
targetLayer.appendChild(someSubmitButton);
targetLayer.appendChild(someCancelButton);
//set the value of the textarea
textarea.value = strText;
//create fckeditor
oFCKeditor = new FCKeditor(’FCKeditor1′);
oFCKeditor.BasePath = ‘/FCKeditor/’ ;
oFCKeditor.ReplaceTextarea();
//now hide the original text layer
hideContentLayer(’story-content’);
//hide the loading msg info
hideContentLayer(’msgLayer’);
} else if (passType == ‘replaceContent’) {
replaceContentInLayer(’story’, strText);
} else {
alert(’Error: No action has been specified for the call to the widget’);
}
}
};
http_request.open(’POST’, url, true);
http_request.setRequestHeader(”Content-type”, “application/x-www-form-urlencoded”);
http_request.setRequestHeader(”Content-length”, parameters.length);
http_request.setRequestHeader(”Connection”, “close”);
http_request.send(parameters);
}
function submitFCKform() {
var txtContent = showContentValue();
if(txtContent.indexOf(’<div id=”story-content”>’) != 0) {
txtContent = ‘<div id=”story-content”>’+ txtContent +’</div>’;
}
txtContent = escape(txtContent);
replaceContentInLayer (’story’, ‘Saving…’);
makeRequest(’http://widget.php’, ‘api=update&itemID=’+itemID+’&FCKeditor1=’+txtContent, ‘replaceContent’);
showContentLayer(’toolbar’);
}
function cancelFCKform() {
//return to the default state
window.location = ‘widget.php?itemID=’+itemID+’&templateID=people’;
}










15 February, 2007 at 10:30 am
Hi,
this is nice. I have been looking to use fckeditor for inplace editing. Do you have any link to a demo that demonstrate it in action. Please let me know if put up a demo.
Regards
21 February, 2007 at 10:02 am
@Ritesh: Will put up a live demo when I return from holidays in a few weeks.
2 March, 2007 at 12:04 am
Hi,
this is excellent.im using fckeditor2.4.i want to know more about ,how we can use ajax features with fckeditor.can u help me?????
2 March, 2007 at 1:54 pm
I’m only just learning about the FCKeditor ajax features now, but will be happy to blog my findings as I go along
16 March, 2007 at 10:39 am
hey,
good work on the writeups, i found your first article first and was quite disappointed when i realized there wasnt a soultion, but then read on in the comments to eventually find this post!
it helped heaps. thanks again.
21 March, 2007 at 2:38 am
Do you know where I can find a complete documentation of all the FCK functions? I’m reading a code making intensive use of their “API” and nothing’s documented, I’m really in PAIN here
Stuff like RegisterCommand, CreateElement, etc.
Thanks.
21 March, 2007 at 8:33 am
I don’t think the people who write FCKeditor write very good documentation. There is the wiki, but it’s not much better than the documentation on the website itself.
I’ve not yet found any other forums with a decent knowledge of the API or its other features together with worked examples.
If I find one, though, I will definitely let everyone know!
18 April, 2007 at 6:21 pm
Thank You
4 May, 2007 at 3:17 am
Hey,
I just figured out how do Edit-In-Place using the FCKEditor, and I did not have to use prototype or any of the other libraries.
Using FireFox, I was able to load up the FCKEditor like you normally would, and then I looked at the DOM to see what changes were made. I am now replicating those changes by using javascript/ajax functionality.
It was a little tricky to figure out, but the code is not that complex. You can see a basic working example at:
http://www.chriscrawford.com/iis/fckajax/fckajax.htm
Feel free to download the source and the .js page. The one page you will not be able to download is the FCKAjax2.asp page, but all it is doing is a response.write and sending the form data back to the calling page.
This works great in IE6, IE7, and FireFox. I’ve not tested it in any other browsers.
Enjoy,
Chris Crawford
8 May, 2007 at 10:39 am
Good work Chris!
My work suggests that is also the case - you don’t need Prototype to do AJAX and FCKeditor.
21 June, 2007 at 8:27 am
Hi,
i am using FCKEditor with ajax in my site, but using it in a very simple way. i was able to post the text and after the ajax responce, i want to clear the content in the FCK Editor, like the way how it works when the new page is clicked. can you please tell me the function to clear the text in FCKEditor.
Thanks ! …
Jaikar
1 August, 2007 at 8:45 pm
Just fire the button code!
function FCKeditor_OnComplete( editorInstance )
{
FCKeditorAPI.GetInstance(’FCKeditor1′).Commands.GetCommand(’NewPage’).Execute();
}
MD
1 August, 2007 at 8:47 pm
Sorry - a copy/paste
You just need this part:
FCKeditorAPI.GetInstance(’FCKeditor1′).Commands.GetCommand(’NewPage’).Execute();
I use the same code to fire ‘FitWindow’ after the editor completes loading - that is why it was there…
MD
27 August, 2007 at 1:36 pm
[...] I’ve been asked a few times about presenting an example of FCKEditor and edit-in-place. I’ve even posted several articles on my thought process making FCKEditor work as an edit-in-place editor (at first here, then here and here). So, I figured I’d let the world see all this Topic Maps Engine work — I currently call it MH.TME (I was going to call it “Bob”, but certain friends didn’t think that name would catch on). [...]
17 September, 2007 at 10:04 pm
Hi
Very interesting information! Thanks!
Bye
25 October, 2007 at 8:41 pm
[...] textareanya jadi kosong, aq dapet dari sini : ni source biar textarea nya jadi kosong: [...]
10 November, 2007 at 8:56 pm
Hi Matt,
Very interesting article. I have been searching for this holy grail for a little while now and I’m really surprised it doesn’t seem to exist online anywhere… It sounds like a bit of a mission though, well done for sorting it out. However I was a little disapointed after all that build up to get to a ‘Sorry, but you are looking for something that isn’t here.’ message when I tried to view your example. Let me know if you get it online again somewhere and I’ll check it out.
Tom
4 January, 2008 at 10:09 pm
Found article very helpful. There is really no one out there with a solution. Now I’m a bit stuck. I’m trying to use an Ajax post back to submit the content inside the text editor. So that the entire form does not reload. But everything besides the editor content goes through. Do you know of a work around for this issue?…much appreciated…jarrett
25 March, 2008 at 12:32 am
Thanks for such a great tutorial. Was searching for that a few days.!
23 May, 2008 at 1:17 pm
Girls Hot…
If you’re a graphics nerd you might like to try playing around with the source code for Really
…