Sergey Gubenko
Sergey Gubenko

Reputation: 13

Loading widget on web page

I'm trying to make a loadable widget that would call an api when a button is clicked.

Widget page itself

widget.html

<html>
<head>
    <title>Widget</title>
</head>
<body>
<button id="test"></button>

<script>
    window.onload = init;

    function clicked() {
        // some logic
    }

    function init() {
        document.getElementById('test').addEventListener('click', clicked, false);
    }

    export {clicked}
</script>

</body>
</html>

The page it embeds on

index.html

<!DOCTYPE HTML>
<html>
<head>
    <title>Getting started</title>
</head>
<body>
<div>
    <div id="widget_box"></div>
    <script src="http://localhost:8080/js/widget.js" type="text/javascript"></script>
    <script type="text/javascript">wdgt.init('widget_box');</script>
</div>

</body>
</html>

js code that loads the widget

widget.js

var wdgt= {
    idBox: 'wdgt',
    url_widget: 'http://localhost:8080/pages/widgets/widget.html',
    url_style: 'http://localhost:8080/css/widget.css',

    init: function (id) {
        console.log("Begin Widget initialization");

        if (!id) {
            id = this.idBox;
        }

        if (document.getElementById(id)) {

            this.addStyle();
            try {
                var XHR = ("onload" in new XMLHttpRequest()) ? XMLHttpRequest : XDomainRequest;
                var xhr = new XHR();
                xhr.open('GET', this.url_widget, true);

                xhr.onload = function () {
                    if (this.response) {
                        document.getElementById(id).innerHTML = this.response;
                    }
                }

                xhr.onerror = function () {
                    console.log('onerror ' + this.status);
                }

                xhr.send();
            } catch (ignore) {
            }
        } else {
            console.log('The specified block id="' + id + '" is missing');
        }
    },

    addStyle: function () {
        style = document.createElement('link');
        style.rel = 'stylesheet';
        style.type = 'text/css';
        style.href = this.url_style;
        document.head.appendChild(style);
    }
}

If I open the widget in a separate window, the code attached to the button works. But when I try to embed it, nothing happens. Moreover, in this script, document.getElementById() returns null when trying to find the button.

The back is a simple Spring application that returns index.html

Upvotes: 1

Views: 1933

Answers (1)

Anton Rusak
Anton Rusak

Reputation: 936

TL;DR: It's not supposed to work. You should use iframe element to fetch and render external pages.

Your script widget.js is trying to render the contents of fetched widget.html into a <div> element.

The browser will to do the best, though it won't be able to create another DOM tree inside of <div>. Because when you feed a whole HTML page to innerHTML, you basically ask the browser to render a document inside a document. This is not allowed.

Still it will render the contents of you document's body. So you'll be able to see the button. But it won't execute any <script> tags. It's a safety measure to prevent XSS attacks. A couple of dirty ways to execute your code do exist, but I won't recommend them.

So what should you do to add an external widget into a page? The answer is neat: meet the <iframe>! This dude is created just for those things. For you case, you should rewrite index.html this way:

<!DOCTYPE HTML>
<html>
<head>
    <title>Getting started</title>
</head>
<body>
    <iframe src="http://localhost:8080/pages/widgets/widget.html" width="800" height="600"></iframe>
</body>
</html>

With iframes, you don't need to manually fetch your widget HTML. The browser will download and render it inside your iframe. Like a document inside a document with all the styles, embedded resources and scripts.

Please note, that when you embed something using iframe, security limitations apply. You won't be able to directly interact with your main page from inside the iframe and vice versa. It will be like a separate window inside you document. And it will have additional constraints. Please address to the docs.

Upvotes: 2

Related Questions