The Developer’s Guide To Conflict-Free JavaScript And CSS In WordPress

Advertisement

Imagine you’re playing the latest hash-tag game on Twitter when you see this friendly tweet:

You might want to check your #WP site. It includes two copies of jQuery. Nothing’s broken, but loading time will be slower.

You check your source code, and sure enough you see this:

<script src="/wp-includes/js/jquery/jquery.js?ver=1.6.1" type="text/javascript"></script>
<script src="/wp-content/plugins/some-plugin/jquery.js"></script>

sm-wp-css-js

What Went Wrong?

The first copy of jQuery is included the WordPress way, while some-plugin includes jQuery as you would on a static HTML page.

A number of JavaScript frameworks are included in WordPress by default, including:

  • Scriptaculous,
  • jQuery (running in noConflict mode),
  • the jQuery UI core and selected widgets,
  • Prototype.

A complete list can be found in the Codex. On the same page are instructions for using jQuery in noConflict mode.

Avoiding the Problem

WordPress includes these libraries so that plugin and theme authors can avoid this problem by using the wp_register_script and wp_enqueue_script PHP functions to include JavaScript in the HTML.

Registering a file alone doesn’t do anything to the output of your HTML; it only adds the file to WordPress’s list of known scripts. As you’ll see in the next section, we register files early on in a theme or plugin where we can keep track of versioning information.

To output the file to the HTML, you need to enqueue the file. Once you’ve done this, WordPress will add the required script tag to the header or footer of the outputted page. More details are provided later in this article.

Registering a file requires more complex code than enqueueing the files; so, quickly parsing the file is harder when you’re reviewing your code. Enqueueing the file is far simpler, and you can easily parse how the HTML is being affected.

For these techniques to work, the theme’s header.php file must include the line <?php wp_head(); ?> just before the </head> tag, and the footer.php file must include the line <?php wp_footer(); ?> just before the </body> tag.

Registering JavaScript

Before registering your JavaScript, you’ll need to decide on a few additional items:

  • the file’s handle (i.e. the name by which WordPress will know the file);
  • other scripts that the file depends on (jQuery, for example);
  • the version number (optional);
  • where the file will appear in the HTML (the header or footer).

This article refers to building a theme, but the tips apply equally to building a plugin.

Examples

We’ll use two JavaScript files to illustrate the power of the functions:

The first is base.js, which is a toolkit of functions used in our example website.

function makeRed(selector){
  var $ = jQuery; //enable $ alias within this scope
  $(function(){
    $(selector).css('color','red');
  });
}

The base.js file relies on jQuery, so jQuery can be considered a dependency.

This is the first version of the file, version 1.0.0, and there is no reason to run this file in the HTML header.

The second file, custom.js, is used to add the JavaScript goodness to our website.

makeRed('*');

This custom.js file calls a function in base.js, so base.js is a dependency.

Like base.js, custom.js is version 1.0.0 and can be run in the HTML footer.

The custom.js file also indirectly relies on jQuery. But in this case, base.js could be edited to be self-contained or to rely on another framework. There is no need for jQuery to be listed as a dependency of custom.js.

It’s now simply a matter of registering your JavaScript using the function wp_register_script. This takes the following arguments:

  • $handle
    A string
  • $source
    A string
  • $dependancies
    An array (optional)
  • $version
    A string (optional)
  • $in_footer
    True/false (optional, default is false)

When registering scripts, it is best to use the init hook and to register them all at once.

To register the scripts in our example, add the following to the theme’s functions.php file:

function mytheme_register_scripts() { 
  //base.js – dependent on jQuery 
  wp_register_script( 
    'theme-base', //handle 
    '/wp-content/themes/my-theme/base.js', //source 
    array('jquery'), //dependencies 
    '1.0.0', //version 
    true //run in footer 
  ); 

  //custom.js – dependent on base.js 
  wp_register_script( 
    'theme-custom', 
    '/wp-content/themes/my-theme/custom.js', 
    array('theme-base'), 
    '1.0.0', 
    true 
  ); 
} 
add_action('init', 'mytheme_register_scripts');

