Override body style for content in an iframe
Asked Answered
R

12

232

How can I control the background image and colour of a body element within an iframe? Note, the embedded body element has a class, and the iframe is of a page that is part of my site.

The reason I need this is that my site has a black background assigned to the body, and then a white background assigned to divs that contain text. A WYSIWYG editor uses an iframe to embed content when editing, but it doesn't include the div, so the text is very hard to read.

The body of the iframe when in the editor has a class that isn't used anywhere else, so I'm assuming this was put there so problems like this could be solved. However, when I apply styles to class.body they don't override the styles applied to body. The weird thing is that the styles do appear in Firebug, so I've no idea what's going on!

Thanks

UPDATE - I've tried @mikeq's solution of adding a style to the class that is the body's class. This doesn't work when added to the main page's stylesheet, but it does work when added with Firebug. I'm assuming this is because Firebug is applied to all elements on the page whereas the CSS is not applied within iframes. Does this mean that adding the css after window load with JavaScript would work?

Railroad answered 27/6, 2011 at 14:49 Comment(4)
While it's not possible to touch anything in an iframe, loading that URL per Ajax into a <div> can at times be a workaround (if given CORS-Header are allowing for that)... (and „sanitizing“ the loaded data by regexp on the way. Yes, all hacky...)Fatality
You can use javascript if the page domains match, but why not just put a style block in your inner page's HTML to override the colors you want changed? Just add more selectors in your override or use important! if all else fails, to override any color styles you want only on that one page...Complex
@FrankNocke I've got your method to work for loading external site content into a <div> via AJAX. However, how can I style the page better this way?Colorant
@MichaelYaeger Well, sadly, you have to bring on fresh, new (or duplicated) styles (with the host document, in which you are placing things), likely specifically targeted (.class, #id) at that div in which you are loading...Fatality
S
141

An iframe is a 'hole' in your page that displays another web page inside of it. The contents of the iframe is not in any shape or form part of your parent page.

As others have stated, your options are:

  • give the file that is being loaded in the iframe the necessary CSS
  • if the file in the iframe is from the same domain as your parent, then you can access the DOM of the document in the iframe from the parent.
Shuttle answered 27/6, 2011 at 16:18 Comment(2)
The options actually are way broader but are much more sophisticated than using client side APIs and involve a custom proxy made available through say REST interface. Best to look at the GRIDNET OS' (gridnet.org) in-native browser web-browser.Piperpiperaceous
false. Don't get tricked!Piebald
S
297

The below only works if the iframe content is from the same parent domain.

The following code works for me. Tested on Chrome and IE8. The inner iframe references a page that is on the same domain as the parent page.

In this particular case, I am hiding an element with a specific class in the inner iframe.

Basically, you just append a style element to the head section of the document loaded in a frame:

frame.addEventListener("load", ev => {
    const new_style_element = document.createElement("style");
    new_style_element.textContent = ".my-class { display: none; }"
    ev.target.contentDocument.head.appendChild(new_style_element);
});

You can also instead of style use a link element, for referencing a stylesheet resource.

Spense answered 19/12, 2012 at 19:36 Comment(7)
@Spense Can you provide the Javascript alternative (without using Jquery) ?Newish
@sincerekamal Here's the code without the need of jQuery: jsfiddle.net/9g9wkpon You just need to know that hyphenated CSS properties are written in camelCase in JavaScript, like in my example. :)Oppenheimer
jquery.js?ver=1.12.4:2 Uncaught DOMException: Failed to read the 'contentDocument' property from 'HTMLIFrameElement': Blocked a frame with origin "xxxxxxxxx.com" from accessing a cross-origin frame.Christachristabel
@AlexStanese as jeremy states, this only works if the iframe page is coming from the same server as the parent page...which typically means you don't need to bother with javascript anyways...just add the CSS to the other page in the f irst place.Shuttle
@KamalakannanJ you don't even need to use Javascript. Just edit the page that's in the iFrame and put the CSS there.Shuttle
@KamalakannanJ in order to get a better understanding, read the following article form MDN that explains what CORS (Cross-Origin Resource Sharing ) is. mzl.la/2E4ehEX I hope it helps!Winker
Does an iframe with an embedded pdf from the same domain qualify ? <iframe type="application/pdf" src="mydomain/resources/file.pdf"></iframe>Autonomous
S
141

An iframe is a 'hole' in your page that displays another web page inside of it. The contents of the iframe is not in any shape or form part of your parent page.

As others have stated, your options are:

  • give the file that is being loaded in the iframe the necessary CSS
  • if the file in the iframe is from the same domain as your parent, then you can access the DOM of the document in the iframe from the parent.
Shuttle answered 27/6, 2011 at 16:18 Comment(2)
The options actually are way broader but are much more sophisticated than using client side APIs and involve a custom proxy made available through say REST interface. Best to look at the GRIDNET OS' (gridnet.org) in-native browser web-browser.Piperpiperaceous
false. Don't get tricked!Piebald
S
38

You cannot change the style of a page displayed in an iframe unless you have direct access and therefore ownership of the source html and/or css files.

This is to stop XSS (Cross Site Scripting)

Subarid answered 27/6, 2011 at 14:54 Comment(4)
What do you mean by 'direct access'? The page in the iframe is from my site, its all one domain.Railroad
Well then you will need to change the source file on your site (you cannot modify any contents in an iframe) - so if the iframe is pointing at mysite.com/test.html then you will need to modify test.html directly...Subarid
Cannot? Under the conditions the OP says you can. For example, his inner iframe URL is same as his parent site so he should be able to access the DOM ok with any flavor of javascript you like...Complex
@Myles Gray Wrong. You can modify the content in an iframe from it's parent page if it's on the same domain, both with JavaScript and CSS.Hollins
S
19

This code uses vanilla JavaScript. It creates a new <style> element. It sets the text content of that element to be a string containing the new CSS. And it appends that element directly to the iframe document's head.

Keep in mind, however, that accessing elements of a document loaded from another origin is not permitted (for security reasons) -- contentDocument of the iframe element will evaluate to null when attempted from the browsing context of the page embedding the frame.

var iframe = document.getElementById('the-iframe');
var style = document.createElement('style');
style.textContent =
  'body {' +
  '  background-color: some-color;' +
  '  background-image: some-image;' +
  '}' 
;
iframe.contentDocument.head.appendChild(style);
Superable answered 7/7, 2016 at 1:2 Comment(4)
Blocked a frame with origin xxxxxxxxx from accessing a cross-origin frame. I'm afraid it won't workPottery
@Pottery I bypassed that by using a serverside lang(PHP) to parse iframe url and return html response to prevent cors errorsPu
Hey @JustineM. would you please help me to achieve thisUnthread
@Unthread Read input html data, output html data under a domain you own. Then you can puppet away your iframe code or even insert custom js code.Pu
S
14

Override another domain iframe CSS

By using part of SimpleSam5's answer, I achieved this with a few of Tawk's chat iframes (their customization interface is fine but I needed further customizations).

In this particular iframe that shows up on mobile devices, I needed to hide the default icon and place one of my background images. I did the following:

Tawk_API.onLoad = function() {
// without a specific API, you may try a similar load function
// perhaps with a setTimeout to ensure the iframe's content is fully loaded
  $('#mtawkchat-minified-iframe-element').
    contents().find("head").append(
     $("<style type='text/css'>"+
       "#tawkchat-status-text-container {"+
         "background: url(https://example.net/img/my_mobile_bg.png) no-repeat center center blue;"+
         "background-size: 100%;"+
       "} "+
       "#tawkchat-status-icon {display:none} </style>")
   );
};

I do not own any Tawk's domain and this worked for me, thus you may do this even if it's not from the same parent domain (despite Jeremy Becker's comment on Sam's answer).

Skyscraper answered 14/10, 2016 at 11:9 Comment(3)
I suspect this only works because the people at Tawk have explicitly allowed it.Thearchy
@Skyscraper Maybe you can help me. Look at this : #60923668Uranology
tried to override the google tranlation bar css and it work!Ricoriki
A
10

An iframe has another scope, so you can't access it to style or to change its content with javascript.

It's basically "another page".

The only thing you can do is to edit its own CSS, because with your global CSS you can't do anything.

Adscription answered 27/6, 2011 at 14:54 Comment(1)
Yes you can, with javascript. Note it works best with the iframe URL and the parent URL sharing the same domain. But, I think you are right in pointing out this can just be done with CSS on the page inside the iframe.Complex
D
4

This should work with cross domain if you're the owner of the both

The trick here is to assign a global css variable to your body, to listen message with the new color, and then to change the global css variable once receive a message.

I'm using angular, but it should work with pure javascript

My use case was to show to the user what he how the color change would impact his website in the iframe before saving it

Domain A

@ViewChildren('iframeContainer') iframeContainer: QueryList<ElementRef>

sendDataToIframe(
  data = {
      type: 'colorChange',
      colors: {primary: '#000', secondary: '#fff'},
  },
): void {
  if (this.targetUrl)
    this.iframeContainer.first.nativeElement.contentWindow.postMessage(data) // You may use document.getElementById('iframeContainer') instead
}

Domain B

acceptedEditOrigins = [
  'https://my.origine.ccom', // Be sur to have a correct origin, to avoid xss injecto: https://en.wikipedia.org/wiki/Cross-site_scripting
]

constructor() {
// Listen to message
window.addEventListener('message', (event) => this.receiveMessage(event), false)
}

receiveMessage(event: MessageEvent) {
  if (this.acceptedEditOrigins.includes(event.origin))
    switch (event.data.type) {
      case 'colorChange': {
        this.setWebsiteConfigColor(event.data.colors)
      }
    }
}

setWebsiteConfigColor(colors: WebsiteConfigColors) {
  if (colors) {
    const root = document.documentElement
    for (const [key, value] of Object.entries(colors)) {
       root.style.setProperty(`--${key}`, value) // --primary: #000, --secondary: #fff
    }
  }
}

body {
  background-color: var(--primary);
}
Dareen answered 30/6, 2021 at 5:55 Comment(0)
B
1

If you have control of the page hosting the iframe and the page of the iframe, you can pass a query parameter to the iframe...

Here's an example to add a class to the iframe based on whether or not the hosting site is mobile...

Adding iFrame:

var isMobile=$("mobile").length; //detect if page is mobile
var iFrameUrl ="https://myiframesite/?isMobile=" + isMobile; 

$(body).append("<div id='wrapper'><iframe src=''></iframe></div>");
$("#wrapper iframe").attr("src", iFrameUrl ); 

Inside iFrame:

//add mobile class if needed
var url = new URL(window.location.href);
var isMobile = url.searchParams.get("isMobile");
if(isMobile == "1") {
    $("body").addClass("mobile");
}
Blackandwhite answered 11/3, 2018 at 18:8 Comment(1)
Maybe you can help me. Look at this : #60923668Uranology
P
1

This can be done if you're constructing the iframe with JavaScript and you have the pages markup as a string. You can parse the response's markup using DOMParser.parseFromString() with text/html then insert your own style element into the <head> of the parsed markup and then construct your iframe accordingly.

Here's an example where the page markup is retrieved from a fetch request and operated on according to my specification:

const response = await fetch(
    "<website url>"
)

const responseMarkup = await response.json()

const frameMarkup = new DOMParser().parseFromString(
    responseMarkup["text"],
    "text/html"
)

const frameStyle = document.createElement("style")
    .innerHTML = `
        .<whatever element> {
            display: none !important;
        }
    `
frameMarkup.head.appendChild(chartStyle)

const frame = document.createElement("iframe")

frame.srcdoc = frameMarkup.documentElement.innerHTML
frame.width = "100%"
frame.height = "100%"

document.body.appendChild(frame)
Pinprick answered 27/10, 2023 at 18:34 Comment(0)
D
0

For juste one iframe, you can do something like this:

document.querySelector('iframe').contentDocument.body.style.backgroundColor = '#1e1e2d';

In case you have multiple iframe you're dealing with:

document.querySelectorAll('iframe').forEach((iframe) => {
    iframe.contentDocument.body.style.backgroundColor = '#1e1e2d';
});
Devindevina answered 2/2, 2023 at 18:5 Comment(1)
... which, of course, will only work if the content of the iframe comes from the same domain. If not, document.querySelector('iframe').contentDocument will simply return null.Yttrium
G
-6

Perhaps it's changed now, but I have used a separate stylesheet with this element:

.feedEkList iframe
{
max-width: 435px!important;
width: 435px!important;
height: 320px!important;
}
Gristmill answered 4/5, 2015 at 23:46 Comment(1)
This only applies to the iFrame element itself - the question asks specifically about the iFrame content, which it currently isn't possible to change through CSS alone.Hessney
C
-34

give the body of your iframe page an ID (or class if you wish)

<html>
<head></head>
<body id="myId">
</body>
</html>

then, also within the iframe's page, assign a background to that in CSS

#myId {
    background-color: white;
}
Caiaphas answered 27/6, 2011 at 14:58 Comment(5)
This is weird. Your solution works for me when I add it to the page with firebug, but doesn't work if its in the page's normal css file. Could this be part of your comment 'assuming the page within the iFrame is yours to edit'? I didnt understand what you meant by that. ThanksRailroad
I mean by that, is the page you are loading into the iFrame from your site and under your control? or is it from an external site that you cannot edit the source to include that id/class.Caiaphas
It is from my site, but as its done in by the editor it wont be easy to change the mark up that is pulled through.Railroad
ps I didnt mean add the id="myId" to your main page, but to the page within the iFrame. That way you can target the body of the iFrame page with a different style to your main page. You are also referencing the CSS file in your iFrame pages also aren't you. You need to treat the page loaded in the iFrame as a completely independant page. If you load just that page into a web browser does it have the correct style?Caiaphas
Im not actually sure where the 'page' is as its not the whole page the editor uses. Thanks for your help, I think I understand how all this stuff works noe.Railroad

© 2022 - 2025 — McMap. All rights reserved.