¿Cómo aplicar CSS a iframe?

1017

Tengo una página simple que tiene algunas secciones de iframe (para mostrar enlaces RSS). ¿Cómo puedo aplicar el mismo formato CSS desde la página principal a la página que se muestra en el iframe?

Akshay Mulgavkar
fuente
66
Es posible pero solo si el dominio del iframe es el mismo que el padre
gawpertron
13
Gawpertron, solo para aclarar, ¿está diciendo que si uso contenido de iFrame de otro dominio que no controlo, no hay forma de que yo controle el CSS para ese contenido?
Ville M
¿Puede incluir un enlace a la página para que podamos ver nuestros cambios?
55
El dominio, el puerto y el protocolo tienen que ser iguales, tampoco funciona con subdominios.
Lee Penkman

Respuestas:

434

Editar: Esto no funciona entre dominios a menos que se establezca el encabezado CORS apropiado .

Aquí hay dos cosas diferentes: el estilo del bloque de marco flotante y el estilo de la página incrustada en el marco flotante. Puede establecer el estilo del bloque de iframe de la forma habitual:

<iframe name="iframe1" id="iframe1" src="empty.htm" 
        frameborder="0" border="0" cellspacing="0"
        style="border-style: none;width: 100%; height: 120px;"></iframe>

El estilo de la página incrustada en el iframe debe establecerse incluyéndolo en la página secundaria:

<link type="text/css" rel="Stylesheet" href="Style/simple.css" />

O se puede cargar desde la página principal con Javascript:

var cssLink = document.createElement("link");
cssLink.href = "style.css"; 
cssLink.rel = "stylesheet"; 
cssLink.type = "text/css"; 
frames['iframe1'].document.head.appendChild(cssLink);
Tamas Czinege
fuente
30
Tenga en cuenta que me parece que algunos de los ejemplos publicados anteriormente ahora no son válidos para html5. Se puede acceder a los contenidos del marco de la siguiente manera: document.getElementById("myframe").contentDocument. Sin embargo, incrustar el CSS todavía no parece funcionar para mí.
Rehno Lindeque
35
el enlace solo puede aparecer en HEAD
Knu
18
Me funcionó solo cuando lo hice ...document.head.appendChild(cssLink): Firefox y Safari.
mojuba
24
¿Esto realmente funciona entre dominios? No creo que sea así.
Simon East
89
Solo así nadie más tiene que probarlo para descubrirlo: correcto, no funciona entre dominios. Inmediatamente después de hacer los marcos ['nombre'] se obtiene "Intento de JavaScript inseguro para acceder al marco con bla bla URL desde el marco con bla bla URL. Los dominios, protocolos y puertos deben coincidir".
Kevin
205

Encontré este problema con Google Calendar . Quería darle un estilo sobre un fondo más oscuro y cambiar la fuente.

Afortunadamente, la URL del código incrustado no tenía restricciones en el acceso directo, por lo que al usar la función PHP file_get_contentses posible obtener todo el contenido de la página. En lugar de llamar a la URL de Google, es posible llamar a un archivo php ubicado en su servidor, ej. google.php, que contendrá el contenido original con modificaciones:

$content = file_get_contents('https://www.google.com/calendar/embed?src=%23contacts%40group.v.calendar.google.com&ctz=America/Montreal');

Agregar la ruta a su hoja de estilo:

$content = str_replace('</head>','<link rel="stylesheet" href="http://www.yourwebsiteurl.com/google.css" /></head>', $content);

(Esto colocará su hoja de estilo al final justo antes de la headetiqueta final).

Especifique la url base de la url original en caso de que css y js se llamen relativamente:

$content = str_replace('</title>','</title><base href="https://www.google.com/calendar/" />', $content);

El google.phparchivo final debería verse así:

<?php
$content = file_get_contents('https://www.google.com/calendar/embed?src=%23contacts%40group.v.calendar.google.com&ctz=America/Montreal');
$content = str_replace('</title>','</title><base href="https://www.google.com/calendar/" />', $content);
$content = str_replace('</head>','<link rel="stylesheet" href="http://www.yourwebsiteurl.com/google.css" /></head>', $content);
echo $content;

