You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Angular2中[innerHTML]绑定HTML丢失JS,显示异常如何解决?

解决Angular中[innerHTML]移除JavaScript导致显示异常的问题

这是Angular的安全防护机制在发挥作用——为了防范XSS(跨站脚本攻击),Angular默认会自动清理[innerHTML]绑定内容里的<script>标签及其他潜在危险代码。而且就算你绕过了Angular的安全检查,浏览器本身也不会自动执行通过innerHTML插入的脚本,这也是一层安全限制。下面分两步帮你解决这个问题:

1. 绕过Angular的HTML安全检查

你需要使用DomSanitizer来标记你的HTML内容为“安全”,让Angular不再清理其中的内容。

组件代码修改:

import { Component, OnInit } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { HttpClient, Observable } from '@angular/common/http';
import { map } from 'rxjs/operators';

@Component({
  selector: 'app-your-component',
  templateUrl: './your-component.component.html',
  styleUrls: ['./your-component.component.css']
})
export class YourComponent implements OnInit {
  response$: Observable<{ status?: boolean, safeHtml: SafeHtml }>;

  constructor(
    private http: HttpClient,
    private sanitizer: DomSanitizer
  ) { }

  ngOnInit(): void {
    // 发起POST请求并处理响应
    this.response$ = this.http.post('/your-api-url', {}).pipe(
      map(response => ({
        ...response,
        // 将原始HTML转为Angular信任的SafeHtml类型
        safeHtml: this.sanitizer.bypassSecurityTrustHtml(response['html'])
      }))
    );
  }
}

模板代码修改:

把原来的response.html替换为处理后的response.safeHtml

<div class="col-sm-12" *ngIf="(response$ | async) as response">
  <div class="col-sm-12" *ngIf="!response.status">
    <div style="height: 800px; overflow: auto" [innerHTML]="response.safeHtml">
    </div>
  </div>
</div>

2. 手动执行HTML中的脚本

完成第一步后,HTML里的脚本标签会被保留,但浏览器不会自动执行它们。你需要手动提取并执行这些脚本:

组件代码新增:

先通过ViewChild获取承载内容的容器,然后在视图初始化完成后处理脚本:

import { ViewChild, ElementRef, AfterViewInit } from '@angular/core';

// 继承AfterViewInit生命周期钩子
export class YourComponent implements OnInit, AfterViewInit {
  // 绑定模板中的容器元素
  @ViewChild('responseContainer') responseContainer: ElementRef;
  // 存储异步获取的响应(方便在ngAfterViewInit中使用)
  private currentResponse: any;

  // ... 之前的代码

  ngOnInit(): void {
    this.response$ = this.http.post('/your-api-url', {}).pipe(
      map(response => {
        // 保存当前响应
        this.currentResponse = response;
        return {
          ...response,
          safeHtml: this.sanitizer.bypassSecurityTrustHtml(response['html'])
        };
      })
    );
  }

  ngAfterViewInit(): void {
    if (!this.currentResponse || this.currentResponse.status) return;

    const container = this.responseContainer.nativeElement;
    const scripts = container.querySelectorAll('script');

    scripts.forEach(oldScript => {
      // 创建新的script元素(浏览器只会执行通过createElement创建的脚本)
      const newScript = document.createElement('script');

      // 复制原脚本的所有属性(比如src、type等)
      Array.from(oldScript.attributes).forEach(attr => {
        newScript.setAttribute(attr.name, attr.value);
      });

      // 复制原脚本的内容
      newScript.textContent = oldScript.textContent;

      // 将新脚本插入到文档中执行
      document.body.appendChild(newScript);
      // 移除原脚本标签(可选,避免重复处理)
      oldScript.remove();
    });
  }
}

模板代码新增标记:

给承载内容的div添加#responseContainer标记:

<div class="col-sm-12" *ngIf="(response$ | async) as response">
  <div class="col-sm-12" *ngIf="!response.status">
    <div #responseContainer style="height: 800px; overflow: auto" [innerHTML]="response.safeHtml">
    </div>
  </div>
</div>

重要注意事项

  • 安全风险:只有当你完全信任这个HTML内容的来源时,才使用上述方法。如果内容来自不可信的用户或第三方,可能会引入XSS攻击风险。
  • 脚本依赖:如果脚本依赖HTML中的DOM元素,要确保脚本执行时对应的元素已经渲染完成(ngAfterViewInit阶段通常可以满足)。
  • 全局污染:手动插入的脚本会在全局作用域执行,可能会和Angular应用的变量、生命周期产生冲突,建议测试验证。

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

火山引擎 最新活动