-
-
Save aranajhonny/974a3f579aa39b27caa95fdf4013a842 to your computer and use it in GitHub Desktop.
Lazy Loading Video Based on Connection Speed
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class LazyVideoLoader { | |
constructor() { | |
this.videos = [].slice.call(document.querySelectorAll('.hero__bgvideo')); | |
// Abort when: | |
// - The browser does not support Promises. | |
// - There no videos. | |
// - If the user prefers reduced motion. | |
// - Device is mobile. | |
if ( | |
typeof Promise === 'undefined' || | |
!this.videos || | |
window.matchMedia('(prefers-reduced-motion)').matches || | |
window.innerWidth < 992 | |
) { | |
return; | |
} | |
this.videos.forEach(this.loadVideo.bind(this)); | |
} | |
loadVideo(video) { | |
this.setSource(video); | |
video.load(); | |
this.checkLoadTime(video); | |
} | |
/** | |
* Find the children of the video that are <source> tags. | |
* Set the src attribute for each <source> based on the | |
* data-src attribute. | |
* | |
* @param {object} video The video element. | |
* @returns {void} | |
*/ | |
setSource(video) { | |
const children = [].slice.call(video.children); | |
children.forEach((child) => { | |
if ( | |
child.tagName === 'SOURCE' && | |
typeof child.dataset.src !== 'undefined' | |
) { | |
child.setAttribute('src', child.dataset.src); | |
} | |
}); | |
} | |
/** | |
* Checks if the video will be able to play through before | |
* a predetermined time has passed. | |
* @param {object} video The video element. | |
* @returns {void} | |
*/ | |
checkLoadTime(video) { | |
// Create a promise that resolves when the | |
// video.canplaythrough event triggers. | |
const videoLoad = new Promise((resolve) => { | |
video.addEventListener('canplaythrough', () => { | |
resolve('can play'); | |
}); | |
}); | |
// Create a promise that resolves after a | |
// predetermined time (2sec) | |
const videoTimeout = new Promise((resolve) => { | |
setTimeout(() => { | |
resolve('The video timed out.'); | |
}, 2000); | |
}); | |
// Race the promises to see which one resolves first. | |
Promise.race([videoLoad, videoTimeout]).then((data) => { | |
if (data === 'can play') { | |
video.play(); | |
setTimeout(() => { | |
video.classList.add('video-loaded'); | |
}, 500); | |
} | |
else { | |
this.cancelLoad(video); | |
} | |
}); | |
} | |
/** | |
* Cancel the video loading by removing all | |
* <source> tags and then triggering video.load(). | |
* | |
* @param {object} video The video element. | |
* @returns {void} | |
*/ | |
cancelLoad(video) { | |
const children = [].slice.call(video.children); | |
children.forEach((child) => { | |
if ( | |
child.tagName === 'SOURCE' && | |
typeof child.dataset.src !== 'undefined' | |
) { | |
child.parentNode.removeChild(child); | |
} | |
}); | |
// reload the video without <source> tags so it | |
// stops downloading. | |
video.load(); | |
} | |
} | |
new LazyVideoLoader(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment