2012/05/01

Recipe: Live preview of CKEditor

Today we're gonna prepare a live preview of the contents edited with CKEditor.

A live preview with a WYSIWYG editor is not so important compared to other text-based editors, but anyways the behavior inside the editor might not match exactly the final output, so in those cases we can provide this option for those that want it.

Ingredients:

1 Instance of CKEditor

1 Preview container (for example a Div)

1 Copy of the onChange plugin

In case that you want to use this method with more than one instances, then you'll need as many preview containers as CKEditor instances you want to use.

Method:

1. Create the CKEditor instance in your page using any method that you like, and add the OnChange plugin to the list of extra plugins:

For example:

<textarea name='contents' id='contents'>This is an editor</textarea>
<script type='text/javascript'>
var config = { extraPlugins: 'onchange'};
CKEDITOR.replace('contents', config);
</script>

2. Now add add a little extra javascript to listen for changes in the editor instance and mirror those changes in the preview div, (based on this StackOverflow question)

CKEDITOR.on('instanceCreated', function (e) {
    e.editor.on('change', function (ev) {
        document.getElementById( ev.editor.name + '_preview').innerHTML = ev.editor.getData();
    });
});

That code will take care of any CKEditor instance and automatically copy its contents into the container with an id like the editor instance with an ending "_preview".

3. The only missing part is to initialize the preview before the user starts typing:

document.getElementById( e.editor.name + '_preview').innerHTML = e.editor.getData();

All the code together

<textarea id="contents" name="contents">This is an editor</textarea></p>
<div id="contents_preview"></div>

<script type='text/javascript'>

CKEDITOR.on('instanceCreated', function (e) {
    document.getElementById( e.editor.name + '_preview').innerHTML = e.editor.getData();
    e.editor.on('change', function (ev) {
        document.getElementById( ev.editor.name + '_preview').innerHTML = ev.editor.getData();
    });
});
var config = { extraPlugins: 'onchange'};
CKEDITOR.replace('contents', config);
</script>

This code in action:

 

 

 

2012/04/23

Hide dialog fields in CKEditor

Sometimes removing an element is not the right solution in a CKEditor dialog: the code might depend on it, or the layout will break too much if the elements are changed without taking care to adjust more things in the dialog, so the right choice at that point is to hide the element.

Of course, the CKEditor API allows such manipulations, but it's boring writing the same code "check if it's the dialog that we want, then get the tab, the field and hide it" ...

So I've added such option to the Configuration Helper plugin as version 1.2:

config.hideDialogFields

This entry uses the same sintax that the 'removeDialogFields' option. The difference is that some fields can't be removed easily as other parts of the dialog might not be ready and might try to always use it, generating a javascript error. In other cases the layout might be broken if the field is removed instead of hidden.
In those cases it's possible to hide the fields using this entry, and the preview in the image dialog is an example of such a field.

config.hideDialogFields="image:info:htmlPreview";

2012/04/16

Placeholder text in CKEditor

This weekend I worked on a little bunch of code to support the HTML5 "placeholder" attribute in CKEditor.
The idea is simple: show a little text instead of empty content when the editor loads for the first time, and make it disappear automatically when the content gets focus.
There are two main problems to do this correctly: find out when the editor is focused or blurred and avoid the placeholder text when the content is read by other scripts.
In theory to get the focus status it should be enough to handle the 'focus' and 'blur' events of the editor instance, but one problem is that when a dialog opens the content fires a blur, but in in this situation we don't want to insert again the placeholder text, so the event listener checks if a dialog has been opened before inserting the text.
Passing that check we must verify if the content is "empty" so we get the raw data of the editor and test it again "empty" strings like "<br>" "<p>&nbsp;</p>" and the like that can be generated by different browsers.
As I didn't want to handle yet another plugin I've included it in the configuration Helper plugin and released as version 1.1. Also, the correct name for this plugin is already being used by a plugin that ships with the default CKEditor install but that it's totally unrelated to this feature that plugin resembles more the mail merge fields from MS Word, but at the time that plugin was created I don't think that HTML5 existed.

Demo:

You can set the focus in and out of the editor, type and delete, etc... when the editor isn't focused and it's empty it will show the text that I've specified for the textarea placeholder: "Type here..." While in Source mode it tries to leave that task to the browser if it already supports this feature.


2012/04/07

Bug in the widget code of Twitter

Description

