WordPress wp_enque_scripts in functions.php

The functions.php file should be known to every wordpress theme developer, as it’s where all your custom theme code is expected to go, including the loading of special files, scripts and overriding the default, out-of-the-box wordpress functions and javascript files.

However, there are a few things one need to remember when writing code in that file, and it’s not always easy to understand. Specifically, registering and enqueueing scripts can be a bit tricky if you don’t understand how the functions.php file is loaded.

One of the biggest problem is understanding the scope and load/execution time of what is in that file. I’ve scratched my head a few times on it, and hopefully, I now understand it better and will manage to write it down correctly!

Scope

First of all, if you look for a functions.php file in your wordpress installation: beware! There is also a file called functions.php in the wordpress core files in the wp-includes folder. We’re NOT talking about this one! We are talking about creating your own functions.php file in your theme folder. So don’t go edit the core one (one should NEVER edit the core files anyways) and instead work on your own, clean and empty version located in your theme directory.

Now, about scope: the functions.php file in your theme is included from the following flow:

  • a request arrives on your web server and process the index.php file.
  • this includes wp-blog-header.php
  • in turn this one includes wp-load.php
  • that file eventually loads wp-config.php (you should know that file!)
  • this config file then includes wp-settings.php
  • wp-settings.php, after doing a whole load of stuff, includes your theme’s functions.php file (at last!)

Because all of these includes happen in the files directly, using php’s include() function, and not in nested calls, they all inherit the global scope, and so does your functions.php file, but it’s not really important as we’ll see later on. What is important however, is the time in the whole page load process when the functions.php file is loaded: it happens before (read well: BEFORE) the wp-init() call is placed, which will then execute all actions, hooks and eventually render the requested page. If you’re interested in more details about what happens in this phases, more details are (and will be available in the near future apparently) on a post just written (funny enough, only a few days ago!), on wordpressinternals.com

At this point, before the theme is loaded and right before the functions.php file of your theme is loaded, you have access to some global functions like is_admin(), and more importantly, wp_deregister_script(), wp_register_script() and wp_enqueue_script().

That’s where it gets interesting, these functions are available to use, but if not carefully wrapped in hooks or actions, they will not return the inteded results. That’s what I was doing wrong, and that’s what prompted me to do this write up.

What was I doing wrong?

Basically, I was coding my theme the wrong way by not wrapping these tests in a hook or action, well before the WP init was called, as described on a wordpress development track ticket. Like many theme developers, I guess, I started to write my new theme by using an existing one, and modified the functions.php file. Along the way I found great resources like digwp’s functions.php template. Initially happy enough to have something working, while blissfully ignorant about its internals, I started to get a strange behaviour when I wanted to include scripts only on some specific pages of my theme.

At the top of my functions.php, I had left the template file function calls to enqueue the scripts:

wp_deregister_script('jquery');
wp_register_script('jquery', (get_bloginfo('stylesheet_directory')."/js/jquery-1.4.2.min.js"));
wp_enqueue_script('jquery');

Lets say I want to add a script only on the home page. I’ve got a custom home page for my theme, so I’m using is_front_page() to detect it. I therefore added:

if( is_front_page()) {
    wp_register_script('home_script', (get_bloginfo('stylesheet_directory')."/js/home.js"));
    wp_enqueue_script('home_script');
}

I also have custom templates like a “alternatepage.php” template file, so to detect it, I used the is_page_template() function:

if( is_page_template('alternatepage.php')) {
    wp_register_script('alternatepage_script', (get_bloginfo('stylesheet_directory')."/js/alternatepage.js"));
    wp_enqueue_script('alternatepage_script');
}

Simple enough eh? Errrrr…. no!

Access any of these pages and the required scripts will not get loaded.

Why does this not work?

The answer is fairly simple, providing you know the existence of the WP and WP_Query objects, and if you followed until here (and understood where I was going with the inclusion of php scripts).

At the point where the functions.php file is loaded, when the is_front_page and is_page_template functions are called (Remember? When you include a php script it gets evaluated as part of the include process!), no query has been made yet, therefore both functions will return false!

So how can I include only the scripts I require on the pages I want? Actions to the rescue!!

Which action do I need

Trawling through the actions list, there is one obvious action that should jump before your eyes: wp_enqueue_scripts. It’s executed right after the wp_head one, and is intended to allow theme developers to hook onto the moment in the page load where the platform enqueues its own scripts, adding theme specific ones.

All I had to do was to wrap my calls to change the enqueued script files into a function tied to this hook:

function register_scripts() {
    wp_deregister_script('jquery');
    wp_register_script('jquery', (get_bloginfo('stylesheet_directory')."/js/jquery-1.4.2.min.js"));
    wp_enqueue_script('jquery');

    if( is_front_page()) {
        wp_register_script('home_script', (get_bloginfo('stylesheet_directory')."/js/home.js"));
        wp_enqueue_script('home_script');
    }

    if( is_page_template('alternatepage.php')) {
        wp_register_script('alternatepage_script', (get_bloginfo('stylesheet_directory')."/js/alternatepage.js"));
        wp_enqueue_script('alternatepage_script');
    }
}
add_action( 'wp_enqueue_scripts', 'register_scripts' );

All is fine now and the correct scripts are loaded on the right pages, but gosh it took some time to drill down the core of wordpress to understand what was going on!

My advice?

Only use hooks in your functions.php unless you have a very good reason not to!

Comments are disabled temporarily until I find a suitable system, but you can still send a comment by email and I'll add it to this post. Thanks!