Shortcodes are very handy for WordPress developers. They are flexible and powerful. They can trigger complex functionality or provide fine tuned control when creating content. The biggest problem with shortcodes is that they are too much like code.

It is easier for most writers to highlight text and click a button than to type out any kind of code in their content. They are not used to thinking like a coder. This is why I don’t believe markdown to be the greatest thing since sliced bread. Don’t misunderstand, I love markdown and every post I write uses it. However for someone that is not familiar with HTML and code, markdown is a lot more difficult to wrap their minds around.

WordPress 3.9 included some great new improvements in media workflows that actually previews what content will look like in the visual editor rather than just using placeholders. Much of this functionality is shortcode driven, but the pseudo code of the shortcodes is replaced in the editor allowing writers to interact more naturally with their content.

Shortcodes in Javascript

The visual editor in WordPress is TinyMCE, which is then customized to make it more WordPress specific. WordPress uses Javascript based TinyMCE plugins for extending the editor’s functionality, including giving these shortcode powered features their rich interactions.

Parsing shortcodes and writing regular expressions is a pain, though. WordPress PHP functions abstract that from developers nicely, but traditionally there was no help in Javascript land.

Good news!

As of WordPress 3.5 there are a couple of very handy WordPress Javascript helpers: wp.shortcode and wp.html.

There was not a lot of fanfare around the Javascript helpers, but they have slowly gained traction and are used heavily to accomplish the interactive feats performed with media in 3.9. And the best part is, they are included in WordPress core so you can make use of them in your code as well.

wp.shortcode( options )

File Location: //wp-includes/js/shortcode.js:199-282
Registered Slug: shortcode

options
Type: Object
A set of parameters for this shortcode.

  • tag: The shortcode tag itself
  • attrs: Attributes in an object, accepts both numeric and named attributes
  • type: Whether or not this is a self contained shortcode or one with an opening and closing tag.
  • content: The content inside the opening and closing tags.

Additional Notes

wp.shortcode is a constructor function for making wp.shortcode Javascript objects. Once constructed the shortcode object has three main methods. shortcode.get( attribute ) and shortcode.set( attribute ) for dealing with attributes, and shortcode.string() which returns a string representation the shortcode object. In addition to these methods, it has tag, type, and content properties you can use and reference.

Other methods

wp.shortcode is a handy constructor, but it doesn’t do the string parsing I mentioned above. Instead it provides an object definition so we have an API of sorts for dealing with shortcodes in Javascript. We’re not done though, because hidden behind the constructor function are some additional helpers to parse strings and make shortcode objects for us.

Nice.

Let’s look at the two most useful helpers. There are some others, too, but they are mostly utility functions used by the two helpers we’ll define here.

wp.shortcode.next( tag, text, index )

File Location: //wp-includes/js/shortcode.js:15-50
Registered Slug: shortcode

tag
Type: String
The type of shortcode tag to find in text

text
Type: String
The text containing a shortcode of the type tag

index
Type: Integer
The character index at which to start searching for the next match of the tag shortcode.

Return
Type: Object
A container object with a few helpful properties:

  • index: The index where the match occurred so you can easily match the next shortcode using the matched index plus 1.
  • content The content actually matched, shortcode tags and all.
  • shortcode The wp.shortcode object created from the matched shortcode string.

Additional Notes

This method is useful when you need to grab shortcodes out of a string for processing elsewhere or to grab and keep a reference to them. This method does not change the input string, only parses it and sends back shortcode object representations of the shortcodes contained within the string.

wp.shortcode.replace( tag, text, callback )

File Location: //wp-includes/js/shortcode.js:61-76
Registered Slug: shortcode

tag
Type: String
The type of shortcode tag to find in text

text
Type: String
The text containing shortcodes of the type tag

callback
Type: Function
The function that will process the matched shortcode object and return a new string to replace it with. It is passed the shortcode object as a parameter.

Return
Type: String
A string with all shortcodes replaced per the callback function.

Additional Notes

This method allows you to process shortcodes in a string replacing them through use of a callback function. This is an invaluable function for doing things like turning all the shortcodes in a TinyMCE instance into rich previews.

But wait, there’s more!

There is one final helper in the shortcode file I want to mention and define before going through an example. This is not a shortcode helper, but rather a companion to the shortcode object.

Enter wp.html.string.

wp.html.string provides an API for defining HTML tags as Javascript objects. It is very similar to how the wp.shortcode object describes a shortcode tag.

wp.html.string( options )

File Location: //wp-includes/js/shortcode.js:322-354
Registered Slug: shortcode

options
Type: Object
A set of parameters for this html tag.

  • tag: The type of tag to create
  • attrs: An object of name-value paired attributes
  • single: Whether or not this is a self closing tag (<input /> vs. <p></p>).
  • content: Either string content for inside the opening and closing tags or an object representing a nested wp.html object definition.

Additional Notes

The power of wp.html.string is that it will recursively call itself for nested elements. That way you can define a fairly complex set of markup as a Javascript object and easily convert it into an html string.

This helper is marked in comments as experimental, but it has been in core and used since WordPress 3.5 so it is unlikely to disappear. It’s a pretty important part of the media modal system at the time of this writing.

wp.html examples

Simplistic “p” tag.

wp.html.string({
  tag: "p",
  content: "Hello World"
});
// Return: <p>Hello World</p>

A “p” tag with attributes

wp.html.string({
  tag: "p",
  content: "Hello World",
  attrs: {
    id: "paragraph-1",
    class: "paragraph example"  
  }
});
// Return: <p id="paragraph-1" class="paragraph example">Hello World</p>

