【问题标题】:Angular 2 - innerHTML stylingAngular 2 - innerHTML 样式
【发布时间】:2025-04-04 12:25:01
【问题描述】:

我从 HTTP 调用中获取了大量的 HTML 代码。我将 HTML 块放在一个变量中,并使用 [innerHTML] 将其插入我的页面,但我无法设置插入的 HTML 块的样式。有人对我如何实现这一点有任何建议吗?

@Component({
  selector: 'calendar',
  template: '<div [innerHTML]="calendar"></div>',
  providers: [HomeService], 
  styles: [`h3 { color: red; }`]
})

我要设置样式的 HTML 是变量“日历”中包含的块。

【问题讨论】:

  • 风格从何而来?从组件内部或从添加到index.html的样式?
  • can not style the inserted HTML block 是什么意思?用一小段代码向我们展示它做了什么。
  • 我用代码 sn-p 更新了我的帖子! :) 谢谢
  • 我在答案中添加了 Plunker 链接
  • @GünterZöchbauer 如果 HTML 代码具有内联 css 怎么办?它将如何呈现?

标签: angular innerhtml


【解决方案1】:

更新 2 ::slotted

::slotted现在被所有新浏览器支持,可以和ViewEncapsulation.ShadowDom一起使用

https://developer.mozilla.org/en-US/docs/Web/CSS/::slotted

更新 1 ::ng-deep

/deep/ 已弃用并由::ng-deep 取代。

::ng-deep 也已标记为已弃用,但尚无可用替代品。

当所有浏览器都正确支持 ViewEncapsulation.Native 并支持跨阴影 DOM 边界的样式时,::ng-deep 可能会停止使用。

原创

Angular 将各种 CSS 类添加到它添加到 DOM 的 HTML 中,以模拟影子 DOM CSS 封装,以防止样式渗入和流出组件。 Angular 还会重写您添加的 CSS 以匹配这些添加的类。对于使用[innerHTML] 添加的 HTML,不会添加这些类,并且重写的 CSS 不匹配。

作为一种解决方法尝试

  • 用于添加到组件的 CSS
/* :host /deep/ mySelector { */
:host ::ng-deep mySelector { 
  background-color: blue;
}
  • 为 CSS 添加到 index.html
/* body /deep/ mySelector { */
body ::ng-deep mySelector {
  background-color: green;
}

&gt;&gt;&gt;(和等价的/deep/,但/deep/ 更适用于 SASS)和 ::shadow 在 2.0.0-beta.10 中添加。它们类似于 shadow DOM CSS 组合器(已弃用),仅适用于 encapsulation: ViewEncapsulation.Emulated,这是 Angular2 中的默认值。它们可能也可以与ViewEncapsulation.None 一起使用,但由于它们不是必需的,因此只会被忽略。 在支持跨组件样式的更高级功能之前,这些组合器只是一个中间解决方案。

另一种方法是使用

@Component({
  ...
  encapsulation: ViewEncapsulation.None,
})

对于所有阻止您的 CSS 的组件(取决于您添加 CSS 的位置以及您想要设置样式的 HTML 的位置 - 可能是应用程序中的所有组件)

更新

Example Plunker

【讨论】:

  • 只是给任何人的注释,这不适用于 node-sass 或 styleUrl。仅在样式中:[...]
  • 在 SASS 中使用 /deep/ 而不是 &gt;&gt;&gt;
  • 您可以在使用inneeHTML 添加的内容中添加指令或组件
  • 如果 HTTP 调用提供的 HTML 很大并且它具有内联 css,因为我没有预定义样式,这怎么可能,我只能从内联 css 中获取它@GünterZöchbauer
  • 在 Angular 8 中拯救了一天!谢谢。为了找到这个答案,很难把问题做对!
【解决方案2】:

您需要遵循的简单解决方案是

import { DomSanitizer } from '@angular/platform-browser';

constructor(private sanitizer: DomSanitizer){}

transformYourHtml(htmlTextWithStyle) {
    return this.sanitizer.bypassSecurityTrustHtml(htmlTextWithStyle);
}

【讨论】:

  • 这确实允许在 innerHTML 中写入文本,但 CSS 仍然无法到达它。
  • @JeremyThille 我遇到了与您提到的内容相关的问题,我无法设置&lt;ul&gt; 列表的样式,我使用的是Emulated::ng-deep,但是,虽然它有效, 它已被弃用。你知道如何让 CSS 到达它吗?
  • 我使用encapsulation: ViewEncapsulation.None(如上面的答案所述)然后CSS到达子组件
  • 您应该改进答案,添加所有必要的步骤以使其正常工作
【解决方案3】:

我们经常以[innerHTML]="content.title" 从我们的 CMS 中提取内容。我们将必要的类放在应用程序的根styles.scss 文件中,而不是在组件的scss 文件中。我们的 CMS 故意去除了内嵌样式,因此我们必须准备好作者可以在其内容中使用的类。请记住,在模板中使用 {{content.title}} 不会从内容中呈现 html。

