jQuery – Why U No Enqueued?

DevoThis is a followup to my how to get your plugin in the WordPress repository post.

While code isn’t outright rejected for being ‘bad’ code (only security holes and guideline violations), a lot of plugins are doing the easy things wrong. These plugins will get approved, but they won’t work with all setups, they’re more likely to have issues with Multisite, and they’re just not thinking forward. They aim to solve a, singular, problem, without looking beyond. Primarily I see this when people are trying to bring in some js code into their plugin. Heavens knows I do it, and I’ve done it wrong too. But as I see more and more plugins, I’m starting to get better and better at knowing what’s wrong when I see it. 1

The easiest way to show you is to give you some really bad examples and go through some of the steps to fix it. The best part is that I’m actually going to use real plugins I’ve seen. Only the name has been changed to protect the innocent.

Ready? Here’s what we’re doing wrong.

Not using functions to define locations

The very bad code:

echo '<script src="/wp-content/plugins/myplugin/myscript.js"></script>';

I wish this was something I made up. Worse, I’ve see it more than once. Recently.

This install is assuming WordPress is installed in the root of your HTML folder (i.e. domain.com). This is not always the case, as many people install WordPress in subfolders. We’ll need to fix that first with home_url().

echo '<script src="'.home_url('wp-content/plugins/myplugin/myscript.js').'"></script>';

Now it’s a little better, as by using home_url() we’re letting WordPress define where ‘home’ is. Great! This has two pretty obvious problems, however. First, if I have WordPress installed in a folder, like /public_html/wordpress/, but I’m running it out of the main domain by giving it its own directory, this won’t work. Your code would point to http://example.com/wp-content… when mine is in http://example.com/wordpress/wp-content.. instead! The ‘easy’ fix is to change home_url() for site_url(), but what if I’m not using wp-content? You didn’t know we could Move wp-content? We can. So let’s address that.

echo '<script src="'.content_url('plugins/myplugin/myscript.js').'"></script>';

By using functions to determine plugin and content directories, we can make this much more flexible. That works, but it could be better. What if we didn’t have to define the plugins or myplugin folders? We could just do something simple like this.

echo '<script src="'.plugins_url('myscript.js',__FILE__).'"></script>';

Now we have a simple, flexible, functional script embed of js. Except there’s one, minor problem. We’re not including the script correctly.

Not enqueuing files

This isn’t ‘wrong’ really. I mean, if I put this in my plugin, it would echo out the script, and that’s what I want, right?

echo '<script src="'.plugins_url('myscript.js',__FILE__).'"></script>';

But let’s say I want to put it in my header:

function my_scripts_method() {
    echo '<script src="'.plugins_url('myscript.js',__FILE__).'"></script>';
}
add_action('wp_head', 'my_scripts_method');

And now I want to include my CSS so it looks pretty:

function my_scripts_method() {
    echo '<script src="'.plugins_url('myscript.js',__FILE__).'"></script>';
    echo '<link rel="stylesheet" type="text/css" href="'.plugins_url('myscript.js',__FILE__).'" media="all" />';
}
add_action('wp_head', 'my_scripts_method');

Oh, wait, no, I wanted my JS in the footer:

function my_scripts_method_foot() {
    echo '<script src="'.plugins_url('myscript.js',__FILE__).'"></script>';
}
function my_scripts_method_head() {
    echo '<link rel="stylesheet" type="text/css" href="'.plugins_url('myscript.js',__FILE__).'" media="all" />';
}
add_action('wp_head', 'my_scripts_method_head');
add_action('wp_footer', 'my_scripts_method_foot');

And really, this will work. But it’s not efficient, I’ve got extra actions, and I’m not considering any jquery dependencies anymore. By using wp_enqueue_script is better. Weblog Tools Collection did a series on how to properly add scripts (note that it’s a bit out of date with the use of WP-CONTENT constants). From that we can extrapolate to use just this to include our js and css:

function my_scripts_method() {
    wp_enqueue_script('my_script', plugins_url('myscript.js',__FILE__) );
    wp_enqueue_style('my_script', plugins_url('myscript.css',__FILE__) );
}
add_action('wp_enqueue_scripts', 'my_scripts_method');

What enqueue does is put your code in the best possible location and can be extended to load dependencies. wp_enque_scripts has a lot of power, and because it’s a WordPress function, it’s got options that make it more flexible. Like when I look at my above code, I remember, oops! I wanted to run my js out of the footer! Not a problem. Look at my options.

wp_enqueue_script('handle', 'source', 'dependencies', 'version', 'in_footer');

jQuery Logo looks like a Devo HatThe ‘handle’ is what I want to name my script, it should be unique. If I register my script, I can call the handle over and over again. We’re using my_script right now. The ‘source’ is where my file is located. We’re lifting that from our other code, the bad code, because it works. Your ‘dependencies’ are the other js files yours needs to function. If I put in array('jquery', 'scriptaculous') then both jQuery and Scriptaculous would get loaded before my script. Curiously, you don’t actually need the ‘version’ option, as you can leave it blank and WordPress will automatically add a version number equal to the current version of WordPress you are running. So every time you upgrade WP, it will get updated and force a re-download. This is good, since if you have dependencies to scripts included in WordPress, and they change with a new version (which is the only way they can change), then you get updated too. Finally we have the value I was looking for, ‘in_footer.’ Leave it blank and it’s in the header, put in true and it’s not.