Luego cambia el iframecódigo de inserción a:

<iframe src="http://www.yourwebsiteurl.com/google.php" style="border: 0" width="800" height="600" frameborder="0" scrolling="no"></iframe>

¡Buena suerte!

SequenceDigitale.com
fuente
71
Puede llamar a ese pirateo por definición si lo desea. Pero no ofreció una solución mejor ... Esta solución no es una forma de dañar el servicio de Google o engañar a las personas para explotar su debilidad.
SequenceDigitale.com
55
Mataría por una forma de hacer que esta solución funcione con Google Docs. Está arrojando todo tipo de errores de JavaScript. "Error de tipo no capturado: no se puede leer la propiedad 'a' de indefinido"
deweydb
1
No
importa
99
@ChrisHoughton FYI, básicamente no lo es. Sin embargo, puede hacer que todo el iframe no tenga sentido (una razón para usar iframes, por ejemplo, es por motivos de seguridad, por ejemplo, con pagos con tarjeta, y si hace lo que se sugiere aquí, probablemente se causará problemas).
alastair
10
Al hacer esto, siempre obtienes el calendario como usuario no conectado. Con el iframe html normal, el usuario vería su propio calendario personal si hubiera iniciado sesión en google, pero como su código PHP no puede conocer el ID de sesión de Google de los usuarios, no puede recuperar su calendario personal.
bdsl
72

Si el contenido del iframe no está completamente bajo su control o si desea acceder al contenido desde diferentes páginas con diferentes estilos, puede intentar manipularlo usando JavaScript.

var frm = frames['frame'].document;
var otherhead = frm.getElementsByTagName("head")[0];
var link = frm.createElement("link");
link.setAttribute("rel", "stylesheet");
link.setAttribute("type", "text/css");
link.setAttribute("href", "style.css");
otherhead.appendChild(link);

Tenga en cuenta que, dependiendo del navegador que utilice, esto podría funcionar solo en las páginas que se envían desde el mismo dominio.

Horst Gutmann
fuente
82
Vale la pena señalar que la misma política de origen dejará de funcionar si la página está en un dominio diferente.
ConroyP
2
En la misma línea de pensamiento pero más sucinto: <iframe onload="this.contentDocument.body.style.overflow='hidden';" />
Protector uno
Incluso Firefox se ha vuelto CORS
59
var $head = $("#eFormIFrame").contents().find("head");

$head.append($("<link/>", {
    rel: "stylesheet",
    href: url,
    type: "text/css"
}));
Rami Sarieddine
fuente
@deweydb lo usé específicamente con un iframe de dominio cruzado. ¡pero solo puedo rogarte tu perdón!
Rami Sarieddine
@ramie y lo probaste en varios navegadores? La mayoría de los navegadores arrojan un error de seguridad.
deweydb
Vi esta idea antes, pero agregué un enlace al archivo, es realmente genial y profesional ... + up
J.Tural
29

La mayoría de los navegadores manejan universalmente un iframe como una página HTML diferente. Si desea aplicar la misma hoja de estilo al contenido del iframe, solo consúltelo desde las páginas utilizadas allí.

colgado
fuente
22
@hangry pero ... ¿cómo?
Un hijo de Dios el
29

Aquí se explica cómo aplicar el código CSS directamente sin usar <link>para cargar una hoja de estilo adicional.

var head = jQuery("#iframe").contents().find("head");
var css = '<style type="text/css">' +
          '#banner{display:none}; ' +
          '</style>';
jQuery(head).append(css);

Esto oculta el banner en la página del iframe. ¡Gracias por tus sugerencias!

domih
fuente
1
¿Esto necesita un iframe con la identificación de iframe?
Jeremy
1
@jmalais Simplemente reemplace #iframecon #<id>o incluso iframepara seleccionar todos los iframes
Zeb McCorkle
3
Esto no parece estar funcionando para mí. El iframe que estoy tratando de editar tampoco tiene cabeza directamente debajo de él en el dom. Entonces, ¿eso significa que necesito acceder a la cabeza dentro del documento html o es algo que la función jquery find debería poder hacer por sí misma?
David A. French
1
Comprueba la consola y parece que se ha bloqueado el acceso al contenido del iframe debido a una discrepancia de dominio. Actualmente estoy usando Chrome y alojando mi entorno de desarrollo localmente.
David A. French
Excepción DOM no capturada: bloqueado un marco con origen, me da este error
priyanka patel
26

