The JavaScript ProgressEvent Object: Monitoring Resource Loading

The ProgressEvent object in JavaScript provides detailed information about the progress of an ongoing operation, such as loading a resource (e.g., image, script, or data file) or uploading data (e.g., submitting a form). This object is particularly useful when working with XMLHttpRequest (XHR), fetch API, and other resource loading mechanisms. Understanding ProgressEvent allows developers to provide meaningful feedback to users about the status of these operations.

Purpose of the ProgressEvent Object

The primary purpose of the ProgressEvent object is to:

  • Track the progress of resource loading or data transfer operations.
  • Provide information about the amount of data loaded or transferred.
  • Indicate whether the total size of the resource is known.
  • Signal the completion or abortion of the operation.

Syntax

A ProgressEvent is created automatically by the browser when a progress-related event occurs. You typically don’t create it directly but access it via event listeners attached to the target element (e.g., XMLHttpRequest, HTMLMediaElement).

target.addEventListener("progress", function (event) {
  // Access ProgressEvent properties here
});

Where target is the element dispatching the progress event.

Properties of the ProgressEvent Object

The ProgressEvent object has several properties that provide information about the progress of the operation:

Property Type Description
`lengthComputable` Boolean Indicates whether the total size of the resource being loaded or transferred is known. If `true`, the `total` property contains a meaningful value.
`loaded` Number The amount of data loaded or transferred so far, in bytes.
`total` Number The total size of the resource being loaded or transferred, in bytes. Meaningful only if `lengthComputable` is `true`.
`target` Object The element to which the listener is attached (e.g., XMLHttpRequest object).
`type` String The type of event (e.g., “progress”, “loadstart”, “load”, “error”, “abort”).

Types of Progress Events

Several events use the ProgressEvent object:

  • loadstart: Dispatched when the loading process starts.
  • progress: Dispatched periodically as data is being loaded or transferred.
  • load: Dispatched when the loading process is complete and successful.
  • error: Dispatched when an error occurs during the loading process.
  • abort: Dispatched when the loading process is aborted.
  • timeout: Dispatched when the loading process times out.
  • loadend: Dispatched when the loading process completes, regardless of whether it was successful or not.

Examples of Using ProgressEvent

Let’s explore how to use the ProgressEvent object in different scenarios, from basic resource loading to more complex file uploads.

Monitoring Image Loading Progress

This example demonstrates how to monitor the loading progress of an image and display a progress bar.

<div id="imageProgressContainer">
  <img id="imageToLoad" src="" alt="Loading Image" style="display: none" />
  <div id="imageProgressBar">
    <div id="imageProgress"></div>
  </div>
  <p id="imageProgressText">Loading: 0%</p>
</div>

<style>
  #imageProgressBar {
    width: 200px;
    height: 20px;
    background-color: #eee;
    border: 1px solid #ccc;
  }

  #imageProgress {
    width: 0%;
    height: 100%;
    background-color: #4caf50;
  }
</style>

<script>
  const imageToLoad_img = document.getElementById("imageToLoad");
  const imageProgressBar_img = document.getElementById("imageProgressBar");
  const imageProgress_img = document.getElementById("imageProgress");
  const imageProgressText_img = document.getElementById("imageProgressText");

  imageToLoad_img.addEventListener("loadstart", function () {
    imageProgressText_img.textContent = "Loading started...";
  });

  imageToLoad_img.addEventListener("progress", function (event) {
    if (event.lengthComputable) {
      const percentComplete = (event.loaded / event.total) * 100;
      imageProgress_img.style.width = percentComplete + "%";
      imageProgressText_img.textContent =
        "Loading: " + percentComplete.toFixed(2) + "%";
    } else {
      imageProgressText_img.textContent = "Loading...";
    }
  });

  imageToLoad_img.addEventListener("load", function () {
    imageProgressText_img.textContent = "Loading complete!";
  });

  imageToLoad_img.addEventListener("error", function () {
    imageProgressText_img.textContent = "Loading failed.";
  });

  imageToLoad_img.src = "https://dummyimage.com/600x400/4caf50/fff"; // Replace with your image URL
</script>

This example demonstrates how to use the loadstart, progress, load, and error events to provide feedback on the image loading process.

Monitoring File Upload Progress with XMLHttpRequest

This example demonstrates how to monitor the progress of a file upload using XMLHttpRequest and the upload property.

<input type="file" id="fileInput" />
<button id="uploadButton">Upload</button>
<div id="uploadProgressBar">
  <div id="uploadProgress"></div>
</div>
<p id="uploadProgressText">Upload: 0%</p>

<style>
  #uploadProgressBar {
    width: 300px;
    height: 20px;
    background-color: #eee;
    border: 1px solid #ccc;
  }

  #uploadProgress {
    width: 0%;
    height: 100%;
    background-color: #2196f3;
  }
