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);
}