19

I'm trying to use the wp_register_script and wp_enqueue_script FUNCTION on WordPress to enqueue a script, which has two attributes: "integrity" and "crossorigin".

Normally I use PHP and my code looks like:

wp_register_script('jquery', 'http' . ($_SERVER['SERVER_PORT'] == 443 ? 's' : '') . '://code.jquery.com/jquery-3.1.1.slim.min.js', false, null);
wp_enqueue_script('jquery');

With any other script. The wp_register_script takes five parameters (in this case four) $handle, $src, $deps, $ver ($media). I'm wondering where I can add the two attributes. I've already tried:

wp_register_script('jquery', 'http' . ($_SERVER['SERVER_PORT'] == 443 ? 's' : '') . '://code.jquery.com/jquery-3.1.1.slim.min.js'.'integrity="sha384-A7FZj7v+d/sdmMqp/nOQwliLvUsJfDHW+k9Omg/a/EheAdgtzNs3hpfag6Ed950n"', false, null);
    wp_enqueue_script('jquery');

But it didn't work.

Anyone who had the same problem? This is the original script from bootstrap 4, which also has bootstrap and tether with the same attributes (integrity and crossorigin) so, since it is pretty new, any kind of help will be very appreciated.

  • 1
    **See also: [How to add crossorigin and integrity to wp_register_style? (Font Awesome 5)](https://wordpress.stackexchange.com/a/319773/212712)** – Sebastian Norr Sep 30 '21 at 14:15
  • A lot of examples here are either bad practice or doesn't take caching and other plugins into account, see the above link for my example and a simple WordPress standards complaint way to do this. Also if you want to include/publish your WP theme or plugin on wp.org than you're required to adhere to the WP coding standards. – Remzi Cavdar Feb 01 '23 at 11:17

7 Answers7

31

I just cracked this by accident. I've had to use conditionals on some CSS files for ancient Internet Explorer versions and figured I may as well try putting an array in the same wp_script_add_data function that I used. It works!

    wp_register_script('jquery3', 'https://code.jquery.com/jquery-3.3.1.min.js', array(), '3.3.1', true); // jQuery v3
    wp_enqueue_script('jquery3');
    wp_script_add_data( 'jquery3', array( 'integrity', 'crossorigin' ) , array( 'sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=', 'anonymous' ) );

Reference: https://developer.wordpress.org/reference/functions/wp_script_add_data/#parameters

For completeness: If adding jQuery v3, you also need Migrate v3: http://jquery.com/download/

cherryaustin
  • 456
  • 4
  • 9
  • 12
    It appears this solution unfortunately doesn't work any more. `wp_script_add_data` appears to only work for specifying "conditional" wrappers for scripts now. (WP 5.1, PHP 7.2.15) – RalphORama Mar 07 '19 at 06:35
25

You can use the script_loader_tag hook (the main part is actually not my code, but I honestly don't remember where I got it, probably somewhere here on SO or WP Stack Exchange):

add_filter( 'script_loader_tag', 'add_attribs_to_scripts', 10, 3 );
function add_attribs_to_scripts( $tag, $handle, $src ) {

// The handles of the enqueued scripts we want to defer
$async_scripts = array(
    'jquery-migrate',
    'sharethis',
);

$defer_scripts = array( 
    'contact-form-7',
    'jquery-form',
    'wpdm-bootstrap',
    'frontjs',
    'jquery-choosen',
    'fancybox',
    'jquery-colorbox',  
    'search'
);

$jquery = array(
    'jquery'
);

if ( in_array( $handle, $defer_scripts ) ) {
    return '<script src="' . $src . '" defer="defer" type="text/javascript"></script>' . "\n";
}
if ( in_array( $handle, $async_scripts ) ) {
    return '<script src="' . $src . '" async="async" type="text/javascript"></script>' . "\n";
}
if ( in_array( $handle, $jquery ) ) {
    return '<script src="' . $src . '" integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ=" crossorigin="anonymous" type="text/javascript"></script>' . "\n";
}
return $tag;
} 
HynekS
  • 2,738
  • 1
  • 19
  • 34
4

This is the correct syntax:

<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js" integrity="sha256-T0Vest3yCU7pafRw9r+settMBX6JkKN06dqBnpQ8d30=" crossorigin="anonymous"></script>

"integrity" and "crossorgin" are not part of the URL. They are part of the script tag.

Unfortunately Wordpress can not handle attributes on enqueued scripts at the moment.
But they working on it... Status: needs-testing :-)
=> Ticket 22249 / Wordpress Core

MadMonday
  • 49
  • 2
0

Thanks for all the postings, they really helped. I did roll my own version to give it some structure and make it easier to read and update. Use enqueue as normal, use script for CSS files with a false tag at the end so that it loads at the top. Though it can probably be simplified somewhat.

add_filter('script_loader_tag', 'add_attributes_to_script', 10, 3); 

function add_attributes_to_script( $tag, $handle, $src ) {

        $scripts_to_load = array (

            (0) => Array
              (
                ('name') => 'popper_min_js',
                ('type') => '<script type="text/javascript" src="',         
                ('integrity') => 'sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49',
                ('close') => '></script>'
              ),

             (1) => Array
               (
                ('name') => 'bootstrap_min_js', 
                ('type') => '<script type="text/javascript" src="',
                ('integrity') => 'sha384-smHYKdLADwkXOn1EmN1qk/HfnUcbVRZyYmZ4qpPea6sjB/pTJ0euyQp0Mk8ck+5T',
                ('close') => '></script>'
               )
        );  

        $key = array_search($handle, array_column($scripts_to_load, 'name'));

        if ( FALSE !== $key){

            $tag = $scripts_to_load[$key]['type'] . esc_url($src) . '" integrity="' . $scripts_to_load[$key]['integrity'] .'" crossorigin="anonymous"' . $scripts_to_load[$key]['close'] . "\n";

        }
        return $tag;
    }
wpNewby
  • 1
  • 3
  • You really shouldn't use script loading for .css, it'll screw up many (if not all,) caching plugins. – InanisAtheos Oct 30 '18 at 12:15
  • Indeed, this should be done with `add_filter`, see: https://wordpress.stackexchange.com/questions/317035/how-to-add-crossorigin-and-integrity-to-wp-register-style-font-awesome-5/319773 – Remzi Cavdar Feb 01 '23 at 11:14
0

This is well explained here:

For each enqueued script you need to add a filter which adds proper attributes;

/**
* load custom JS
*/
add_action('wp_enqueue_scripts', function() {
    wp_enqueue_style('jquery', 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js', [], null);

    add_filter('style_loader_tag', __NAMESPACE__ . '\\add_jquery_sri', 10, 2); 
});
 
/**
* add SRI attributes to jQuery style loader element

* @param string $html
* @param string $handle
* @return string
*/
function add_jquery_sri($html, $handle) {
    if ($handle === 'jQuery') {
        $html = str_replace(' />', ' integrity="sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg==" crossorigin="anonymous" />', $html);
    }
 
    return $html;
}

URL & hash taken from https://cdnjs.com/libraries/jquery All praise to the authors!

emendelski
  • 317
  • 2
  • 12
0

I've created a function that works just like I imagined wp_script_add_data would work if it was used to add attributes. ( it's not, read below)

Hope you find it useful! ☺️

function add_script_attributes($handle, $keys, $values) {
  $GLOBALS['targeted_script_handle'] = $handle;
  $GLOBALS['targeted_script_keys'] = $keys;
  $GLOBALS['targeted_script_values'] = $values;

  add_filter( 'script_loader_tag', function ( $tag, $handle, $src ) {
    if ($GLOBALS['targeted_script_handle'] !== $handle) {
      return $tag;
    }

    $attrs = array();

    if (is_array($GLOBALS['targeted_script_keys'])) {
      foreach ($GLOBALS['targeted_script_keys'] as $key => $value) {
        $target_value = '';

        if (!empty($GLOBALS['targeted_script_values'])) {
          if (is_array($GLOBALS['targeted_script_values'])) {
            $target_value = array_key_exists($key, $GLOBALS['targeted_script_values']) ? $GLOBALS['targeted_script_values'][$key] : '';
          } else {
            if ($key === 0) {
              $target_value = $GLOBALS['targeted_script_values'];
            }
          }
        }

        array_push($attrs, sprintf('%s="%s"', $value, esc_attr($target_value)));
      }
    } else {
      $target_value = '';

      if (!empty($GLOBALS['targeted_script_values'])) {
        if (is_array($GLOBALS['targeted_script_values'])) {
          $target_value = $GLOBALS['targeted_script_values'][0];
        } else {
          $target_value = $GLOBALS['targeted_script_values'];
        }
      }

      array_push($attrs, sprintf('%s="%s"', $GLOBALS['targeted_script_keys'], esc_attr($target_value)));
    }
    $GLOBALS['targeted_script_handle'] = null;
    $GLOBALS['targeted_script_keys'] = null;
    $GLOBALS['targeted_script_values'] = null;

    if (empty($attrs)) {
      return $tag;
    }

    return preg_replace('/^<script/i', '<script '. implode(' ', $attrs), $tag);
  }, 10, 3);
}

NOTE: This function doesn't know which attributes <script> element already has, and it doesn't remember which attributes you added. Can be refactored as a singleton class which memorizes the handles with added attributes, though.

Usage Example 1

wp_enqueue_script('jquery');
add_script_attributes('jquery', 'crossorigin', 'anonymous');
add_script_attributes('jquery', 'integrity', 'sha256- 
 QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=');

This renders as

<script integrity="sha256- 
 QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=" crossorigin="anonymous" src="path_to_jquery.js" id="jquery-js"></script>

Usage Example 2

wp_enqueue_script('jquery');
add_script_attributes('jquery', array('crossorigin', 'integrity'), array('anonymous', 'sha256- 
 QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc='));

This also renders as

<script integrity="sha256- 
 QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=" crossorigin="anonymous" src="path_to_jquery.js" id="jquery-js"></script>

Usage Example 3

wp_enqueue_script('jquery');
add_script_attributes('jquery', array('crossorigin', 'integrity', 'foo', 'baz'), array('anonymous', 'sha256- 
 QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=', 'bar'));

This will render as

<script integrity="sha256- 
 QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=" crossorigin="anonymous" foo="bar" baz="" src="path_to_jquery.js" id="jquery-js"></script>

TL;DR;

wp_script_add_data() function is NOT a way to go (doc). It is only accepts conditional as a key, and is used to add HTML browser conditional comments so your script will run on specific browsers. This was useful at the time when IE was around, thank the divine Lord it's not anymore.

wp_print_script_tag() function MIGHT be a way to go (doc), if you don't want to use the queue and you bind it to wp_head and wp_footer actions. It is used to print out a <script> element with provided attributes.

script_loader_tag filter IS the way to go (doc). In WP 6.0.2 on line 406 in wp-includes/class.wp-scripts.php (permalink) this filter is triggered and is used to filter individual script tags before they are "echo"ed to your templates. If I read comments here carefully I wouldn't have to dig through the source... ‍♂️ But, TMYK!

P.S. I wonder why they didn't refactor wp-includes/class.wp-scripts.php to use wp_print_script_tag , probably cause they have this filter script_loader_src, but still it could be made to work.

Bonus

The almost identical function can be used for styles with style_loader_tag (doc, permalink)! It can be made as one function but I'm too lazy... (just like guys that contribute to WP).

function add_style_attributes($handle, $keys, $values) {
  $GLOBALS['targeted_script_handle'] = $handle;
  $GLOBALS['targeted_script_keys'] = $keys;
  $GLOBALS['targeted_script_values'] = $values;

  add_filter( 'style_loader_tag', function ( $tag, $handle, $href, $media ) {
    if ($GLOBALS['targeted_script_handle'] !== $handle) {
      return $tag;
    }

    $attrs = array();

    if (is_array($GLOBALS['targeted_script_keys'])) {
      foreach ($GLOBALS['targeted_script_keys'] as $key => $value) {
        $target_value = '';

        if (!empty($GLOBALS['targeted_script_values'])) {
          if (is_array($GLOBALS['targeted_script_values'])) {
            $target_value = array_key_exists($key, $GLOBALS['targeted_script_values']) ? $GLOBALS['targeted_script_values'][$key] : '';
          } else {
            if ($key === 0) {
              $target_value = $GLOBALS['targeted_script_values'];
            }
          }
        }

        array_push($attrs, sprintf('%s="%s"', $value, esc_attr($target_value)));
      }
    } else {
      $target_value = '';

      if (!empty($GLOBALS['targeted_script_values'])) {
        if (is_array($GLOBALS['targeted_script_values'])) {
          $target_value = $GLOBALS['targeted_script_values'][0];
        } else {
          $target_value = $GLOBALS['targeted_script_values'];
        }
      }

      array_push($attrs, sprintf('%s="%s"', $GLOBALS['targeted_script_keys'], esc_attr($target_value)));
    }
    $GLOBALS['targeted_script_handle'] = null;
    $GLOBALS['targeted_script_keys'] = null;
    $GLOBALS['targeted_script_values'] = null;

    if (empty($attrs)) {
      return $tag;
    }

    return preg_replace('/^<link/i', '<link '. implode(' ', $attrs), $tag);
  }, 10, 4);
}
stamat
  • 1,832
  • 21
  • 26
-1

You can add a CDN with integrity with Crossorigin by using add_action :

function add_jquery(){
echo '<script src="https://code.jquery.com/jquery-3.5.1.js" integrity="sha256- 
 QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=" crossorigin="anonymous"></script>';
 }

 add_action( 'wp_head', 'add_jquery' );
tom hikari
  • 357
  • 1
  • 3