There is no need to register jQuery, because WordPress already has. Re-registering it could lead to problems.

You Have Achieved Nothing!

All of this registering JavaScript files the WordPress way has, so far, achieved nothing. Nothing will be outputted to your HTML files.

To get WordPress to output the relevant HTML, we need to enqueue our files. Unlike the relatively long-winded commands required to register the functions, this is a very simple process.

Outputting the JavaScript HTML

Adding the <script> tags to your HTML is done with the wp_enqueue_script function. Once a script is registered, it takes one argument, the file’s handle.

Adding JavaScript to the HTML is done in the wp_print_scripts hook with the following code:

function mytheme_enqueue_scripts(){ 
  if (!is_admin()): 
    wp_enqueue_script('theme-custom'); //custom.js 
  endif; //!is_admin 
} 
add_action('wp_print_scripts', 'mytheme_enqueue_scripts');

Of our two registered JavaScript files (base.js and custom.js), only the second adds JavaScript functionality to the website. Without the second file, there is no need to add the first.

Having enqueued custom.js for output to the HTML, WordPress will figure out that it depends on base.js being present and that base.js, in turn, requires jQuery. The resulting HTML is:

<script src="/wp-includes/js/jquery/jquery.js?ver=1.6.1" type="text/javascript"></script>
<script src="/wp-content/themes/my-theme/base.js?ver=1.0.0" type="text/javascript"></script>
<script src="/wp-content/themes/my-theme/custom.js?ver=1.0.0" type="text/javascript"></script>

Registering Style Sheets

Both of the functions for adding JavaScript to our HTML have sister PHP functions for adding style sheets to the HTML: wp_register_style and wp_enqueue_style.

As with the JavaScript example, we’ll use a couple of CSS files throughout this article, employing the mobile-first methodology for responsive Web design.

The mobile.css file is the CSS for building the mobile version of the website. It has no dependencies.

The desktop.css file is the CSS that is loaded for desktop devices only. The desktop version builds on the mobile version, so mobile.css is a dependency.

Once you’ve decided on version numbers, dependencies and media types, it’s time to register your style sheets using the wp_register_style function. This function takes the following arguments:

  • $handle
    A string
  • $source
    A string
  • $dependancies
    An array (optional, default is none)
  • $version
    A string (optional, the default is the current WordPress version number)
  • $media_type
    A string (optional, the default is all)

Again, registering your style sheets using the init action is best.

To your theme’s functions.php, you would add this:

function mytheme_register_styles(){ 
  //mobile.css for all devices 
  wp_register_style( 
    'theme-mobile', //handle 
    '/wp-content/themes/my-theme/mobile.css', //source 
    null, //no dependencies 
    '1.0.0' //version 
  ); 

  //desktop.css for big-screen browsers 
  wp_register_style( 
    'theme-desktop', 
    '/wp-content/themes/my-theme/desktop.css', 
    array('theme-mobile'), 
    '1.0.0', 
    'only screen and (min-width : 960px)' //media type 
  ); 

  /* *keep reading* */ 
} 
add_action('init', 'mytheme_register_styles');

We have used CSS3 media queries to prevent mobile browsers from parsing our desktop style sheet. But Internet Explorer versions 8 and below do not understand CSS3 media queries and so will not parse the desktop CSS either.

IE8 is only two years old, so we should support its users with conditional comments.

Conditional Comments

When registering CSS using the register and enqueue functions, conditional comments are a little more complex. WordPress uses the object $wp_styles to store registered style sheets.

To wrap your file in conditional comments, add extra information to this object.

For Internet Explorer 8 and below, excluding mobile IE, we need to register another copy of our desktop style sheet (using the media type all) and wrap it in conditional comments.

In the code sample above, /* *keep reading* */ would be replaced with the following:

global $wp_styles; 
wp_register_style( 
  'theme-desktop-ie', 
  '/wp-content/themes/my-theme/desktop.css', 
  array('theme-mobile'), 
  '1.0.0' 
); 

$wp_styles->add_data( 
  'theme-desktop-ie', //handle 
  'conditional',  //is a conditional comment 
  '!(IEMobile)&(lte IE 8)' //the conditional comment 
);

