Pixelastic

You can cut our wings but we will always remember what it was like to fly.

Posts tagged with "Javascript"

Tabindexing and inserting tabs with tinyMCE

You may have noticed that you can't press tab to jump from field to field in a form that uses tinyMCE. There is a plugin named tabfocus in the core that is here to allow just that.

But it does not seem to work correctly if you already have tabindex values defined on your inputs. So I decided to code my own.

I choose the easiest way, letting the browser do most of the job. I only copied the textarea tabindex value to the create iframe and it did the trick.

I created a tinyMCE for that, here the code of the init() method. By the way, I'm using the jQuery version of tinyMCE.

init : function(editor, url) {
// We set a tabindex value to the iframe instead of the initial textarea
editor.onInit.add(function() {
var editorId = editor.editorId;
var textarea = $('#'+editorId);
$('#'+editorId+'_ifr').attr('tabindex', textarea.attr('tabindex'));
textarea.attr('tabindex', null);
});
}

What it does is grabbing the initial tabindex value of your textarea and setting it to the tinyMCE iframe. You have to wrap this in onInit.add because at the time the plugin init method is called, the iframe is not yet created. I also removed the tabindex value from the original textarea, two elements aren't supposed to have the same tabindex value.

This method does not work in Chrome. Chrome always add a tab character when you press the tab key in a tinyMCE editor, it does not jump to the next tabindexed element. Is that a good behavior ? I don't know, it surely is useful to be able to insert tab characters, but it also is useful to tab through the whole form.

Listening to the tab key

Anyway, I decided to hook on the keyDown event and listen to the tab key being pressed. This way I could manually jump focus to the next field when tab is pressed, or insert a tab character when Shift+Tab is pressed (for example).

I used the tinyMCE event helper methods and wrote this (just add it right after the previous editor.onInit.add code) :

// We hook on the tab key. One press will jump to the next focusable field. Maj+tab will insert a tab
editor.onKeyDown.add(function(editor, event) {
// We only listen for the tab key
if (event.keyCode!=9) return;
       
// Shift + tab will insert a tab
if (event.shiftKey) {
editor.execCommand('mceInsertContent', false, "\t");
tinymce.dom.Event.cancel(event);
return;
}
// Just pressing tab will jump to the next element
var tabindex = $('#'+editor.editorId+'_ifr').attr('tabindex');
// We get all the tabindexed elements of the page
var inputs = [];
$(':input[tabindex]').each(function() {
inputs[$(this).attr('tabindex')] = this;
});
// We find the next after our element and focus it
for (var position in inputs) {
if (position<=tabindex) continue;
inputs[position].focus();
break;
}

tinymce.dom.Event.cancel(event);
return;
});

First, we discard any key press that is not a tab key.

Then we check if the Shift key is pressed, and if so we add a tab character and stop there.

The biggest part is jumping to the next field. I can't revert to the browser default for that because every browser default behavior is different and I surely don't want to do some browser sniffing.

I first get the list of all the input fields that have a tabindex value (your fields should have one), then I sort them in tabindex order and then loop through the list and stop when I've found one with a bigger tabindex that the actual field. I focus this one and stop the loop.

One final word

I've tested under Firefox 3.6, Chrome, Safari and Opera. Haven't tested IE yet because I still have a lot of other scripts to debug in IE first.

As said earlier, maybe you could skip the whole tabindex listing if you intelligently revert to browser default for the browser that will jump to the next field but I have no idea how to test for that.

Give your variables meaningful names

I just realized that the fullscreen plugin I was using with tinyMCE (v3.3.5) was throwing an error in my Firebug panel everytime I closed it.

As I wrote some tinyMCE plugins myself I thought I may have done something that was causing this. So I opened up the javascript file and checked for the error line :

