2
votes

I am using lazy load function for images in a section of my web page. (Basically the images currently shown in the viewport are fetched. Rest are not fetched from the server.) The following JavaScript works.

Usage Example: img class="lazy" src="/images/pixel.gif" data-src="/images/actual-image.jpg" alt="alt text"

side info: Just below above image tag I am also having no-script tag with below image tag to support non-JavaScript situation.

img src="/images/actual-image.jpg" alt="alt text"

For the purpose of brevity I didn't add any css or html in the snippet. Sorry.

Working JavaScript:

  var lazy = [];
  registerListener('load', setLazy);
  registerListener('load', lazyLoad);
  registerListener('scroll', lazyLoad);
  registerListener('resize', lazyLoad);
  function setLazy(){
    lazy = document.getElementsByClassName('lazy');
  }
  function lazyLoad(){
    for(var i=0; i<lazy.length; i++){
      if(isInViewport(lazy[i])){
        if (lazy[i].getAttribute('data-src')){
          lazy[i].src = lazy[i].getAttribute('data-src');
          lazy[i].removeAttribute('data-src');
        }
      }
    }
    cleanLazy();
  }
  function cleanLazy(){
    lazy = Array.prototype.filter.call(lazy, function(l){ return l.getAttribute('data-src');});
  }
  function isInViewport(el){
    var rect = el.getBoundingClientRect();
    return (
      rect.bottom >= 0 &&
      rect.right >= 0 &&
      rect.top <= (window.innerHeight || document.documentElement.clientHeight) &&
      rect.left <= (window.innerWidth || document.documentElement.clientWidth)
    );
  }
  function registerListener(event, func) {
    if (window.addEventListener) {
      window.addEventListener(event, func);
    } else {
      window.attachEvent('on' + event, func);
    }
  }

Technically the lazy load script takes the image url in the data-src attribute and replace it in the src attribute for the images shown within the viewport window.

Need/Requirement:

when I print the web page I would like to see all the images in the print (either pdf/physical printed sheet).

Issue:

Unless the user scrolls through the entire set of images, there will be blank or black placeholders (based on different browsers).

I would like to avoid above situation. I can't disable the script. So I have modified it to support print media detection and load all the images even when the user doesn't need to scrolls through the entire lazy load images.

Here is my non working script. The script need a fix.

  var lazy = [];
  registerListener('load', setLazy);
  registerListener('load', checkPrintMedia);
  registerListener('scroll', lazyLoad);
  registerListener('resize', lazyLoad);
  function setLazy(){
    lazy = document.getElementsByClassName('lazy');
  }
  function checkPrintMedia(){
    if (window.matchMedia) {
      var mediaQueryList1 = window.matchMedia('print');
      mediaQueryList1.addListener(function(mql) {
        if (mql.matches) {
          for(var i=0; i<lazy.length; i++){
            if (lazy[i].getAttribute('data-src')){
              lazy[i].src = lazy[i].getAttribute('data-src');
              lazy[i].removeAttribute('data-src');
            }
          }
          cleanLazy();
        }
        else{
          lazyLoad();
        }
      });
    }
  }
  function lazyLoad(){
    for(var i=0; i<lazy.length; i++){
      if(isInViewport(lazy[i])){
        if (lazy[i].getAttribute('data-src')){
          lazy[i].src = lazy[i].getAttribute('data-src');
          lazy[i].removeAttribute('data-src');
        }
      }
    }
    cleanLazy();
  }
  function cleanLazy(){
    lazy = Array.prototype.filter.call(lazy, function(l){ return l.getAttribute('data-src');});
  }
  function isInViewport(el){
    var rect = el.getBoundingClientRect();
    return (
      rect.bottom >= 0 &&
      rect.right >= 0 &&
      rect.top <= (window.innerHeight || document.documentElement.clientHeight) &&
      rect.left <= (window.innerWidth || document.documentElement.clientWidth)
    );
  }
  function registerListener(event, func) {
    if (window.addEventListener) {
      window.addEventListener(event, func);
    } else {
      window.attachEvent('on' + event, func);
    }
  }

I need help. Please.

1

1 Answers

1
votes

Printing detection in JS is possible in IE 5+, Firefox 6+, Chrome 9+, and Safari 5.1+. I guess your failed attempt is from Detecting Print Requests with JavaScript

You should implement the Combined Approach. From looking at your code: you listen on the window.matchMedia which only works in Chrome and Safari.

You have to listen on window.onbeforeprint as well. Something like this.

var beforePrint = function() {
    for(var i=0; i<lazy.length; i++){
        if (lazy[i].getAttribute('data-src')){
          lazy[i].src = lazy[i].getAttribute('data-src');
          lazy[i].removeAttribute('data-src');
        }
     }
     cleanLazy();
}

if (window.matchMedia) {
    var mediaQueryList = window.matchMedia('print');
    mediaQueryList.addListener(function(mql) {
        if (mql.matches) beforePrint();
    })
}

window.onbeforeprint = beforePrint;

Update 2

As it turns out, the problem is that the print won't wait for the images to load, even when waiting inside the beforePrint.

The way I see to solve this would be to reload the page in beforePrint and add a parameter. This would be in the head of the page.

var reloadForPrint = function() {
    if(location.search.indexOf('print') === -1) {
      var url = window.location.href;
      if (url.indexOf('?') > -1) window.location.href = url += '&print'
      else window.location.href = url += '?print'
    }
}
window.matchMedia && window.matchMedia('print').addListener(function(mql) {if (mql.matches) reloadForPrint()})
window.onbeforeprint = reloadForPrint

Now when the page loads again check for that parameter, disable lazyload and execute the window.print() method. This would be in a script tag after the </body>. (make sure the rest of your script is loaded before this, this should be the last block)

if(location.search.indexOf('print') > -1) {
    for(var i=0; i<lazy.length; i++){
        if (lazy[i].getAttribute('data-src')){
          lazy[i].src = lazy[i].getAttribute('data-src');
          lazy[i].removeAttribute('data-src');
        }
     }
     cleanLazy();
     window.print()
}

What will this do?

Basically, when someone wants to print, it will reload the page and add the ?print parameter to the URL. When this parameter is present, all the images will be loaded and the print command will be executed programmatically. However, this time it will wait for all the images to be loaded.