replace-tag: Dynamically load content based on device capabilities

btopro

Bryan Ollendyke

Posted on February 17, 2021

replace-tag: Dynamically load content based on device capabilities

I had an idea the other day that quickly consumed me; The magic script (see series) is great for developer workflow and integration is simple, but it seems like it could be even more performant. I was reading a blog post about the idea of querying a phone for it's connection speed and went down the rabbit hole to this idea: combining the two.

The code powering replace-tag

Video version

What replace-tag looks like

<replace-tag with="meme-maker"
  top-text="Web components"
  bottom-text="Its the platform"
  image-url="https://media2.giphy.com/media/3cB7aOM6347PW/giphy.gif"
></replace-tag>
Enter fullscreen mode Exit fullscreen mode

replace-tag works with the wc-registry.json file produced in the unbundled-webcomponents build routine.

Decision tree

replace-tag has the following checks in place:

  • Am I visible? NOW you can import my definition, then replace the replace-tag with whatever is listed in with=""
  • If I have the import-only attribute, then import() and
  • Am I a low performance device? If import-method="view" then still activate based on visibility; otherwise, don't import and replace until the user specifically clicks me to do so.

What is a low performance device?

By our definition, it's any of the following being true:

  • Less than 1 gig of memory
  • Less than 4 core processor
  • 2g or 3g connection speed
  • user saying they want to save data
  • user's device having less than 25% power

Here's the cool block of code where we're able to do these detections using a series of navigator calls - PerformanceDetect.js with this specific block:

async updateDetails() {
    let details = {
      lowMemory: false,
      lowProcessor: false,
      lowBattery: false,
      poorConnection: false,
      dataSaver: false,
    };
    if (navigator) {
      // if less than a gig we know its bad
      if (navigator.deviceMemory && navigator.deviceMemory < 1) {
        details.lowMemory = true;
      }
      // even phones have multi-core processors so another sign
      if (navigator.hardwareConcurrency && navigator.hardwareConcurrency < 2) {
        details.lowProcessor = true;
      }
      // some platforms support getting the battery status
      if (navigator.getBattery) {
        navigator.getBattery().then(function (battery) {
          // if we are not charging AND we have under 25% be kind
          if (!battery.charging && battery.level < 0.25) {
            details.lowBattery = true;
          }
        });
      }
      // some things report the "type" of internet connection speed
      // for terrible connections lets save frustration
      if (
        navigator.connection &&
        navigator.connection.effectiveType &&
        ["slow-2g", "2g", "3g"].includes(navigator.connection.effectiveType)
      ) {
        details.poorConnection = true;
      }
      // see if they said "hey, save me data"
      if (navigator.connection && navigator.connection.saveData) {
        details.dataSaver = true;
      }
    }
    return details;
  }
Enter fullscreen mode Exit fullscreen mode

This function has to be async because getBattery() (which is not in all platforms) is a Promise().

💖 💪 🙅 🚩
btopro
Bryan Ollendyke

Posted on February 17, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related