Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inject external HTML & JS into a page

I'm trying to build some small widgets tools that webmasters can embed in their websites.

Is there any way that the webmaster can simply load this tool by including a script like this?

<script src="http://mysite/widget.js"></script>

I tried to inject it by loading it in AJAX and then do an appendChild() on the body, it's working this way but the JS is not executed.

Content to be injected:

<div>one div</div>
<script>alert('some js')</script>

widget.js

function load(content) {
  var $body = document.body,
      $div = document.createElement("div");
  $div.id = "widget";
  $div.innerHTML = content; 
  $body.appendChild($div);
}

The content variable contains HTML and JS but the JS is not executed when injected.

like image 638
TheShun Avatar asked Mar 10 '23 15:03

TheShun


2 Answers

Since you don't know where you script will be added - to the head or body tag - and consequently when it will be executed, you need to make sure that DOM is ready, so:

 function load(content) {

    if (document.readyState === "complete" || document.readyState === "loaded") {
         // manipulate DOM
    } else {
       document.addEventListener('DOMContentLoaded', function() {
        // manipulate DOM
    })
}

Also, since scripts elements added using innerHTML don't get executed, you'd have to either use eval or better create a script like that:

var s = document.createElement('script');
s.text = "alert(\"hi\");"
document.getElementsByTagName('head')[0].appendChild(s);

However, there is no point to add <script> tag to execute some script in your template, since your script is already running when you're adding DOM so do all necessary setups there.

like image 50
Max Koretskyi Avatar answered Mar 16 '23 00:03

Max Koretskyi


UPDATE

please see Maximus' answer.


Right, the default native innerHTML doesn't execute js for safe sake.

jQuery or zepto $.fn.append() can satisfy your need, here is the reason (from zepto source code)

if (el.nodeName != null && el.nodeName.toUpperCase() === 'SCRIPT' &&
           (!el.type || el.type === 'text/javascript') && !el.src)
          window['eval'].call(window, el.innerHTML)

You can see in the function $.fn.append it will see if it's a script, if so it will use eval to run the content. You may need eval to, but be careful since some webmasters may deliver 'vice' code

like image 32
jiajianrong Avatar answered Mar 15 '23 22:03

jiajianrong