The Problem
Ok, so you are back in web coding and want to add the nifty CKeditor to your site, so some admin will be edit to add, say descriptions or notes in crisp, nicely formatted HTML (or maybe crufty, luridly formatted HTML - it really makes no difference).
More than that, you need to do that in a form, want to keep your elements as textareas, and you need more than one editor - in fact, you want one per language on a multilanguage site. Also you would like to keep not in use editors tucked away from view.
So you gather a tabbing widget, put textareas where they belong, deploy your editors in their place (no, not going into details here, but they will be shown momentarily).
And behold! Everything is painfully slow, even with a paltry 4 editors on your page (four europeans languages, is that asking too much?). Ouch.
The idea. The recipe
Of course, what's needed is:
- lazy creation of the editors
$('textarea').ckeditor()
provided by CKeditor's jquery plugin won't cut it, as it will do everything at once, and your page will pop back up to consciousness only after it's finished - several seconds later.
This is not enough, however. CKeditor's 'classic' view, sitting in its own iframe, is quite hefty in itself - and even with lazy instantiation, mapping the GUI takes forever. So I'll also need:
That's lingo meaning that CKeditor will be attached to an element until it is clicked, when it springs into existence. That turns out to be much lighter.
CKeditor will inline a lot of elements which set the HTML5 attribute contenteditable to true - DIVs, notably . I am an old timer, however, and I want this to happen in a form's textarea, so I can enjoy the automatic variable population that happens when my little lusers hit the "Submit" button. (Aside: can this also be made to happen with DIVs, I wonder? Comment if you know it.)
Critically, CKeditor allows to do inline on textareas since V4.2. And this is another reason why the jquery plugin for CKeditor cannot be used - its .ckeditor() method appears to force 'classic' view on textareas (a feature request that appears to be promising on the CK's blog gave me no joy whatsoever).
What else? Oh, right, I'm gonna need
unless, that is, one wants to do the click-hide-show wiring and dance by hand. (Which I did, on first iteration. Darn me, for not knowing my manuals - but I'm old, did I already mention it? So never mind me). Not to worry, bootstrap (which makes everything oh, so 2.0 not to mention, responsive) has a perfectly fine tabbing widget camped on an appropriatedly marked up UL section. Be sure to read up on it carefully, or you will be liable to miss (as I did) the critical part on javascript plumbing that makes the tabbing automatic.
And because it's bootstrap, and 2015 to boot, I'm going to spruce it all up by adding:
- jquery and its event wiring
- a modern, html5 compliant browser
And voilà. Sounds easy? It's not.
The reasearch
And so, google and stackoverflow to the rescue, you start looking for the solution's pieces, and put them together , and it takes a day or more. Unless of course you stumbled on this page, where I laid it all down for you, my trusty reader. (It took more than a day, thank you very much - show your gratitude by sending money - lots of it - in a stamped envelope. Yes, I'm that greedy.)The execution
Markup: the header
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>CKEditor ALF</title> <link href="/core/jquery-ui-1.11.4/jquery-ui.min.css" rel="stylesheet" type="text/css"> <script type="text/javascript" src="/core/jquery/jquery-1.11.1.min.js"></script> <script type="text/javascript" src="/core/jquery-ui-1.11.4/jquery-ui.min.js"></script> <!-- Bootstrap --> <script type="text/javascript" src="/core/bootstrap/js/bootstrap.min.js"></script> <!-- Bootstrap core CSS --> <link href="/core/bootstrap/css/bootstrap.min.css" rel="stylesheet"> <!-- Optional Bootstrap Theme --> <link data-href="/core/bootstrap/css/bootstrap-theme.min.css" rel="stylesheet" id="bs-theme-stylesheet"> <script src="../ckeditor.js"></script><!-- the editor --> <script type="text/javascript" src="../adapters/jquery.js"></script> <script src="tab4.js"></script> </head>
Here I'm pulling all of my ingredients - plus my own code - on board, html5-style.
Not much to see, here - we're loading the CKeditor jquery plugin for the heck of it, since we are not going to use it.
Markup: the body
<body id="main"> <div style="width:100%; height:400px; border: 3px solid red;"> <!--First tabset --> <ul class="nav nav-tabs"> <li><a data-target="#oita" data-toggle="tab" class="editor">Italiano</a></li> <li><a data-target="#oeng" data-toggle="tab" class="editor">English</a></li> <li><a data-target="#oesp" data-toggle="tab" class="editor">Spagnolo</a></li> <li><a data-target="#odeu" data-toggle="tab" class="editor">Deutsch</a></li> </ul> <span style="border:3px solid #0f0;">Seleziona il testo per editare</span> <div class="tab-content" style="box-shadow: inset 0 0 10px #999999;border:3px solid yellow;" > <div id="oita" class="tab-pane" style="height:300px"><textarea id="ita" name="ita">Ciao!</textarea></div> <div id="oeng" class="tab-pane" style="height:300px"><textarea id="eng" name="eng"><i>Hello!</i></textarea></div> <div id="oesp" class="tab-pane" style="height:300px"><textarea id="esp" name="esp"><b>Hola!</b></textarea></div> <div id="odeu" class="tab-pane" style="height:300px"><textarea id="deu" name="deu"><b>Guten tag!</b></textarea></div> </div> </div> </body> </html>
The meat and potato of the UI, these is textbook stile bootstrap JS tabbing, with all its data-xxx thingies wired up just right. I am also adding an 'editor' class to seize upon later with a jquery selector, and a few containers and visual cues that will tell the user that yes - click the text and editing will happen.
Javascript: the wiring
It is now time to sprinkle our jquery magic ppowder, tucked away in tab4.js (yes, the fourth iteration):$(document).ready( function() { CKEDITOR.config.height = 150; CKEDITOR.config.width = 'auto'; $('.editor').on('shown.bs.tab', function (e) { var target = $($(this).data("target")); target.find('textarea').each(function () { if(!CKEDITOR.instances[this.id]) { CKEDITOR.inline(this); } }); return 1; }); });
A bit of explanation is in order, here.
- $(document).ready(): wrap everyhting inside this. No need to worry, I can register as many ready- handlers as you like and they will fire on a first-defined, first-served basis.
- $('.editor'): seize the editor-classed elements . These are the anchors that compose the links on the tabs
- .on('shown.bs.tab', function (e) {: attach a callback to the "expose" event . the funky event name took me forever to locate
- $($(this).data("target")): select the target that will be exposed (it is one of the divs, see markup). Is this syntax bit ugly, or what?
- target.find('textarea').each(function () {: iterate on the descended textares (better have one per container, or there goes your efficiencies
- if(!CKEDITOR.instances[this.id]) { CKEDITOR.inline(this): if it does not already contain an editor, create it.
Six easy pieces.
Catharsis
And there you have it. Efficient CKeditor tabbing