31

I couldn't add defer tag in plugin javascripts. Google developer pagespeed test suggests me to add defer tag in contact form 7 javascripts.

This is how contact form 7 includes javascript in header.

add_action( 'wp_enqueue_scripts', 'wpcf7_enqueue_scripts' );

function wpcf7_enqueue_scripts() {
    // jquery.form.js originally bundled with WordPress is out of date and deprecated
    // so we need to deregister it and re-register the latest one
    wp_deregister_script( 'jquery-form' );
    wp_register_script( 'jquery-form', wpcf7_plugin_url( 'jquery.form.js' ),
        array( 'jquery' ), '2.52', true );

    $in_footer = true;
    if ( 'header' === WPCF7_LOAD_JS )
        $in_footer = false;

    wp_enqueue_script( 'contact-form-7', wpcf7_plugin_url( 'scripts.js' ),
        array( 'jquery', 'jquery-form' ), WPCF7_VERSION, $in_footer );

    do_action( 'wpcf7_enqueue_scripts' );
}

Now how to add defer="defer" tag in the above code?

2

2 Answers 2

63

As of WordPress 4.1 there is a filter: script_loader_tag. You can use it to find the correct script:

add_filter( 'script_loader_tag', function ( $tag, $handle ) {

    if ( 'contact-form-7' !== $handle )
        return $tag;

    return str_replace( ' src', ' defer="defer" src', $tag );
}, 10, 2 );

Old answer

There is no dedicated filter available … at least I cannot see one. But …

  • wp_print_scripts() calls WP_Scripts->do_items()
  • which calls WP_Scripts->do_item()
  • which uses esc_url()
  • which does offer a filter: 'clean_url'.

And here we go:

function add_defer_to_cf7( $url )
{
    if ( FALSE === strpos( $url, 'contact-form-7' )
      or FALSE === strpos( $url, '.js' )
    )
    { // not our file
        return $url;
    }
    // Must be a ', not "!
    return "$url' defer='defer";
}
add_filter( 'clean_url', 'add_defer_to_cf7', 11, 1 );

Caveat: not tested, just an idea. :)

Update

I have written and tested a plugin with this code.

6
  • This is perfect also to use with data-main in requirejs Commented Jun 6, 2012 at 15:07
  • That's a nice hack, and so simple. I reckon it'd be good also for adding charset='utf-8' when necessary!
    – webaware
    Commented Jan 16, 2013 at 8:16
  • Great, but why: Must be a ', not "! ? Commented Feb 1, 2014 at 2:09
  • @henrywright WordPress add ' on both side of the returned string, an " would result in invalid HTML.
    – fuxia
    Commented Feb 1, 2014 at 10:54
  • 2
    Probably a good idea in case someone wants to turn this to work with other scripts will be to make sure to validate to use it only on the font-end maybe using if( ! is_admin() ){} popular plugins like ACF might give you a headache.
    – crissoca
    Commented Apr 28, 2015 at 2:09
0

The accepted answer is correct, but I wanted to make some improvements/updates for a more general case for anyone, including my future self, who happens across this while searching for an answer. For general cases not pertaining to this contact-form-7 plugin, where you are adding the defer tag to all scripts, you probably want to exclude it from the admin pages, because those rely on the scripts being executed in a certain order. Also, for the non-admin pages, you want to exclude jQuery. because jQuery should probably be loaded in the head before anything else. Also, instead of manipulating the tag string, a better way to go about this would be to load it into a DOMDocument object, manipulate that, and convert it back to a string afterward. Also, newer versions of php have type checking, so you should probably add your parameter and return types into your function call.

<?php

    add_filter('script_loader_tag', function (string $tag, string $handle, string $src):string {
        if (is_admin()) {
            return $tag;
        }
        if (in_array($handle, array('jquery-core', 'jquery-migrate'))) {
            return $tag;
        }
        $dom = new \DomDocument();
        $dom->loadHTML($tag, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
        $dom->getElementsByTagName('script')[0]->setAttribute("defer", "defer");
        $tag = $dom->saveHTML();
        return $tag;
    }, 10, 3);

?>

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