WordPress.org

Make WordPress Core

Opened 18 months ago

Last modified 3 months ago

#22249 new feature request

Add ability to set or remove attributes on enqueued scripts and styles.

Reported by: ryanve Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version:
Component: Script Loader Keywords: dev-feedback
Focuses: Cc:

Description

I think it should be easier to customize the loading of scripts and styles (easier to customize the markup generated by the script/style system). Proposed solutions:

Solution 1: Allow wp_enqueue_script, wp_enqueue_style, wp_register_script, wp_register_style to accept an array of attributes as the $src parameter. For example:

wp_enqueue_script( 'my-plugin', array(
    'src' => 'http://example.com/js/app.js'
    'defer' => ''
    'data-my-plugin' => 'custom data attr value'
), array('jquery'), null, true );

Solution 2: Add a filter before the markup is generated that allows devs to filter the attributes while they are in array format. For example:

add_filter('script_loader_attrs', function ($attrs, $handle) {
    unset ( $attrs['type'] );
    'my-plugin' === $handle and $attrs['data-my-plugin'] = 'plugin data';
    $attrs['src'] = remove_query_arg( $attrs['src'] );
    return $attrs;
}, 12, 2);

In class.wp-scripts.php it might look something like:

$attrs = (array) apply_filters('script_loader_attrs', $attrs, $handle);

and/or:

$attrs = (array) apply_filters("{$handle}_script_loader_attrs", $attrs );

I imagine that solution 2 would be easier to implement than 1, and 2 allows for themes/plugins to modify scripts/styles w/o re-registering resources.

The key feature of both solutions is the ability to modify the attrs while in array format. There are other ways that one could achieve the same results, but the array is by far the cleanest. Dirty alternatives include:

  • Use preg_replace() on the markup after it is generated (see #22245)
  • Use output buffers and PHP's DOMElement interface
  • Filter away the "print_scripts_array" and regenerate the markupmanually.)

Change History (9)

comment:1 alex-ye18 months ago

  • Cc nashwan.doaqan@… added

Yes It might be a good idea , I was needing something like this when I was using LessCSS sometimes you need to change "rel" value to "stylesheet/less" .

comment:2 jtsternberg17 months ago

  • Cc justin@… added

This is also needed for some 3rd party scripts. Outbrain looks like:

<script type="text/javascript" async="async" src="http://widgets.outbrain.com/outbrain.js"></script>

comment:3 bpetty17 months ago

Related: #19742

comment:4 ryanve15 months ago

#23236 would make this easier.

comment:5 raphaelvalerio15 months ago

This feature would also be useful for removing type='text/javascript', since it's redundant in HTML5. As far as I know, currently the only way of using a <script> element without the type attribute is to hardcode it into your theme.

Styles do have a filter hook you can use to alter the markup:

add_filters( 'style_loader_tag' )

(see wp-includes/class.wp-styles.php, line 83)

I haven't tried hooking into this filter, but I think the whole <link> element is a string, not an array, which could be improved upon. But at least it's hookable.

Scripts, on the other hand, have no filter to hook. Not only that, but the <script> element is printed to the site with a simple echo statement right in the function, so there's no variable to modify.

This happens in wp-includes/class.wp-scripts.php, in the do_item() function on line 125. I toyed around with adding in a filter. Although this doesn't correspond exactly to what you're asking, here's one solution for the script tag side of things:

Replace lines 122-125 of wp-includes/class.wp-scripts.php with:

$attrs = array( 'type' => 'text/javascript' );

$attrs = apply_filters( 'script_loader_attrs', $attrs );

$attrs_string = '';

if(!empty( $attrs ) ) {
	foreach ( $attrs as $key => $value ) {
		$attrs_string .= "$key='" . esc_attr( $value ) . "' ";
	}
	$attrs = ltrim( $attrs_string );
}

if ( $this->do_concat )
	$this->print_html .= "<script " . $attrs . " src='$src'></script>\n";
else
	echo "<script " . $attrs . "src='$src'></script>\n";

You can then use add_filter in your themes/plugins like so:

add_filter( 'script_loader_attrs', 'my_function' );

function my_function( $attrs ) {
   $attrs = array('async' => 'async', 'charset' => 'utf8') // whatever attributes you want

   // alternatively, eliminate type='text/javascript' by emptying $attrs:
   // $attrs = '';

   return $attrs;
}

The code above will always produce XHTML type attributes, e.g. async='async'. I'd need to add in a little code in the loop if the author wanted the more succinct async HTML5 version of the boolean.

For backward compatibility, the code will default back to including type='text/javascript' if no filter hooks in.

If people are interested in trying this solution, I could upload a diff file.

comment:6 raphaelvalerio15 months ago

  • Cc raphaelvalerio added

comment:7 Volker_E.13 months ago

  • Cc Volker_E. added

comment:8 lkraav8 months ago

  • Cc leho@… added

comment:9 nacin3 months ago

  • Component changed from General to Script Loader
Note: See TracTickets for help on using tickets.