var win, de = DOM.doc.documentElement;
if (ed.getParam('fullscreen_is_enabled')) {
if (ed.getParam('fullscreen_new_window'))
closeFullscreen(); // Call to close in new window
else {
DOM.win.setTimeout(function() {
tinymce.dom.Event.remove(DOM.win, 'resize', t.resizeFunc);
                tinyMCE.get(ed.getParam('fullscreen_editor_id')).setContent(ed.getContent({format : 'raw'}), {format : 'raw'});
                tinyMCE.remove(ed);
                DOM.remove('mce_fullscreen_container');
                ed.style.overflow = ed.getParam('fullscreen_html_overflow');
                DOM.setStyle(DOM.doc.body, 'overflow', ed.getParam('fullscreen_overflow'));
                DOM.win.scrollTo(ed.getParam('fullscreen_scrollx'), ed.getParam('fullscreen_scrolly'));
                tinyMCE.settings = tinyMCE.oldSettings; // Restore old settings
}, 10);
}
        return;
}

If you're not familiar with the complex tinyMCE syntax this may seems a little... well... complex.  I'll focus on the error line but I wanted to paste the whole code block so you can see my point.

The error line is this one :

ed.style.overflow = ed.getParam('fullscreen_html_overflow');

Of course it will throw an error, we try to access a property of an element we just removed from the DOM (two lines before : tinyMCE.remove(ed);). Why would someone want to do that ?

Well in fact, this is not ed (that we should try to access, but de (short for DOM.doc.documentElement). In fact the code was correct in 3.3.2, but someone changed it around 3.3.3.

My guess is that someone had the file opened, saw this bit of code, spotted the de, and seeing a lot of references to ed all around, thought it was a typo and 'fixed' it.

Reading the tinyMCE code (whenever you want to understand how it works, or want to study plugins before creating your own) is pretty hard. There are almost no comments, and variable names are only one or two letters long.

The tinyMCE team released a survey asking user what should be improved on the tinyMCE product. I answered it, and my main point was to improve documentation, because reading the tinyMCE core to understand its inner goings is quite a chore.

Opening a new tab using Ctrl+Click on Opera

One thing that bugs me everytime I use Opera are the weird keyboard shortcuts. You open link in a new tab by using the middle mouse button (classic) or Shift+click (less classic, we are accustomed to use the Ctrl+Click, Shift+Click being used to open in a new window).

Today it was bugging me one too many times, so I decided to use the power of AutoHotKey to help fix this.

Here is the little snippet I added to my default AutoHotKey script :

;        Open page in new tab in Opera using Ctrl + Click
#ifWinActive, ahk_class OpWindow
^LButton::Send +{LButton}
#IfWinActive

It basically catch every Shift+Click in Opera and return a Ctrl+Click instead, allowing me to finally open tabs the way I'm used to.

Never use alert() for debugging

One thing I learned today is to never use alert() for debugging pruposes. When the javascript alert() function is called it blocks every other action until the OK button is pressed. It means that your javascript code that is immediatly after the alert() call is delayed until you press OK.

It also means that the thread your browser gave to the javascript engine is halted during all this time.

Imagine you are binding keyboard shortcuts on your website to specific actions. Like pressing Ctrl+S will submit the current form. Of course, Ctrl+S is already defined as a shortcut in the browser, so you will take care of preventing the default behavior in your custom function (using return false, e.preventDefault(), e.stopPropagation() or any method defined in your framework to do that)

The problem is that is you call alert() in your function, the Javascript thread will be halted before you can make the call to stop the propagation and thus, the browser will take control again and firing its default shortcut.

So my advice is that you should never use alert for debugging. Use the console.debug() method shipped in firebur or directly write to the DOM but do not use alert().

Writing a class in Javascript

I always have to refer to a manual or a webpage to remember the correct syntax for creating a new class in Javascript. Here it is :

myClass = function() {
// Here I can define private variables and methods
var myPrivateVar = "foo";
var myPrivateMethod = function() {
};

// And now I will return an object, so it is the place
// to put public variables and methods
return {
publicVariable = 'bar',
publicMethod = function() {
},
init: function() {
// And here I place the code to execute when I'm ready to
// start executing, eg. when the DOM is ready
}
};
}(); // Note that this function is executed and so the object is returned.