Browsers process HTML, styles and scripts.
Since we are talking about scripts today. Your site might have a combination of scripts:
External Scripts, which are stored in a separate file and linked to the HTML.
<html>
<head>
<script src="scripts/main.js"></script>
</head>
</html>
Inline Scripts: These are scripts written directly within the HTML file, usually inside <script>
tags.
<html>
<head>
<script>
document.addEventListener('DOMContentLoaded', () => {
alert('This is an inline script example!');
});
</script>
</head>
</html>
Third Party Scripts which are hosted and maintained by an external provider
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
</html>
You can optimize those scripts using different methods and one of them is delaying the execution of scripts.
Delaying the execution of a script follows three steps
- Adding a specific script that will delay other scripts on the page.
- Indicate which scripts need to be delayed or not.
The scripts targeted by the script will not run immediately but at later time usually when there is user interactions such as clicks, scrolling, and hovering.
Delaying the loading of non-essential scripts, improving initial page load time and overall user experience.
Table of Contents
What Plugins Delay the Execution of Scripts?
The first time I heard about delaying execution of JavaScript files was when I was introduced to Flying Scripts.
Then WPRocket, Perfmatters, and other speed optimization plugins added similar features to their plugins.
What those and other speed optimization plugins do is:
- Add an inline script that delays the execution of other scripts.
- Add a class to all external and third-party scripts with contain the keywords you specify.
- When you have inline scripts, the scripts tend to be encoded.
If you don’t want to use a plugin to delay the execution of JavaScript, you can use the following code snippets.
Using the Flying Scripts Plugin Code
This is the code that you have to insert into your website footer or header to delay external scripts or third party scripts.
You can add the script to your website using a code snippets plugin or a mu-plugin.
This is the minified version of the script used by Flying Scripts:
<script type="text/javascript" id="flying-scripts">const loadScriptsTimer=setTimeout(loadScripts,2*1000);const userInteractionEvents=["mouseover","keydown","touchstart","touchmove","wheel"];userInteractionEvents.forEach(function(event){window.addEventListener(event,triggerScriptLoader,{passive:!0})});function triggerScriptLoader(){loadScripts();clearTimeout(loadScriptsTimer);userInteractionEvents.forEach(function(event){window.removeEventListener(event,triggerScriptLoader,{passive:!0})})}
function loadScripts(){document.querySelectorAll("script[data-type='lazy']").forEach(function(elem){elem.setAttribute("src",elem.getAttribute("data-src"))})}</script>
These are some examples that show how to wrap scripts manually:
This is the before:
<script src="https://www.example.com/scripts/myscript.js"></script>
And this is the after
<script type="text/javascript" data-type="lazy" data-src="https://www.example.com/scripts/myscript.js"></script>
If you use Optad360, this is how you delay both of those scripts:
<script async data-type="lazy" data-src="//get.optad360.io/sf/66de3669-901a-4936-acaf-aca945c17acb/plugin.min.js"></script>
<script async data-type="lazy" data-src="//cmp.optad360.io/items/c554b8cd-d417-4f47-bb44-858e45a171a7.min.js"></script></head>
If you want to delay a theme script, you have to dequeue the script and add it back using a header inserter plugin or simple code snippet.
<script data-type="lazy" data-src="https://wpsurfer.com/wp-content/themes/generatepress/assets/js/menu.min.js?ver=3.2.4"></script>
If the scripts are added by plugins and themes, you won’t be able to modify them easily but if you want to delay third-party scripts, you can modify them before adding them to the header or footer of your site.
I haven’t figure out how to target inline scripts with it and how to add the attribute automatically.
Perfmatter’s Script to Delay all External Scripts
I think Perfmatters is a plugin that every site must have.
When I have time off, I spend time figuring out how to optimize my site without it.
The options to delay the execution of all scripts was kinda hard to replace using code snippets.
This is the inline minified JavaScript that Perfmatters adds to the footer to delay all external and third-party scripts:
<script id="perfmatters-delayed-scripts-js">const pmDelayClick=true;const pmUserInteractions=["keydown","mousedown","mousemove","wheel","touchmove","touchstart","touchend"],pmDelayedScripts={normal:[],defer:[],async:[]},jQueriesArray=[],pmInterceptedClicks=[];var pmDOMLoaded=!1,pmClickTarget="";function pmTriggerDOMListener(){"undefined"!=typeof pmDelayTimer&&clearTimeout(pmDelayTimer),pmUserInteractions.forEach(function(e){window.removeEventListener(e,pmTriggerDOMListener,{passive:!0})}),document.removeEventListener("visibilitychange",pmTriggerDOMListener),"loading"===document.readyState?document.addEventListener("DOMContentLoaded",pmTriggerDelayedScripts):pmTriggerDelayedScripts()}async function pmTriggerDelayedScripts(){pmDelayEventListeners(),pmDelayJQueryReady(),pmProcessDocumentWrite(),pmSortDelayedScripts(),pmPreloadDelayedScripts(),await pmLoadDelayedScripts(pmDelayedScripts.normal),await pmLoadDelayedScripts(pmDelayedScripts.defer),await pmLoadDelayedScripts(pmDelayedScripts.async),await pmTriggerEventListeners(),document.querySelectorAll("link[data-pmdelayedstyle]").forEach(function(e){e.setAttribute("href",e.getAttribute("data-pmdelayedstyle"))}),window.dispatchEvent(new Event("perfmatters-allScriptsLoaded")),pmReplayClicks()}function pmDelayEventListeners(){let e={};function t(t,r){function n(r){return e[t].delayedEvents.indexOf(r)>=0?"perfmatters-"+r:r}e[t]||(e[t]={originalFunctions:{add:t.addEventListener,remove:t.removeEventListener},delayedEvents:[]},t.addEventListener=function(){arguments[0]=n(arguments[0]),e[t].originalFunctions.add.apply(t,arguments)},t.removeEventListener=function(){arguments[0]=n(arguments[0]),e[t].originalFunctions.remove.apply(t,arguments)}),e[t].delayedEvents.push(r)}function r(e,t){let r=e[t];Object.defineProperty(e,t,{get:r||function(){},set:function(r){e["perfmatters"+t]=r}})}t(document,"DOMContentLoaded"),t(window,"DOMContentLoaded"),t(window,"load"),t(window,"pageshow"),t(document,"readystatechange"),r(document,"onreadystatechange"),r(window,"onload"),r(window,"onpageshow")}function pmDelayJQueryReady(){let e=window.jQuery;Object.defineProperty(window,"jQuery",{get:()=>e,set(t){if(t&&t.fn&&!jQueriesArray.includes(t)){t.fn.ready=t.fn.init.prototype.ready=function(e){pmDOMLoaded?e.bind(document)(t):document.addEventListener("perfmatters-DOMContentLoaded",function(){e.bind(document)(t)})};let r=t.fn.on;t.fn.on=t.fn.init.prototype.on=function(){if(this[0]===window){function e(e){return e=(e=(e=e.split(" ")).map(function(e){return"load"===e||0===e.indexOf("load.")?"perfmatters-jquery-load":e})).join(" ")}"string"==typeof arguments[0]||arguments[0]instanceof String?arguments[0]=e(arguments[0]):"object"==typeof arguments[0]&&Object.keys(arguments[0]).forEach(function(t){delete Object.assign(arguments[0],{[e(t)]:arguments[0][t]})[t]})}return r.apply(this,arguments),this},jQueriesArray.push(t)}e=t}})}function pmProcessDocumentWrite(){let e=new Map;document.write=document.writeln=function(t){var r=document.currentScript,n=document.createRange();let a=e.get(r);void 0===a&&(a=r.nextSibling,e.set(r,a));var i=document.createDocumentFragment();n.setStart(i,0),i.appendChild(n.createContextualFragment(t)),r.parentElement.insertBefore(i,a)}}function pmSortDelayedScripts(){document.querySelectorAll("script[type=pmdelayedscript]").forEach(function(e){e.hasAttribute("src")?e.hasAttribute("defer")&&!1!==e.defer?pmDelayedScripts.defer.push(e):e.hasAttribute("async")&&!1!==e.async?pmDelayedScripts.async.push(e):pmDelayedScripts.normal.push(e):pmDelayedScripts.normal.push(e)})}function pmPreloadDelayedScripts(){var e=document.createDocumentFragment();[...pmDelayedScripts.normal,...pmDelayedScripts.defer,...pmDelayedScripts.async].forEach(function(t){var r=t.getAttribute("src");if(r){var n=document.createElement("link");n.href=r,n.rel="preload",n.as="script",e.appendChild(n)}}),document.head.appendChild(e)}async function pmLoadDelayedScripts(e){var t=e.shift();return t?(await pmReplaceScript(t),pmLoadDelayedScripts(e)):Promise.resolve()}async function pmReplaceScript(e){return await pmNextFrame(),new Promise(function(t){let r=document.createElement("script");[...e.attributes].forEach(function(e){let t=e.nodeName;"type"!==t&&("data-type"===t&&(t="type"),r.setAttribute(t,e.nodeValue))}),e.hasAttribute("src")?(r.addEventListener("load",t),r.addEventListener("error",t)):(r.text=e.text,t()),e.parentNode.replaceChild(r,e)})}async function pmTriggerEventListeners(){pmDOMLoaded=!0,await pmNextFrame(),document.dispatchEvent(new Event("perfmatters-DOMContentLoaded")),await pmNextFrame(),window.dispatchEvent(new Event("perfmatters-DOMContentLoaded")),await pmNextFrame(),document.dispatchEvent(new Event("perfmatters-readystatechange")),await pmNextFrame(),document.perfmattersonreadystatechange&&document.perfmattersonreadystatechange(),await pmNextFrame(),window.dispatchEvent(new Event("perfmatters-load")),await pmNextFrame(),window.perfmattersonload&&window.perfmattersonload(),await pmNextFrame(),jQueriesArray.forEach(function(e){e(window).trigger("perfmatters-jquery-load")});let e=new Event("perfmatters-pageshow");e.persisted=window.pmPersisted,window.dispatchEvent(e),await pmNextFrame(),window.perfmattersonpageshow&&window.perfmattersonpageshow({persisted:window.pmPersisted})}async function pmNextFrame(){return new Promise(function(e){requestAnimationFrame(e)})}function pmClickHandler(e){e.target.removeEventListener("click",pmClickHandler),pmRenameDOMAttribute(e.target,"pm-onclick","onclick"),pmInterceptedClicks.push(e),e.preventDefault(),e.stopPropagation(),e.stopImmediatePropagation()}function pmReplayClicks(){window.removeEventListener("touchstart",pmTouchStartHandler,{passive:!0}),window.removeEventListener("mousedown",pmTouchStartHandler),pmInterceptedClicks.forEach(e=>{e.target.outerHTML===pmClickTarget&&e.target.dispatchEvent(new MouseEvent("click",{view:e.view,bubbles:!0,cancelable:!0}))})}function pmTouchStartHandler(e){"HTML"!==e.target.tagName&&(pmClickTarget||(pmClickTarget=e.target.outerHTML),window.addEventListener("touchend",pmTouchEndHandler),window.addEventListener("mouseup",pmTouchEndHandler),window.addEventListener("touchmove",pmTouchMoveHandler,{passive:!0}),window.addEventListener("mousemove",pmTouchMoveHandler),e.target.addEventListener("click",pmClickHandler),pmRenameDOMAttribute(e.target,"onclick","pm-onclick"))}function pmTouchMoveHandler(e){window.removeEventListener("touchend",pmTouchEndHandler),window.removeEventListener("mouseup",pmTouchEndHandler),window.removeEventListener("touchmove",pmTouchMoveHandler,{passive:!0}),window.removeEventListener("mousemove",pmTouchMoveHandler),e.target.removeEventListener("click",pmClickHandler),pmRenameDOMAttribute(e.target,"pm-onclick","onclick")}function pmTouchEndHandler(e){window.removeEventListener("touchend",pmTouchEndHandler),window.removeEventListener("mouseup",pmTouchEndHandler),window.removeEventListener("touchmove",pmTouchMoveHandler,{passive:!0}),window.removeEventListener("mousemove",pmTouchMoveHandler)}function pmRenameDOMAttribute(e,t,r){e.hasAttribute&&e.hasAttribute(t)&&(event.target.setAttribute(r,event.target.getAttribute(t)),event.target.removeAttribute(t))}window.addEventListener("pageshow",e=>{window.pmPersisted=e.persisted}),pmUserInteractions.forEach(function(e){window.addEventListener(e,pmTriggerDOMListener,{passive:!0})}),pmDelayClick&&(window.addEventListener("touchstart",pmTouchStartHandler,{passive:!0}),window.addEventListener("mousedown",pmTouchStartHandler)),document.addEventListener("visibilitychange",pmTriggerDOMListener);</script>
Perfmatters inlines the script to the footer, you can add it to the header or footer using a plugin, code snippet or mu-plugin.
In case you want to take the PHP snippet solution, you can use this mu-plugin:
<?php
/*
Plugin Name: Custom Footer Inline Script
Description: Adds an inline script to the footer.
Author: TicoLibre
Version: 1.0
*/
function insert_inline_script_to_footer() {
echo "<script>
// Your custom inline JavaScript code here
</script>";
}
add_action('wp_footer', 'insert_inline_script_to_footer');
In case you are wondering why you should add that freaking long script to the footer.
I think that the developers behind Perfmatters didn’t want to create one more requests and affect your speed.
Converting the Inline Script into an External Script
In case you want an external script,
- Remove the script tags at the beginning and at the end of the code.
- Create a file and name it “whatever you want.js”, I named mine “delay.js”.
- Use a file manager and go to your wordpress installation.
- Create a folder called “files” or “js” inside the “uploads folder”
- Copy your file into that folder.
- Use the following code to output the js into the head of your pages and posts.
<?php
/*
Plugin Name: Header Scripts
Description: Add Scripts to the headers
Version: 1.0
Author: TicoLibre
*/
//Insert Styles and Scripts in the Header
function insert_custom_header_files() {
$domain = home_url();
// Enqueue script in the header
wp_enqueue_script( 'delay-js', $domain . '/wp-content/uploads/files/delay.js', array(), false, false );
}
add_action( 'wp_enqueue_scripts', 'insert_custom_header_files' );
Using this second method will reduce the size of the HTML but it will add a request to your site.
How to Add the Attribute Manually
Adding the previous code to your footer is not enough to lazy load all scripts, so you need to add the type=”pmdelayedscript” to your scripts so they are lazy loaded
You can do it manually if you don’t have that many scripts.
This is how you delay the execution of the Google Analytics Scripts:
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX" type="pmdelayedscript"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
</script>
Mu-Plugin to Add the Attribute to External Links
I bet that you don’t want to add type=”pmdelayedscript” to all scripts manually and sometimes you just can since some scripts are added by plugins and themes.
This PHP snippet will add type=”pmdelayedscript to all external scripts
<?php
/*
Plugin Name: Script Delayer
Description: Add Attribute to External Scripts
Version: 1.0
Author: TicoLibre
*/
// This will add the pmdelayedscript to all external scripts
function add_delayed_script_type($tag, $handle, $src) {
// Check if the current request is not in the admin area
if ( ! is_admin() ) {
// Define an array of keywords to exclude
$exclude_keywords = array( 'menu.min.js', 'delay.js' );
// Check if the script source contains any of the excluded keywords
foreach ($exclude_keywords as $keyword) {
if (strpos($src, $keyword) !== false) {
// If the source contains an excluded keyword, return the original script tag
return $tag;
}
}
// If the script is not excluded, add the type attribute with the value pmdelayedscript
$tag = str_replace('<script', '<script type="pmdelayedscript"', $tag);
}
return $tag;
}
add_filter('script_loader_tag', 'add_delayed_script_type', 10, 3);
The code won’t add the type=”pmdelayedscript to:
#1 | admin scripts |
#2 | inline scripts |
#4 | scripts you exclude |
So you won’t break anything on your WordPress dashboard and you won’t add the attribute to inline scripts.
What Script to Use?
I am not a coder so I don’t really know why Flying Scripts used those few lines of code and Perfmatters approach requires so many lines of code to accomplish the same thing.
I have used both ways to delay scripts and the one by Perfmatters helped me delay H5P scripts without breaking the plugin.
Try the one by Flying Scripts First and if delays what you wanted to delay. Don’t bother using the one by Perfmatters.
If you don’t want to complicate your lives dealing with this stuff, consider using Flying Scripts, FlyingPress or Perfmatters to speed up your site