Si controla la página en el iframe, como dijo Hangy, el enfoque más fácil es crear un archivo CSS compartido con estilos comunes, luego simplemente vincúlelo desde sus páginas html.

De lo contrario, es poco probable que pueda cambiar dinámicamente el estilo de una página desde una página externa en su iframe. Esto se debe a que los navegadores han reforzado la seguridad en los scripts de dom de tramas cruzadas debido al posible uso indebido para la suplantación de identidad y otros hacks.

Este tutorial puede proporcionarle más información sobre secuencias de comandos de iframes en general. Acerca de las secuencias de comandos de marcos cruzados explica las restricciones de seguridad desde la perspectiva de IE.

Ceniza
fuente
19

Lo anterior con un pequeño cambio funciona:

var cssLink = document.createElement("link") 
cssLink.href = "pFstylesEditor.css"; 
cssLink.rel = "stylesheet"; 
cssLink.type = "text/css"; 

//Instead of this
//frames['frame1'].document.body.appendChild(cssLink);
//Do this

var doc=document.getElementById("edit").contentWindow.document;

//If you are doing any dynamic writing do that first
doc.open();
doc.write(myData);
doc.close();

//Then append child
doc.body.appendChild(cssLink);

Funciona bien con ff3 y ie8 al menos

Eric
fuente
77
¿Qué es myData y de dónde viene?
Tigran
1
@Tigran es paradynamic data
Akash
13

Si desea reutilizar CSS y JavaScript desde la página principal, tal vez debería considerar reemplazarlo <IFRAME>por un contenido cargado de Ajax. Esto es más amigable con SEO ahora cuando los robots de búsqueda pueden ejecutar JavaScript.

Este es un ejemplo de jQuery que incluye otra página html en su documento. Esto es mucho más amigable con SEO que iframe. Para estar seguro de que los bots no están indexando la página incluida, solo agréguela para no permitirrobots.txt

<html>
  <header>
    <script src="/js/jquery.js" type="text/javascript"></script>
  </header>
  <body>
    <div id='include-from-outside'></div>
    <script type='text/javascript'>
      $('#include-from-outside').load('http://example.com/included.html');
    </script> 
  </body>
</html>

También puede incluir jQuery directamente desde Google: http://code.google.com/apis/ajaxlibs/documentation/ , esto significa la inclusión automática opcional de versiones más nuevas y un aumento significativo de la velocidad. Además, significa que debe confiar en ellos para entregarle solo jQuery;)

Sorin
fuente
1
Cabe señalar que esta solución no funciona si el contenido de la página es dinámico de alguna manera.
nickdnk
12

Lo siguiente funcionó para mí.

var iframe = top.frames[name].document;
var css = '' +
          '<style type="text/css">' +
          'body{margin:0;padding:0;background:transparent}' +
          '</style>';
iframe.open();
iframe.write(css);
iframe.close();
Peter
fuente
1
Error:Uncaught TypeError: Cannot read property 'open' of undefined
KingRider
10

Ampliando la solución jQuery anterior para hacer frente a cualquier retraso en la carga del contenido del marco.

$('iframe').each(function(){
    function injectCSS(){
        $iframe.contents().find('head').append(
            $('<link/>', { rel: 'stylesheet', href: 'iframe.css', type: 'text/css' })
        );
    }

    var $iframe = $(this);
    $iframe.on('load', injectCSS);
    injectCSS();
});
David Bradshaw
fuente
1
El '$ this' debe ser un '$ (this)' para que funcione. Agradable cuando lo hace :)
h.coates
9

Mi versión compacta :

<script type="text/javascript">
$(window).load(function () {
    var frame = $('iframe').get(0);
    if (frame != null) {
        var frmHead = $(frame).contents().find('head');
        if (frmHead != null) {
            frmHead.append($('style, link[rel=stylesheet]').clone()); // clone existing css link
            //frmHead.append($("<link/>", { rel: "stylesheet", href: "/styles/style.css", type: "text/css" })); // or create css link yourself
        }
    }   
});
</script>

