• Anfrageformular
  • Erstgespräch
  • Beispiele
  • Testimonials
  • Wartungsservice
  • Redaktionsservice
  • Verlagswebsites
  • Dokumente

Bubsites

  • Angebot
  • Über uns
  • Details
  • Kontakt
  • Search
  • Angebot
  • Über uns
  • Details
  • Kontakt
  • Search

Masonry-Layout ohne Nebenwirkungen

David DeSandros Masonry.js ist eine weitverbreitete und unter ästhetischen Gesichtspunkten überaus sinnvolle Lösung, egal ob es um Blog-Archive, Galerien oder Shopartikel geht.

So sehr ich dieses durch Pinterest berühmt gewordene Layout auch schätze, ich hatte von Anfang an meine Sorgen damit. Wie konnte ich es implementieren, ohne mit einem von zwei für mich unerträglichen Nebeneffekten leben zu müssen?

Variante 1: Schnell etwas Sichtbares, mit verwirrendem Nachspiel

Wie mir scheint, wird Masonry.js nicht mehr oft auf diese einfache Weise implementiert, obwohl es unter Umständen ein akzeptables Ergebnis hervorbringen kann.

Hierbei baut sich das Grid zuerst ohne Einmischung des Skripts auf der Seite auf. Es ist also unmittelbar etwas sichtbar, und erst wenn alle Bilder vollständig geladen sind, kommt das Masonry-Skript zur Anwendung.

/**
 * Masonry Variant: Show Items Immediately, Later Load Masonry.js
 *
 * Items load immediately and Masonry.js gets applied as
 * soon as all images in the grid are fully loaded.
 *
 * @uses imagesLoaded.js
 */
$( document ).ready( function() {
  var $container = $( '.bub-masonry' );
  $container.imagesLoaded( function() {
    // Or use window.load() to also account for iframes.
    $container.masonry({
      itemSelector: 'article'
    })
  });
});

Die Variante ist geradeaus. Es gibt nichts zu tüfteln.

Vorteil: Der Leser bekommt von Anfang an etwas zu sehen.

Nachteil: Die Elemente werden vor den Augen des Lesers neu angeordnet, ein Layout wechselt das andere ab. Ein möglicherweise bereits begonnener Lesefluss wird jäh unterbrochen und seltsam ist so ein Wechsel auf jeden Fall.

Für mich kam diese Variante nie in Frage. Der Layout-Sprung ärgert mich einfach. Erträglich kann ich es mir nur mit reinen Bildergalerien vorstellen.

Variante 2: Warten bis alle Bilder geladen sind

Dieses allerorts empfohlene Setup, bei dem gewartet wird, bis alle Elemente des Grids vollständig geladen sind, führt letztendlich zwar zu einem hübschen Ergebnis, hat aber ebenfalls einen unangenehmen Nebeneffekt: die resultierende Wartezeit.

Dem Leser wird daher meist eine Spinner-Animation aufgezwungen, bis das fertige Layout endlich eingeblendet werden kann.

/**
 * Masonry Variant: Items Hidden until All Images Loaded
 *
 * A spinner element shows until all images on the page are 
 * loaded and Masonry.js can be applied.
 */
$( document ).ready( function() {

  var $container = $( '.bub-masonry' );
  var $spinner = $( '.bub-spinner' );

  //* Setup Masonry.
  var masonrySetup = function( contentFadeInTime ) {

    // Use 'visibility: hidden' for container in stylesheet.
    $spinner.fadeOut( 200 )

    // After fade out, hide spinner completely in case window gets resized.
    setTimeout( function() {
      $spinner.css( 'display', 'none' );
    }, 200);

    $container.css( 'visibility', 'visible' ).hide().fadeIn( contentFadeInTime );

    $container.masonry({
      // options
      itemSelector: 'article',
    });
  }; // End: Masonry setup function.

  // For multiple column layouts (depending on viewport size) load masonry after 
  // everything else on the page has loaded (prevent snapping articles).
  var browserWidth = ( window.innerWidth > 0 ) ? 
    window.innerWidth : document.documentElement.clientWidth;
  
  if ( browserWidth > 500 ) {
    
    // Run Masonry.
    $( window ).on( 'load', function() {
      masonrySetup( 750 );
    });

  } else {
    
    // Still setup Masonry in case window gets resized.
    return;
  }
});

Auch wenn es sich um nur ein oder zwei Sekunden handelt, ist eine programmierte Wartezeit niemals ideal. Spinner-Animationen haben das Web überflutet wie ein Designtrend. Klar haben sie eine wichtige Funktion, aber wo es keinen wirklich guten Grund für einen Spinner gibt, möchte ich für ein paar Jahre auch keinen Spinner mehr sehen.

