Post-specific Javascript and CSS in WordPress

A couple of recent posts here —

— required some post-specific Javascript and CSS styling. It took me a little while to figure out how to achieve this, given my limited exposure to PHP in general and WordPress programming in particular. This is how I went about it; there may well be better ways.

Start writing post

At this point your draft is assigned a post-ID. This is the value at the end of the post’s URL. For example, this post has URL

https://redfrontdoor.org/blog/?p=970

and its post-ID is 970.

Write post-specific Javascript

Make a self-contained Javascript file, wrapping the logic in

jQuery(document).ready(function($) {
  // [... code goes here ...]
});

A reasonable location for this file within your WordPress installation is a ‘js‘ subdirectory of the theme directory. In my case, this is

[WP root]/wp-content/themes/donovan/js/filename.js

where I choose filename to represent the title of the post.

Write post-specific PHP functions

Using WordPress’s wp_enqueue_script and wp_enqueue_style functions, write a function for the post which enqueues the required JS or CSS. For example, the jam-jar-lid oscillator depends on jQuery, and so required:

function bn_js_for_546() {
    wp_enqueue_script(
        'jam-jar-lid-oscillator',
        get_template_directory_uri()
          . '/js/jam-jar-lid-oscillator.js',
        array('jquery'));
}

This is inserted at the end of the theme’s functions.php file. The PHP for post 925 is more involved and enqueues the ‘slider’ jQuery-UI component and its styling, the D3 library, and my own custom Javascript and CSS. (Without the styling, the Javascript correctly turns the element into a slider, but it is not rendered. Here I downloaded a copy of the CSS files into the WordPress directory, but see also this post by Ross McKay.) The result is

function bn_js_for_925() {
    wp_register_script(
        'd3',
        'https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.2/d3.min.js',
        false, null);
    wp_enqueue_script(
        'interactive-graphs',
        get_template_directory_uri()
          . '/js/interactive-graphs.js',
        array('jquery', 'jquery-ui-slider', 'd3'));
    wp_enqueue_style(
        'post-925-css',
        get_template_directory_uri()
          . '/js/second-opinion.css',
        false, null);
    wp_enqueue_style(
        'jquery-ui-theme',
        get_template_directory_uri()
          . '/css/jquery-ui-themes-1.10.4'
          . '/themes/smoothness/jquery-ui.css',
        false, null);
}

Excuse the fudge of putting the post-specific CSS into the ‘js‘ directory.

Write PHP to hook the post-specific function

A dispatching PHP function checks whether this post requires custom JS or CSS and adds the appropriate function as an ‘wp_enqueue_scripts‘ action if so. The test for requiring custom JS or CSS is whether a function named ‘bn_js_for_NNN‘ exists, where NNN is the post-ID. This is not especially elegant, but it avoids repeating myself, and this is PHP so there’s no huge incentive to strive for elegance anyway.

The code

function bn_maybe_enqueue_scripts()
{
    global $post;
    $post_ID = $post->ID;
    $post_js_fun_name = 'bn_js_for_' . $post_ID;
    if (function_exists($post_js_fun_name))
        add_action('wp_enqueue_scripts', $post_js_fun_name);
}

does what we need.

Add hook function where post-ID is available

I did not reach a full understanding of the details, it seems that you can add functions to be called by WordPress at various stages of its rendering of your post. According to a blog post on HyperSpatial, the ‘wp‘ action is suitable for this. The final step, then, is to hook our bn_maybe_enqueue_scripts function into the ‘wp‘ action.

Adding

add_action('wp', 'bn_maybe_enqueue_scripts');

to the end of functions.php completes the process.

Conclusions

This approach requires shell access to the host, is quite clunky, and does not scale well. Nonetheless for my purposes it does the job.