Example “pre” tag with nested “code” tag.

wp.html.string({
  tag: "pre",
  content: {
    tag: "code",
    content: "$foo = $bar;",
    attrs: {
      class: "lang-php"
    }
  }
});
// Return: <pre><code>$foo = $bar;</code></pre>

Example self closing “input” tag

wp.html.string({
  tag: "input",
  single: true,
  attrs: {
    type: "text",
    name: "my-input",
    id: "my-input",
    class: "widefat"
  }
});
// Return: <input type="text" name="my-input" id="my-input" class="widefat" />

Example

TinyMCE plugins are pretty complex, so rather than fight with them to demonstrate how these helpers work, let’s continue to use the comments example we’ve been using through my wp.ajax and wp.template articles.

I write a lot about code, and it might be nice to have an easier way to trigger code highlighting in comments. First, let’s filter the tags allowed in comments to support the markup required to trigger the prism highlighter I’ve been using.

//PHP
function lw_comment_codeblocks( $allowed_tags, $context ) {
  if ( "pre_comment_content" === $context ) {
    $allowed_tags["code"] = array( "class" => true );
    $allowed_tags["pre"] = array();
  }
  return $allowed_tags;
}
add_filter( "wp_kses_allowed_html", "lw_comment_codeblocks", 10, 2 );

Now we can use all of the HTML tags needed for code highlighting in comments. Let’s also drop a note to the user and let them know they can use a shortcode to highlight code blocks.

//PHP
function lw_comment_codeblocks_notes( $defaults ){
  $note  = "<p class="form-allowed-tags">";
  $note .= "You may also print out highlighted code blocks with <code>[ code language=""]{{code goes here}}[ /code]</code>. ";
  $note .= "Supported languages are "markup" (HTML), "css", "javascript", and "php"";
  $note .= "</p>";
  $defaults["comment_notes_after"] .= $note;
  return $defaults;
}
add_filter( "comment_form_defaults", "lw_comment_codeblocks_notes" );

Nothing too fancy here, just an extra note below the comment form block.

One last bit of PHP and then we’ll move into the Javascript. We need to include the shortcode parsing helper file before our script. We’ll add it to the list of dependencies as we enqueue our Javascript file.

//PHP
  wp_enqueue_script(
    "lw-comments",
    get_stylesheet_directory_uri() . "/assets/js/comments.js",
    array( "wp-util", "shortcode" ),
    "1.0.0",
    true //makes sure this is enqueued in the footer
  );

Alright, all the pieces are in place. let’s take a look at parsing some shortcodes and turning them into code blocks. We’re interested in outright replacing the shortcodes with HTML, so we make use of the wp.shortcode.replace() helper. To trigger it all we need to do is call it when we grab the content from the text box.

//Javascript
formData.comment = wp.shortcode.replace( "code", formData.comment, processCodeBlocks );

That’s it, that line will process formData.comment replacing all instances of the code shortcode with the return of the processCodeBlocks() callback function. We don’t have to worry about parsing the shortcodes ourself, the .replace helper does all of that for us and passes the resulting wp.shortcode object to the callback function.

Let’s peak inside the callback now.

function processCodeBlocks( shortcode ) {
  // If this codeblock has no code, strip it out.
  if ( ! shortcode.content ) {
    return " ";
  }
  // Set up the codeblock with the parsed shortcode attrs.
   return wp.html.string({
    tag: "pre",
    content: {
      tag: "code",
      attrs: {
        class: "language-" + shortcode.get( "language" )
      },
      content: _.escape( shortcode.content.trim() )
    }
  });
}

This is an amazingly simple function for what it is accomplishing.

We are passed a single argument, the shortcode object as defined by wp.shortcode. If we don’t have any content there is nothing to highlight, so we return an empty string. Then we immediately return a new value, an HTML string, generated by using wp.html.string(). This is similar to the <pre><code> example in the wp.html definition above. However in this case we’re mapping the wp.shortcode values to our markup objects.

One final thing to note, code blocks require escaped HTML characters otherwise things like <tag> will actually be processed as HTML. To this end we use the Underscore library to turn characters like < into &lt; which tells the browser to render the code rather than trying to parse it.

One line of code to parse all of the ‘code’ shortcodes, one callback to send back nicely formatted nested markup. Win!

Check out the demo of the finished product, it works well.

When to use this

In this case, we could have processed the shortcode back on the server easily enough since we are already sending the data back there via ajax. However, this allows us to only target the ‘code’ shortcode without worrying about other shortcodes being available in comments. Still, as I mentioned at the outset, the biggest use case for this is inside a TinyMCE plugin. You can make your shortcodes more interactive and usable for writers who use the WordPress visual editor.

WordPress 3.9 gave us a leg up here as well by fleshing out another helper for creating TinyMCE plugins as Backbone views. Take a look at the Example Thing by Scott Taylor for an example of how you can create TinyMCE views, and feel good when you understand the calls to the wp.shortcode.next helper.

The only additional caution I’ll give is this: with great power comes great responsibility. Yes, Uncle Ben was right. Shortcodes are an easy way to make something dynamic, but I’ve found developers tend to reach for them a little too often because they are easy.

When shortcodes get used too often, in the wrong context, and for the wrong reasons, they get a reputation for being bad or at least confusing. Let’s not dirty this tool by using it inappropriately. Take a step back and ask yourself if a shortcode is the right tool for the job, and if you can come up with a reason it might not be, use something else.

Now, go make something awesome!