The profile widget from Twitter (https://twitter.com/about/resources/widgets/widget_profile) has a bug if you try to load and execute the code after the page has been loaded in IE.
In this case, the widget colors aren't shown as the stylesheets aren't appended to the head

To test it, click the button below and compare IE vs any other browser. The culprit is this code that doesn't take into account executing the code after the document has been loaded (http://twitter.com/javascripts/widgets/widget.js)

        // oh IE we love you.
        // this is needed because you can't modify document body when page is loading
        if (!browser.ie || isLoaded) {
          append();
        }
        else {
          window.attachEvent('onload', function() {
            isLoaded = true;
            append();
          });
        }
Fix: change isLoaded to (document.readyState == "complete")
      twttr.css = function(rules) {
        var styleElement = document.createElement('style');
        styleElement.type = 'text/css';
        if (browser.ie) {
          styleElement.styleSheet.cssText = rules;
        }
        else {
          var frag = document.createDocumentFragment();
          frag.appendChild(document.createTextNode(rules));
          styleElement.appendChild(frag);
        }
        function append() {
          document.getElementsByTagName('head')[0].appendChild(styleElement);
        }

        // oh IE we love you.
        // this is needed because you can't modify document body when page is loading
        if (!browser.ie || document.contentReady == 'complete') {
          append();
        }
        else {
          window.attachEvent('onload', append);
        }
      };
(and the anonymous function might not be no longer needed)

2012/02/26

Configuration helper for CKEditor

This plugin is just a little helper for those that aren't too used to javascript or that they just prefer to use some existing code instead of adjusting the samples to their situation.
The plugin provides two features related to the dialogs: The first one is the removal of individual fields, and the second one is setting default values different from the original ones.
There's nothing special in this plugin, You can do the very same thing following these two articles at the CKEditor documentation center:  customizing dialogs and setting default values, but with this plugin is slightly easier as you don't have to change any code , you only have to adjust the config values.
For detailed instructions read the doc included in the plugin, but the basic steps are:
Include the plugin
config.extraPlugins='confighelper';
Define the fields to remove
config.removeDialogFields='image:info:txtBorder';
And/Or define the default values for fields:
config.dialogFieldsDefaultValues =
{
    image:
        {
            advanced:
                {
                    txtGenClass : 'myClass',
                    txtGenTitle : 'Image title'
                }
        }
};

Version 1.1 includes support for the Placeholder attribute.
Version 1.2 allows to hide dialog fields.
Version 1.3 is compatible with CKEditor 4.
Version 1.4 enables the Placeholder while using inline editing.
Version 1.5 fixes compatibility with CKEditor 3.6.
Version 1.6 checks the calls to setData in the editor to update the "placeholder" status
Version 1.7 fixes initial placeholder text with latest CKEditor.
You can download the plugin here: Configuration Helper

2012/02/15

Smaller selection plugin for CKEditor

This post introduces a little plugin for CKEditor, it's basically a patch to fix a little problem with selections, but proving that it works correctly in all the situations might not be easy and anyway landing a patch takes a long time and in this case it can be applied easily as a plugin.
The problem: When the user double clicks on a word the space at the end of the word is also selected, and usually that's not what we want. For many situations it doesn't matter if the space is inside or outside the selection, but currently I'm creating a plugin for a client and due to the CSS the space is collapsed due to the css if it's inside the generated tag, so we want it out.
The plugin listens for the double click on the content and then tries to check first the boundaries of the selection that the browser has created, so it tries to select only a text node and after that if the end of the selection is a space just moves it one place back.
You can download it here: Smaller Selection 0.1
Tested with IE8, IE9, Firefox 10, Firefox 11, Chrome, Opera and it seems to work fine in my tests. If anyone finds problems, please report your browser and the html where it fails.

2012/02/09

Vendor Css prefixes

This post tries to explain my points of view as a web developer about the Vendor prefixes issue:

Introduction

Css prefixes for experimental features are fine. They are a clear signal that something is being tested but it's not a standard, they allow browser vendors to create new features and test them without having to wait for the approval from everyone else, they allow to check how they can be used and whether they create unexpected problems or not.

So yes to keep vendor prefixes alive.

But...

The problem is when those prefixes spread all around the web and people start using them all over the place. Then the browser vendor will argue that they can't change them as people are already relying on them, other vendors have to provide their own implementation (with their prefixes) to avoid being left behind but even if they provide those same features they see that people are testing only on the native browsers of iPhones and Androids, both based on Webkit.

And this is the current situation: non-webkit browsers areclaiming that the use and abuse of -webkit is so strong that they are being forced to support in their own engines some -webkit features.

My opinion about how to deal with it

First rule should be that no browser manufacturer is allowed to ship experimental features in release versions of their browsers. Those experimental features should be restricted only to the alphas and betas, when a final version is released, it should allow only the unprefixed version if that feature has gained traction and it has been "approved" or don't ship it at all.

With "approved" I don't mean that it requires a final status, just a state where other vendors agree that it can be useful, it will be (or it's already) implemented that way by them and of course there's a spec stating how does it work.

Of course this is a big problem, you'll see them screaming and kicking around before accepting this. They prefer to keep on doing their little tricks and pushing whatever it's in their heads and releasing it to the wild to give them an advantage over the competition. But the fact is that even if this is the standarized way to create propietary features, we're back to the Netscape 3 era where each browser introduced new features without caring about the other one and then that other browser had to copy and replicate the successful features of the first one.

We know that it was bad for the developers, why is that good now?

Second step:

When a browser replicates the propietary feature of another one, it should support ASAP the feature both with their own prefix as well as the unprefixed one. If the feature is not good enough or not clearly defined to support the unprefixed version then it's obvious that it doesn't make sense to support the prefix from the other browser.

That would allow web developers to start testing the prefixed version from the first vendor and if it's clear that it's good enough they can put only the unprefixed version and all the browsers will be handled at once.

Third course of action:

Evangelism on sites that teach new CSS3 features, they shouldn't generate anything with a vendor prefix, just use the standard so it can work in all the browsers.

I don't think that most of the web developers are going to write huge amounts of css by themselves, instead they will look at some generator for backgrounds, buttons, patterns and copy whatever it's there. They might not know what's a vendor prefix, they just see that they can copy that code and it works. Given that some vendors (I'm looking at you Microsoft) make it really difficult to test the new versions of their browsers, you can't expect the developers to remember to add the -ms all around because they won't notice if it works or not; they just know that they have to keep on supporting IE8 for a number of years and that it doesn't support fancy things so they won't care too much about what's next for the few ones willing to buy a new computer.

I know that almost no one will care about my opinion, but at least I hope that someone else in the whole world agrees with me that these would be good steps.