阻止 `innerHTML` 中的样式影响父级

Stop styles in `innerHTML` from impacting parent

我创建了一个 HTML 电子邮件,它在我测试过的几个电子邮件客户端中正确显示,但是当我在我的页面上提供它的预览时,innerHTML 中的嵌入样式影响家长。必须有可能避免这种情况,因为当我在 gmail 中查看电子邮件时,它会正确显示而无需更改 gmail 本身的样式。

这里有一些 HTML 显示了问题(但在 gmail 中显示没有副作用):

<!DOCTYPE HTML>
<html>
    <head>
        <style type="text/css">
            * { font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; }
        </style>
    </head>
    <body>
        <pre style="white-space:pre-wrap;">Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test </pre>
    </body>    
</html> 

我使用的是 Angular 4,所以我将它以这种方式包含在我的页面中:

<div [innerHTML]="myHtml | safeHtml">
</div>

...而 safeHtml 只是一个管道,用于绕过 Angular 我正在注入的 HTML 检查:

import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer, SafeHtml } from "@angular/platform-browser";

@Pipe({name: 'safeHtml'})
export class SafeHtmlPipe implements PipeTransform {
  constructor(private sanitizer:DomSanitizer){}

  transform(html) {

    return this.sanitizer.bypassSecurityTrustHtml(html);

  }
}

它起作用了,因为它确实在 div 中显示了 html,但是样式信息改变了页面上的所有内容。我明白了——我确实在我的嵌入式风格中使用了 *。 ...但正如我所说,gmail 以某种方式设法 'contain' 这样它就不会重新设置父元素的样式。我该如何实现?

理想情况下,我希望完全隔离样式 - 就像我不希望父样式受到子样式的影响或子样式受到父样式的影响(这似乎是 gmail 的行为方式).

更新

将 HTML 更改为以下内容使其适用于电子邮件,并包含 CSS,但无法嵌入 Angular:

<!DOCTYPE HTML>
<html class="my-class">
    <head>
        <style type="text/css">
            .my-class * { 
                font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; 
                color: blue;
            }
            .my-class pre {
                white-space:pre-wrap;
            }
        </style>
    </head>
    <body>
        <pre>Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test </pre>
    </body>    
</html>   

这是因为(看起来)当您将其嵌入 div 时,htmlbody 标签被删除了(这在某种程度上是有道理的)。因此,只需添加一个包含 div 和 class 的父项,它就可以正常工作 - 嵌入和电子邮件中:

<!DOCTYPE HTML>
<html>
    <head>
        <style type="text/css">
            .my-class * { 
                font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; 
                color: blue;
            }
            .my-class pre {
                white-space:pre-wrap;
            }
        </style>
    </head>
    <body>
        <div class="my-class">
            <pre>Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test Test test </pre>
        </div>
    </body>    
</html> 

@petryuno1 建议的最佳方法是使用一个组件,因为它会将 CSS/styles 的范围仅限于该组件,但在基本级别上,您可以将 class 应用于 <div [innerHTML]="myHtml | safeHtml"></div> 如:

<div class="foo" [innerHTML]="myHtml | safeHtml"></div>

然后修改 css 以 class 的 * 为目标,而不是如果组件不可用?

.foo * { font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; }

<div> 上的 class 属性 将保留,无论对 innerHTML.

的修改如何

希望对您有所帮助!

我发现使用 Angular 解决类似问题(必须在页面中显示电子邮件的 HTML)的另一个解决方案是创建一个新组件来显示预览和设置封装到ShadowDom.

  @Component({
  selector: 'app-email-view',
  templateUrl: './email-view.component.html',
  styleUrls: ['./email-view.component.scss'],
  encapsulation: ViewEncapsulation.ShadowDom
})
export class EmailViewComponent implements OnInit {

  @Input()
  preview: any;

之后,我只需在 [innerHtml] 元素中插入 preview 变量。

ShadowDom负责封装Style,不让它影响其他组件。有关详细信息,您可以查看 here.