How to Combine Javascript Files In WordPress without Plugin?

How to Combine Javascript Files In WordPress without Plugin?

To combine the javascript files in WordPress with php and without Plugin, first, we must know all of the javascript files along with its handle name loaded on the web page without seeing the web source as we did in the manual way.

Knowing all of the javascript files loaded in a web page

We can find all of information about the javascript files loaded in a WordPress page in the WP_Scripts object.

This object is stored in the $wp_scripts variable, we can call this variable in any hook, such as wp_head, wp_print_scripts (now wp_enqueue_scripts), wp_footer, init, etc.

Each hook gives different results (the number of javascript file is different) depending on how we load the javascript files.

Clearly, there are two locations where we can find all javascript files, that are:

  1. In the header (tag). We can determine this using the wp_head hook. WordPress run this hook before loading the tag.
  2. In the footer (before the ) tag, We can determine this using the wp_footer hook. WordPress run this hook right before the closing tag.

You can try this by opening the functions.php file located in the theme folder that is being used.

add_action( 'wp_head', 'show_head_scripts', 9999 );
add_action( 'wp_footer', 'show_footer_scripts', 9999 );

// Appear on the top, before the header

function show_head_scripts(){
	global $wp_scripts;
	echo print_r($wp_scripts->done);
}

// Appear on the bottom, after the footer

function show_footer_scripts(){
	global $wp_scripts;
	echo print_r($wp_scripts->done);
}

Combining javascript files into one file in WordPress

To be able to combine all javascript files into one file, we must know javascript files that can be detected automatically before our web content displayed.

For this purpose, we can use the wp_enqueue_scripts hook, although this hook has some weaknesses (described later), at least this is the only trick we can use.

Furthermore, to combine javascript files into one file, the necessary steps are:

  1. Sort the javascript file handles according to its dependency, so that the merged file(later) can run without any errors. We can do this by invoking the all_deps method on the WP_Scripts object ( $wp_scripts->all_deps($wp_scripts->queue) )
  2. Get the code within javascript file (using file_get_contents) and combine each of that code. Don’t forget to include the localize script wp_localize_script (if any). This script can be found in the extra data $wp_scripts->registered['handle']->extra['data']
  3. Write the combined code into a file (using file_put_contents) and load it using the wp_enqueue_scripts function.
  4. Deregister all script/handle that have been combined, do this only after the combined process is completed, this is because, if we deregister a script, then its dependent script will also be deregistered too.

Next, to do all of those tasks, copy-paste the following code into your functions.php file:

add_action( 'wp_enqueue_scripts', 'merge_all_scripts', 9999 );
function merge_all_scripts() 
{
	global $wp_scripts;
	
	/*
		#1. Reorder the handles based on its dependency, 
			The result will be saved in the to_do property ($wp_scripts->to_do)
	*/

	$wp_scripts->all_deps($wp_scripts->queue);	
	
	$merged_file_location = get_stylesheet_directory() . DIRECTORY_SEPARATOR . 'merged-script.js';
	
	$merged_script	= '';
	
	// Loop javascript files and save to $merged_script variable
	foreach( $wp_scripts->to_do as $handle) 
	{
		/*
			Clean up url
		*/

		$src = strtok($wp_scripts->registered[$handle]->src, '?');
		
		/**
			#2. Combine javascript file.
		*/

		// If src is url http / https
		
		if (strpos($src, 'http') !== false)
		{
			// Get our site url
			$site_url = site_url();
		
			/*
				If we are on local server, then change url to relative path
			*/

			if (strpos($src, $site_url) !== false)
				$js_file_path = str_replace($site_url, '', $src);
			else
				$js_file_path = $src;
			
			/*
				To be able to use file_get_contents function we need to remove slash
			*/

			$js_file_path = ltrim($js_file_path, '/');
		}
		else 
		{			
			$js_file_path = ltrim($src, '/');
		}
		
		// Check wether file exists then merge

		if  (file_exists($js_file_path)) 
		{
			// #3. Check for wp_localize_script

			$localize = '';

			if (@key_exists('data', $wp_scripts->registered[$handle]->extra)) {

				$localize = $obj->extra['data'] . ';';
			}

			$merged_script .=  $localize . file_get_contents($js_file_path) . ';';
		}
	}
	
	// write the merged script into current theme directory

	file_put_contents ( $merged_file_location , $merged_script);
	
	// #4. Load the URL of merged file

	wp_enqueue_script('merged-script',  get_stylesheet_directory_uri() . '/merged-script.js');
	
	// 5. Deregister handles

	foreach( $wp_scripts->to_do as $handle ) 
	{
		wp_deregister_script($handle);
	}
}

Make sure the combine process succeeded

Further, check whether the merger process has been successful, open your web page, and view the source.

Then, make sure the merged script has no errors, to do so, open the browser’s Developer Tools (Press F12 on Google Chrome) and select the console tab.

If you find any errors, then double check the error message, make sure the error was not caused by the javascript code in our merged script.

Check the rest of the scripts that has not been merged

Check our web page source again and search if there are other scripts that are not being merged.

Some scripts are enqueued very late and we won’t be able to catch them earlier. This condition typically occurs on scripts that are loaded in the footer. If you use Akismet plugin and activate the comment post, then for sure you will face this as akismet.js is used in a comment form

How to solve it? the only way is we have to use the manual method

To do so, first, run wp_footer() hook and use $wp_scripts global variable to show all of the scripts, further, compare those scripts with scripts that have been combined to find the rest of the scripts, then, save those scripts to someplace e.q. a database table, next time, while merging javascript files, we include those files.

Why not using wp_footer() hook while merging javascript files?

It is true that on wp_footer hook, all javascript files can be detected, however, we can not use it because:

  1. We must deregister those scripts earlier add_action('wp_footer', 'deregister_scripts') while to be able to know all the files that are loaded, we need to run our function deregister_scripts  very late add_action('wp_footer','merge_all_scripts',999) Notice the number of 999, that number means that merge_all_scripts function will be loaded very late. Yet when it deregistered, the file will be lost, it can not be detected again.
  2. If we use wp_footer (), we can not put the merged file into the header, because it was too late, the header has already been executed.

Leave a Reply

About Rafasashi

Currently managing RECUWEB an IT Company providing cloud hosting and SaaS delivery model.

Related posts