Unfortunately, there is no equivalent for wrapping JavaScript files in conditional comments, presumably due to the concatenation of JavaScript in the admin section.

If you need to wrap a JavaScript file in conditional comments, you will need to add it to header.php or footer.php in the theme. Alternatively, you could use the wp_head or wp_footer hooks.

Outputting The Style Sheet HTML

Outputting the style sheet HTML is very similar to outputting the JavaScript HTML. We use the enqueue function and run it on the wp_print_styles hook.

In our example, we could get away with telling WordPress to queue only the style sheets that have the handles theme-desktop and theme-desktop-ie. WordPress would then output the mobile/all media version.

However, both style sheets apply styles to the website beyond a basic reset: mobile.css builds the website for mobile phones, and desktop.css builds on top of that. If it does something and I’ve registered it, then I should enqueue it. It helps to keep track of what’s going on.

Here is the code to output the CSS to the HTML:

function mytheme_enqueue_styles(){ 
  if (!is_admin()): 
    wp_enqueue_style('theme-mobile'); //mobile.css 
    wp_enqueue_style('theme-desktop'); //desktop.css 
    wp_enqueue_style('theme-desktop-ie'); //desktop.css lte ie8 
  endif; //!is_admin 
} 
add_action('wp_print_styles', 'mytheme_enqueue_styles');

What’s The Point?

You may be wondering what the point is of going through all of this extra effort when we could just output our JavaScript and style sheets in the theme’s header.php or using the wp_head hook.

In the case of CSS in a standalone theme, it’s a valid point. It’s extra work without much of a payoff.

But with JavaScript, it helps to prevent clashes between plugins and themes when each uses a different version of a JavaScript framework. It also makes page-loading times as fast as possible by avoiding file duplication.

WordPress Frameworks

This group of functions can be most helpful when using a framework for theming. In my agency, Soupgiant, we’ve built a framework to speed up our production of websites.

As with most agencies, we have internal conventions for naming JavaScript and CSS files.

When we create a bespoke WordPress theme for a client, we develop it as a child theme of our framework. In the framework itself, we register a number of JavaScript and CSS files in accordance with our naming convention.

In the child theme, we then simply enqueue files to output the HTML.

function clienttheme_enqueue_css() { 
  if (!is_admin()): 
    wp_enqueue_style('theme-mobile'); 
    wp_enqueue_style('theme-desktop'); 
    wp_enqueue_style('theme-desktop-ie'); 
  endif; //!is_admin 
} 
add_action('wp_print_styles', 'clienttheme_enqueue_css'); 

function clienttheme_enqueue_js() { 
  if (!is_admin()): 
    wp_enqueue_script('theme-custom'); 
  endif; //!is_admin
} 
add_action('wp_print_scripts', 'clienttheme_enqueue_js');

Adding CSS and JavaScript to our themes the WordPress way enables us to keep track of exactly what’s going on at a glance.

A Slight Limitation

If you use a JavaScript framework in your theme or plugin, then you’re stuck with the version that ships with the current version of WordPress, which sometimes falls a version or two behind the latest official release of the framework. (Upgrading to a newer version of the framework is technically possible, but this could cause problems with other themes or plugins that expect the version that ships with WordPress, so I’ve omitted this information from this article.)

While this prevents you from using any new features of the framework that were added after the version included in WordPress, the advantage is that all theme and plugin authors know which version of the framework to expect.

A Single Point Of Registration

Register your styles and scripts in a single block of code, so that when you update a file, you will be able to go back and update the version number easily.

If you use different code in different parts of the website, you can wrap the logic around the enqueue scripts.

If, say, your archive pages use different JavaScript than the rest of the website, then you might register three files:

  • base JavaScript (registered as theme-base),
  • archive JavaScript (registered as theme-archive),
  • general JavaScript (registered as theme-general).

Again, the base JavaScript adds nothing to the website. Rather, it is a group of default functions that the other two files rely on. You could then enqueue the files using the following code:

