I read on the WP docs, which pointed to this gist that the correct way to enqueue styles for IE is by using the $wp_styles. I'm guessing that this would be true then for scripts as well.

Take these examples for instance...

Option One - Using wp_scripts

add_action('wp_print_scripts', function() {
    global $wp_scripts;
    wp_enqueue_script( 'html5shiv', 'https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js', array( 'bootstrap' )  );
    $wp_scripts->add_data( 'html5shiv', 'conditional', 'lt IE 9' );
} );

Option Two - Use wp_scripts along with is_IE

add_action('wp_print_scripts', function() {
    global $wp_scripts, $is_IE;
    if($is_IE) {
        wp_enqueue_script( 'html5shiv', 'https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js', array( 'bootstrap' )  );
        $wp_scripts->add_data( 'html5shiv', 'conditional', 'lt IE 9' );
} );

Option Three - Just echo it in the head :

add_action('wp_head', function(){
    echo '<!--[if lt IE 9]><script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script><![endif]-->' . "\n";

Option Four - Enqueue it to the head :

add_action('wp_print_scripts', function(){
        wp_enqueue_script( 'html5shiv', 'https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js', array( 'bootstrap' )  );

The second option. seems cool because it looks a little cleaner on non-IE browsers. But I'm not sure if it actually has any speed advantage or if the check alone slows it down more. Other than that I can't seem to figure out any reason why option one is better than option 3 or 4.

The reason for this question

I'm using the Genesis framework and they include HTML5shiv like the example below which just didn't seem right to me:

add_action( 'wp_head', 'genesis_html5_ie_fix' );
 * Load the html5 shiv for IE8 and below. Can't enqueue with IE conditionals.
function genesis_html5_ie_fix() {
    if ( ! genesis_html5() )
    echo '<!--[if lt IE 9]><script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->' . "\n";

To extend on @gmazzap suggestion on not using globals when you can use wp_scripts(), there is a shortcut for wp_scripts() for adding conditional comments called wp_script_add_data and likewise wp_style_add_data for conditional styles.

So the correct way to use conditionals as of Wordpress 4.2 is like this:

 * IE enqueue HTML5shiv with conditionals
 * @link http://tiny.cc/html5shiv
function wpse_213701_enqueue_html5shiv()  {
    wp_enqueue_script( 'html5shiv',
    wp_script_add_data( 'html5shiv', 'conditional', 'lt IE 9' );
add_action('wp_enqueue_scripts', 'wpse_213701_enqueue_html5shiv');

However, the example above is using HTML5shiv which is a unique situation. Since it has to be loaded in the head you could do something like this next example if you're worried about a plugin like Roots Soil removing all the scripts from the head.

Most likely you won't run into a situation like this though. It's a very unstable thing to do since a lot of scripts are required to load in the head. Putting scripts in the footer should be done by setting $in_footer variable to true when enqueuing scripts. So if you do use roots or any cache plugin for that matter don't plan on everything working exactly how you want right out of the box.

Here's an ugly example of what you can do:

add_action( 'wp_head', 'wpse_213701_check_html5shiv', 1 );
function wpse_213701_check_html5shiv() {
    remove_action( 'wp_head', 'genesis_html5_ie_fix' );
    if ( !has_filter( 'wp_head', 'wp_enqueue_scripts' ) && !wp_script_is( 'html5shiv', 'done' ) ) {
        add_action('wp_head', 'wpse_213701_echo_html5shiv');
function wpse_213701_echo_html5shiv() {
    echo '<!--[if lt IE 9]><script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script><![endif]-->' . "\n";

This is overkill though and it still isn't a guarantee that it will work. Over the past 4 years html5shiv has taken on many names which has caused it to be registered / enqueued with several different handles (html5, html5shiv, html5shim, themename-html5shiv, html5-ie-fix, and html5-polyfill just to name a few). Additionally, it's often bundled with modernizr. With that in mind, if you think the above example is ridiculous, you're just as well off adding the script to wp_head in your child theme since a plugins html5shiv handle will most likely named something else.

Update (Moving scripts to footer with this approach) :

This sparked some action on Roots/Soil Github page. Here's probably the best way to do this ( as suggested by @grappler ) and still move scripts to the footer. A similar approach was posted on @kaiser's blog a few years back.

function grappler_move_js_to_footer() 
    $scripts = wp_scripts();
    foreach( $scripts->registered as $script ) {
        if ( 'html5' == $script->handle ) {
            wp_script_add_data( $script->handle, 'group', 0 );
        } else {
            wp_script_add_data( $script->handle, 'group', 1 );
add_action( 'wp_enqueue_scripts', 'grappler_move_js_to_footer', 99 );

As for what Mark Kaplan suggested and what toscho mentioned here , you shouldn't be using option 2's $is_IE method. Apparently, if the HTTP header Vary: User-Agent doesn't get sent, you will send the wrong output to users behind a cache causing it to break for those users. In regards to browser side detection here is a long thread of examples on how that might be done. Even client-side detection has it's pitfalls.

At the end of the day, maybe the best answer is don't use any of these methods and forget about legacy browsers since Microsoft has dropped support for them as of January 12.

| improve this answer | |
  • 3
    forget about legacy browsers since Microsoft has dropped support for them as of January 12. I thought this would never happen. No more crap having to support dinosaurs, YEAH!!!! – Pieter Goosen Jan 31 '16 at 8:14

And option 5 is to detect on client side what browser it is, and create a script element for your script directly into the dom, like google analytics an FB SDK do.

This has the advantage of doing browser detection in the only place it should be done, the browser, and loading the IE specific scripts only when needed.

In spirit it is very similar to your third option, just more generic.

Sidenote: is_IE like all the other server side browser detection should be avoided as they will break caching.

| improve this answer | |

Generally speaking, scripts and styles should never be directly printed to page.

Reason is that another plugin or theme might add the same script again, and you get the same script added 2 or more times.

If you enqueue asset the proper way, if other code enqueue the same asset again, it will be added once.

For this reason you should skip options 3 and 4.

Regarding option 2, as Mark Kaplun pointed out, it makes use of server-side browser detection, that is never very affordable, and even if it would, don't let you select the version of the browser and in your case it will include the script even in modern IE versions, that don't need it.

So, actually, the option 1 is the best among the ones you proposed.

The only changes I would do:

  • use 'wp_enqueue_scripts' action, instead of 'wp_print_scripts'
  • use wp_scripts() function istead of accessing global

Something like:

add_action('wp_enqueue_scripts', function() {
       array( 'bootstrap' ),
    wp_scripts()->add_data( 'html5shiv', 'conditional', 'lt IE 9' );
} );

Doing like that if another plugin enqueue 'html5shiv' script again, it will be added just once.

Note that using snippet above, what is print on page is:

<!--[if lt IE 9]>
<script type='text/javascript' src='https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js'></script>

This conditional is parsed by the browser (client side), and not server side, so it is affordable and let you add the script only for the IE version you want to target.

If you need to know more about the client platform (OS, screen type and resolution, specific detailed version of browser...) then the only way is to use some javascript to sniff this details and dynamically add the script to DOM (as per Mark Kaplun solution), but if knowing the "browser family" and major version is enough for you, my suggestion is to use this solution.

| improve this answer | |
  • Thanks for the well explained answer! The only thing I'm wondering is what the benefit of wp_enqueue_scripts is over wp_enqueue_scripts? The only reason I used wp_print_scripts was that it got loaded later, similar to how bootstrap does it. – Bryan Willis Jan 6 '16 at 7:53
  • 1
    @BryanWillis wp_enqueue_scripts is the standard hook to enqueue assets. When wp_print_scripts fires, all the scripts should be already enqueued and other plugins may assume that. For example take github.com/roots/soil. One of its modules move all the scripts to footer. To do that, it completely removes wp_print_scripts action, assuming all the scripts are enqueued with wp_enqueue_scripts. Your code will be incompatible with that plugin. So, expecially if you plan to share / sell your code, always use standard hooks to improve compatibility. – gmazzap Jan 6 '16 at 10:14
  • that definitely makes sense. Ironically, speaking of compatibility, soil was one of the reasons I was asking that question, because it actually breaks this code regardless. In order for htm5shiv to function properly, it needs to be loaded anywhere between the opening and closing </head> tag. I personally have been using soil for a few years now, minus the soil-js-to-footer theme support, because it more often than not causes problems when a script was meant to be loaded in the head. – Bryan Willis Jan 7 '16 at 16:40
  • this was just brought up in soil, would love to hear your opinion. github.com/roots/soil/issues/33 – Bryan Willis Jan 22 '16 at 0:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.