2014
Mar
10

Why may we want to preload images?

Preloading images is a great way to improve the user experience. When images are preloaded in the browser, the visitor can surf around your site and enjoy extremely faster loading times. This is especially beneficial for photo galleries and other image-heavy sites where you want to deliver the goods as quickly and seamlessly as possible. Preloading images definitely helps users without broadband enjoy a better experience when viewing your content. In this article, we’ll explore three different preloading techniques to enhance the performance and usability of your site.

Soultions To Preload Images

Method 1: With CSS And JavaScript

as Nanda suggests, it is better to preload images using only CSS. Using the CSS background property, we can preload images via existing div, span, or other elements in the (X)HTML markup.

HTML Markup
  1. <ul id="thumbnails" class="thumbnails">
  2. <li class="thumbnail"><div class="thumbnail-001 preload"></div></li>
  3. <li class="thumbnail"><div class="thumbnail-002 preload"></div></li>
  4. <li class="thumbnail"><div class="thumbnail-003 preload"></div></li>
  5. <li class="thumbnail"><div class="thumbnail-004 preload"></div></li>
  6. <li class="thumbnail"><div class="thumbnail-005 preload"></div></li>
  7. </ul>
CSS
  1. .preload {
  2. display: none;
  3. }
  4. .thumbnails {
  5. list-style: none;
  6. margin: 0;
  7. padding: 0;
  8. }
  9. .thumbnail {
  10. background: #fafafa;
  11. border: 1px solid #ccc;
  12. float: left;
  13. height: 200px;
  14. margin: 0 5px 10px 0;
  15. padding: 2px;
  16. width: 300px;
  17. }
  18. .thumbnail-001 {
  19. background: transparent url(images/golden_gate_bridge.jpg) no-repeat -500px -200px;
  20. height: 200px;
  21. width: 300px;
  22. }
  23. .thumbnail-002 {
  24. background: transparent url(images/harley_davidson_cafe_001.jpg) no-repeat -300px -250px;
  25. height: 200px;
  26. width: 300px;
  27. }
  28. .thumbnail-003 {
  29. background: transparent url(images/palace_of_fine_arts.jpg) no-repeat -500px -150px;
  30. height: 200px;
  31. width: 300px;
  32. }
  33. .thumbnail-004 {
  34. background: transparent url(images/castro.jpg) no-repeat -500px -350px;
  35. height: 200px;
  36. width: 300px;
  37. }
  38. .thumbnail-005 {
  39. background: transparent url(images/pier_39.jpg) no-repeat -500px -150px;
  40. height: 200px;
  41. width: 300px;
  42. }
JavaScript
  1. var divElements = document.getElementsByTagName("div");
  2. for (var i = 0; i < divElements.length; i++) {
  3. var element = divElements[i];
  4. element.className = element.className.replace(" preload", "");
  5. }

View Demo

Method 2: With JavaScript Only

There are some considerations we need to be aware of here:

  • It needs to be threaded in parallel (to use the browser’s maximum threads, normally 6 per domain).
  • It needs to also be sequential (pipelined) for when you want to use a single thread.
  • It needs to handle individual image events – onload, onabort, onerror.
  • We need to clean up resources and memory, not leak memory and prevent IE GIF onload from firing multiple times. In Internet Explorer 5/6/7, the onload event fires ALL THE TIME for animated GIFs. That's right, on each animation loop it fires an onload for you. Which is why you need to make sure you release your image objects for GC by not saving a reference – or should you need to store them as objects into a new array – remove all onload and other events attached to them.
HTML Markup
  1. <div class="progress">
  2. <div id="progress-bar" class="progress-bar"></div>
  3. <div id="progress-percent" class="progress-percent"></div>
  4. </div>
  5.  
  6. <ul id="thumbnails" class="thumbnails">
  7. </ul>
