jsdom.jsdom() doesn't process script elements (even with the correct features) #426

Closed
middlenamefirst opened this Issue Mar 27, 2012 · 10 comments

Projects

None yet

5 participants

@middlenamefirst

I have been unable to get jsdom to execute script elements within an HTML page. Below is some example code:

var jsdom = require('jsdom');

jsdom.defaultDocumentFeatures = {
  FetchExternalResources   : ['script'],
  ProcessExternalResources : ['script'],
  MutationEvents           : false,
  QuerySelector            : false
};

var htmlDoc = '<html lang="en-US">' +
'head>' +
    '<title>Test document</title>' +
    '<script>' +
        'var testVar = true;' +
    '</script>' +
    '<script src=\'http://code.jquery.com/jquery-latest.js\'></script>' +
    '<script>' +
    '</script>' +
'</head>' +
'<body id="mainPage">' +
'</body>' +
'</html>';

var document = jsdom.jsdom(htmlDoc);

var window = document.createWindow();

var elementsArray = window.document.getElementsByTagName('script');

console.log(elementsArray.length);
console.log(window.document.testVar);

window.close();

I would expect to be able to have access to the testVar variable via the document. But if you execute the above the value of window.document.testVar will be undefined. I am using jsdom.jsdom because I need the flexibility of fetching and executing external references. As an example I did include a script element which points to the latest jQuery library. As far as I can tell I am using the library exactly as documented. Hence why I suspect an issue. But it is indeed possible that I am doing something wrong.

@brianmcd
Collaborator

There are a few issues with your sample code:

  • You need to enable mutation events for ProcessExternalResources to work.
  • Global variables go on the window object, not the document, so it should be window.testVar.
  • External scripts are fetched asynchronously, so if you want to have access to window.$, you need to wait for either the jQuery script element's load event or the window's.

Here's the code with those changes:

var jsdom = require('jsdom');

jsdom.defaultDocumentFeatures = { 
  FetchExternalResources   : ['script'],
  ProcessExternalResources : ['script'],
  MutationEvents           : '2.0',
  QuerySelector            : false
};

var htmlDoc = '<html lang="en-US">' +
'head>' +
    '<title>Test document</title>' +
    '<script>' +
        'var testVar = true;' +
    '</script>' +
    '<script src=\'http://code.jquery.com/jquery-latest.js\'></script>' +
    '<script>' +
    '</script>' +
'</head>' +
'<body id="mainPage">' +
'</body>' +
'</html>';

var document = jsdom.jsdom(htmlDoc);

var window = document.createWindow();

var elementsArray = window.document.getElementsByTagName('script');

console.log(elementsArray.length);
console.log(window.testVar);
window.addEventListener('load', function () {
    console.log(typeof window.$ == 'function');
    window.close();
});
@middlenamefirst

Thank you for your comments. In regards to your first point:

  • You need to enable mutation events for ProcessExternalResources to work.

Why is this required? Can't external resources be processed without mutation events? By the way, I read the warning on W3 website which seems to state that it is deprecated in the Level 3 Event specification. Anyhow, I believe that is outside the scope of the issue I reported :). The docs (i.e. readme) don't say that MutationEvents must be '2.0' for scripts to execute.

Thanks for pointing out that globals go in the window object and not the document. I didn't understand this and should probably attempt to better understand the browser model and its scoping.

As well, thank you for recommending a way to listen for the load event.

@brianmcd
Collaborator

Why is this required? Can't external resources be processed without mutation events?

JSDOM uses the mutation events internally to detect when script nodes are inserted into the document (see here). I think the iframe and css code use them, too. It would be better to use an internal mechanism, and I think someone did a little work on that, but not sure where it ended up.

The docs (i.e. readme) don't say that MutationEvents must be '2.0' for scripts to execute.

It is mentioned in the MutationEvents section of the docs, but it probably isn't as clear as it should be ("Note: ProcessExternalResources requires this to be enabled.") It's definitely a weird and non-obvious requirement.

Thanks for pointing out that globals go in the window object and not the document. I didn't understand this and should probably attempt to better understand the browser model and its scoping.

As well, thank you for recommending a way to listen for the load event.

Glad to help!

@tmpvar
Owner
tmpvar commented Mar 27, 2012

Is window.testVar defined?

@middlenamefirst

Yes, it is defined under a script element in the code samples shown above.

@tmpvar
Owner
tmpvar commented Mar 27, 2012

oh wow, sorry about that. I didn't get notification emails for Brian's posts.

@brianmcd maybe another approach would work.. if ProcessExternalResources is detected then we automatically turn mutation events on. Thoughts?

@brianmcd
Collaborator

That seems reasonable, possibly coupled with a warning message. For reference, @fgnass mentioned not using MutationEvents for this stuff in #295. I also wonder how big the performance gain is with these disabled, since it looks like the option was added for performance reasons (#128). If it's a small gain, it might be best just to remove the MutationEvents option.

@fishbar
fishbar commented Apr 11, 2012

when Dynamicly creating a <script> tag , why it does not work? the script src will never loaded

@tmpvar

@fishbar
fishbar commented Apr 11, 2012

the following code , script node can't trigger onload event

    var snode = document.createElement('script');
    document.body.appendChild(snode);
    snode.onload = function(){ /** can not trigger the load event **/ }
    snode.src="http://some_jsonp_interface";
@domenic
Collaborator
domenic commented Oct 5, 2012

Closed in favor of #500.

@domenic domenic closed this Oct 5, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment