I'm working in a Tableau driven system, with large numbers of charts, I needed a way to reduce the strain of the initial load of our app. It's fairly simple, but wanted to ensure I accounted for every scenario, I don't generally utilize lazy loading.
Print isn't a priority.
jsfiddle: Demo (Tableau doesn't work within SO's snippets because of the iframes)
Relevant Javascript
const throttle = (fn, delay) => {
let canCall = true;
return (...args) => {
if (canCall) {
fn.apply(null, args);
canCall = false;
setTimeout(() => {
canCall = true;
}, delay);
}
};
};
const setAttributes = (el, options) =>
Object.keys(options).forEach(attr => el.setAttribute(attr, options[attr]));
const w = window;
const d = document;
const b = d.body;
const x = w.innerWidth || e.clientWidth || b.clientWidth;
const y = w.innerHeight || e.clientHeight || b.clientHeight;
/* unecessary, but to have same object format, e.g. readability */
const win = Object.create(null);
win.y = y;
const frame = d.querySelectorAll("iframe");
/** may be over doing it here... test results */
const preloadPath = () =>
frame.forEach(item => {
const source = item.getAttribute("data-src");
const preloadLink = d.createElement("link");
const head = d.getElementsByTagName("head")[0];
setAttributes(preloadLink, {
rel: "preload",
as: "document",
type: "text/html",
crossorigin: "anonymous",
href: source
});
head.insertBefore(preloadLink, head.firstChild);
});
const loadFrame = () =>
frame.forEach(item => {
/* only run if not src is present */
if (!item.src) {
const source = item.getAttribute("data-src"); // path
const parent = item.parentElement; // wrapper
const frameRect = parent.getBoundingClientRect(); // wrapper dimensions
if (
w.pageYOffset + win.y >= frameRect.y && // if frame top exceeds page bottom
frameRect.y + frameRect.height >= w.pageYOffset // if frame bottom exceeds page top
) {
item.src = source; // set path
item.removeAttribute("data-src"); // remove placeholder attr
}
}
});
/** load on docReady */
d.addEventListener("DOMContentLoaded", [preloadPath, loadFrame]);
/** throttle on scroll */
const t = throttle(loadFrame, 64);
w.addEventListener("scroll", t);
preloadPath
? \$\endgroup\$<link>
element is used to preload a resource, thenload
event of<link>
should be used beforeloadFrame()
is called. PresentlypreloadPath
does not have direct relevance to the<iframe>
elements, though can be utilized ifimport
is used; see Is there a way to know if a link/script is still pending or has it failed; How to querySelector an element in an html import from a document that imports it?. \$\endgroup\$