Building a Masonry Grid View with Bootstrap in Drupal 8

 

I’m in the process of moving my photography site into my current Drupal 8 site.  After a little thought I decided that I wanted to have my photos in a grid using a masonry layout (you know, the layout made famous by Pinterest).   The image attached to this article gives a quick demonstration.   On many sites this is accomplished using David DeSandro’s masonry.js.  

Drupal has a Masonry Api and a companion Masonry Views Module.  I installed the modules and they mostly worked but didn't quite meet my needs.  I wanted a little more control of the grid.  I wanted to have a responsive design with different number of columns based on screen size (1 or 2 columns on phones, 3 columns on tablets, 4 on large desktops).  Although my current design doesn't require it, I suspect  that down the road I might want certain images to span multiple columns.  I use bootstrap on my sites to make things simple so I didn't want to write custom CSS to manage column sizes.  I knew I could get the results I wanted with some work but I got to wondering, "how much work would it take to just integrate the library and write some JS".  I found the answer was under 10 lines of code and a little configuration work.

A quick google search led me to this codepen built by the developer of masonry.js which made things seem simple enough. 

So let's break down what I did:

  1. I already have a theme built which is using Bootstrap 3.  The theme's machine name and directory is seanreiser (replace seanreiser with your theme's machine name)
  2. Download the masonry library from here.
  3. Place masonry.pkgd.min.js in themes/seanreiser/libraries/masonry (so the file is located at themes/seanreiser/libraries/masonry/masonry.pkgd.min.js).
  4. Add the masonry library to the theme.  Add this code to my theme's seanreiser.libraries.yml :
    1. masonry:
    2.   js:
    3.   libraries/masonry/masonry.pkgd.min.js: {}
  5. Create a view:
    1. View Name : Photo Gallery
    2. Style : Unformatted List
    3. Settings -> Row Class -> col-xs-12 col-sm-6 col-md-4 col-lg-3
    4. Show: Fields
    5. Add the image to the field list.
  6. Attach the library to the views the view template  (themes/seanreiser/templates/view/views-view-unformatted--photo_gallery.html.twig)
    1. {#
    2. /**
    3.   * @file
    4.   * Default theme implementation to display a view of unformatted rows.
    5.   *
    6.   * Available variables:
    7.   * - title: The title of this group of rows. May be empty.
    8.   * - rows: A list of the view's row items.
    9.   * - attributes: The row's HTML attributes.
    10.   * - content: The row's content.
    11.   * - view: The view object.
    12.   * - default_row_class: A flag indicating whether default classes should be
    13.   * used on rows.
    14.   *
    15.   * @see template_preprocess_views_view_unformatted()
    16.   *
    17.   * @ingroup themeable
    18.   */
    19.  #}
    20.  
    21. {{ attach_library('seanreiser/masonry') }}
    22.  
    23. {% for row in rows %}
    24. {%
    25. set row_classes = [
    26. default_row_class ? 'views-row',
    27. loop.first ? 'views-row-first',
    28. loop.last ? 'views-row-last',
    29. ]
    30. %}
    31. <div {{ row.attributes.addClass(row_classes) }}>
    32. {{- row.content -}}
    33. </div>
    34. {% endfor %}
  7. To my site's themes/seanreiser/js/script.js I added:
    1. $('.views-row').masonry({
    2. itemSelector: '.views-row',
    3. percentPosition: true
    4. });
  8. Clear cache and rock and roll.

And it worked... mostly.  I had an issue where image overlapped occasionally.  The issue was that the grid would be initialized before the images were loaded.  Thankfully, David DeSandro has another library, ImagesLoaded, which detects if all your images have been loaded.  This required a couple of changes:

  1. I downloaded the library and put it in: themes/seanreiser/libraries/imagesloaded
  2. Add the imagesLoaded library to seanreiser.libraries.yml:
    1. imagesloaded:
    2.   js:
    3.   libraries/imagesloaded/imagesloaded.pkgd.min.js: {}
  3. Attach the library to the view (themes/seanreiser/templates/view/views-view-unformatted--photo_gallery.html.twig):
    1. {#
    2. /**
    3.   * @file
    4.   * Default theme implementation to display a view of unformatted rows.
    5.   *
    6.   * Available variables:
    7.   * - title: The title of this group of rows. May be empty.
    8.   * - rows: A list of the view's row items.
    9.   * - attributes: The row's HTML attributes.
    10.   * - content: The row's content.
    11.   * - view: The view object.
    12.   * - default_row_class: A flag indicating whether default classes should be
    13.   * used on rows.
    14.   *
    15.   * @see template_preprocess_views_view_unformatted()
    16.   *
    17.   * @ingroup themeable
    18.   */
    19.  #}
    20.  
    21. {{ attach_library('seanreiser/masonry') }}
    22. {{ attach_library('seanreiser/imagesloaded') }}
    23.  
    24. {% for row in rows %}
    25. {%
    26. set row_classes = [
    27. default_row_class ? 'views-row',
    28. loop.first ? 'views-row-first',
    29. loop.last ? 'views-row-last',
    30. ]
    31. %}
    32. <div {{ row.attributes.addClass(row_classes) }}>
    33. {{- row.content -}}
    34. </div>
    35. {% endfor %}
  4. Modify your JS to hold off on initially the grid until the images are loaded (themes/seanreiser/js/script.js)
    1. var $grid = $('.view-photo-gallery');
    2. $grid.imagesLoaded( function() {
    3. $grid.masonry({
    4. itemSelector: '.views-row',
    5. percentPosition: true
    6. });
    7. });

Now it's working just as I want.  Answers to some questions that I've been asked:

  • Why did you include the library manually instead of using the Masnory API module?
    Since I was using the library on one view in one theme, I didn't see the benefit in having the overhead of the module.  If, in the future I use masonry in other ways, I'll change this.
  • You mentioned photos spanning columns where is that?
    I haven't implemented that.  If I decide to do it I'll add a link here to the todo.
  • Where is the photography site?
    I'm in the middle of a major rebuild of a number of my sites.  Stay Tuned.
  • Update 4/24/2019 1:00AM EDT - You said less then 10 lines, there's a lot more there?
    My bad. views-view-unformatted--photo_gallery.html.twig is a copy of the default views-view-unformatted.html.twig with 2 lines added (the attach library). I am counting that as 2 lines.

Share and Enjoy!