L'uso di questo sito
autorizza anche l'uso dei cookie
necessari al suo funzionamento.
(Altre informazioni)

Friday, July 31, 2015

Efficient CKeditor tabbing with bootstrap and jquery

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
This is OK, since most of them are already out of view when the page starts up - that's because we're tabbing, remember?

This also means that the admirably short:

$('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:

to the mix - after all, bootstrap needs jquery, and will everybody wanting to do browser sniffing  on their own please raise their hands? I thought so. Which of course also implies we will want a last ingredient:

anything above IE8 will do, but surely you have that - you are not still supporting IE6 and Netscape 4.7, are you? Or, God forbid, IE7.

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.

  1. $(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.
  2.  $('.editor'): seize the editor-classed elements . These are the anchors that compose the links on the tabs
  3. .on('shown.bs.tab', function (e) {:  attach a callback to the "expose" event . the funky event name took me forever to locate
  4. $($(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?
  5. target.find('textarea').each(function () {: iterate on the descended textares (better have one per container, or there goes  your efficiencies
  6. 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

No comments: