0

Is it possible to create a new HTMLDocument and evaluate JavaScript within it like:

let doc = document.implementation.createHTMLDocument()
let el = doc.createElement("script")

el.setAttribute("src", "https://gist.github.com/ayushn21/e6f101f2e28ae609446b9aec95d4e2da.js")
doc.head.appendChild(el)

After running that code, doc.body.innerHTML is still an empty string. Is there any way to run that remote script within the context of a new HTMLDocument object?

ayushn21
  • 305
  • 2
  • 7
  • Does this answer your question? [Dynamically add script tag with src that may include document.write](https://stackoverflow.com/questions/13121948/dynamically-add-script-tag-with-src-that-may-include-document-write) – uminder May 24 '20 at 14:06
  • Not really, that's exactly what I tried as shown in my code above but it didn't work :( – ayushn21 May 24 '20 at 14:17
  • Well, the approach you've used to create the document and link the javascript to it looks okay. How about showing the code you've used to actually run the code in the script? The helpdocs for `createHTMLDocument()` for instance, run it by setting it as the contents of an on-screen iframe. – enhzflep May 24 '20 at 15:37
  • That's exactly the code I was using. I was just typing it into the JS console in the developer tools. I did try adding it as an iframe to the document but that didn't render anything either. I'm just spiking an idea so I haven't used it in a proper script yet. Was wondering if this is even possible – ayushn21 May 24 '20 at 18:46

1 Answers1

3

Yes, use an <iframe> element. You'd need to insert it in the main document, but you can hide it with display:none and even remove it as soon as its inner document will have loaded.
Also, since the script you try to load is using document.write, it's better we also insert our <script> this way (some browsers will block the call from an async script).

Unfortunately, StackSnippet's null-origined iframes won't allow us to demonstrate it live here, but you can check this jsfiddle for a live demo.

function loadScript( source ) {

  return new Promise( (resolve, reject) => {

    const iframe = document.createElement( 'iframe' );
    iframe.style = "display:none"//"opacity:0;z-index:-999;position:fixed";
    iframe.src = "about:blank";
    document.body.append( iframe );

    const doc = iframe.contentDocument;
    doc.write( `<script src="${ source }" sandbox="allow-scripts"><\/script>` );
    doc.close();

    iframe.contentWindow.onload = (evt) => {
      iframe.remove();
      resolve( doc );
    };
    iframe.contentWindow.onerror = (evt) => {
      iframe.remove();
      reject( evt );
    };

  } );

}

loadScript( "https://gist.github.com/ayushn21/e6f101f2e28ae609446b9aec95d4e2da.js" )
  .then( doc => {
    // do whatever with the Document that has loaded your script
    const elem = doc.getElementById( 'gist40148058' );
    console.log( elem.textContent );
  } )
  .catch( console.error );
Kaiido
  • 123,334
  • 13
  • 219
  • 285