阻止“innerHTML”中的样式影响父级

Posted

技术标签:

【中文标题】阻止“innerHTML”中的样式影响父级【英文标题】:Stop styles in `innerHTML` from impacting parent 【发布时间】:2017-12-19 19:57:13 【问题描述】:

我创建了一个 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 只是一个管道,可以绕过对我正在注入的 HTML 的 Angular 检查:

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设法以某种方式“包含”它,因此它不会重新设置父元素的样式。我该如何做到这一点?

理想情况下,我希望完全隔离样式 - 就像我不希望父样式受子样式影响或子样式受父样式影响(这似乎是 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>   

这是因为(看起来)htmlbody 标记在您将其嵌入 div 时被剥离(这很有意义)。因此,只需在该类中添加一个包含 div 的父级,这一切都可以正常工作 - 嵌入和电子邮件中:

<!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> 

【问题讨论】:

Angular 4 被设计为在每个组件的基础上处理 CSS,这应该防止它干扰父组件。也许您可以使用 Angular 组件而不是 [innerHTML] 来防止该问题。这是一个参考页面:angular.io/guide/component-styles 但它已经影响了完全不相关的组件——它影响了整个页面,无论哪个组件包含 html。就像我的菜单上的字体发生了变化,它甚至不是父组件。 【参考方案1】:

@petryuno1 建议的最佳方法是使用组件,因为它将 CSS/样式的范围仅限于该组件,但在基本级别上,您可以将类应用于 &lt;div [innerHTML]="myHtml | safeHtml"&gt;&lt;/div&gt;,例如:

&lt;div class="foo" [innerHTML]="myHtml | safeHtml"&gt;&lt;/div&gt;

如果无法使用组件,则将 css 修改为以类的 * 为目标?

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

&lt;div&gt; 上的 class 属性将保持不变,无论对 innerHTML 的修改如何。

希望对您有所帮助!

【讨论】:

正如我上面提到的,嵌入式样式已经影响了不相关组件中的样式。我认为这是因为我专门绕过了安全信任,所以 css 只是给浏览器不变,浏览器尽职尽责地应用它们。 您提出的解决方案可以帮助我嵌入 HTML,但问题是我正在生成整个 HTML 文档以用作电子邮件 - 我想我可以试试这个并将这个类应用于每个元素在嵌入的 HTML 中?这可能行得通 - 让我检查一下... * 将应用于页面和父页面上的所有内容。尽管* 的存在使您的目标似乎是将font-family 应用于[innerHTML] 中的ALL 元素。为类添加前缀/名称间距将有助于防止父元素受到* 的影响。默认情况下,带有stylesstylesUrl 的组件会使用自己的命名空间,从而防止父元素受到影响。 对 - 但是这个 HTML 是在服务器端生成的,并用作 HTML 电子邮件,我只是想给用户一个预览。我明白你在说什么,但我不明白它如何解决这个问题 - 生成角度组件服务器端并不实际(我可以看到这可能是如何实现的,但如果涉及到我'只会将其呈现为PDF)。谷歌以某种方式在网络浏览器的电子邮件中显示我的 HTML,而不会破坏他们的页面,所以它一定是可能的。不过,在嵌入的 HTML 中使用唯一类作为前缀似乎可以工作 - 我会尝试一下。 另一个注意事项 - 你说* 的存在使我的目标似乎是将font-family 应用于[innerHTML] 中的所有元素 - 是的,这就是我的目标。只是不要让它影响[innerHTML] 之外的项目-无论如何,前缀想法很好,POC 对我有用。让我完成转换真正的交易,如果它成功了,我会接受这个答案(似乎会)。【参考方案2】:

我发现使用 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。

【讨论】:

以上是关于阻止“innerHTML”中的样式影响父级的主要内容,如果未能解决你的问题,请参考以下文章

如何阻止innerHtml表一遍又一遍地重复自己?

尝试使用javascript添加样式标签(IE8中的innerHTML)

javascript中的事件冒泡和事件捕获

css里怎么去掉从父级元素继承下来的 css 样式吗

带有外部样式表的div?

有办法去掉从父级元素继承下来的 CSS 样式吗