Sajjan Sarkar
Sajjan Sarkar

Reputation: 4198

Dynamically adding script to a div does not execute the script

PROBLEM:

Why does this not show the alert? And how can I make it so?

<script>
    function onSuccess() {
        var response= "<script> alert(1);</\script>";
        document.getElementById("xxx").innerHTML = response;
    }
</script>
<div id="xxx">existing text</div>
<button id="click" onclick="onSuccess();">click</button>

http://jsfiddle.net/7hWRR/1/

This is just a simplified version of my problem. In our application (in one very old module in particular) we use an ancient home-grown AJAX class which just innerHTMLs all AJAX responses.Traditionally we have only sent back HTML as AJAX response but I would like to execute JS in the success handler.I do not have access to the JS file so cannot modify the way the response is handled. I can only work with the fact that the success handler calls div.innerHTML='<my response>'

So stupid as it may be, I'm hoping for some help using these constraints!

SIMILAR LINKS:

Dynamically adding script element to a div does not execute the script

Dynamically added script will not execute

Upvotes: 0

Views: 334

Answers (1)

Renato Zannon
Renato Zannon

Reputation: 29941

Caveat: Here I'm assuming the <div> on which the results are inserted is known.

A possible solution is to use a MutationObserver (and the DOMNodeInserted event, to support IE 9 and 10) to watch said <div> for changes on its contents, and execute the code on any inserted <script> tags.

Example built upon your jsFiddle:

watchNodeForScripts(document.getElementById("xxx"));

function watchNodeForScripts(scriptRecipient) {
  if ('MutationObserver' in window) {
    // Prefer MutationObserver: https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
    watchUsingMutationObserver();
  } else {
    // Fallback to Mutation Events: https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Events/Mutation_events
    watchUsingDeprecatedMutationEvents();
  }

  function watchUsingMutationObserver() {
    var observer = new MutationObserver(function (mutations) {
      mutations.forEach(function (mutation) {
        var i, addedNodes = mutation.addedNodes;

        for (i = 0; i < addedNodes.length; i++) {
          handleAddedNode(addedNodes[i]);
        }
      });
    });

    observer.observe(scriptRecipient, {
      childList: true
    });
  }

  function watchUsingDeprecatedMutationEvents() {
    scriptRecipient.addEventListener("DOMNodeInserted", function (event) {
      handleAddedNode(event.target);
    });
  }

  function handleAddedNode(node) {
    // Don't try to execute non-script elements
    if (!(node instanceof HTMLScriptElement)) return;

    // Don't try to execute linked scripts
    if (node.src !== "") return;

    // Use 'new Function' instead of eval to avoid
    // the creation of a (undesired) closure
    fn = new Function(node.textContent);
    fn.call(window);
  }
}

Updated fiddle: http://jsfiddle.net/7hWRR/13/

Edit: Changed innerText to the more cross-compatible textContent.

Edit2: Don't execute code that isn't inside a <script> element.

Edit3: Don't execute scripts with the src attribute, and add mutation events fallback

Upvotes: 1

Related Questions