Wordpress - Any advantage of using wp_scripts and is_IE when enqueuing scripts

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',
        'https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js',
        array(),
        false,
        false
    );
    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');
        wp_dequeue_script('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.


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.


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() {
    wp_enqueue_script(
       'html5shiv',
       'https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js',
       array( 'bootstrap' ),
       '3.7.2',
       false
    );
    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>
<![endif]-->

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.