Example
  1. /********************
  2. Author: Vivian Huang
  3. Date: March 10, 2014
  4.  
  5. Related Posts
  6. http://fragged.org/preloading-images-using-javascript-the-right-way-and-without-frameworks_744.html
  7. http://perishablepress.com/3-ways-preload-images-css-javascript-ajax/
  8. ********************/
  9.  
  10. var IMGPRELOADER = function (images, options) {
  11. var defaults,
  12. isNative = "addEventListner" in (new Image()),
  13. index = 0,
  14. queues = [],
  15. completed = [],
  16. errors = [],
  17. handleLoad,
  18. handleError,
  19. handleAbort,
  20. reset,
  21. setOptions,
  22. removeEvents,
  23. load,
  24. checkProgress;
  25.  
  26. defaults = {
  27. auto: true,
  28. onStart: function () {},
  29. onProgress: function () {},
  30. onError: function () {},
  31. onComplete: function () {}
  32. };
  33.  
  34. handleLoad = function () {
  35. console.log("**** handleLoad ****");
  36.  
  37. removeEvents.call(this);
  38. completed.push(this.src);
  39. checkProgress(this.src, this);
  40. };
  41.  
  42. handleError = function () {
  43. console.log("**** handleLoad ****");
  44.  
  45. removeEvents.call(this);
  46. errors.push(this.src);
  47. checkProgress(this.src, this);
  48. options.onError(this.src);
  49. };
  50.  
  51. handleAbort = function () {
  52. console.log("**** handleAbort ****");
  53.  
  54. removeEvents.call(this);
  55. errors.push(this.src);
  56. checkProgress(this.src, this);
  57. options.onError(this.src);
  58. };
  59. reset = function () {
  60. index = 0;
  61. queues = completed = errors = [];
  62. };
  63.  
  64. setOptions = function (options) {
  65. for (var key in defaults) {
  66. if (!options.hasOwnProperty(key)) {
  67. options[key] = defaults[key];
  68. }
  69. }
  70. return options;
  71. };
  72.  
  73. removeEvents = function () {
  74. if (isNative) {
  75. this.removeEventListner("load", handleLoad);
  76. this.removeEventListner("error", handleError, false);
  77. this.removeEventListner("abort", handleError, false);
  78. } else {
  79. this.onload = this.onerror = this.onabort = null;
  80. }
  81. };
  82.  
  83. load = function () {
  84. if (index >= queues.length) {
  85. return;
  86. }
  87.  
  88. var image = new Image(),
  89. src = queues[index];
  90.  
  91. if (isNative) {
  92. image.addEventListner("load", handleLoad, false);
  93. image.addEventListner("error", handleError, false);
  94. image.addEventListner("abort", handleError, false);
  95. } else {
  96. image.onload = handleLoad;
  97. image.onerror = handleError;
  98. image.onabort = handleError;
  99. }
  100.  
  101. image.src = src;
  102. };
  103.  
  104. checkProgress = function (imgSrc, imgElement) {
  105. options.onProgress(imgSrc, imgElement, (completed.length + errors.length), queues.length);
  106. index++;
  107. load();
  108.  
  109. if (index === queues.length - 1) {
  110. options.onComplete(completed, errors);
  111. }
  112. };
  113.  
  114. (function () {
  115. options = (options && typeof options === "object") ? setOptions(options) : defaults;
  116. queues = (images && images instanceof Array) ? images.slice() : [];
  117. options.onStart(queues);
  118.  
  119. if (queues.length && options.auto) {
  120. load();
  121. }
  122. })();
  123. };
Example
  1. // For IE < 8
  2. if (!Array.prototype.map) {
  3. Array.prototype.map = function(fun /*, thisp*/) {
  4. var len = this.length;
  5. if (typeof fun != "function") {
  6. throw new TypeError();
  7. }
  8. var res = new Array(len);
  9. var thisp = arguments[1];
  10. for (var i = 0; i < len; i++) {
  11. if (i in this) {
  12. res[i] = fun.call(thisp, this[i], i, this);
  13. }
  14. }
  15. return res;
  16. };
  17. }
  18.  
  19. var imagesArray = new Array(20).join(",").split(","),
  20. progressBarElement = document.getElementById("progress-bar"),
  21. progressPercentElement = document.getElementById("progress-percent"),
  22. thumbnailsElement = document.getElementById("thumbnails");
  23. imagesArray = imagesArray.map(function(el, i) {
  24. return "images/golden_gate_bridge.jpg?" + +new Date() + i;
  25. });
  26. imagesArray.push("images/golden_gate_bri.jpg");
  27.  
  28. var preloader = new IMGPRELOADER(imagesArray, {
  29. onStart: function (images) {
  30. console.log("**** onStart ****");
  31.  
  32. console.log(images);
  33. },
  34. onProgress: function (imgSrc, imgElement, progressed, total) {
  35. console.log("**** onProgress ****");
  36.  
  37. var defaultWidth = 200,
  38. ratio = 1,
  39. width = imgElement.width,
  40. height = imgElement.height,
  41. percent = 0;
  42.  
  43. if (width > defaultWidth) {
  44. ratio = defaultWidth / width;
  45. height = Math.round(height * ratio);
  46. width = defaultWidth;
  47. }
  48.  
  49. var liElement = document.createElement("li");
  50. liElement.className = "thumbnail";
  51. liElement.innerHTML = '<img src="' + imgSrc + '" width="' + width + '" height="' + height + '">';
  52. thumbnailsElement.appendChild(liElement);
  53.  
  54. percent = Math.floor(progressed / total * 100) + "%";
  55. progressBarElement.style.width = percent;
  56. progressPercentElement.innerHTML = percent;
  57. },
  58. onError: function (imgSrc) {
  59. console.log("**** onError ****");
  60.  
  61. console.log(imgSrc);
  62. },
  63. onComplete: function (loaded, errors) {
  64. console.log("**** onComplete ****");
  65.  
  66. console.log(loaded);
  67. console.log(errors);
  68. }
  69. });

View Demo

Related Posts


回應 (Leave a comment)