Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stop styles in `innerHTML` from impacting parent

I have created an HTML email that displays correctly in a few email clients I've tested, but when I provide a preview of it on my page, the embedded styles in the innerHTML impact the parent. It must be possible to avoid this because when I view the email in gmail it displays correctly without changing the style of gmail itself.

Here's some HTML that shows the problem (but displays with no side effects in 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> 

I'm using Angular 4 so I'm including it in my page this way:

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

...and safeHtml is just a pipe to bypass the Angular checks on the HTML I'm injecting:

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);

  }
}

It works, in that it does show the html in the div, but the style info changes everything on the page. I get it - I'm literally using * in my embedded style. ...but as I say, gmail manages to 'contain' this somehow so it doesn't restyle the parent elements. How do I accomplish this?

Ideally I'd like total style isolation - like I don't want the parent styles to be impacted by the child or the child styles to be impacted by the parent (which, it seems, is how gmail is behaving).

Update

Changing the HTML to the following had it working for email, and contained the CSS, but didn't work embedded in 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>   

This is because (it seems) the html and body tags are stripped out when you embed this in a div (which makes some sense). So, just add a parent containing div with the class and it all works - both embedded and in the email:

<!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> 
like image 481
WillyC Avatar asked Nov 02 '25 00:11

WillyC


2 Answers

Another solution I found using Angular for a similar problem (having to show the HTML of an e-mail in a page) is to create a new Component to display the preview and setting the encapsulation to 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;

After that, I simply insert the preview variable in the [innerHtml] element.

The ShadowDom is responsible for encapsulating the Style and not letting it affect the other components. For more information you can check here.

like image 159
Gui Freire Avatar answered Nov 03 '25 13:11

Gui Freire


The best approach as @petryuno1 advised is to use a component as it would scope the CSS/styles to only that component, but at a basic level can you apply a class to the <div [innerHTML]="myHtml | safeHtml"></div> such as:

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

Then modify the css to target * of the class instead if a component is not possible?

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

The class property on that <div> would remain regardless of modifications to innerHTML.

Hopefully that helps!

like image 27
Alexander Staroselsky Avatar answered Nov 03 '25 14:11

Alexander Staroselsky