function mytheme_enqueue_js(){ 
  if (is_archive()) { 
    wp_enqueue_script('theme-archive'); 
  } 
  elseif (!is_admin()) { 
    wp_enqueue_script('theme-general');
  }
} 
add_action('wp_print_scripts', 'mytheme_enqueue_js');

Using The Google AJAX CDN

While using JavaScript the WordPress way will save you the problem of common libraries conflicting with each other, you might prefer to serve these libraries from Google’s server rather than your own.

Using Jason Penny’s Use Google Libraries plugin is the easiest way to do this. The plugin automatically keeps jQuery in noConflict mode.

Putting It All Together

Once you’ve started registering and outputting your scripts and styles the WordPress way, you will find that managing these files becomes a series of logical steps:

  1. Registration to manage:
    • version numbers,
    • file dependencies,
    • media types for CSS,
    • code placement for JavaScript (header or footer);
  2. Enqueue/output files to HTML:
    • logic targeting output to specific WordPress pages,
    • WordPress automating dependencies.

Eliminating potential JavaScript conflicts from your WordPress theme or plugin frees you to get on with more important things, such as following up on sales leads or getting back to that hash-tag game that was so rudely interrupted.

(al)

↑ Back to top

  1. 1

    Thanks for this info—finding this level of detail has been difficult for me.

    I need to be able to add conditional javascript to my theme the ‘proper’ way for it to get approval from the theme directory. Just sticking the conditional comments in header.php means it won’t get through.

    I’ve tried using wp_head but found it simply doesn’t work.

    Looks like it’s not possible—but that’s good to know.

    0
  2. 4

    Gabriele Romanato

    October 12, 2011 8:27 am

    Most developers simply ignore the fact that they can use self-executing functions to create a new namespace inside their application. So (function($) {…})(jQuery) operates on jQuery, but you can use the same principle to include your library code: (function() { var MyLib = {};)(); It is safe to use jQuery within that context, because it’s like a sandbox.

    Another thing to keep in mind is the order by which jQuery plugins are executed: generally, they all use the ready() event, so you may have multiple ready() calls. You can tackle this by creating an autoload method and wrap the plugin calls within the methods of your library:

    var MyApp = new function() {

    this.twitterPlugin = function() {

    };

    // other plugin calls

    this.autoload = function() {

    for(var i in this) {

    if(typeof this[i] === ‘function’ && this[i] !== arguments.callee) {

    this[i]();

    }

    }

    };
    }();

    Then you’ll have a single call:

    $(function() {

    MyApp.autoload();

    });

    Generally is better to include all the JavaScript code at the end of the page for a better perceived performance (see Steve Souders, Yahoo! team), but there may be some plugins that need a different source placement.

    Another thing to keep in mind, if you use web fonts, is that your include CSS must come before the CSS that uses it. This is the case of Google Web Fonts. Also consider the fact that this kind of CSS may actually negatively affect the performance of your site if the requested font is not available. Browsers, in this case, tend to freeze and slow down the rendering of the page.

    0
  3. 5

    “wordpress for developers” should be existing. imagine that. (without the unnecessary comfort functions it would be a way better cms.)

    0
  4. 6

    There are problems using add_data for negative conditionals (see http://core.trac.wordpress.org/ticket/16118). Instead you can use the style_loader_tag filter:

    add_filter(‘style_loader_tag’, ‘custom_css_filter’, 10, 2);
    function custom_css_filter($css_html_tag, $handle) {
    if ($handle == ‘whatever-handle-you-use’) {
    return “<!–[if !IE]>–>”.$css_html_tag.”<!–<![endif]–><!–[if gt IE 8]>”.$css_html_tag.”<![endif]–>”;
    }
    return $css_html_tag;
    }

    0
  5. 7

    Edouard Duplessis

    October 12, 2011 10:06 am

    a better hook to use is “template_redirect” for loading your js or css

    1 it’s loading only in front end… so no ” if(!is_admin()) ”
    2 You know which template file is loading so if you need a conditionnal loading you have it.
    if(is_home()){
    wp_enqueue_style(‘home’);
    }else{
    wp_enqueue_style(‘page’);
    }

    and if you use a parent theme for development

    register your style or script in “after_setup_theme” hook in your parent theme
    and in your child register it in “after_setup_theme” but in level 12, so it register after your parent theme.

    And for a WordPress for developers, read the book “Professional WordPress Plugin Development” by Brad Williams, Ozh Richard, Justin Tadlock

    0
  6. 8

    why not use

    jQuery(document).ready(function($) {
    // $() will work as an alias for jQuery() inside of this function
    });

    or

    (function($) {
    // $() will work as an alias for jQuery() inside of this function
    })(jQuery);

    for the alias enabling?

    0
  7. 9

    I’ve had so many conflicts with jQuery so this article is really worth a lot to me!
    Thanks bro!

    0
  8. 10

    Quite long article for the 4 functions you need for that :D
    But ofcourse usefull for beginners

    0
  9. 11

    Great insight! How do we insert location into the boolean search string?

    0
  10. 12

    I’m not really getting how registering your javascript and css will prevent conflicts. There’s still the potential for conflicting javascripts and css as it doesn’t stop a plugin for registering their own copy of jquery and have a second plugin use wordpress’ jquery and you’re back to the same problem?

    wp_register_script( “a-different-version-of-jquery”, … );
    wp_enqueue_script(‘a-different-version-of-jquery’);

    Am I missing something?

    1
    • 13

      That’s true, it only works if all your plugins register & enqueue JavaScript the WordPress way. I wish to encourage all authors to do so.

      If a plugin uses its own version of a jQuery library could be seen as a warning to review its code.

      0
  11. 14

    What would be the best why to add a chuck of code in the head of the site. I’m registering the main script the way you described above but would like to know the correct way of adding something like the example below:

    function function_name() {
    echo ”

    jQuery(document).ready(function(){
    jQuery(‘ul.sf-menu’)
    .supersubs()
    .superfish ({
    delay:500,
    animation:{opacity:’show’,height:’show’},
    speed:’normal’,
    dropShadows:true
    });
    });

    “;
    }
    add_action(‘wp_head’, ‘function_name’, 9);

    0
    • 15

      That would do the trick with some html script tags in output.

      You would be better off putting it in the site’s main JavaScript file rather than outputting it in the HTML. If you must put it in the HTML, attach the function to the wp_footer hook – instead of wp_head – your jQuery doesn’t run until the DOM has fully loaded.

      0
  12. 16

    This one caught me. All the Wizylab Premium themes seem to have created their own wizy_functionname including to call jquery. So now my blog loads jquery twice. Since I am not a php coder I have not been able to figure out how to modify the Wizylab Gridnik theme to properly enqueue and register scripts, though I did at least get it to load the same version of jquery as what comes with WP.

    0
  13. 18

    En queuing the mobile.css (using the function mytheme_enqueue_styles()) was’nt necessary as it will get automatically loaded bcoz it is required the desktop.css style sheet just as in the previous example we did not enquqe the jquery…

    0
  14. 19

    This article is excellent and very useful!!

    0
  15. 20

    Muhammad Sajjad

    June 7, 2013 12:19 pm

    Hi Everyone !
    i m new in plugin development please help me..i m including the css files by code given below but it doesn’t works..
    add_action(‘wp_enqueue_script’, ‘bd_styles’);

    function bd_styles() {
    wp_register_style(‘slidesjs_example’, plugins_url(‘css/example.css’, __FILE__));
    wp_enqueue_style(‘slidesjs_example’);
    wp_register_style(‘slidesjs_fonts’, plugins_url(‘css/font-awesome.min.css’, __FILE__));
    wp_enqueue_style(‘slidesjs_fonts’);

    }

    the css file is with in css folder with is inside my plugin folder . i.e
    Myplugin/css/example.css

    0
  16. 21

    Hello! Still, it seems to me that the best way to connect JavaScript in Posts iframe is used to demonstrate his work.

    0

Leave a Comment

Yay! You've decided to leave a comment. That's fantastic! Please keep in mind that comments are moderated and rel="nofollow" is in use. So, please do not use a spammy keyword or a domain as your name, or else it will be deleted. Let's have a personal and meaningful conversation instead. Thanks for dropping by!

↑ Back to top