</style>

<script>
  const fileInput_xhr = document.getElementById("fileInput");
  const uploadButton_xhr = document.getElementById("uploadButton");
  const uploadProgressBar_xhr = document.getElementById("uploadProgressBar");
  const uploadProgress_xhr = document.getElementById("uploadProgress");
  const uploadProgressText_xhr = document.getElementById("uploadProgressText");

  uploadButton_xhr.addEventListener("click", function () {
    const file_xhr = fileInput_xhr.files[0];
    if (file_xhr) {
      const xhr = new XMLHttpRequest();
      const formData = new FormData();
      formData.append("file", file_xhr);

      xhr.upload.addEventListener("loadstart", function () {
        uploadProgressText_xhr.textContent = "Upload started...";
      });

      xhr.upload.addEventListener("progress", function (event) {
        if (event.lengthComputable) {
          const percentComplete = (event.loaded / event.total) * 100;
          uploadProgress_xhr.style.width = percentComplete + "%";
          uploadProgressText_xhr.textContent =
            "Upload: " + percentComplete.toFixed(2) + "%";
        } else {
          uploadProgressText_xhr.textContent = "Upload in progress...";
        }
      });

      xhr.addEventListener("load", function () {
        uploadProgressText_xhr.textContent = "Upload complete!";
      });

      xhr.addEventListener("error", function () {
        uploadProgressText_xhr.textContent = "Upload failed.";
      });

      xhr.open("POST", "/upload", true); // Replace with your upload URL
      xhr.send(formData);
    } else {
      uploadProgressText_xhr.textContent = "Please select a file.";
    }
  });
</script>

This example uses the XMLHttpRequest object to upload a file and monitors the upload progress using the upload property’s progress event.

Note: The /upload endpoint in the xhr.open() method is a placeholder. You need to replace it with an actual server-side endpoint that handles file uploads. ⚠️

Monitoring Download Progress with fetch API

While the fetch API doesn’t directly provide progress events like XMLHttpRequest, you can still monitor download progress by reading the response body as a stream and tracking the amount of data received.

<button id="downloadButton">Download File</button>
<div id="downloadProgressBar">
  <div id="downloadProgress"></div>
</div>
<p id="downloadProgressText">Download: 0%</p>

<style>
  #downloadProgressBar {
    width: 300px;
    height: 20px;
    background-color: #eee;
    border: 1px solid #ccc;
  }

  #downloadProgress {
    width: 0%;
    height: 100%;
    background-color: #9c27b0;
  }
</style>

<script>
  const downloadButton_fetch = document.getElementById("downloadButton");
  const downloadProgressBar_fetch = document.getElementById(
    "downloadProgressBar"
  );
  const downloadProgress_fetch = document.getElementById("downloadProgress");
  const downloadProgressText_fetch = document.getElementById(
    "downloadProgressText"
  );

  downloadButton_fetch.addEventListener("click", async function () {
    const url = "https://dummyimage.com/1024x768/9c27b0/fff"; // Replace with your download URL

    try {
      const response = await fetch(url);
      const contentLength = response.headers.get("content-length");

      if (!contentLength) {
        downloadProgressText_fetch.textContent =
          "Content length not available.";
        return;
      }

      const total = parseInt(contentLength, 10);
      let loaded = 0;

      const reader = response.body.getReader();

      const stream = new ReadableStream({
        start(controller) {
          function push() {
            reader.read().then(({ done, value }) => {
              if (done) {
                controller.close();
                downloadProgressText_fetch.textContent = "Download complete!";
                return;
              }

              loaded += value.length;
              const percentComplete = (loaded / total) * 100;
              downloadProgress_fetch.style.width = percentComplete + "%";
              downloadProgressText_fetch.textContent =
                "Download: " + percentComplete.toFixed(2) + "%";

              controller.enqueue(value);
              push();
            });
          }

          push();
        },
      });

      const blob = await new Response(stream).blob();
      const downloadUrl = URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = downloadUrl;
      a.download = "downloaded-file"; // Set the desired file name
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      URL.revokeObjectURL(downloadUrl);
    } catch (error) {
      downloadProgressText_fetch.textContent = "Download failed: " + error;
    }
  });
</script>

This example uses the fetch API to download a file and monitors the download progress by reading the response body as a stream. This is a more advanced technique but provides a way to track download progress with fetch.

Note: Make sure the server sends the content-length header in the response for this example to work correctly. 💡

Browser Support

The ProgressEvent object is widely supported across modern browsers.

Conclusion

The ProgressEvent object is a valuable tool for providing feedback to users during resource loading and data transfer operations. By monitoring progress events and updating the user interface accordingly, you can create a more engaging and user-friendly experience. Whether you are loading images, uploading files, or downloading data, the ProgressEvent object can help you keep your users informed and engaged.