I use the WordPress HTML editor exclusively, with the Text Control plugin with no formatting, so the posts contain exactly the markup I want. The editor comes with "Quick Tag" buttons, that let you insert HTML tags with one click, and allows you to add custom buttons. So, for all my code samples, I want to have a <pre> button, I just create a javascript file (say, called quicktags.custom.js):

QTags.addButton('pre', 'pre', '<pre>', '</pre>\n');

And include it with the following PHP either in a plugin or my theme functions.php:

if(is_admin()){
	// it's an admin page. Add the custom editor buttons
	wp_register_script('customeditor', plugins_url('quicktags.custom.js', __FILE__), array('quicktags')); // this script depends on 'quicktags'
	wp_enqueue_script('customeditor');
}

And now I have a button that inserts <pre></pre> pairs. But there's much more you can do.

The documentation, as far as I can tell, is only in the unminified source code, wp-includes/js/quicktags.dev.js. The only "official" API is the QTags.addButton function, with the following parameters:

	 * @param id string required Button HTML ID
	 * @param display string required Button's value="..."
	 * @param arg1 string || function required Either a starting tag to be inserted like "<span>" or a callback that is executed when the button is clicked.
	 * @param arg2 string optional Ending tag like "</span>"
	 * @param access_key string optional Access key for the button.
	 * @param title string optional Button's title="..."
	 * @param priority int optional Number representing the desired position of the button in the toolbar. 1 - 9 = first, 11 - 19 = second, 21 - 29 = third, etc.
	 * @param instance string optional Limit the button to a specifric instance of Quicktags, add to all instances if not present.

So QTags.addButton('pre', 'pre', '<pre>', '</pre>\n'); creates a button, <input type="button" id="qt_content_pre" class="ed_button" title="" value="pre"> (the id is a predefined prefix followed by what was passed as the id parameter), that inserts <pre> before the selection and </pre> after the selection (plus a newline).

But the power is in using a function rather than a string as the arg1. They provide a function QTags.insertContent(string) that replaces the current selection with string, but you also need a simple selection-getting function:

function getSelectedText(canvas){ // "canvas" is what they call the textarea of the editor
	canvas.focus();
	if (document.selection) { // IE
		return document.selection.createRange().text;
	}else{ // standards
		return canvas.value.substring(canvas.selectionStart, canvas.selectionEnd);
	}
}

And now I can add buttons to do the inconvient HTML encoding/decoding:

QTags.addButton('toHTML', 'HTML Entities', function(el, canvas){
	QTags.insertContent(getSelectedText(canvas).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'));
}, 'Encode HTML Entities');
QTags.addButton('fromHTML', 'Decode HTML', function(el, canvas){
	QTags.insertContent(getSelectedText(canvas).replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>'));
}, 'Decode HTML Entities');

The callback function is passed two paramers, el, the element that was clicked, and canvas, the textarea of the editor.

But I want even more. My code examples use the language-javascript etc. standard, which involves a lot of typing. I could create separate buttons for '<code class="language-javascript">', '<code class="language-html">', '<code class="language-php">', ad nauseum, but that gets very cluttered. I'd rather have a pull-down menu of languages that will change the code button itself.

To do this, I need to hack a bit: the buttons are all represented by a global array, edButtons of objects that represent the data that goes into creating each button. We need the index of the code button (which is 110, by inspecting the source), so our pull-down menu will be index 111. Each object in the array has a member html, which is a function that is called with the prefix to affix to the id:

var languages = ['html','javascript','lisp','pdf','php','vb'];
edButtons[111] = { // insert right after the code button
	html: function(idPrefix){
		return '<select id="'+idPrefix+'code_language" class="language-select">'+
		'<option></option>'+ // include a blank option
		'<option>'+languages.join('</option><option>')+'</option>'+
		'</select>';
	}
}

The other members of the objects in the edButtons don't concern us now, except for the tagStart member, which is the string that is inserted as the start tag. This is what we will modify in the code button itself:

$('body').on('change', 'select.language-select', function(){
	var lang = $(this).val();
	edButtons[110].tagStart = lang ? '<code class="language-'+lang+'" >' : '<code>';
});

In reality, we'd like to make this more general without the 110 index hard-wired, but that is straightforward. And now we have a customizable code button along with our HTML encoding goodies, and editing code snippets is much easier.

You can see a sample page with my custom Prism syntax highlighting. It avoids hard-coding the index of the button by searching edButtons for the appropriate button.

10 Comments

  1. Computerking says:

    Hello really new to wordpress and coding would love to be able to add the pre button, took alook at your sample page for your editor and i look s awsome.

    Guess i am a bit of a newbie but where do i put the quicktags.custom.js also where do i add the php probably theme would be best? but how?

    PS have tried a couple plugins to get this done but they are either over kill and/or not working

  2. Danny says:

    @Computerking:
    You can add it either in your theme files or a plugin file; anywhere that you can add some PHP to execute wp_enqueue_script to load the javascript file.
    –Danny

  3. Doc says:

    Does this still owrk with WordPress 2.9.1?

    I’ve spent the last few days trying everyones plugins and tutorials usuccessfully :(

    I can get the buttons to display they just don’t do anything o.O

  4. Danny says:

    WordPress 2.9.1 was a long time ago. The buttons work for me in 3.9

  5. Doc says:

    Sorry that was a typo I was referring to 3.9.1 I have tried several of these tutorials and plugins I get the same results with them all regardless of theme.

    I want to give this one another try but will most likely have more questions as I go, please and thank you for any help along the way o.O

    The wp-includes/js/quicktags.dev.js file doesn’t exist in my installation I assume wp-includes/js/quicktags.js woudl be the same file?

  6. Danny says:

    @Doc:
    Yes, the file has been renamed wp-includes/js/quicktags.js . Are you sure your javascript is being included on the page? will a simple
    QTags.addButton('test', 'Test', function(el, canvas){
    console.log('Hello, world');
    });

    work?
    –Danny

  7. Doc says:

    It turned out to be a plugin conflict with …

    HTML Editor Syntax Highlighter
    http://wordpress.org/plugins/html-editor-syntax-highlighter/

    … I’m not sure why it conflicted, but had AddQuickTags plugin author point me in the right direction by encouraging me disable plugins. I’m going to try adding this as a plugin once I learn how to make plugins lol

    Want a plugin for qTags that allows adding functions like what you did with the code dropdown I know it won’t allow hacking the core buttons but turning the default code button off and making our own to function as described above solves the problems nicely :)

    Thanks you for such a wonderful tutorial I have learned more tahn I ever wanted to know in the first place from you :D

  8. Doc says:

    I was wondering if you could help me out making a dedicated prism button. I want to leave the code button as is and add the prism with the language code attached to it. Can that be done to avoid the need of the DOM call? When I try using what you have above it fails to modify the output code button o.O

  9. Anbu says:

    How to delete a button from the editor through javascript?

  10. Danny says:

    @Anbu:
    It’s pretty straightforward. Each button has a unique id of the form qt_content_id, so use the “inspect element” in your browser to get the id of the button you want to delete, and do, for example,

    $('#qt_content_img').remove()

    –Danny

Leave a Reply


Warning: Undefined variable $user_ID in /home/public/blog/wp-content/themes/evanescence/comments.php on line 75