We have nodes with a lot of inline images in them. And we are looking for a solution on how to lazyload inline images in Drupal 8.

We are trying to get the Blazy module to lazyload them but it is not working that smoothly since we have a ton of posts already and can't manually apply the src-data attribute and class to all existing images.

This is how I am trying to get blazy to lazyload all images, but it is not working. Any ideas on what we are doing wrong? We are open to all recommendations.

Apply lazyload / blazy to all images.

1) In mytheme.libraries.yml I added the following:

dependencies:
- blazy/load

2) In MYTHEME.info.yml I added the following

libraries:
- 'mytheme/global-styling'
- 'mytheme/local'

3) In MYTHEME.theme I added the following:

/**
* Implements hook_page_attachments_alter()
*/
function MYTHEME_page_attachments_alter(array &$attachments) {
  $attachments['#attached']['library'][] = 'blazy/load';
}

What is wrong?

Currently getting this error:

Fatal error: Cannot use assign-op operators with overloaded objects nor string offsets in /core/lib/Drupal/Core/Asset/LibraryDiscoveryParser.php on line 138

You can apply class and data-src to existing images with code below in MYTHEME.theme.

/**
 * Template_preprocess_image().
 */
function mytheme_preprocess_image(&$variables) {
  $variables['attributes']['data-src'] = $variables['attributes']['src'];
  unset($variables['attributes']['src']);
  if (!isset($variables['attributes']['class'])) {
    $variables['attributes']['class'] = [];
  }
  $variables['attributes']['class'] = array_merge($variables['attributes']['class'], ['b-lazy']);
}
  • Thank you for your help. For some reason, this is not working. It gives an error in the last closing bracket. – Progonat Mar 8 at 14:31
  • Drupal coder might be help to fix your code. drupal.org/project/coder – snize Mar 12 at 11:31
  • Isn't that snippet only for images added from image fields? Can you really preprocess CKEditors inline images that way? Just curious ... – leymannx Apr 8 at 21:12
  • 1
    @leymannx There was a limitation with this method. If you use Entity Embed, it works. – snize May 29 at 8:41
  • Ah yes, I see. Would be a relevant info to edit into your answer. – leymannx May 29 at 8:46

When you need to lazy-load inline-images, I'd recommend doing that so via Filter plugin system.

That way you wouldn't need to alter any of the HTML in the node body/content.

The filter would be responsible of making the necessary markup changes to support lazy loading images.

The filter plugin should be FilterInterface::TYPE_TRANSFORM_REVERSIBLE type to avoid making changes to node content. That way in CKEditor the images could be maintained same as before. But when they are viewed in the page they would be lazy-loaded.

Here's the plugin I created for my projects:

<?php
// This plug needs to be placed in a custom module:
// `modules/custom/YOURMODULE/src/Plugin/Filter/FilterLazyload.php`

namespace Drupal\YOURMODULE\Plugin\Filter;

use Drupal\Component\Utility\Html;
use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;

/**
 * Provides a filter to lazy-load images.
 *
 * @Filter(
 *   id = "filter_lazyload",
 *   title = @Translation("Lazyload images"),
 *   description = @Translation("Lazy load images via bLazy"),
 *   type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_REVERSIBLE,
 *   weight = 20
 * )
 */
class FilterLazyload extends FilterBase {

  /**
   * {@inheritdoc}
   */
  public function process($text, $langcode) {
    $placeholder_src = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';

    $result = new FilterProcessResult($text);
    $html_dom = Html::load($text);
    $images = $html_dom->getElementsByTagName('img');

    foreach ($images as $image) {
      $src = $image->getAttribute('src');
      $image->removeAttribute('src');
      $image->setAttribute('data-src', $src);
      $image->setAttribute('src', $placeholder_src);
      $classes = $image->getAttribute('class');
      $classes = (strlen($classes) > 0) ? explode(' ', $classes) : [];
      $classes[] = 'b-lazy';
      $image->setAttribute('class', implode(' ', $classes));
    }

    $result->setProcessedText(Html::serialize($html_dom));

    return $result;
  }

  /**
   * {@inheritdoc}
   */
  public function tips($long = FALSE) {
    return $this->t('All inline-images will be lazy-loaded.');
  }

}

You still need to attach bLazy library and the initiator js to all pages. But you don't necessarily need the Blazy module to make this plugin work.

  • osman - Thanks for suggesting your solution. Did this work on your environment? Also, I can attach the bLazy library but what's the initiator js? – Progonat Apr 10 at 13:35
  • @Progonat I use the same class in a more complete solution of this, but yes it works. By the initiator js I meant the js that calls the blazy: i.e. var bLazy = new Blazy(); – osman Apr 10 at 14:20

The error you get actually sounds as if one of your theme's *.yml files has wrong indentation.

You have wrong indentation at least in your question. That's how it's supposed to look like:

dependencies:
  - blazy/load

libraries:
  - 'mytheme/global-styling'
  - 'mytheme/local'

Finally, ensure your theme's *.theme file is existent:

<?php

/**
 * @file
 * Functions to support theming in the mytheme theme.
 */

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.