lozad.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. /**
  2. * Detect IE browser
  3. * @const {boolean}
  4. * @private
  5. */
  6. const isIE = typeof document !== 'undefined' && document.documentMode
  7. /**
  8. *
  9. * @param {string} type
  10. *
  11. */
  12. const support = type => window && window[type]
  13. const validAttribute = ['data-iesrc', 'data-alt', 'data-src', 'data-srcset', 'data-background-image', 'data-toggle-class']
  14. const defaultConfig = {
  15. rootMargin: '0px',
  16. threshold: 0,
  17. enableAutoReload: false,
  18. load(element) {
  19. if (element.nodeName.toLowerCase() === 'picture') {
  20. let img = element.querySelector('img')
  21. let append = false
  22. if (img === null) {
  23. img = document.createElement('img')
  24. append = true
  25. }
  26. if (isIE && element.getAttribute('data-iesrc')) {
  27. img.src = element.getAttribute('data-iesrc')
  28. }
  29. if (element.getAttribute('data-alt')) {
  30. img.alt = element.getAttribute('data-alt')
  31. }
  32. if (append) {
  33. element.append(img)
  34. }
  35. }
  36. if (element.nodeName.toLowerCase() === 'video' && !element.getAttribute('data-src')) {
  37. if (element.children) {
  38. const childs = element.children
  39. let childSrc
  40. for (let i = 0; i <= childs.length - 1; i++) {
  41. childSrc = childs[i].getAttribute('data-src')
  42. if (childSrc) {
  43. childs[i].src = childSrc
  44. }
  45. }
  46. element.load()
  47. }
  48. }
  49. if (element.getAttribute('data-poster')) {
  50. element.poster = element.getAttribute('data-poster')
  51. }
  52. if (element.getAttribute('data-src')) {
  53. element.src = element.getAttribute('data-src')
  54. }
  55. if (element.getAttribute('data-srcset')) {
  56. element.setAttribute('srcset', element.getAttribute('data-srcset'))
  57. }
  58. let backgroundImageDelimiter = ','
  59. if (element.getAttribute('data-background-delimiter')) {
  60. backgroundImageDelimiter = element.getAttribute('data-background-delimiter')
  61. }
  62. if (element.getAttribute('data-background-image')) {
  63. element.style.backgroundImage = `url('${element.getAttribute('data-background-image').split(backgroundImageDelimiter).join('\'),url(\'')}')`
  64. } else if (element.getAttribute('data-background-image-set')) {
  65. const imageSetLinks = element.getAttribute('data-background-image-set').split(backgroundImageDelimiter)
  66. let firstUrlLink = (imageSetLinks[0].substr(0, imageSetLinks[0].indexOf(' ')) || imageSetLinks[0]) // Substring before ... 1x
  67. firstUrlLink = firstUrlLink.indexOf('url(') === -1 ? `url(${firstUrlLink})` : firstUrlLink
  68. if (imageSetLinks.length === 1) {
  69. element.style.backgroundImage = firstUrlLink
  70. } else {
  71. element.setAttribute('style', (element.getAttribute('style') || '') + `background-image: ${firstUrlLink}; background-image: -webkit-image-set(${imageSetLinks}); background-image: image-set(${imageSetLinks})`)
  72. }
  73. }
  74. if (element.getAttribute('data-toggle-class')) {
  75. element.classList.toggle(element.getAttribute('data-toggle-class'))
  76. }
  77. },
  78. loaded() {}
  79. }
  80. function markAsLoaded(element) {
  81. element.setAttribute('data-loaded', true)
  82. }
  83. function preLoad(element) {
  84. if (element.getAttribute('data-placeholder-background')) {
  85. element.style.background = element.getAttribute('data-placeholder-background')
  86. }
  87. }
  88. const isLoaded = element => element.getAttribute('data-loaded') === 'true'
  89. const onIntersection = (load, loaded) => (entries, observer) => {
  90. entries.forEach(entry => {
  91. if (entry.intersectionRatio > 0 || entry.isIntersecting) {
  92. observer.unobserve(entry.target)
  93. if (!isLoaded(entry.target)) {
  94. load(entry.target)
  95. markAsLoaded(entry.target)
  96. loaded(entry.target)
  97. }
  98. }
  99. })
  100. }
  101. const onMutation = load => entries => {
  102. entries.forEach(entry => {
  103. if (isLoaded(entry.target) && entry.type === 'attributes' && validAttribute.indexOf(entry.attributeName) > -1) {
  104. load(entry.target)
  105. }
  106. })
  107. }
  108. const getElements = (selector, root = document) => {
  109. if (selector instanceof Element) {
  110. return [selector]
  111. }
  112. if (selector instanceof NodeList) {
  113. return selector
  114. }
  115. return root.querySelectorAll(selector)
  116. }
  117. export default function (selector = '.lozad', options = {}) {
  118. const {root, rootMargin, threshold, enableAutoReload, load, loaded} = Object.assign({}, defaultConfig, options)
  119. let observer
  120. let mutationObserver
  121. if (support('IntersectionObserver')) {
  122. observer = new IntersectionObserver(onIntersection(load, loaded), {
  123. root,
  124. rootMargin,
  125. threshold
  126. })
  127. }
  128. if (support('MutationObserver') && enableAutoReload) {
  129. mutationObserver = new MutationObserver(onMutation(load, loaded))
  130. }
  131. const elements = getElements(selector, root)
  132. for (let i = 0; i < elements.length; i++) {
  133. preLoad(elements[i])
  134. }
  135. return {
  136. observe() {
  137. const elements = getElements(selector, root)
  138. for (let i = 0; i < elements.length; i++) {
  139. if (isLoaded(elements[i])) {
  140. continue
  141. }
  142. if (observer) {
  143. if (mutationObserver && enableAutoReload) {
  144. mutationObserver.observe(elements[i], {subtree: true, attributes: true, attributeFilter: validAttribute})
  145. }
  146. observer.observe(elements[i])
  147. continue
  148. }
  149. load(elements[i])
  150. markAsLoaded(elements[i])
  151. loaded(elements[i])
  152. }
  153. },
  154. triggerLoad(element) {
  155. if (isLoaded(element)) {
  156. return
  157. }
  158. load(element)
  159. markAsLoaded(element)
  160. loaded(element)
  161. },
  162. observer,
  163. mutationObserver
  164. }
  165. }