Slick Lazy Load Photo Grid Using Webpack 3

Lightbox

Create a new file named lightbox.js in the src/js folder with jQuery and slick-carousel import statements at the top.

lightbox.js
import $ from 'jquery'
import 'slick-carousel'

Install both jQuery and Slick using npm with the --save option. This will list them as dependencies in the package.json file.

npm install --save jquery slick-carousel
  • Note that multiple packages can be installed with a single npm install command. For even less typing, use the npm install alias npm i. More info available in the npm-install documentation.

Add the Lightbox class to the lightbox.js module.

lightbox.js
...

export default class Lightbox {
    constructor(options) {

        this.settings = $.extend({/* defaults */}, options);

        this.init();
    }

    init() {

        let source = $(this.settings.source);

        if (source.length) {

            source.each((index, el) => {

                this.create(index, el);
            });
        }
    }

    create(index, el) {

        let lightbox = this.settings.name + '__' + index,
            opener = $(el).find(this.settings.opener);

        $('body').append('<div data-lightbox="' + lightbox + '" class="lightbox"><div></div></div>');

        if (this.settings.type === 'slider') {

            $('div[data-lightbox="' + lightbox + '"] > div')
                .append('<div class="lightbox-slider"></div>');

            var slider = $('div[data-lightbox="' + lightbox + '"] .lightbox-slider');

            slider.slick({
                dots: true
            });

            opener.each((index, el) => {
                this.popSlider(lightbox, slider, el);
            });
        }

        // close button
        $('div[data-lightbox="' + lightbox + '"] > div')
            .prepend('<a class="lightbox-close" href="javascript:void(0)">+</a>');

        $('.lightbox-close').on( 'click', function() {
            $('[data-lightbox="' + lightbox + '"]').removeClass('is-open');
        });

        //close on outside click
        window.onclick = function(evt) {
            if (evt.target.dataset.lightbox == lightbox) {
                $('[data-lightbox="' + lightbox + '"]').removeClass('is-open');
            }
        }

        // close on escape
        $(document).keyup(function(evt) {
            if (evt.which === 27) {
                $('[data-lightbox="' + lightbox + '"]').removeClass('is-open');
            }
        });

    }

    popSlider(lightbox, slider, el) {

        let img = $(el).find('img'),
            src = img.prop('src'),
            slide = document.createElement('div'),
            slideImg = document.createElement('img');

        slideImg.src = src;

        slide.appendChild(slideImg);

        if (img.attr('alt')) {
            let caption = document.createElement('p'),
                captionText = document.createTextNode(img.attr('alt'));

            caption.appendChild(captionText);
            slide.appendChild(caption);
        }

        slider.slick('slickAdd', slide);

        img.wrap('<a href="' + src + '"></a>').on( 'click', function(evt) {

            evt.preventDefault();

            $('[data-lightbox="' + lightbox + '"]').addClass('is-open');

            let index = $(this).closest(el).index();

            slider.slick('slickGoTo', index);
        });
    }
}

In the app entry point, import the lightbox module and set the options to target photogrid.

index.js
...

import Lightbox from './lightbox'

new Lightbox({
    name: 'lightbox',
    source: '.photogrid',
    opener: 'li',
    type: 'slider'
});

In the src/sass folder, create a new Sass partial named _lightbox.scss.

_lightbox.scss
/* overlay (background) */
.lightbox {
  max-height: 0; /* instead of `display: none` so slider can init properly. */

  position: fixed; /* stay in place */
  z-index: 1;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  overflow: auto; /* enable scroll if needed */
  background-color: rgb(0,0,0); /* fallback color */
  background-color: rgba(0,0,0,0.80); /* black w/ opacity */

  > div {
    position: relative;
    background-color: rgb(0,0,0);
    padding: 20px;
    color: #fff;
    //width: 100%; /* could be more or less, depending on screen size */
    width: 90vw;
    margin: 5vw auto;

    .slick-prev,
    .slick-next {
      z-index: 10;
    }
    .slick-prev {
      left: -20px;
    }
    .slick-next {
      right: -20px;
    }
    .slick-dots {
      li button:before {
        color: #fff;
      }
    }
  }

  &.is-open {
    max-height: 100%; /* unhide */
  }

  @media only screen and (max-width: 600px) {
    background-color: rgba(0,0,0,0.95); /* black w/ opacity */

    > div {
      padding: 40px 0 20px 0;
      width: 100vw;
      margin: 0 auto;

      .slick-slide p {
        padding: 0 15px;
      }

      .slick-next, .slick-prev {
        display: none;
      }
    }
  }

  @media only screen and (min-width: 1025px) {
    > div {
      max-width: 1024px;
    }
  }
}

/* close button */
.lightbox-close {
  position: absolute;
  top: 2px;
  right: 2px;
  text-align: center;
  line-height: 20px;
  font-size: 20px;
  font-weight: bold;
  color: rgba(255,255,255,0.75);
  width: 20px;
  height: 20px;
  transform: rotate(45deg);
  text-decoration: none;
  z-index: 10;

  &:hover {
    color: rgb(255,255,255);
  }

  @media only screen and (max-width: 600px) {
    top: 4px;
    right: 4px;
    line-height: 32px;
    font-size: 32px;
    width: 32px;
    height: 32px;
  }
}

Import the lightbox partial, slick and slick-theme into the style Sass file.

style.scss
...

@import "lightbox";

/* node_modules */
@import "~slick-carousel/slick/slick.scss";
@import "~slick-carousel/slick/slick-theme.scss";

Build

Before building the app and css again, the webpack configuration needs to be updated for font url loading in the slick-theme. Here is the updated module configuration.

webpack.config.js
...

  module: {
    rules: [
    {
      test: /\.(css|scss)$/,
      use: ExtractTextPlugin.extract({
        fallback: 'style-loader',
        use: [
          {
            loader: 'css-loader',
            options: {
              minimize: true || {/* CSSNano Options */}
            }
          },
          {
            loader: 'postcss-loader'
          },
          {
            /* for ~slick-carousel/slick/slick-theme.scss */
            loader: 'resolve-url-loader'
          },
          {
            /* for resolve-url-loader:
               source maps must be enabled on any preceding loader */
            loader: 'sass-loader?sourceMap'
          }
        ]
      })
    },
    {
      test: /\.js$/,
      use: 'babel-loader'
    },
    { /* for ~slick-carousel/slick/slick-theme.scss */
      test: /\.(eot|woff|woff2|ttf|svg|png|jpg|gif)$/,
      loader: 'url-loader?limit=30000&name=[name]-[hash].[ext]'
    }
    ]
  }

...

Install both url-loader and resolve-url-loader for webpack to handle the relative paths in the slick-theme.

npm install --save-dev url-loader resolve-url-loader

Run the build.

npm run build
  • For development, use npm run dev to watch for changes and build incrementally when changes are saved.

That’s it, refresh the browser and select a photo to open the photo preview lightbox and slider.

Published by

Jim Frenette

Web Developer - views here are my own except those taken from people more clever than me.

Loading Disqus Comments ...
Loading Facebook Comments ...