Selectively loading plugin scripts

I’m working on a site with a performance budget, so I want to be sure to only load certain assets as needed (e.g. via IntersectionObserver, etc).

I know I can omit, say, jQuery, by setting head_js(false), but is there any way I can selectively choose which assets to return with fire_plugin_hook('public_head')?

I’ve tried to get the output using the methods below but they both return empty strings from header.php, so I might not be understanding the documentation.

<?php
echo get_specific_plugin_hook_output('Geolocation','public_head', array('view'=>$this));
echo get_plugin_hook_output('public_head', array('view'=>$this));
?>

The trouble is that generally things that are adding scripts are using the “queue” functions, so they don’t actually output anything themselves.

You should be able to use get_specific_plugin_hook_output anyway, but you won’t actually use its output. Just making the call(s) before head_js/head_css actually runs should let things get added to the queue.

That makes sense. I’m actually trying to reduce the contents of the queue and was hoping I could get some kind of output like:

queued_styles: ['/path/to/file.css', '/path/to/file.css'],
queued_scripts: ['/path/to/file.js', '/path/to/file.js'] 

… which would allow me to asynchronously micro-manage when/if/how each asset is loaded.

Right now, I’m just omitting the call to fire_plugin_hook('public_head') and loading select plugin assets manually, but obviously whitelisting doesn’t scale very well. A blacklist would work fine, though (e.g. load all the stuff except for Geolocation scripts).

Is there any way to view/modify the contents of the queue before head_js() and head_css() are called?

The headScript and headStyle helpers are what store the queue. They implement IteratorAggregate and ArrayAccess so you should be get at their contents with foreach or similar, and index/unset on them like an array.

Looking at the Zend docs (and searching the web for examples), I’m not seeing a way to read the queue before altering it.

I don’t see anything here I can use…

$this->headScript();

This seems like it might be closer to reflecting what I’m looking for but also not quite it…

$this->getAssetPaths();

Scratch that, I forgot to re-add fire_plugin_hook

fire_plugin_hook('public_head',array('view'=>$this));
$a = $this->headScript();
foreach($a as $b){
  var_dump($b);
}

Ok, so this looks like the way to do it. For whatever reason, the index on the array of assets was getting reset with each unset(), which was sort of confusing/unexpected, so rather than dealing with that, I just set all the properties for each asset to null.

fire_plugin_hook('public_head',array('view'=>$this));
// Omit Geolocation scripts
$scripts = $this->headScript();
foreach($scripts as $key=>$file){
	if(strpos($file->attributes['src'], '/plugins/Geolocation') !== false){
		$scripts[$key]->type = null;
		$scripts[$key]->attributes['src'] = null;
		$scripts[$key]->attributes['source'] = null;
	}
}
// Omit Geolocation styles
$styles = $this->headLink();
foreach($styles as $key=>$file){
	if(strpos($file->href, '/plugins/Geolocation') !== false){
		$styles[$key]->href = null;
		$styles[$key]->type = null;
		$styles[$key]->rel = null;
		$styles[$key]->media = null;
		$styles[$key]->conditionalStylesheet = null;
	}
}
head_css(); 
head_js(false);

In case anyone is ever trying to do this, here’s a handy function you can use in your theme.

/*
** Remove select plugin/core assets from queue
*/
function assets_blacklist($view=null,$paths=array()){
	if($view){
		$scripts = $view->headScript();
		foreach($scripts as $key=>$file){
			foreach($paths as $path){
				if(strpos($file->attributes['src'], $path) !== false){
					$scripts[$key]->type = null;
					$scripts[$key]->attributes['src'] = null;
					$scripts[$key]->attributes['source'] = null;
				}	
			}
		}
		$styles = $view->headLink();
		foreach($styles as $key=>$file){
			foreach($paths as $path){
				if(strpos($file->href, $path) !== false){
					$styles[$key]->href = null;
					$styles[$key]->type = null;
					$styles[$key]->rel = null;
					$styles[$key]->media = null;
					$styles[$key]->conditionalStylesheet = null;
				}
			}
		}
	}
}

Usage: place before head_css and head_js and after fire_plugin_hook('public_head')

	fire_plugin_hook('public_head',array('view'=>$this));
	assets_blacklist($this,array('/plugins/Geolocation','admin-bar','family=Arvo:400'));
	echo head_css(); 
	echo head_js();

In the example usage, I’m removing all Geolocation plugin scripts as well as the remote font request and stylesheet for the admin bar.

I also found it useful to only load jQuery where it’s going to be used (in my case, for the search form), which you can do like so…

$includeJQuery = (0 === strpos(current_url(), '/items/search')) ? true : false;
echo head_js($includeJQuery);

Hope this is helpful to someone at some point.