Ist die Wartezeit länger, etwa aufgrund einer langsamen Internetverbindung oder weil das Grid (manchmal leider notwendige) etwas größere Bilddateien, ja vielleicht sogar eingebettete Videos enthält, wird es für den Leser irgendwann unzumutbar.

Variante 3: Elemente werden nacheinander eingeblendet, sobald sie geladen sind

Es gibt eine naheliegende Lösung für dieses Problem: Zu Beginn müssen alle Elemente ausgeblendet sein. Das erste Element des Grids wird eingeblendet, sobald das Bild oder sonstige sich darin befindende Objekt geladen ist. Dasselbe geschieht anschließend mit dem zweiten Element, dann kommt das dritte dran usw.

Nach einer mehrstündigen Google-Suche war ich verwundert, wie gering die Nachfrage nach einer alternativen Lösung wie dieser offenbar ist. Ich bin auf eine Handvoll Snippets gestoßen, die zwar einen „Ony-by-one“-Ansatz verfolgen, aber entweder die vorgegebene Reihenfolge der Elemente völlig ignorieren oder (im Zuge meiner Tests) andere wirre Resultate hervorgebracht haben.

Schließlich habe ich mich selbst an einer Lösung versucht und bin dabei auf das folgende, für mich bisher problemlos funktionierende Ergebnis gekommen.

/**
 * Masonry Variant: Reveal Items 1 by 1
 *
 * Items are revealed one by one as soon as they are fully loaded.
 * Great solution that should be avoided in grids, that have lots 
 * of iframe content (slow).
 *
 * Parameters:
 * - 'containerSelector' (string): HTML selector of Masonry container.
 * - 'itemSelector' (string): HTML selector of Masonry item.
 * - 'minViewportWidth' (int): Minimum viewport width pixels for 
 *    the function to get applied.
 */
function setupMasonry( containerSelector, itemSelector, minViewportWidth ) {
  
  'use strict';
  
  var $container = $( containerSelector );
  var $items = $container.find( itemSelector );
  var itemsArray = $items.toArray();
  var minViewportWidth;
  var count = 0;

  // The masonry container should be hidden via CSS 
  // and made visible with the script being loaded.
  $container.css( 'visibility', 'visible' );

  // Always apply masonry on window resize.
  $( window ).on( 'resize', applyMasonry );

  // Get exact browser width.
  var browserWidth = ( window.innerWidth > 0 ) ? 
    window.innerWidth : document.documentElement.clientWidth;
  
  // Setup Masonry for minimum viewport width.
  if ( browserWidth >= minViewportWidth ) {
  
    // First hide all Masonry items, then reveal one by one.
    $items.css( 'visibility', 'hidden' );
    // Note: We cannot use 'hide()' or 'display: none', because it makes
    // images and text look a bit blurry. The items must stay on the page.
    // 'Opacity' + 'animate()' would work, but css animations are smoother.
    revealMasonryItems();
  }

  // Masonry function.
  function applyMasonry() {
    $container.masonry({
      itemSelector: itemSelector
    });
  }

  // Reveal items one by one.
  function revealMasonryItems() {

    var $nextItem = $( itemsArray[count] );
    var $nextItemImg = $nextItem.find( 'img' );
    
    // Apply Masonry for every item being loaded.
    applyMasonry();

    // Account for iframe content, e.g. Youtube videos.
    // Anyway, we shouldn't use iframes in a Masonry wall at all (slow and unreliable).
    if ( $nextItem.has( 'iframe' ).length ) {
      containsIframe();
    } else containsImagesOrNothing();

    function containsIframe() {
      // Though much slower than 'load', we use 'get' since
      // it doesn't fail as often (throughout browsers).
      $.get( $nextItem ).always( function() {
        showNext();
      });
    }
 
    // Account for images.
    function containsImagesOrNothing() {
      $nextItem.imagesLoaded().always( function() {
        showNext();
      });
    }
    
    // Reveal item and turn to next one.
    function showNext() {
      $nextItem.add( $nextItemImg )
        .css( 'visibility', 'visible' )
        // Add the fade-in effect via css.
        .addClass( 'masonry-item-visible' ); 
      if ( count < itemsArray.length ) {
        count += 1;
        return revealMasonryItems();
      }
      else return;
    }
  }
}

Die Sichtbarkeit des Masonry-Containers muss auf hidden gesetzt sein, damit es funktioniert. Außerdem sorgt eine kleine CSS-Animation für einen angenehm sanften Aufbauvorgang des Grids.

