2

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.

nzkks
  • 86
  • 7

1 Answers1

1

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.

arc
  • 3,984
  • 4
  • 31
  • 40
  • Thanks arcs. When I do Print/Print Preview on Chrome & Firefox browsers, they make the requests for all the images as expected and fetches _(checked in respective browser developer tools - Network)_. But **The browser's print preview utility renders the rest of the page before all _these images_ get downloaded**. Obviously when I do cancel the print and re-click the print, all the images are shown as expected because the images are already got fetched and stored in the browser cache. We can't assume the user try for the second time. Is there any way to fix? Thanks. – nzkks Dec 28 '16 at 09:24
  • @nzkks I'll look into it – arc Dec 28 '16 at 09:53
  • @nzkks it doesn't look like there's a solution for your problem. Now, if you need this feature, the only way I see is to reload page in the `beforePrint()` with `document.location.href=""`. You could then add a URL parameter (e.g. `?print=true`) and in you method, when that one is present, disable lazy load. – arc Dec 28 '16 at 12:12
  • No luck @arcs. I have tried your approach. I just saw an annoying loop of print dialog box (Firefox) / print preview (Chrome). `http://localhost:3000/?print=true&print=true&print=true&print=true (continuing)`. Firefox didn't create big loop. Just stopped `http://localhost:3000/?print=true&print=true`. But I can't disable lazy load script. Thanks for helping me. – nzkks Dec 28 '16 at 13:48
  • @nzkks You need some more logic for it to work ;) I'll do another edit to my answer in an hour. – arc Dec 28 '16 at 14:58
  • @nzkks The shown approach works with chrome, I had problems with MS Edge and hadn't tested on Firefox. – arc Dec 29 '16 at 11:26
  • Yes. Your approach works as expected in Chrome. In Firefox, the head script (reloadForPrint) is not working. To test the other half (body script), I manually added the querystring in Firefox address bar and refreshed. The images are there in the pdf file as expected. Thanks. – nzkks Dec 29 '16 at 13:53