6

I'm using jsdom with jquery, and it's working just fine. However, I'm trying to modularize my code a bit so I don't repeat myself, so I made a basic function out of some jsdom code that takes in some html (DOM), tweaks it with jquery, and spits it back out. However, I'm unable to return my result and thus assign it to a calling var. I'm probably not returning in the right place, but I'm just not seeing the obvious if so. Could use a little help.

Here's the code:

function tweakIt(html_in){
  var jsdom = require('jsdom');
  jsdom.env({
    html: html_in,
    scripts: [
      '../public/javascripts/jquery-1.7.1.min.js',
    ],
    done: function(errors, window) {
      var $ = window.$;
      // do some jquery magic and manipulate the dom
      $('body').append('<div>foo</div>');

      console.log('Freshly Manipulated HTML: '+ $('body').html()); // it logs perfectly
      return $('body').html(); // this isn't returned to where I called tweakIt() from, why not?
    }
  });
}

var oldhtml = '<html><body><div>some text</div></body></html>';
var newhtml = tweakIt(oldhtml); // never gets set because nothing gets returned, why?

EDIT:

It was indeed an async issue, so here's how it should be done using a callback instead of a return:

function tweakIt(html_in, callback){
  var jsdom = require('jsdom');
  jsdom.env({
    html: html_in,
    scripts: [
      '../public/javascripts/jquery-1.7.1.min.js',
    ],
    done: function(errors, window) {
      var $ = window.$;
      // do some jquery magic and manipulate the dom
      $('body').append('<div>foo</div>');

      console.log('Freshly Manipulated HTML: '+ $('body').html()); // it logs perfectly
      callback($('body').html()); // instead of a return, pass the results to the callback
    }
  });
}

var oldhtml = '<html><body><div>some text</div></body></html>';
var newhtml = tweakIt(oldhtml, function(newstuff){
  console.log(newstuff); // woohoo! it works!
});
k00k
  • 17,314
  • 13
  • 59
  • 86

2 Answers2

5

I don't think you can do this using a return value, because done: is an async function. Try adding a callback to your tweakIt and get the new html by sending it as a parameter, e.g.

tweakIt(oldHtml, function(newHtml) {/*use the result here*/})

Bijou Trouvaille
  • 8,794
  • 4
  • 39
  • 42
  • ok, but how do I send the newHtml to the callback from `done:` ? – k00k Feb 23 '12 at 19:05
  • Nevermind, I figured it out. I'll edit my OP to show how it should be done. Thanks! – k00k Feb 23 '12 at 19:10
  • 1
    assuming you've named your second parameter in the tweakIt function `callback` then instead of your return statement in the done function you would do something like this: `callback ($('body').html())` – Bijou Trouvaille Feb 23 '12 at 19:10
  • Yep, that's what I did. Thanks again. – k00k Feb 23 '12 at 19:12
2

The new version of the JSDOM API no longer includes the 'done' callback option.

So I wrote a 'poor man's callback' to access a DOM variable only after it has been set.

function getSomeDOMVar(callback) {

    const jsdom = require("jsdom");
    const { JSDOM } = jsdom;

    const dom = new JSDOM(`
    <!DOCTYPE html>
    <html>
        <body>
            <script>
                var result; // globally scoped, undefined var, accessible in the node scope as dom.window.result

                function doSomething() {
                    // your code goes here
                }

                // then assign the data you want back to your the globally scoped var
                result = doSomething();

            </script>
        </body>
    </html>
    `, {
        runScripts: "dangerously",
        resources: "usable"
    });

    // poor man's callback
    function waitForVar() {
        if (typeof dom.window.result !== 'undefined') {
           cb(dom.window.result);
        }
    }
    setTimeout(waitForVar, 1000);
}

getSomeDOMVar(function(result) {
    console.log(result)
});
metaColin
  • 1,973
  • 2
  • 22
  • 37
  • Oh duh, I forgot that `dom.window` shares access between the node and browser environments. Thanks! – Civilian Jun 21 '19 at 22:31