WordPress: Fixing shortcodes that enqueue JavaScript when used in ACF fields

There wasn’t really a shorter title for this post.

Too long? Didn’t read?

Here’s the quick bit of learning: WordPress runs the wp_enqueue_scripts action hook during the execution of wp_head and it encourages you to register scripts on this hook. SO if you do something that you want to enqueue scripts correctly, you must do it AFTER wp_head runs – which usually means after wp_header() is called in the theme.

My use case and problem

I was working for a client doing some maintenance on a theme that someone else had built. The theme used Advanced Custom Fields, and in some cases shortcodes were used inside ACF WYSIWYG fields.

One of the shortcodes used needed some styles and/or scripts to be enqueued to make the shortcode work. But this should be fine right? Executing the shortcode would enqueue the assets. No problem there.

Well, then my theme had some code that was using the ACF field’s content. It set up some values near the top of the file, grabbing values from the ACF fields using the get_field() function, before outputting the template’s HTML content. Something like this:

<?php
$title = get_field('title');
$content = get_field('content');
?>

<?php
  wp_header();
?>

<main>
  // Output the content here
</main>

But, while the shortcode was definitely running, as I was seeing it’s output, the scripts that made the shortcode work were not being enqueued.

What’s going on?

The plugin that gave me the shortcode actually worked correctly:

  • Scripts were registered correctly using wp_register_script() on the wp_enqueue_scripts action hook
  • The callback function that generated the shortcode output was running the wp_enqueue_script() function
  • The scripts were registered to be output in the footer, so enqueuing them during the shortcode output function should be fine

BUT…because the wp_enqueue_scripts action hook fires during wp_header() this code was failing.

It failed because the shortcode callback was run when I did the get_field() call on the WYSIWYG field. The get_field() call is before wp_header() and therefore before the scripts are registered. So at the point the shortcode call back runs, the script is not registered so it can not be enqueued.

Phew. Are you still with me?

The fix

The fix is simple – get the content, and therefore run the shortcodes, after wp_header() has been called so that the scripts are registered before they are enqueued:

<?php
  wp_header();
?>

<?php
$title = get_field('title');
$content = get_field('content');
?>

<main>
  // Output the content here
</main>