Sin embargo, a veces iframeno está listo en la ventana cargada, por lo que es necesario usar un temporizador .

Código listo para usar (con temporizador):

<script type="text/javascript">
var frameListener;
$(window).load(function () {
    frameListener = setInterval("frameLoaded()", 50);
});
function frameLoaded() {
    var frame = $('iframe').get(0);
    if (frame != null) {
        var frmHead = $(frame).contents().find('head');
        if (frmHead != null) {
            clearInterval(frameListener); // stop the listener
            frmHead.append($('style, link[rel=stylesheet]').clone()); // clone existing css link
            //frmHead.append($("<link/>", { rel: "stylesheet", href: "/styles/style.css", type: "text/css" })); // or create css link yourself
        }
    }
}
</script>

... y enlace jQuery:

<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.min.js" type="text/javascript"></script>
Zbigniew Wiadro
fuente
8

Otras respuestas aquí parecen usar enlaces jQuery y CSS.

Este código usa JavaScript vainilla. Crea un nuevo <style>elemento. Establece el contenido de texto de ese elemento como una cadena que contiene el nuevo CSS. Y agrega ese elemento directamente a la cabeza del documento del iframe.

var iframe = document.getElementById('the-iframe');
var style = document.createElement('style');
style.textContent =
  '.some-class-name {' +
  '  some-style-name: some-value;' +
  '}' 
;
iframe.contentDocument.head.appendChild(style);
2540625
fuente
7

Cuando dice "doc.open ()" significa que puede escribir cualquier etiqueta HTML dentro del iframe, por lo que debe escribir todas las etiquetas básicas para la página HTML y si desea tener un enlace CSS en su cabeza de iframe simplemente escriba un iframe con enlace CSS en él. Te doy un ejemplo:

doc.open();

doc.write('<!DOCTYPE html><html><head><meta charset="utf-8"/><meta http-quiv="Content-Type" content="text/html; charset=utf-8"/><title>Print Frame</title><link rel="stylesheet" type="text/css" href="https://stackoverflow.com/css/print.css"/></head><body><table id="' + gridId + 'Printable' + '" class="print" >' + out + '</table></body></html>');

doc.close();
Parham Fazel
fuente
7

uso puede intentar esto:

$('iframe').load( function() {
     $('iframe').contents().find("head")
     .append($("<style type='text/css'>  .my-class{display:none;}  </style>"));
  });
Ajay Malhotra
fuente
3
Si su iframe proviene de un origen diferente, el mecanismo CORS no permitirá esta solución.
Sr. Anderson el
¿Qué sucede si el iframe-url tiene CORS habilitado para todas las ubicaciones?
adrhc
6

No podrá diseñar el contenido del iframe de esta manera. Mi sugerencia sería utilizar secuencias de comandos en el servidor (PHP, ASP o una secuencia de comandos Perl) o buscar un servicio en línea que convierta una fuente a código JavaScript. La única otra forma de hacerlo sería si puede incluir un servidor.

Peter Mortensen
fuente
29
Cuidado cuando dices que algo no se puede hacer, cuando en realidad es difícil
Lathan
4

En caso de que tenga acceso a la página de iframe y desee que se aplique un CSS diferente solo cuando lo cargue a través de iframe en su página, aquí encontré una solución para este tipo de cosas

esto funciona incluso si el iframe está cargando un dominio diferente

consultar sobre postMessage()

plan es, envíe el CSS al iframe como un mensaje como

iframenode.postMessage('h2{color:red;}','*');

* es enviar este mensaje independientemente del dominio en el que se encuentre el iframe

y reciba el mensaje en iframe y agregue el mensaje recibido (CSS) al encabezado de ese documento.

código para agregar en la página de iframe

window.addEventListener('message',function(e){

    if(e.data == 'send_user_details')
    document.head.appendChild('<style>'+e.data+'</style>');

});
CodeRows
fuente
4

Como se escriben muchas respuestas para los mismos dominios, escribiré cómo hacer esto en dominios cruzados.