【讨论】:

    【解决方案4】:

    如果您尝试在 Angular 组件中为动态添加的 HTML 元素设置样式,这可能会有所帮助:

    // inside component class...
        
    constructor(private hostRef: ElementRef) { }
        
    getContentAttr(): string {
      const attrs = this.hostRef.nativeElement.attributes
      for (let i = 0, l = attrs.length; i < l; i++) {
        if (attrs[i].name.startsWith('_nghost-c')) {
          return `_ngcontent-c${attrs[i].name.substring(9)}`
        }
      }
    }
        
    ngAfterViewInit() {
      // dynamically add HTML element
      dynamicallyAddedHtmlElement.setAttribute(this.getContentAttr(), '')
    }
    

    我的猜测是,该属性的约定不能保证在 Angular 版本之间保持稳定,因此在升级到新版本的 Angular 时可能会遇到此解决方案的问题(尽管更新此解决方案可能是在那种情况下微不足道)。

    【讨论】:

      【解决方案5】:

      Günter Zöchbauer 推荐的版本运行良好,但我还有一个补充。在我的情况下,我有一个未设置样式的 html 元素,但我不知道如何设置它的样式。因此我设计了一个管道来为其添加样式。

      import { Pipe, PipeTransform } from '@angular/core';
      import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
      
      
      @Pipe({
          name: 'StyleClass'
      })
      export class StyleClassPipe implements PipeTransform {
      
          constructor(private sanitizer: DomSanitizer) { }
          transform(html: any, styleSelector: any, styleValue: any): SafeHtml {
              const style = ` style = "${styleSelector}: ${styleValue};"`;
              const indexPosition = html.indexOf('>');
              const newHtml = [html.slice(0, indexPosition), style, html.slice(indexPosition)].join('');
              return this.sanitizer.bypassSecurityTrustHtml(newHtml);
          }
      
      }
      

      然后你可以像这样为任何 html 元素添加样式:

      <span [innerhtml]="Variable | StyleClass: 'margin': '0'"> </span>
      

      与:

      Variable = '<p> Test </p>'
      

      【讨论】:

        【解决方案6】:

        使用下面的方法允许innerhtml中的CSS样式。

        import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
        .
        .
        .
        .
        html: SafeHtml;
        
        constructor(protected _sanitizer: DomSanitizer) {
           this.html = this._sanitizer.bypassSecurityTrustHtml(`
                <html>
                <head></head>
                <body>
                   <div style="display:flex; color: blue;">
                      <div>
                         <h1>Hello World..!!!!!</h1>
                      </div>
                   </div>
                </body>
                </html>`);
        }
        

        示例代码 stackblitz

        或者使用下面的方法直接用HTML编写。 https://gist.github.com/klihelp/4dcac910124409fa7bd20f230818c8d1

        【讨论】:

          【解决方案7】:

          对于任何想要将某种样式应用于 innerHTML 的人:

          关注Create a safe HTML pipe

          您可以将 HTML 字符串与 CSS 样式连接,如下所示:

          return this.sanitizer.bypassSecurityTrustHtml(value+='<style type="text/css">.image img { width: 100% }</style>');
          

          这个value 来自transform(value, ...args)

          【讨论】:

            【解决方案8】:

            最简单直接的方法是使用位于 Angular 项目 src 文件夹中的全局样式文件。

            假设组件选择器是:app-my-component

            为 app-my-component 模板中承载 innerHtml 内容的元素添加一个类:

            <div class="innerhtml-class" [innerHTML]="variable.innerHtml"></div>
            

            添加到全局样式文件:

            app-my-component { 
             .innerhtml-class { 
               declaration goes here 
             } 
            }
            

            【讨论】:

            • 这仅在您正在编写的规则没有覆盖其他一些预设规则时才有效。在我们的例子中,innerHTML 带有 p 标签,这些标签是由我们的框架预定义的。如果没有 ng-deep,覆盖 p 标签的规则将不起作用。
            • @FilipeMelo 你能给我一个收到的 innerHTML 的例子以及你想要实现的目标吗?我不认为我明白你的意思?我已经使用我的答案来设置收到的 innerHTML 中的样式元素,其中包括 p 和 span 标签。
            【解决方案9】:

            我最初走的是this.sanitizer.bypassSecurityTrustHtml()路线,并将封装设置为ViewEncapsulation.NONE,但有两个问题:

            1. ViewEncapsulation.NONE 导致我的组件出现其他样式问题
            2. 我的“安全”html 似乎不适用于 css 变量,即 var(--blue)

            这对我有用(无需更改任何其他内容): InsertAdjacentHTML

            模板:

            <div id=template></div>
            

            代码:

            ngOnInit() {
              const el = document.getElementById('template');
              el.insertAdjacentHTML('afterbegin', `<span style="color: var(--blue)">hello</span>`);
            }
            

            免责声明:就我而言,我是从配置文件中解析 html。您不会希望使用用户输入的 html 走这条路线。

            【讨论】:

              【解决方案10】:

              如果您的动态样式有限,则使用 inline CSS variables 是一种替代解决方案。

              // file.ts
              someVarWithHtml = 'Hello <span class="dynamic">World</span>';
              
              // file.ng.html
              <div [style]="'--my-var: ' + value"
                   [innerHTML]="someVarWithHtml"></div>
              
              // style.css
              .dynamic {
                background: var(--my-var);
              }
              

              【讨论】:

                【解决方案11】:

                如果您使用 sass 作为样式预处理器,您可以通过以下方式切换回原生 Sass 编译器以获取开发依赖:

                npm install node-sass --save-dev

                这样您就可以继续使用 /deep/ 进行开发。

                【讨论】: