WPSurfer.com

How to Delay The Execution of JavaScript without a plugin

Published on December 8, 2022 | Updated on July 7, 2024

Browsers process HTML, styles and scripts.

Since we are talking about scripts today. Your site might have a three types 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 its execution.

Delaying the execution of a script requires two simple technical steps

  1. Adding a specific script that will delay other scripts on the page.
  2. 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.



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 added a similar feature to their plugins.

You will also find the feature on FlyingPress (Obviously since Gijo Varghese is the developer behind Flying Scripts)

What those and other speed optimization plugins do is:

  1. Add an inline script that delays the execution of other scripts.
  2. Add a class to all external and third-party scripts that contain the keyword or keywords you specify.
  3. When you have inline scripts, the scripts tend to be encoded.

For those who don’t want to complicate their lives and with more stuff to do, I recommend using Perfmatters since it is cheaper than the rest of the solutions out there.

Click the image below, speed up your WordPress website and support more honest reviews and guides like this one.

Perfmatters WordPress

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 plugin, simple code snippet or mu-plugin.

<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 without dequeuing and enqueuing them.

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 figured out how to:

  • Target inline scripts since you need to encode the scripts.
  • Add the attribute automatically so you can add the scripts the way you are currently doing it.

Perfmatter’s Script to Delay all External Scripts

Delay Scripts without a Plugin

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.

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 footer using a plugin, code snippet or using my 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 site speed.


How to Convert the Inline Script into an External Script

In case you want an external script,

  1. Remove the script tags at the beginning and at the end of the code.
  2. Create a file and name it “whatever you want.js”, I named mine “delay.js”.
  3. Use a file manager and go to your WordPress installation.
  4. Create a folder called “files” or “js” inside the “uploads folder”
  5. Copy your file into that folder.
  6. 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 an additional 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’t do it 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:

#1admin scripts
#2inline scripts
#4scripts you exclude

This plugin 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

Perfmatters all the way.

Perfmatters WordPress

Speed up your WordPress site Today


Manuel Campos

Manuel Campos

I am José Manuel. I am writing about things I know and things that I am learning about WordPress. I hope you find the content of this blog useful.