Primero, debes conocer la API de publicación de mensajes . Necesitamos un mensajero para comunicarnos entre dos ventanas.

Aquí hay un mensajero que creé.

/**
 * Creates a messenger between two windows
 *  which have two different domains
 */
class CrossMessenger {

    /**
     * 
     * @param {object} otherWindow - window object of the other
     * @param {string} targetDomain - domain of the other window
     * @param {object} eventHandlers - all the event names and handlers
     */
    constructor(otherWindow, targetDomain, eventHandlers = {}) {
        this.otherWindow = otherWindow;
        this.targetDomain = targetDomain;
        this.eventHandlers = eventHandlers;

        window.addEventListener("message", (e) => this.receive.call(this, e));
    }

    post(event, data) {

        try {
            // data obj should have event name
            var json = JSON.stringify({
                event,
                data
            });
            this.otherWindow.postMessage(json, this.targetDomain);

        } catch (e) {}
    }

    receive(e) {
        var json;
        try {
            json = JSON.parse(e.data ? e.data : "{}");
        } catch (e) {
            return;
        }
        var eventName = json.event,
            data = json.data;

        if (e.origin !== this.targetDomain)
            return;

        if (typeof this.eventHandlers[eventName] === "function") 
            this.eventHandlers[eventName](data);
    }

}

Usar esto en dos ventanas para comunicarse puede resolver su problema.

En las ventanas principales,

var msger = new CrossMessenger(iframe.contentWindow, "https://iframe.s.domain");

var cssContent = Array.prototype.map.call(yourCSSElement.sheet.cssRules, css_text).join('\n');
msger.post("cssContent", {
   css: cssContent
})

Luego, reciba el evento del Iframe.

En el iframe:

var msger = new CrossMessenger(window.parent, "https://parent.window.domain", {
    cssContent: (data) => {
        var cssElem = document.createElement("style");
        cssElem.innerHTML = data.css;
        document.head.appendChild(cssElem);
    }
})

Vea el tutorial completo de Javascript y Iframes para más detalles.

Supun Kavinda
fuente
3

Encontré otra solución para poner el estilo en el html principal como este

<style id="iframestyle">
    html {
        color: white;
        background: black;
    }
</style>
<style>
    html {
        color: initial;
        background: initial;
    }
    iframe {
        border: none;
    }
</style>

y luego en iframe hacer esto (ver la carga js)

<iframe  onload="iframe.document.head.appendChild(ifstyle)" name="log" src="/upgrading.log"></iframe>

y en js

<script>
    ifstyle = document.getElementById('iframestyle')
    iframe = top.frames["log"];
</script>

Puede que no sea la mejor solución, y ciertamente puede mejorarse, pero es otra opción si desea mantener una etiqueta de "estilo" en la ventana principal

jperelli
fuente
3

Aquí, hay dos cosas dentro del dominio.

  1. Sección iFrame
  2. Página cargada dentro del iFrame

Entonces, desea diseñar esas dos secciones de la siguiente manera,

1. Estilo para la sección iFrame

Puede diseñar usando CSS con ese nombre ido classnombre respetado . También puede diseñarlo en sus hojas de estilo principales también.

<style>
#my_iFrame{
height: 300px;
width: 100%;
position:absolute;
top:0;
left:0;
border: 1px black solid;
}
</style>

<iframe name='iframe1' id="my_iFrame" src="#" cellspacing="0"></iframe>

2. Diseñe la página cargada dentro del iFrame

Estos estilos se pueden cargar desde la página principal con la ayuda de Javascript

var cssFile  = document.createElement("link") 
cssFile.rel  = "stylesheet"; 
cssFile.type = "text/css"; 
cssFile.href = "iFramePage.css"; 

luego establezca ese archivo CSS en la respetada sección de iFrame

//to Load in the Body Part
frames['my_iFrame'].document.body.appendChild(cssFile); 
//to Load in the Head Part
frames['my_iFrame'].document.head.appendChild(cssFile);

Aquí, también puede editar la parte principal de la página dentro del iFrame de esta manera.