This makes my code:

function my_scripts_method() {
    wp_enqueue_script('my_script', plugins_url('myscript.js',__FILE__), '','', true ););
    wp_enqueue_style('my_script', plugins_url('myscript.css',__FILE__) );
}
add_action('wp_enqueue_scripts', 'my_scripts_method');

Yeah, isn’t that a lot easier?

Using a different jQuery

This last one I’m going to touch on today is the exact code I saw, in the wild, and it’s got two of my three buggaboos in it.

wp_enqueue_script('jquery-1.4.3.min.js', '/wp-content/plugins/myplugin/js/jquery-1.4.3.min.js');

Okay. You already know the right way to call the script, so we’ll edit that into something more flexible.

wp_enqueue_script('jquery-1.4.3.min.js', plugins_url('js/jquery-1.4.3.min.js',__FILE__) );

That should be okay, but it’s totally not.

Most importantly here, we’re calling jquery, which is actually built in to WordPress. Now, we’re calling it by a different handle, but that’s no guarantee that it won’t cause conflicts. In fact, I’m pretty sure this will cause no end of problems with some plugins. The right thing to do would be this:

function my_scripts_method() {
    wp_deregister_script( 'jquery' );
    wp_register_script( 'jquery', plugins_url('js/jquery-1.4.3.min.js',__FILE__) );
    wp_enqueue_script( 'jquery' );
}
add_action('wp_enqueue_scripts', 'my_scripts_method');

Now we’re making sure we won’t have conflicts by re-registering jquery, replacing it, and moving on.

A lot of people would actually recommend using Google instead, as it takes the responsibility off you for including a file you don’t ‘control.’ Also it makes your plugin smaller and load faster.

function my_scripts_method() {
    wp_deregister_script( 'jquery' );
    wp_register_script( 'jquery', 'http://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js' );
    wp_enqueue_script( 'jquery' );
}
add_action('wp_enqueue_scripts', 'my_scripts_method');

Devo Hat looks like Devo HatGreat! Now we’re done, right? Wrong. As of this writing, WordPress is using jQuery 1.7.2. Now I couldn’t come up with a reason to include an old version of jQuery in WordPress (newer, yes, older, no), so I asked around and none of my friends could either. Using an older version is more likely to cause issues with newer code included in WordPress, as well as plugins which are upgraded to take advantage of the new features. You’re shooting yourself in the foot. The only thing you might be using this for is to include deprecated fictions, and really you need to update your code to fix that instead.

If the whole point is to load the scripts from Google, though, there’s an awesome plugin for that.

About these ads

Notes:

  1. “I know it when I see it” thanks to United States Supreme Court Justice Potter Stewart
StudioPress Theme of the Month

Comments

  1. Also, if you only want to load the scripts when a shortcode is being used, check this out:

    http://scribu.net/wordpress/conditional-script-loading-revisited.html

    The last comment mentions registering the script in wp_enqueue_script, then loading it in the shortcode handler

  2. Random tip … plugins_url() doesn’t work as you might expect outside of the root plugin folder, so it’s often best to use it in concert with a constant in that scenario so that you can have the path to the root folder, instead of a sub-folder where your PHP files may lie.

    • Really? That’s odd, you’d think it would auto-correct itself if you changed your defines in wp-config … Oh wait, do you mean for mu-plugins and such? :?:

  3. That’s a good tutorial to refer newbie devs to :-) Even better if it followed the WP Coding Standards and good PHP practise of not using double-quotes for plain strings…

    There’s also absolutely no need to register and immediately enqueue a script – just enqueue it and let WP handle the registration behind the scenes.

    “So every time you upgrade WP, it will get updated and force a re-download.” – this gives a wrong impression about the point of the version argument – since it should be the version of your plugin or theme script, it shouldn’t make any difference if WP updates – it should be versioned against your plugin or theme, so that when *that* updates, then the cache buster kicks in. As it stands, you’re suggesting that I can release version 1.0.0, but not get a clean update for users to 1.1.0 until the next version of WP is released.

    • Heh. Well I didn’t claim to be perfect ;) Doublequotes are habit for me. I just worked myself out of double-spaces between sentences!

      Yeah, on review that “so every time…” was an incomplete thought…. When you upgrade WP, the JS will flush the cache, which is what you WANT actually, since many upgrades can cause JS/Ajax weirdness. I’m not sure how to phrase that right.

  4. For GaryJ, I fixed my quotes ;)

    For everyone asking about themes, WP Candy and I are on the same brainwave! :!:

  5. Personally, I would never tell people to deregister the default jQuery and load it from Google in plugins. The only time that should ever be done, in my thoughts, is when you are running a site that you have complete control over. With plugins, we have no way of knowing how jQuery has been loaded by other plugins or themes, so it’s best to always use the default jQuery.

    I’ve run into so many conflicts that were caused just by people deregistering the default jQuery and including it from Google.

    • If you do it in a plugin, you damn well better disclose that IMO. But … There are lots of reason I can see to do that. And lots of reasons not to. The point I was making was you damn well better deregister before you register, or worse things happen.

      (I don’t use Google Libraries anymore. I did. Found it made things slower. Stopped.)

Half-Elf? Try Half OFF WordPress ebooks!