.bub-masonry {
  visibility: hidden;
}

.bub-masonry article.masonry-item-visible {
  animation: masonryItemFadeIn 200ms ease-in-out;
}

@keyframes masonryItemFadeIn {
    0% {opacity: 0;}
  100% {opacity: 1;}
}

Fazit: Masonry-Grids mit gutem Gewissen einsetzbar

Ich hatte zwar bedenken, weil die Masonry-Funktion für jedes Element einzeln aufgerufen wird, allerdings scheinen alle gängigen Browser gut damit zurechtzukommen.

iframe-Inhalte (z.B. Youtube-Videos) werden in diesem Snippet per Ajax geladen, das dauert natürlich seine Zeit, aber keine andere Variante hat bei meinen Experimenten in Chrome, Firefox und Edge derart zuverlässig funktioniert.

Die jQuery-Funktion load() wäre naheliegend und deutlich schneller als $get , doch bei mindestens einem von fünf Versuchen hat sie überhaupt nichts geladen.

Masonry-Grids und iframe-Content sind naturgemäß ein ganz schlechtes Gespann. Allerdings müssen Videos nur in seltenen Fällen unbedingt schon in die Artikelvorschau eingebettet sein. Und niemand hat außerdem behauptet, dass die Ziegelbauweise eine Allround-Lösung ist.

Mein Verhältnis zum Ziegel-Grid ist jedenfalls besser denn je. Ein Masonry-Layout ohne Nebenwirkungen. Endlich!

Rubrik: Developer Notes

Stefan Buchberger

Über den Autor

Stefan Buchberger war lange als Buchverleger und Redakteur tätig, bis er seine Begeisterung für die Webtechnologie entdeckte.

Ich bin Ihr Ansprechpartner für Webentwicklung, WordPress-Wartung und weitere Services.

Seitenleiste

Top-Beiträge

Buch & Berger ist nun Bubsites Website Service

Seit August 2023 ist die Buch & Berger Webagentur als Bubsites Website Service unter www.bubsites.com zu finden. Der neue Name, eine Kurzversion …

Website-Lösungen: Maßgeschneidert oder Vorlage

Bei der Entwicklung von Websites unterscheiden wir zwischen zwei grundsätzlichen Herangehensweisen: Jede Methode hat ihre Vor- und Nachteile, die wir hier erläutern …

Projektablauf: Vom Erstgespräch zur fertigen Website

Wir entwickeln Websites für verschiedenste Einsatzbereiche, meist auf WordPress-Basis. Hier erläutern wir unsere Vorgehensweise. Angebot und Kostenschätzung Im Erstgespräch klären wir Ihre …

Workshop für Webprojekte

Ein einfaches Beratungsgespräch kann die Vielschichtigkeit eines Webprojekts oftmals nicht umfassend abbilden. Der Workshop ist daher das optimale Format, um in die …

WordPress-Wartung und technischer Service

Wir übernehmen die zuverlässige Wartung und technische Betreuung Ihrer WordPress-Website. Für direkte Anfragen besuchen Sie bitte unsere Kontaktseite. Kernpunkte der Wartungsvereinbarung Unser …

Details zur Website-Wartung und Wartungsvereinbarung

Hier erhalten Sie eine ausführliche Beschreibung unserer Wartungsvereinbarung. Für einen kurzen Überblick empfehlen wir den Beitrag: WordPress-Wartung und technischer Service. Laufende oder …

FAQ zur Wartungsvereinbarung

Sie sind bereits Wartungskunde oder interessieren sich für dieses Thema? Hier erläutern wir einige Begriffe rund um unsere Wartungsvereinbarung und Wartungsberichte. Lizenzpauschale: …

Redaktioneller Service für Ihre Website

Der Inhalt bildet das Fundament einer erfolgreichen Website. Wir sorgen dafür, dass Ihre Inhalte professionell, präzise und zielgruppengerecht sind. Unsere Web-Content-Services Warum …

Testimonials: Das sagen unsere Kunden

Ihre Zufriedenheit steht bei uns an erster Stelle. Ehrliches Feedback ist uns wichtig, um unsere Leistungen kontinuierlich zu verbessern und Ihren Anforderungen …

Website-Briefing: Ein Leitfaden zur Projektvorbereitung

Damit Ihr Webprojekt reibungslos startet, haben wir diesen Leitfaden erstellt. Er gibt uns einen klaren Überblick über Ihre Wünsche und Anforderungen und …
↑
  • Impressum
  • Datenschutz
↑

Zeige alle ...

Click to Copy