var $iFrameHead = $("#my_iFrame").contents().find("head");
$iFrameHead.append(
   $("<link/>",{ 
      rel: "stylesheet", 
      href: urlPath, 
      type: "text/css" }
     ));
K.Suthagar
fuente
2

Podemos insertar etiquetas de estilo en iframe. Publicado también aquí ...

<style type="text/css" id="cssID">
.className
{
    background-color: red;
}
</style>

<iframe id="iFrameID"></iframe>

<script type="text/javascript">
    $(function () {
        $("#iFrameID").contents().find("head")[0].appendChild(cssID);
        //Or $("#iFrameID").contents().find("head")[0].appendChild($('#cssID')[0]);
    });
</script>
Palanikumar
fuente
1
Esto no funciona Parece insertar la etiqueta de estilo correctamente, pero no hay contenido dentro ni identificación.
darylknight
2

var link1 = document.createElement('link');
    link1.type = 'text/css';
    link1.rel = 'stylesheet';
    link1.href = "../../assets/css/normalize.css";
window.frames['richTextField'].document.body.appendChild(link1);

Jeeva
fuente
1
He comprobado esta respuesta muchas veces, ¿qué hay richTextFieldaquí?
Kirankumar Dafda
1
es el nombre del iframe
Jeeva
No lo intenté, pero supongo que no lo hará porque está contra la caja de arena
Jeeva
1

Creo que la forma más fácil es agregar otro div, en el mismo lugar que el iframe, luego

haga que sea z-indexmás grande que el contenedor de iframe, para que pueda diseñar fácilmente su propio div. Si necesita hacer clic en él, simplemente use pointer-events:noneen su propio div, por lo que el iframe estaría funcionando en caso de que necesite hacer clic en él;)

Espero que ayude a alguien;)

Mateusz Winnicki
fuente
1

Hay un script maravilloso que reemplaza un nodo con una versión de iframe de sí mismo. CodePen Demo

ingrese la descripción de la imagen aquí

Ejemplos de uso:

// Single node
var component = document.querySelector('.component');
var iframe = iframify(component);

// Collection of nodes
var components = document.querySelectorAll('.component');
var iframes = Array.prototype.map.call(components, function (component) {
  return iframify(component, {});
});

// With options
var component = document.querySelector('.component');
var iframe = iframify(component, {
  headExtra: '<style>.component { color: red; }</style>',
  metaViewport: '<meta name="viewport" content="width=device-width">'
});
karlisup
fuente
1
¡¿Por qué querrías hacer eso?!
Dylan Watson el
1

Como alternativa, puede usar la tecnología CSS-in-JS, como la siguiente lib:

https://github.com/cssobj/cssobj

Puede inyectar objetos JS como CSS a iframe, dinámicamente

James Yang
fuente
0

¡Esto es solo un concepto, pero no lo implemente sin controles de seguridad y filtrado! De lo contrario, el script podría hackear su sitio.

Respuesta: si controla el sitio de destino, puede configurar el script del receptor como:

1) establezca el enlace iframe con el styleparámetro, como:

http://your_site.com/target.php?color=red

(la última frase está a{color:red} codificada por urlencodefunción.

2) configura la página del receptor de target.phpesta manera:

<head>
..........
$col = FILTER_VAR(SANITIZE_STRING, $_GET['color']);
<style>.xyz{color: <?php echo (in_array( $col, ['red','yellow','green'])?  $col : "black") ;?> } </style>
..........
T.Todua
fuente
3
Advertencia: esta es la inyección en su máxima expresión.
kano
1
Yap, no hace esto, salvo que desee una carga de robots de pen-test y niños de la escritura en su servidor =) ...
exside
1
He actualizado la respuesta ahora, con una advertencia de seguridad adicional
T.Todua
-18

Bueno, he seguido estos pasos:

  1. Div con una clase para sostener iframe
  2. Añadir iframea la div.
  3. En archivo CSS,
divClass { width: 500px; height: 500px; }
divClass iframe { width: 100%; height: 100%; }

Esto funciona en IE 6. ¡Debería funcionar en otros navegadores, verifique!

JannuD
fuente
99
necesita controlar div dentro de iframe, esto no lo hace
MSD