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

Angular模态框跳转搜索页面后遮罩层残留问题求助

解决Angular中Bootstrap模态框跳转后遮罩层残留问题

问题根源

当前代码在路由跳转前未主动关闭模态框,Bootstrap模态框的遮罩层(.modal-backdrop)是动态插入到<body>中的,Angular路由跳转时HomeComponent销毁,但遮罩层因属于body节点不会被自动清理,导致残留。同时混用jQuery和原生DOM事件绑定的方式,也会导致模态框状态控制不彻底。

修复方案

核心思路

  1. 改用Angular风格的模态框控制方式,避免原生DOM和jQuery操作
  2. 搜索提交时先关闭模态框,再执行路由跳转
  3. 组件销毁时强制清理残留的遮罩层和页面滚动限制

修改后的代码示例

TypeScript文件(home.component.ts)

import { Component, ElementRef, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { Router } from '@angular/router';
import { Modal } from 'bootstrap';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css'],
})
export class HomeComponent implements OnInit, OnDestroy {
    searchTerm: string = '';
    private readonly imagesToShowInLandscape = ['bowl', 'paella', 'hotpot', 'omelette', 'brownChunks', 'tomYum', 'fish', 'raznjici', 'chickenThigh'];
    private readonly imagesToHideInPortrait = ['omelette', 'chickenThigh', 'tomYum'];
    
    @ViewChild('modal1') modal1!: ElementRef;
    @ViewChild('modal2') modal2!: ElementRef;
    private modal1Instance?: Modal;
    private modal2Instance?: Modal;
  
    constructor(private router: Router) {}
  
  ngOnInit() {
    // 初始化Bootstrap模态框实例
    this.modal1Instance = new Modal(this.modal1.nativeElement);
    this.modal2Instance = new Modal(this.modal2.nativeElement);

    this.adjustImageVisibility();
    window.addEventListener('resize', this.adjustImageVisibility);
  }

  ngOnDestroy() {
    window.removeEventListener('resize', this.adjustImageVisibility);
    // 销毁模态框实例,清理资源
    this.modal1Instance?.dispose();
    this.modal2Instance?.dispose();
    // 强制清理残留遮罩层和页面滚动限制
    this.cleanupModalBackdrops();
  }

  search() {
    // 先关闭模态框,等待动画完成后跳转
    this.modal1Instance?.hide();
    setTimeout(() => {
      this.redirectToRecipes(this.searchTerm);
    }, 300); // 匹配Bootstrap模态框关闭动画时长
  }

  searchModal2() {
    // 处理modal2的搜索逻辑
    this.modal2Instance?.hide();
    setTimeout(() => {
      const inputValue = this.modal2.nativeElement.querySelector('input').value;
      this.redirectToRecipes(inputValue);
    }, 300);
  }

  redirectToRecipes(searchTerm: string) {
    this.router.navigate(['/recipes'], { queryParams: { search: searchTerm } });
  }

  openModal(target: 'modal1' | 'modal2') {
    // 统一的模态框打开方法
    target === 'modal1' ? this.modal1Instance?.show() : this.modal2Instance?.show();
  }

  adjustImageVisibility() {
    // 保留原有图片可见性调整逻辑
    // ... 你的原有代码
  }

  private cleanupModalBackdrops() {
    // 移除body中所有残留的遮罩层
    document.querySelectorAll('.modal-backdrop').forEach(el => el.remove());
    // 恢复页面滚动
    document.body.classList.remove('modal-open');
  }
}

HTML文件(home.component.html)

<div class="row d-flex justify-content-center mt-5">
  <!-- Kuham Button -->
  <div class="col-md-6 d-flex justify-content-center justify-content-md-end">
    <div class="card bg-transparent border-0">
      <div class="card-body d-flex justify-content-center" (click)="openModal('modal1')">
        <a class="izberi">
          <span class="button-text">Kuham</span>
          <img src="assets/images/cooking-icon.png" alt="Button Image" id="choice-images" />
        </a>
      </div>
    </div>
  </div>
  <!-- Jem Button -->
  <div class="col-md-6 d-flex justify-content-center justify-content-md-start">
    <div class="card bg-transparent border-0">
      <div class="card-body d-flex justify-content-center" (click)="openModal('modal2')">
        <a class="izberi">
          <span class="button-text">Jem</span>
          <img src="assets/images/eating-icon2.png" alt="Button Image" id="choice-images" />
        </a>
      </div>
    </div>
  </div>
</div>

<div class="modal" id="modal1" #modal1>
  <div class="modal-dialog modal-dialog-centered">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title">Kaj kuhaš?</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">
        <form (ngSubmit)="search()">
          <div class="mb-3">
            <input class="form-control" type="text" [(ngModel)]="searchTerm" name="searchTerm" placeholder="Vpiši ime jedi ali sestavine">
          </div>
          <button type="submit" class="btn btn-primary">Poišči</button>
        </form>
      </div>
    </div>
  </div>
</div>
<div class="modal" id="modal2" #modal2>
  <div class="modal-dialog modal-dialog-centered">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title">Kaj bi jedel?</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">
        <form (ngSubmit)="searchModal2()">
        <div class="mb-3">
          <input class="form-control" type="text" placeholder="Vpiši ime jedi ali sestavine" aria-label="default input example">
        </div>
        <button type="submit" class="btn btn-primary">Poišči</button>
        </form>
      </div>
    </div>
  </div>
</div>

关键修改说明

  1. 移除原生DOM事件绑定和jQuery调用,改用ViewChild和Bootstrap官方Modal类控制模态框,符合Angular组件化规范
  2. 搜索提交时先关闭模态框,等待动画完成后再跳转,确保遮罩层有时间被正常清理
  3. 组件销毁时强制清理残留遮罩层,并移除body上的modal-open类,彻底恢复页面滚动和交互能力
  4. 统一模态框打开逻辑,简化代码结构

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 04:22:01