You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular中通过检测DOM类实现ng-lazyload-image加载时显示骨架屏

在Angular中结合ng-lazyload-image与ngx-skeleton-loader实现图片懒加载+骨架屏

问题分析

你之前的方案存在两个核心问题:

  1. 模板表达式中无法直接访问DOM元素的classList属性,Angular模板上下文不支持这类直接DOM操作。
  2. 使用@ViewChild('productImage')只能获取第一个匹配的图片元素,导致第一张图片加载完成后,所有骨架屏都跟着隐藏,无法为每个图片维护独立的加载状态。

下面提供三种可行的解决方案,按推荐程度排序:


方案一:纯CSS控制(最简洁,无需TS逻辑)

利用ng-lazyload-image加载完成后自动添加的ng-lazyloaded类,通过CSS相邻兄弟选择器控制骨架屏的显示/隐藏:

模板代码

<div class="image-wrapper" *ngFor="let item of imageList">
  <ngx-skeleton-loader class="image-cover" [theme]="{ height: '100%', 'margin-bottom': '0' }"></ngx-skeleton-loader>
  <img class="image-cover" [defaultImage]="defaultImage" [lazyLoad]="item.image" />
</div>

CSS代码

.image-wrapper {
  position: relative;
  width: 200px; /* 根据你的布局调整 */
  height: 200px;
}

.image-cover {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* 默认隐藏图片,显示骨架屏 */
img.image-cover {
  display: none;
}

/* 图片加载完成后显示图片,隐藏骨架屏 */
img.image-cover.ng-lazyloaded {
  display: block;
}
img.image-cover.ng-lazyloaded + ngx-skeleton-loader {
  display: none;
}

方案二:利用ng-lazyload-image的加载事件(更可靠)

通过(load)事件监听图片加载完成,为每个图片维护独立的加载状态:

组件TS代码

import { Component } from '@angular/core';

interface ImageItem {
  image: string;
  isLoaded: boolean;
}

@Component({
  selector: 'app-lazy-image-grid',
  templateUrl: './lazy-image-grid.component.html',
  styleUrls: ['./lazy-image-grid.component.css']
})
export class LazyImageGridComponent {
  defaultImage = 'path/to/default/placeholder.jpg';
  imageList: ImageItem[] = [
    { image: 'path/to/image1.jpg', isLoaded: false },
    { image: 'path/to/image2.jpg', isLoaded: false },
    // 更多图片项
  ];

  onImageLoad(index: number) {
    this.imageList[index].isLoaded = true;
  }
}

模板代码

<div *ngFor="let item of imageList; let i = index" class="image-wrapper">
  <!-- 骨架屏:未加载时显示 -->
  <ngx-skeleton-loader 
    *ngIf="!item.isLoaded"
    class="image-cover" 
    [theme]="{ height: '100%', 'margin-bottom': '0' }">
  </ngx-skeleton-loader>
  
  <!-- 图片:加载完成后显示 -->
  <img 
    *ngIf="item.isLoaded"
    class="image-cover" 
    [defaultImage]="defaultImage" 
    [lazyLoad]="item.image"
    (load)="onImageLoad(i)">
</div>

方案三:监听DOM类变化(依赖ng-lazyloaded类)

如果必须通过判断ng-lazyloaded类实现,可使用ViewChildren获取所有图片元素,结合MutationObserver监听类变化:

组件TS代码

import { Component, QueryList, ViewChildren, ElementRef, AfterViewInit } from '@angular/core';

interface ImageItem {
  image: string;
  isLoaded: boolean;
}

@Component({
  selector: 'app-lazy-image-grid',
  templateUrl: './lazy-image-grid.component.html',
  styleUrls: ['./lazy-image-grid.component.css']
})
export class LazyImageGridComponent implements AfterViewInit {
  defaultImage = 'path/to/default/placeholder.jpg';
  imageList: ImageItem[] = [
    { image: 'path/to/image1.jpg', isLoaded: false },
    { image: 'path/to/image2.jpg', isLoaded: false },
    // 更多图片项
  ];

  @ViewChildren('lazyImg') lazyImages!: QueryList<ElementRef>;

  ngAfterViewInit() {
    this.lazyImages.forEach((imgEl, index) => {
      const observer = new MutationObserver(mutations => {
        mutations.forEach(mutation => {
          if (mutation.attributeName === 'class' && imgEl.nativeElement.classList.contains('ng-lazyloaded')) {
            this.imageList[index].isLoaded = true;
            observer.disconnect(); // 监听一次即可
          }
        });
      });
      observer.observe(imgEl.nativeElement, { attributes: true });
    });
  }
}

模板代码

<div *ngFor="let item of imageList; let i = index" class="image-wrapper">
  <ngx-skeleton-loader 
    *ngIf="!item.isLoaded"
    class="image-cover" 
    [theme]="{ height: '100%', 'margin-bottom': '0' }">
  </ngx-skeleton-loader>
  
  <img 
    #lazyImg
    class="image-cover" 
    [defaultImage]="defaultImage" 
    [lazyLoad]="item.image">
</div>

内容的提问来源于stack exchange,提问作者NiceToMytyuk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 00:45:53