On PDF load doesn't seem to get triggered

This loader is like a dummy - it always timesout at the setTimeout function. I can’t seem to trigger the onload of a PDF object load pdfObject.addEventListener('load', () => {})

<div>
    <!-- PDF wrapper with loader inside -->
    <div id="pdfWrapper" class="pdf-wrapper">
        <div id="loader" class="loader"></div>
        <object id="pdfObject" class="pdf" data="<?php echo $pdf ?>"></object>
    </div>

    <script>
        // Show loader initially
        const loader = document.getElementById('loader');
        const pdfWrapper = document.getElementById('pdfWrapper');
        const pdfObject = document.getElementById('pdfObject');

        // Event to hide loader when PDF is loaded
        pdfObject.addEventListener('load', function () {
            loader.style.display = 'none';
            pdfObject.style.display = 'block';
            pdfWrapper.style.height = "1000px";
        });

        // Optional: timeout to hide loader in case the PDF does not trigger the load event
        setTimeout(() => {
            loader.style.display = 'none';
            pdfObject.style.display = 'block';
        }, 10000); // Adjust timeout as needed
    </script>

</div>

How do I detect when the PDF has loaded ?
PS: I’m generating the PDF dynamically using PHP’s mPDF library.

I assume you have first checked that the PDF will output directly in a timely manner without error when not being included in this page using javascript.

Yes, the PDF is getting generate in the object element and it 100% readable. But PHP takes some good 20 seconds for the PDF to be generated for which I can’t seem to detect onload.

And you are timing out at 10 seconds.

Thing is, the timeout was supposed to be a backup. If I remove the timeout function the loader will run forever because the pdfObject.addEventListener('load', function () doesn’t seem to trigger.

Hello, this code will first try to hide the loader when the PDF is loaded. If the PDF does not trigger the load event within 10 seconds (adjustable with the setTimeout function), it will hide the loader anyway.

<div>
    <!-- PDF wrapper with loader inside -->
    <div id="pdfWrapper" class="pdf-wrapper">
        <div id="loader" class="loader"></div>
        <object id="pdfObject" class="pdf" data="<?php echo $pdf ?>"></object>
    </div>

    <script>
        // Show loader initially
        const loader = document.getElementById('loader');
        const pdfWrapper = document.getElementById('pdfWrapper');
        const pdfObject = document.getElementById('pdfObject');

        // Function to hide loader and display PDF
        function hideLoader() {
            loader.style.display = 'none';
            pdfObject.style.display = 'block';
            pdfWrapper.style.height = "1000px"; // Set the height as needed
        }

        // Event to hide loader when PDF is loaded
        pdfObject.onload = hideLoader;

        // Optional: timeout to hide loader in case the PDF does not trigger the load event
        setTimeout(hideLoader, 10000); // Adjust timeout as needed
    </script>
</div>

Is it not possible to detect when the PDF has actually finished processing ?

is there some reason it shouldnt have already loaded by the time the script executes?

Yes, mPDF generates the PDF via PHP on the server back-end, so I would assume all that happens on the sever well before there is any front-end page with any HTML or Javascript.
Surely the the front-end result is dependant on the outcome of what you do on the back-end.
If there is an error while creating the PDF, you detect and handle it. If not you server the PDF.
If the PDF creation hangs/times-out, that’s a PHP timeout.

You could try something like this but the issue is that browsers all handle things differently so cross-browser support and other issues get in the way.

    <div id="pdfWrapper" class="pdf-wrapper">
        <div id="loader" class="loader"></div>
        <object id="pdfObject" class="pdf" data="<?php echo $pdf ?>"></object>
    </div>
    <script>
        // Show loader initially
        const loader = document.getElementById('loader');
        const pdfWrapper = document.getElementById('pdfWrapper');
        const pdfObject = document.getElementById('pdfObject');

        // Function to hide loader and display PDF
        function hideLoader() {
            loader.style.display = 'none';
            pdfObject.style.display = 'block';
            pdfWrapper.style.height = '1000px'; // Set the height as needed
        }

        // Function to check if PDF is loaded based on the document title
        function checkPDFLoaded() {
            try {
                if (pdfObject.contentDocument.title !== '') {
                    hideLoader();
                } else {
                    setTimeout(checkPDFLoaded, 500); // Check again after 500ms
                }
            } catch (e) {
                // If accessing contentDocument throws an error, PDF is likely not yet loaded
                setTimeout(checkPDFLoaded, 500); // Check again after 500ms
            }
        }

        // Event to hide loader when PDF is loaded
        pdfObject.onload = hideLoader;

        // Optional: timeout to hide loader in case the PDF does not trigger the load event
        setTimeout(hideLoader, 10000); // Adjust timeout as needed

        // Start checking if PDF is loaded
        checkPDFLoaded();
    </script>

This was very much trial and error, but it seems to work.

HTML
Note: using a temporary dataset property called data-filename

<div id='pdfWrapper' class='pdf-wrapper'>
    <div id='loader' class='active'></div>
    <object id='pdfObject' class='pdf' data-filename='<?= $pdf_file ?>'></object>
</div>

Javascript
Using the fetch api and no setTimeouts

async function fetchPDF(url, target, callback) {
  try {
    const response = await fetch(url,
      {
        method: 'GET',
        mimeType: 'application/pdf',
        headers: {
          'Content-Type': 'application/pdf',
        },
      }
    );

    if (!response.ok) {
      throw new Error('Network response was not OK');
    }
    callback(target, await response.blob());
  } catch (error) {
    console.error('Error:', error);
  }
}

function onPDFLoaded(target, blob) {
  const loader = document.querySelector('#loader');
  loader.classList.remove('active');

  target.data = URL.createObjectURL(blob);
  URL.revokeObjectURL(blob);
}

document.addEventListener('DOMContentLoaded', () => {
  const pdfObject = document.querySelector('#pdfObject');
  const { filename } = pdfObject.dataset;

  fetchPDF(filename, pdfObject, onPDFLoaded);
});

I don’t know if this is flawed or bad practice. I tested it with a throttled network setting in chrome using laragon for my php files and it seemed to work as intended.

Another option which might be worth looking into

The load event is fired when the whole page has loaded, including all dependent resources such as stylesheets, scripts, iframes, and images.