Jack Barham
Jack Barham

Reputation: 3219

Append styles in iframe with Javascript (vue.js)

Within the admin area of a website builder, I want the user to visually see the style changes before saving the updates.

I iframe the users website in so styles can be adjusted visually (over the top, so to speak). I have access and own both the admin site although they are different codebases and a users site can use a custom domain name, which I believe could be an issue.

I've set up Content-Security-Policy to allow the admin area to pull in the iframe and that works as expected. But I can't append a temporary <style> tag to the head.

Code:

<template>
    <div class="designer">
        <div class="designer-sidebar">
        <!-- style options -->
        </div>

        <div class="designer-content">
            <iframe id="the-iframe" class="designer-frame" src="http://thechildsite.com" frameborder="0" width="100%" height="100%">
                <p>Your browser does not support iframes.</p>
            </iframe>
        </div>
    </div>
</template>

<script>
    export default {
        data () {
            return {
                updating: false,
                device: 'desktop'
            }
        },
        ready () {
            var iframe = document.getElementById('the-iframe')
            var style = document.createElement('style')
            style.textContent =
                'body {' +
                '  background-color: lime;' +
                '  color: pink;' +
                '}'
            ;
            iframe.contentDocument.head.appendChild(style)
        }
    }
</script>

Upvotes: 1

Views: 11378

Answers (2)

Jack Barham
Jack Barham

Reputation: 3219

A collegue helped me find a solution based on postMessage. More details here: http://blog.teamtreehouse.com/cross-domain-messaging-with-postmessage

In the Vue.js app:

<div class="designer-content">
    <iframe ref="iframeContent" class="designer-frame" :src="liveSite" frameborder="0" width="100%" height="100%" @load="iframeStyles">
        <p>Your browser does not support iframes.</p>
    </iframe>
</div>

methods: {
    iframeStyles() {
        this.frame = this.$refs.iframeContent.contentWindow;

        const style =
            '.layout__wrapper {background:' + this.background + ';} ' +
            'body {color:' + this.text + ';} '
        this.frame.postMessage(style, '*');
    }
}

In the site you want to iframe in:

window.addEventListener('message', function(e) {
    if (e.origin === 'https://app.theparentapp.com') {
        var style = document.createElement('style');
        style.textContent = e.data;
        document.head.appendChild(style);
    }
});

Hope it helps someone else.

Upvotes: 1

techytuppers
techytuppers

Reputation: 39

Just replicated your code, for me the var iframe is always null?

Switching the ready() to mounted() populates var iframe with the iframe.

The style tag wasn't getting populated either so I tried this:

var css = document.createElement('style');
css.type = 'text/css';

var styles = 'body {' +
    '  background-color: lime;' +
    '  color: pink;' +
'}';

css.appendChild(document.createTextNode(styles));

iframe.contentDocument.head.appendChild(css);

It still doesn't actually append the style to the iframe (could be a permissions thing on my part?), but console logging out the css looks a bit more like it should? Hope this is at least a bit helpful!

Upvotes: 1

Related Questions