/** * Detect IE browser * @const {boolean} * @private */ const isIE = typeof document !== 'undefined' && document.documentMode /** * * @param {string} type * */ const support = type => window && window[type] const validAttribute = ['data-iesrc', 'data-alt', 'data-src', 'data-srcset', 'data-background-image', 'data-toggle-class'] const defaultConfig = { rootMargin: '0px', threshold: 0, enableAutoReload: false, load(element) { if (element.nodeName.toLowerCase() === 'picture') { let img = element.querySelector('img') let append = false if (img === null) { img = document.createElement('img') append = true } if (isIE && element.getAttribute('data-iesrc')) { img.src = element.getAttribute('data-iesrc') } if (element.getAttribute('data-alt')) { img.alt = element.getAttribute('data-alt') } if (append) { element.append(img) } } if (element.nodeName.toLowerCase() === 'video' && !element.getAttribute('data-src')) { if (element.children) { const childs = element.children let childSrc for (let i = 0; i <= childs.length - 1; i++) { childSrc = childs[i].getAttribute('data-src') if (childSrc) { childs[i].src = childSrc } } element.load() } } if (element.getAttribute('data-poster')) { element.poster = element.getAttribute('data-poster') } if (element.getAttribute('data-src')) { element.src = element.getAttribute('data-src') } if (element.getAttribute('data-srcset')) { element.setAttribute('srcset', element.getAttribute('data-srcset')) } let backgroundImageDelimiter = ',' if (element.getAttribute('data-background-delimiter')) { backgroundImageDelimiter = element.getAttribute('data-background-delimiter') } if (element.getAttribute('data-background-image')) { element.style.backgroundImage = `url('${element.getAttribute('data-background-image').split(backgroundImageDelimiter).join('\'),url(\'')}')` } else if (element.getAttribute('data-background-image-set')) { const imageSetLinks = element.getAttribute('data-background-image-set').split(backgroundImageDelimiter) let firstUrlLink = (imageSetLinks[0].substr(0, imageSetLinks[0].indexOf(' ')) || imageSetLinks[0]) // Substring before ... 1x firstUrlLink = firstUrlLink.indexOf('url(') === -1 ? `url(${firstUrlLink})` : firstUrlLink if (imageSetLinks.length === 1) { element.style.backgroundImage = firstUrlLink } else { element.setAttribute('style', (element.getAttribute('style') || '') + `background-image: ${firstUrlLink}; background-image: -webkit-image-set(${imageSetLinks}); background-image: image-set(${imageSetLinks})`) } } if (element.getAttribute('data-toggle-class')) { element.classList.toggle(element.getAttribute('data-toggle-class')) } }, loaded() {} } function markAsLoaded(element) { element.setAttribute('data-loaded', true) } function preLoad(element) { if (element.getAttribute('data-placeholder-background')) { element.style.background = element.getAttribute('data-placeholder-background') } } const isLoaded = element => element.getAttribute('data-loaded') === 'true' const onIntersection = (load, loaded) => (entries, observer) => { entries.forEach(entry => { if (entry.intersectionRatio > 0 || entry.isIntersecting) { observer.unobserve(entry.target) if (!isLoaded(entry.target)) { load(entry.target) markAsLoaded(entry.target) loaded(entry.target) } } }) } const onMutation = load => entries => { entries.forEach(entry => { if (isLoaded(entry.target) && entry.type === 'attributes' && validAttribute.indexOf(entry.attributeName) > -1) { load(entry.target) } }) } const getElements = (selector, root = document) => { if (selector instanceof Element) { return [selector] } if (selector instanceof NodeList) { return selector } return root.querySelectorAll(selector) } export default function (selector = '.lozad', options = {}) { const {root, rootMargin, threshold, enableAutoReload, load, loaded} = Object.assign({}, defaultConfig, options) let observer let mutationObserver if (support('IntersectionObserver')) { observer = new IntersectionObserver(onIntersection(load, loaded), { root, rootMargin, threshold }) } if (support('MutationObserver') && enableAutoReload) { mutationObserver = new MutationObserver(onMutation(load, loaded)) } const elements = getElements(selector, root) for (let i = 0; i < elements.length; i++) { preLoad(elements[i]) } return { observe() { const elements = getElements(selector, root) for (let i = 0; i < elements.length; i++) { if (isLoaded(elements[i])) { continue } if (observer) { if (mutationObserver && enableAutoReload) { mutationObserver.observe(elements[i], {subtree: true, attributes: true, attributeFilter: validAttribute}) } observer.observe(elements[i]) continue } load(elements[i]) markAsLoaded(elements[i]) loaded(elements[i]) } }, triggerLoad(element) { if (isLoaded(element)) { return } load(element) markAsLoaded(element) loaded(element) }, observer, mutationObserver } }